I have written this program in mplab v8.63 with c compiler C18 on the pic 18F4550. if I press the button on my picdem (S3) and there is a led connected on RB5 (with a resistor) then the led goes on. When i pressed on (S3) and there is a led on RB4, the the led will not be on (while I expected this) the same with RB3. Thit I forgot something to set?
The goal is for the red, green and blue LED separately measured with a LDR. but first I obviously must enable the ports RB5, RB4 and RB3.
#pragma code
/******************************************************************************/
void main (void)
{
TRISD = 0x00; // PORTD as output
TRISB = 0b00110000; // RB4 en RB5 as input
TRISA = 0x00; // RA output
RCONbits.IPEN = 0; // priority
INTCONbits.GIE = 1; // enable interrupt
INTCONbits.RBIE = 1; // interrupt portB on
while(1)
{
_asm sleep _endasm
}
}
#pragma interrupt ISR
void ISR (void)
{
//int red= 01110010011011110110111101100100;
int on = 1;
int off = 0;
if (INTCONbits.RBIF==1)
{
if(PORTBbits.RB5==0) // S3 pressed?
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
if(PORTBbits.RB4==0)
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
if(PORTBbits.RB3==0)
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
}
INTCONbits.RBIF = 0;
}
In the PIC TRIS registers, a bit set to 1 means input and 0 means output. You set TRISB = 0b00110000 which is all outputs on port B apart from RB5 and 4 which are inputs. If you need RB3 as a digital input as well, you need to set TRISB = 0b00111000. The bits are counted b7,b6,b5,b4,b3,b2,b1,b0.
However, you are expecting RB3 to trigger the RBIF which it does not as I explained here. So as you have it, only RB5 and 4 would trigger RBIF, but if RB3 was low at that time it would toggle D1 and A2. So move this code to main loop as I suggested until you get it working, then make it work on interrupt.
As ChrisJ says, some pins default to analog input unless you disable it so you wont get the digital input and interrupt on pin change that you are expecting using ADCON1
If the PBADEN bit is set, PORTB is initialized with RB4:RB0 set as analog inputs. If so you must use ADCON1 to disable analog inputs on RB4:RB0. The example program at the beginning of Section 10.2 of the manual wites 0x0E to ADCON1 to properly initialize PORTB.
See also Section 21.0 for an explanation of ADCON1 bits.
Related
The below is a code to enable interrupt INT0_vect when PD2 is pressed. The code doesn't ever execute the ISR, but always executes the counter loop from 0 to 9 on the 7 segment in PORT C in the main Function. Also tried the sei(); instead of enabling the I-bit in the SREG. Any ideas?
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define ISR_INT0_PD2 INT0_vect
ISR( ISR_INT0_PD2 ){
PORTC = 0x00;
_delay_ms(100);
}
int main(void)
{
int i=0;
DDRC=0xff; //portc is o/p
PORTC=0x00; // all pins on portc is 0 volt
MCUCR |= (1<<1); // falling edge
GICR |=(1<<6); // enable INT0 set pin6
SREG |=(1<<7); // set GIE pin7
while(1)
{
for(i=0;i<10;i++)
{
PORTC=i;
_delay_ms(1000);
}
}
}
[Below is the screenshot from the simulator I've been using]
For the interrupt to execute you need to call sei() defined in <avr/interrupt.h>.
https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html#gaad5ebd34cb344c26ac87594f79b06b73
EDIT: I was mistaken when I removed the line SREG |= (1 << 7) according to my link that is equivalent to sei(); After I wrote the below example I realized the registers are named differently on the ATMega32, so unfortunately the code below won't run.
Based on the data sheet for an ATMega32 your code should work, have you tried removing the for loop and driving PORTC to logic high (e.g. PORTC = 255)? I noticed when I was writing the code for the ATMega168 that the LED I was using was very dim with the code in your while loop. Also check that INT0 pin is connected via pull-up/pull-down resistor.
This is the code that works on my ATMega168, if I swap the register names to the ones used on the ATMega32 I end up with your code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define ISR_INT0_PD2 INT0_vect
ISR( ISR_INT0_PD2 ){
// If there is a logic change on any pin hold the pins attached to PORTC low for 100ms.
PORTC = 0;
_delay_ms(100);
// Relase PORTC to logic high.
PORTC = 255;
}
int main(void)
{
DDRC = 255; // Set all pins on PORTC to be outputs.
PORTC= 255; // Set all pins on PORTC to be logic high.
EIMSK = 0b00000001; // Set external interupt request enable.
EICRA = 0b00000001; // Set the external interrupt control register A to so that
// any logical change on INT0 generates an interrupt request.
sei(); // Set global interupts enable.
while(1)
{
PORTC=255; // Blink the entire PORTC bank.
_delay_ms(20);
PORTC=0;
_delay_ms(20);
}
}
The program is suppose to detect what switch is turned on and from there change the traffic light to either red, yellow, or green. I'm stuck and I don't know if my initialization are right for Port A and Port E. we are only suppose to change the DEN and DIR. When I run it on debug, nothing happens. It's supposed to start with the red light.
// Input/Output:
// PE2 - Red
// PE1 - Yellow
// PE0 - Green
// PA3 - South
// PA2 - West
// Preprocessor Directives
#include <stdint.h>
#include "tm4c123gh6pm.h"
// Global Variables
uint8_t In0, In1;
uint8_t Out;
// Function Prototypes - Each subroutine defined
void Delay(void);
int main(void) {
// Initialize GPIO on Ports A, E
unsigned long int delay;
SYSCTL_RCGC2_R |= 0x11; // enables clock for e and a
delay = SYSCTL_RCGC2_R;
GPIO_PORTE_AMSEL_R = 0x00; //diables analog
GPIO_PORTE_AFSEL_R = 0x00; //disable alternate function
GPIO_PORTE_PCTL_R = 0x00000000; //enable GPIO
GPIO_PORTE_DEN_R = 0x07; // Ports E0-2
GPIO_PORTE_DIR_R = 0x07; //inputs PE0-2
GPIO_PORTA_AMSEL_R = 0x00; //disables analog
GPIO_PORTA_AFSEL_R = 0x00; //disable alternate function
GPIO_PORTA_PCTL_R = 0x00000000; //enable GPIO
GPIO_PORTA_DEN_R = 0x0C; // Ports A2 and A3
GPIO_PORTA_DIR_R = ~0x0C; //outputs PA2 and PA3
// Initial state: Red LED lit
Out = (GPIO_PORTE_DATA_R & 0x04); //red starting
while(1) {
In0 = GPIO_PORTA_DATA_R&0x08 ; // Read value of south
In1 = GPIO_PORTA_DATA_R&0x04 ; // read west
// Check the following conditions and set Out appropriately:
// If south is enabled and red LED is on, then red LED turns off and green LED turns on.
// If west is enabled and green LED is on, then green LED turns off and yellow LED turns on.
// If yellow LED is on, then yellow LED turns off and red LED turns on.
if ((In0 == 0x08) & ((Out&0x04) == 0x04)){ // south and red
GPIO_PORTE_DATA_R = 0x01; //green light turns on
Delay();
Out = GPIO_PORTE_DATA_R;
}
if ((In1== 0x04) & ((Out&0x01) == 0x01)){ //west and green
GPIO_PORTE_DATA_R = 0x02; //yellow light turns on
Delay();
Out = GPIO_PORTE_DATA_R;
}
if ((Out&0x02) == 0x02){ //if yellow
GPIO_PORTE_DATA_R = 0x04; //red light turns on
Delay();
Out = GPIO_PORTE_DATA_R;
}
Out = GPIO_PORTE_DATA_R;
// ??? = Out; // Update LEDs based on new value of Out
}
}
// Subroutine to wait about 0.1 sec
// Inputs: None
// Outputs: None
// Notes: the Keil simulation runs slower than the real board
void Delay(void) {
volatile uint32_t time;
time = 727240*200/91; // 0.1sec
while(time) {
time--;
}
}
are you sure about the direct register modifications?, try to double check if you are flipping the correct bits in registers. Also check what these hex values you writing are correct. Take a multi-meter and measure the voltage on the mentioned GPIO, does it change as supposed?, if not then either your electric circuitry has bug, or the inits().
My Problem is that I have a digital Input initialized on PORTB.0. On that Pin I have a button and a 100k Pull-down Resistor. When I run my Pic with literally nothing ( void main() {while(1){}} ) the Voltage level on this pin works as I want it to (0 to 3.3V). But after I test my programm and initialize as shown in the Code below, it's range goes from 2.8 to 3.3V.
I am using a PIC18LF2520 for my graduation work. I am Programming in MPlab V5.10 with the XC8 1.45 Compiler. I just want an Interrupt for my Button which lies on the RB0 Pin of the uP. The initialisations I made will be shown on the Code down below. I already worked a lot with this particular PIC, but I have never had such an Error before.
void main() {
init();
while(1) {
/*
if(isEXT0Set()) { //If the Button gets pushed
EXT0Int(); //Interrupt Function call
}
*/
}
}
void init() {
//PORT Definitions
TRISA = 0x00;
TRISB = 0xC3;
TRISC = 0x00;
//Oscillator Settings
OSCCON = 0x7F; //8Mhz; Internal Oscillator; INTOSC stable
//Interrupt Settings
INTCON = 0xF0; //Global Enable; Peripherals Enable; TMR0 Enable; INT0 Enable
INTCON2 = 0x64; //INT0 rising Edge; INT1 rising Edge; TMR0 High Priority
INTCON3 = 0x08; //INT1 Enable
ADCON1 = 0x0F; //set all Inputs to Digital ones.
//Timer Settings
T0CON = 0x86; //16-Bit; LtoH; 1/128
T1CON = 0x31; //2 8-Bit; FOSC/4; 1/8
T2CON = 0x07; //Postscaler 1/1; Prescaler 1/16;
TMR0H = 0xE1; //Setting TMR0 to 1 Second
TMR0L = 0x7B;
TMR1IE = 1;
TMR2IE = 0;
}
I expect that when I push the button that INt0IF will be set, but that doesn't happen. I testes it on the Hardware, the result is decribed up above. I hope someone sees what I missed. Thanks in advance. :D
PIC controller has internal pull-up resistors on PORTB and usually, they're much less than 100kOhm. You can turn all of them just changing INTCON2 initialization to:
INTCON2 = 0xE4;
I'm trying to make a PWM output with this code, but apparently something is missing. Because when I check the Logic Analyzer in MPLab, nothing happens on CCP2 output. I'm working with the pic18f25k80.
void main() {
// Set up PWM
CCP2CON = 0b00001100; // Enable PWM on CCP2, bits 5:4 are LSB part of duty cycle
CCPTMRS = 0b00000000; // Use timer2 for all CCP modules
CCPR2L = 31; // MSB part of duty cycle
TRISC = 0b00000000; // Set port C as output
PORTC = 0; // Clear port C
// Set Up timer2
PR2 = 249; // PWM period = (PR+1)*4*Tcy = 1ms
T2CON = 0b00000100; // Enable TMR2 with prescaler = 1
while(1)
{
}
}
I expect when
TMR2 = PR2, CCP2 output toggles and timer is reset
further, TMR2 = CCPR2L (duty cycle), CCP2 output toggles
TMR2 keeps counting until step 1 is reached.
I suppose this is what should happen automatically. I mean I don't have to write the code for that, because that's THE function of the PWM module, right?
What am I missing?
Additional info:
TMR 2 is counting.
When I add PORTC = 0xFF; in the while loop and debug the code again. All signals on Port C are set, except RC2 (RC2 = corresponding output of CCP2).
When I try the same code for CCP3, all signals on port C are set, except RC2 and RC6 (RC6 = corresponding output of CCP3).
When I replace PORTC = 0xFF; with PORTCbits.CCP2 = 1;, only RC1 is set high.
Does this last bullet mean that the CCP2 is muxed with RC1 instead of RC2?
I have an atmega168a chip. I use Counter 0 to toggle PORTC by using ISR(TIMER0_COMPA_vect) and ISR(TIMERB_COMPA_vect) interrupt sub-routines. I would like to activate the 16-bit timer when if condition is true. So, I use TIMSK1 = (1<<OCIE1A), but this line calls ISR(TIMER1_COMPA_vect) interrupt instantly where I want 16 bit timer to be interrupted only when the counter reaches to OCR1A value. How can I activate the 16-bit timer on the run-time without causing an instant interrupt?
here is my code:
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
volatile uint8_t counter;
int main (void){
DDRC = 0xFF; //sets PORTC as output
PORTC = 0xFF; //initial output value
/*COUNTER0 settings*/
TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B)); // Enable Interrupt TimerCounter0 Compare Match A & B
TCCR0A = (1<<WGM01); // Mode = CTC
TCCR0B = (1<<CS01) | (1<<CS00); // Clock/64, 1/(8000000/64)= 0.000008 seconds per tick
OCR0A = 200; // 0.000008 *230 = 1.6 ms
OCR0B = 100; // 0.8 ms
/*16bit timer - counter1 settings*/
TIMSK1 &= ~(1<<OCIE1A); // Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12)); // Clock/1024, 1/(8000000/1024) = 0.000128 seconds per tick, Mode=CTC
OCR1A = 40; // 0.000128*40 ~= 5.12 milliseconds
sei(); //interrupts are globally switched on
counter =0;
while(1){
if(counter >= 4){
TCNT1 = 0; // clear the counter 1
TIMSK1 = (1<<OCIE1A);// Enables the interrupt for Counter 1,(TIMER1_CMPA_vect)
TIMSK0 &= ~((1<<OCIE0A) | (1<<OCIE0B)); //disables the Counter 0's interrupts
counter = 0;
}
}
return 0;
}
ISR(TIMER0_COMPA_vect){ //1.6ms
PORTC = 0xFF;
counter++;
}
ISR(TIMER0_COMPB_vect){ //0.8 ms
PORTC = ~PORTC;
}
ISR(TIMER1_COMPA_vect){ // 5.2 milisecond interrupt
PORTC = 0x00;
TCNT0 = 0; //clear the counter of counter0
// TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B)); //Enable the Counter 0 interrupts
// TIMSK1 &= ~(1<<OCIE1A);// Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
}
here is an oscilloscope output that shows why I don't want the interrupt to be set instantly, because it set the signal 0 instantly.
I think the problem might be that in CTC mode, interrupt is generated when OCF1A flag is set (in TIFR). Since your timer is always running, just not generating interrupts, it sets OCF1A flag, which never gets cleared. On page 142 in the datasheet it says:
OCF1B is automatically cleared when the Output Compare Match B
Interrupt Vector is executed. Alternatively, OCF1B can be cleared by
writing a logic one to its bit location.
This means that when you set up timer 1, you also need to clear OCF1A:
TIFR1 &= ~(1<<OCF1A)
However, I think you can do better. You could just stop the timer when not needed, and start it when you do, instead of twiddling the TIMSK and having timer 1 run always. If you set TCCR1B to zero, that clears CS12, CS11, and CS10, which, according to the datasheet means "Timer stopped." Then, when your counter reaches 4 you can turn on timer1 as you have it above:
TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12));
If you do this, you shouldn't need to turn timer 1 interrupts on and off: just leave them on, and only turn the counting on when you need it.
Also I am wondering if it is actually necessary to fire off two interrupts to toggle pins on PORTC? Are you not using PWM for that because it doesn't give you the pulse lengths precisely enough?