I wrote a program for the PIC 16f690 microcontroller after noticing my programs seemed to be exiting an endless loop.
There are LEDs on pins 3,4,5 of PORTC.
#include <xc.h>
#define _XTAL_FREQ 4000000
void main(void) {
TRISC = 0x00;
PORTC = 0b00111000;
while (1)
{
__delay_ms(1000);
PORTC = 0x00;
}
}
As far as I understand, the LEDS should be on for ~1sec, and then be off forever. Instead they keep blinking every second.
I wrote this in MPLABX and programmed using PICkit3 and C8 compiler.
You are likely being bitten by the watchdog. Disable the watchdog for your tests or clear it before it reset the MCU.
I had the same issue but in place of leds, I was using serial communications. while(1) was not really a loop forever and execute the stuff between brackets.
As soon as I disabled the watchdog, only the stuff inside the brackets was called after the first iteration.
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT enabled)
I was also observing trash after every uart packet and that went away with the watchdog disabled.
FYI pic 16f1708.
This caused some erratic behavior since the uart initialization functions, IO setup, and oscillator divider were called every loop iteration.
Related
Implemented interrupt function on TIMER1 on PIC16F877A MCU on PIC-DIP40 development board. Configured the timer Prescaler to 1 and auto preload value to 55536 so that the interrupt time is 0.01s. Using a counter of 100 to count 1s interval. The Fosc is 4Mhz. So my calculation is :
interrupt time = (4 / Fosc) * (65536 - 55536) = (4/4000000) * (65536 - 55536) = 0.01 s
And used a counter of 100 to generate a 1s interval.
Currently, I have no oscilloscope to test the actual 1s interval so, I am blinking an LED (LED2) on the timer interrupt and another LED (LED1) on the same time interval 1s using __delay_ms(1000); function.
So as expected the two LEDs will blink synchronously (Turn ON and OFF at the same Time). But for some first iterations, they blink synchronously. After some iterations, there is a clear difference in time between their blinking time (Turning ON and OFF time). After several minutes the difference is almost 1s. So the timer interrupt is not working as expected.
So is my calculation wrong for interrupt time or I am missing something in the timer1 configuration?
The overall goal is to generate a 1s time interval and test the validity without using an oscilloscope.
Here is my code :
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#include <xc.h>
#include <pic16f877a.h>
#define _XTAL_FREQ 4000000
#define LED1_ON PORTDbits.RD7 = 0
#define LED1_OFF PORTDbits.RD7 = 1
#define LED2_ON PORTDbits.RD6 = 0
#define LED2_OFF PORTDbits.RD6 = 1
#define LED2_TOGGLE PORTDbits.RD6 = ~PORTDbits.RD6
uint16_t preloadValue = 55536 ;
uint16_t counter = 0 ;
uint16_t secCounter1 = 100 ;
void io_config() {
TRISD &= ~((1 << _PORTD_RD7_POSITION) | (1 << _PORTD_RD6_POSITION)) ; //RD7 and RD6 are output LEDs
}
void timer1_init(){
TMR1 = preloadValue ; //loading the preload value
T1CON &= ~((1 << _T1CON_T1CKPS1_POSN) | (1 << _T1CON_T1CKPS0_POSN) | (1 << _T1CON_TMR1CS_POSN)) ; //prescalar is 1 clock is Fosc
T1CONbits.TMR1ON = 1 ; //timer 1 is ON
LED2_ON ;
}
void interrupt_en_configure(){
INTCON |= (1 << _INTCON_GIE_POSITION) | (1 << _INTCON_PEIE_POSITION) ; //global and peripheral interrupt on
PIE1 |= _PIE1_TMR1IE_MASK ; //timer 1 interrupt enable
TMR1IF = 0 ; //clearing interupt flag
}
void __interrupt() ISR(){
if(TMR1IF){
counter ++ ;
if (counter == secCounter1){
counter = 0 ;
LED2_TOGGLE ;
}
TMR1 = preloadValue ;
TMR1IF = 0 ;
}
}
void main(void) {
io_config();
interrupt_en_configure() ;
timer1_init() ;
while (1) {
LED1_ON ;
__delay_ms(1000);
LED1_OFF ;
__delay_ms(1000);
}
}
You should not expect them to operate synchronously for the following reasons:
First you do not know how __delay_ms() is implemented or any "promises" of precision it may make - it is certainly not using TIMER1, because you are controlling that. In fact the documentation gives some implementation details, and you really cannot expect precision.
Secondly, even if __delay_ms() were both accurate and synchronous, you are invoking it in a loop with the software overhead of the loop, function call and whatever you are doing to toggle the LED. That is a few cycles on every iteration that do not affect the interrupt interval which is locked to the hardware, and independent of the software timing.
The issue of precision of __delay_ms() is in fact addressed in this Microchip support article where it starts:
If an accurate delay is required, or if there are other tasks that can be performed during the delay, then using a timer to generate an interrupt is the best way to proceed.
In this case you should trust your code over the library provided delay which is intentionally crude (because it does not use up a valuable H/W timer resource).
__delay_ms() delays by running an empty loop, but it commonly cannot be exact. You would need to look into the actual machine code that is run to calculate the real delay. BTW, this is not rocket science and a great learning task. (Been there, done that.)
Now the rest of your loop (LED switching, looping) adds to this. Therefore, your pure software driven blinker is not exact.
However, your interrupt driven blinker is not, too. You reset the timer at the end of the ISR, after several clock cycles have passed. You need to take this into account, and don't forget the interrupt latency. Even worse, depending on the conditional statement, the reset happens at different times after the timer overflow.
Producing exact timing is difficult, especially with such a simple device.
The solution is to avoid software at all for the reset of the timer. Please read chapter 8 of the data sheet and use the capture/compare/PWM module to reset the timer on the appropriate value.
The worst thing that could still happen is some jitter, just because the ISR might have different latencies. But the timer runs as exactly as your system's crystal. In average your LED will blink correctly.
Anyway, if your timing requirements are not that hard, consider to live with some inaccuracy. Then use the most simple solution you like best.
I'm trying to put my AtTiny 13 to sleep and wake it up with interrupt. It does go to sleep but it never wakes up. The whole code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <avr/sleep.h>
#define RED (1<<PB4)
#define RED_HIGH PORTB |=RED
#define RED_LOW PORTB &= ~RED
#define RED_TOG PORTB ^= RED
#define BUTTON 1<<PB1
volatile static bool is_sleeping;
ISR(INT0_vect)
{
RED_TOG;
is_sleeping = true;
}
int main(void){
GIMSK |= 1<<INT0;
MCUCR |= 0<<ISC00 | 1<<ISC01;
sei();
DDRB |= RED;
DDRB &= ~BUTTON;
PORTB |= BUTTON;
RED_HIGH;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(1){
if(is_sleeping){
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
}
}
}
According to sleep.h data it should work. Any ideas?
Update: it does not have problems with waking up from IDLE mode;
Assuming no hardware wiring issues your code is working as follow: after booting your LED is turned on and while-loop is idling since is_sleeping is initially set to zero. First INT0 interrupt toggles LED (turns it off) and sets is_sleeping flag so that while-loop will enter to guarded code in next turn. That code turns MCU to sleep on sleep_mcu() line. Once INT0 interrupt awaits MCU it continues from last place i.e. it goes back to sleep because is_sleeping is still set! (and in your code is never turned back to false). It means that right after MCU awakes it goes to sleep almost instantly and is off until next INT0 interrupt.
So to answer you question it never wakes up I would say: it does wake up but for really short moment. If you measure current (e.g. with scope and shunt resistor) you would observe spikes when it wakes and goes asleep immediatelly.
Regardless of you main problem pay attention to code quality. Embedded programming is far from forgiving and you may stuck for hours on trivial mistakes. For instance always be defensive with macro definitions. You defined BUTTON as 1<<PB1 without parens. Difference is that later on you get hit by operators precedence. For instance using DDRB &= ~BUTTON you do not have what you expect. Your right side expression unfolds to 11111100 (because ~1<<1 is 11111110 << 1) while you wanted 11111101 (because ~(1<<1) is ~ 00000010). If you use PB0 for something else you would expect unwanted behavior.
Also when copying sample code make sure you understand what it stands for. The sample in sleep.h relies on using both sei and cli complementary. In your code you only insist on re-enablig interrupts in loop, which is pointless here.
EDIT: Since you claim wake up works in "idle" mode, then your next issue is that you expect system to wake up on falling edge by setting pair (ISC00,ISC01) to (0,1) in MCUCR. See datasheet chapter 9.2 that says "Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock" while table in chapter 7.1 says that Clk_I/0 not present in power-down mode. Your only choice it to make INT0 external interrupt to trigger on low level by setting pair (ISC00,ISC01) to (0,0) in MCUCR.
I have 2 questions.
The first: I have a problem in the behavior of this code; when I run it in Proteus the program make flasher "repeat the code in the main function"
what should I do?
This is the code:
#include <p18f452.h>
#include <delays.h>
#include <io.h>
void main ()
{
TRISC=0x00;
PORTC=0xff;
Delay1KTCYx(900);
PORTC=0x00;
Delay1KTCYx(900);
while(1)
{
}
}
The second question: what is the proper delay function I can use? and how can I measure the delay time?
Is the watchdog disabled in simulation ? If it is enabled it will cause the repetition of the program.
Try adding this line after the includes.
#pragma config WDT = OFF
You only have code to generate one flash. Move the flash and delays into the loop:
for(;;)
{
PORTC = 0xff;
Delay1KTCYx(900);
PORTC = 0x00;
Delay1KTCYx(900);
}
Measuring roughly can be made manually by timing N flashes with a stopwatch. It's of course easier to use a measurement intrument (an oscilloscope is nice for this) if you have it.
Also, since your duty cycle is 50%, you can simplify the code:
PORTC = 0;
for (;;)
{
PORTC = ~PORTC;
Delay1KTCYx(900);
}
This uses bitwise not (~) to invert the bits of PORTC, which will make them toggle from one to zero and vice versa. Setting the entire port to 0 before the loop makes sure all pins are at a known state.
I'm new to microcontrollers and picked out a PIC10f204. I am using MPLAB XIDE with the free XC8 C compiler.
I'm trying to slowly learn right now. All i am trying to do is set GPIO bit 0, which is the GP0 pin out, to out put high.
So far my code looks like this but I am not measuring 5V from the pin GP0 or any of the other GPIO pins, except GP3.
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config WDTE = OFF // Watchdog Timer (WDT disabled)
#pragma config CP = OFF // Code Protect (Code protection off)
#pragma config MCLRE = OFF // Master Clear Enable (GP3/MCLR pin fuction is digital I/O, MCLR internally tied to VDD)
int main(void) {
OSCCAL= 0x00;
TRISGPIO = 0x00;
GPIO= 0xFF;
return 0; // we should never reach this
}
Any help would be much appreciated.
Thanks!
A couple changes might help:
1) Try adding an infinite loop in your code. The program on a microcontroller should follow a general structure of initialization functions followed by an infinite loop.
2) Looking at the datasheet for the PIC10F204, it says that pin 3 is input only. Try modifying as follows: TRISGPIO = 0x08;
I modified your code below to illustrate this. Hopefully this works for you.
int main(void) {
// PIC Initializations should go here
OSCCAL= 0x00;
CMCON0 = 0x51;
TRISGPIO = 0x08;
GPIO= 0xFF;
while(1) {
// Program main loop (should never end)
}
return 0; // we should never reach this
}
I'm programming an Intel 8051 (C8051F312) microcontroller. I just want to make a blinking led program by using interrupts. It compiles, but the led does not blink. Any ideas I would appreciate. Thank you!
My code is:
#include <C8051F310.H>
#include <stdio.h>
sbit led = P2^7; //LED connected to D7 of Port2
void timer(void) interrupt 1 //interrupt no. 1 for Timer 0
{
led=~led; // toggle LED on interrupt
TH0=0xFC; // initial values loaded to timer
TL0=0x66;
}
void main(void)
{
TMOD = 0x01; // mode1 of Timer0
TH0 = 0xFC; // initial values loaded to timer
TL0 = 0x66;
EA = 1; // global interrupt enable
ET0 = 1; // enables Timer 0 interrupt
TR0 = 1; // start timer
while(1); // do nothing
}
Like Mike Jablonski above said, you need to knock down your interrupt rate. Your original code is interrupting at 3.0625MHz / 12 / 922 ~= 277Hz. Part of your CKCON addition disables scaling to the timer (by setting T0M), so now you're interrupting at ~3.3kHz. You won't be able to see that.
Seeing anything presumes that you have a functional circuit. You're not configuring your output pin. You said your LED is on "now", but hopefully not meaning that it wasn't before. That wouldn't make a lot of sense since you didn't change anything about what the pin is doing.
Get rid of your CKCON line to keep the /12 scaling, and reload TH0 and TL0 with 0x00 on interrupt. That will get you interrupting at a little less than 4Hz, much more visible.
Make that pin push-pull:
P2MDOUT = 0x80;
XBR1 = 0x40;
Start reading the datasheet to your micro.