I have been working on a simple project on AVR microcontroller atmega16 on proteus simulation.
The project is:
When I push the push button it increments the seven segment to number 9 and after that if the we pushed the button again it overflows and return again to 0.
When I change the (flag=1;) position at while loop code it gives me different outputs and by this I mean when I push the push button it didn't response after the pushing.
It may require another pushing to the button to increment the seven segment proteus simulation
The only code that worked properly is when set flag=1; before exiting (the second if and else conditions)
So my question is what actually happened when I change the flag=1; statement position at the code.
#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;
void INT2_Init(void){
DDRB&=~(1<<2);
SREG|=(1<<7);
GICR|=(1<<INT2);
MCUCSR|=(1<<ISC2);
}
ISR(INT2_vect){
flag=0;
GIFR|=(1<<INTF2);
}
int main(void){
DDRC=0x0f;
PORTC=0;
DDRB&=~(1<<2);
INT2_Init();
while(1){
if(flag==0){
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
flag=1;
}
}
If some variable changes in main code and interrupt, you have to mark it volatile. Otherwise, the compiler may decide to cache variable value and omit its reading inside a loop.
volatile char flag=1;
Second, pay attention when and where your flag variables are changed. E.g.:
while(1){
// at this point flag is 0, comparison failed
if(flag==0) {
...
}
// Interrupt happens here, changing flag to 0
// Then flag is immediately reset, thus flag change is missed in the loop
flag=1;
}
Instead consider to use such a pattern:
while (1) {
cli(); // disabling interrupts ensuring no interrupt happens inside
if (flag==0) {
...
}
flag = 1;
sei(); // re-enable interrupts
}
or, in this case, it can be simpler
while (1) {
if (flag==0) {
flag = 1; // Reset flag only if it set, right after comparison
...
}
}
It is not entirely clear to me what you are asking. Do you just struggle to understand the code? Do my comments help?
#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;
void INT2_Init(void){
DDRB&=~(1<<2);
SREG|=(1<<7);
GICR|=(1<<INT2);
MCUCSR|=(1<<ISC2);
}
// sets flag to 0 on external interrupt (button press)
ISR(INT2_vect){
flag=0;
GIFR|=(1<<INTF2);
}
int main(void){
DDRC=0x0f;
PORTC=0;
DDRB&=~(1<<2);
INT2_Init();
while(1){
// if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
if(flag==0){
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
// reset the flag to 1 to prevent num from being incremented continuously
flag=1;
}
}
I would suggest to move flag=1; inside the if to make the intent clear that the statement should only execute once with every button press.
while(1) {
// if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
if(flag==0){
// reset the flag to 1 to prevent num from being incremented continuously
flag=1;
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
}
It is also advisable to use self descriptive variable names. Like buttonWasPressed instead of flag for example. That makes code like that much easier to read.
Related
On this circuit and code, I tried to make a counter that when no one pass through (for example it is a passage at the metro station), there will read 1 value at RC7 lead of the processor. If someone pass through the signal change to 0. And program will count the how many people pass away over there and show the number of people on the 7-Segment LCD until 10. When 10 people pass through of the passage, the LED (D1) will be blinking for 1 seconds.
I tried to write a code about this algorithm, and when I load it to Pic18F45K22 but, it is not working. Proteus show error message like,
[PIC18] PC=0x0000. $MCLR$ is low. Processor is in reset. [U1]
The circuit that I designed given at below Figure 1:
The solutions that I tried:
I used pull-up resistors. It did not work.
We describe the frequency value in Micro C code. It did not work.
And the algorithm given at below:
#include <xc.h>
#define _XTAL_FREQ 4000000
unsigned char x=0;
void MSDelay(unsigned int);
void main()
{
TRISC=0xff;
TRISA=0x00;
while(1)
{
if (PORTC==0)
{
x++;
MSDelay(200);
}
if (x==1)
{
PORTA==0x3f;
}
if (x==2)
{
PORTA==0x06;
}
if (x==3)
{
PORTA==0x5b;
}
if (x==4)
{
PORTA==0x4f;
}
if (x==5)
{
PORTA==0x66;
}
if (x==6)
{
PORTA==0x6d;
}
}
}
void MSDelay(unsigned int itime){ //for delay
unsigned int i;
unsigned int j;
for(i=0;i<itime;i++){
for(j=0;j<165;j++){
}
}
}
So I make the answer for you:
The error ist here:
if (x==1)
{
PORTA==0x3f;
}
If you want assign a value you need = and not ==
if (x==1)
{
PORTA=0x3f;
}
I'm trying to detect rising and falling temperatures using PIC16F877A MCU and two DS8B20 sensors. I'm facing with problems when I try to detect temperature falling. Here is my code what am I trying to do:
#include "main.h"
void main() {
//Turn on LCD backlight
output_high(PIN_D7);
// Initialize LCD module
lcd_init();
float Threshold_Value = 30; // Temperature threshold value
while (TRUE) {
Show_User_Info();
delay_ms(10);
Read_Sensors();
// Starting to read user button values
User_Buttons();
delay_ms(20); // Minimum amount of time to read user button values
// Starting to compare user set temperature value and upper sensor temperature read value.
Compare_Upper_Temp();
delay_ms(20);
//================================
// Checking, if the MCU pin connected to pump is high. If yes - do the waiting 'animation'
if (input(PIN_B5)) {
while(temp > Threshold_Value);
{
Bottom_Waiting_Animation();
}
// Experimenting....
// break;
// continue;
}
if (input(PIN_B5)) {
while(temp < Threshold_Value);
{
Bottom_Waiting_Animation();
}
// break;
}
// If the set temp is less than threshold - turn the pump off.
if (temp < Threshold_Value) {
input(PIN_B5) == 0;
}
}
}
When the pump is switched on, I need to wait until second sensor reaches threshold value (30C), after that I need to 'detect' when that temperature starts to drop from that 30C. This code which I uploaded 'works' only with one While(temp > Threshold_Value) loop. But when I insert the next one under it while(temp < Threshold_Value), MCU jumps in undefined area and gets stuck. This task sounds pretty easy, but I tried lots of different ways to solve this problem. Maybe one of the problem reasons could be multiple while loops?
Don't use a semicolon after the while condition.
Replace
while (condition);
{
... looped code ...
}
with
while (condition)
{
.... looped code ...
}
This is one reason I like to put open braces at the end of the condition (as you do in your if statements): it helps to see the obnoxious accidental semicolon.
Using a 8-bit AVR micro, I arrived to a simple situation which might not be that easy to solve.
Consider the following snippet:
static volatile uint8_t counter;
//fires often and I need all the values of the counter.
void isr(void) {
counter++;
}
int main (void) {
while(1) {
send_uart(counter);
counter = 0;
delay_ms(1000); //1 sec pause
}
return 0;
}
1.) It can happen that send_uart is followed by an isr which increases the counter, and then the next statement zeroes it out.
Therefore I'll miss one data from the counter.
2.) If I use ATOMIC_BLOCK(ATOMIC_RESTORESTATE) in the main fn, I can avoid the problems declared in (1), but it can happen that I miss an ISR because in this case INTs are disabled for a short time.
Is there a better way to pass information from the main fn to ISR?
If the counter is sampled rather than reset, there won't be any timing issues. Increments happening while sending will be accounted for in the next iteration. The unsigned data type of the counter variables will guarantee well-defined overflow behavior.
uint8_t cs = 0; // counter sample at time of sending
uint8_t n = 0; // counter as last reported
while (1) {
cs = counter; // sample the counter
send_uart((uint8_t)(cs - n)); // report difference between sample and last time
n = cs; // update last reported value
delay_ms(1000);
}
In the code below, I can see that the timer is working normally as the LED is always blinking. But the value of the count variable never changes inside the second while.
I don't know what could possibly go wrong?
// count variable used only in main and TIM2_IRQHandler.
uint8_t count=0;
int main(void)
{
count=0;
SystemInit();
GPIOInit();
NVIC_Configuration();
TIM_Configuration();
init_USART3(115200);
// All initialization is ok.
USART_puts(USART3, "\r\nConnection ok.\r\n");// Working normally
while (1)
{
if(asterixok==1)// No problem. This code if ok ->>process continue next step.
{
GPIO_SetBits(GPIOD , GPIO_Pin_12); // Led on (ok)
count=0;// count going to zero, timer working, must be change in there
while(1)
{
//Led blinking continue
//Timer query working normal led (13) blink.
//There is a problem
if(count>5) // Timer working, count never change in timer interrupt query (WHY)
{
GPIO_SetBits(GPIOD , GPIO_Pin_14); // LED OFFFFFFFFFFFFFFFF
USART_puts(USART3, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n");
goto nextstate;
}
}
nextstate:
GPIO_SetBits(GPIOD , GPIO_Pin_15); // Led never going on because code step in while loop.
}
}
}
void USART3_IRQHandler(void)
{
if( USART_GetITStatus(USART3, USART_IT_RXNE) )
{
unsigned char t = USART3->DR;
if(t=='*')
{
asterixok=1;
}
}
}
void TIM2_IRQHandler(void)
{
if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET )
{
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);
count++;
if(count>100)
count=0;
if( display )
{
GPIO_ResetBits(GPIOD , GPIO_Pin_13);
}
else
{
GPIO_SetBits(GPIOD , GPIO_Pin_13);
}
display = ~display;
}
}
I have tried with another Discovery board but the problem continues.
Please help. I'm going crazy!
You should declare count as volatile, as such :
volatile uint8_t count;
While compiling main the compiler was able to prove that count was not modified in the loop body, and so it probably cached its value in a register and maybe even optimized out the if statement. You could verify that by looking at a disassembly. The compiler does not know about interrupts as per the standard and so is permitted to perform such optimizations. Qualifying count as volatile will forbid the compiler from making these optimizations, forcing it to reload the variable from memory each time it is used.
In this simple case volatile will be enough but please be aware that it doesn't guarantee atomicity of operations, and it doesn't prevent the compiler and CPU from reordering instructions around accesses to the variable. It only forces the compiler to generate memory access instructions each time the variable is used. For atomicity you need locks, and to prevent reordering you need memory barriers.
Where does this return statement return to if it is inside this infinite while(1) loop? More importantly, I had no idea that a while(1) loop could be broken???
void __attribute__((interrupt, no_auto_psv)) _T3Interrupt(void)
{
int count;
IFS0bits.T3IF = 0; // clear Tmr3 interrupt flag
StopMotor();
IEC0bits.ADIE = 0; // disable ADC interrupt
IEC0bits.CNIE = 0; // disable CN interrupt
IEC0bits.T3IE = 0; // disable Tmr3 interrupt
T3CONbits.TON = 1; // restart tmr3
count = 0;
while (1)
{
if (IFS0bits.T3IF)
{
IFS0bits.T3IF = 0; // clear flag
if (count++ >= RESTART_COUNT)
{
IEC0bits.ADIE = 1; // enable ADC interrupt
IEC0bits.CNIE = 1; // enable CN interrupt
T3CONbits.TON = 0; // stop tmr3
IEC0bits.T3IE = 1; // enable Tmr3 interrupt
return;
}
}
}
return;
}
All return statements will return to wherever the function was called from, regardless of where they are located within the function.
For instance, if I wrote:
int main()
{
_iT3Interrupt();
}
Then the return statement in _iT3Interrupt will revert control flow back to main.
Also, any loop can be exited (even if the condition is 1, true, or some equivalent) with any of the following constructs:
break; //exits the loop
return; //exits the function, thus ending the loop
goto <label-outside-loop>; //self-explanatory
exit(); abort(); //exits the program. Bit of a stretch to say that this ends the loop...
And in C++, throw, which will unwind the stack until it reaches a corresponding catch, thus exiting the function. The C setjmp and longjmp functions may also be applicable here, but I don't know enough C to be certain of their usage.
There are multiple ways to get out of a loop with return break goto
with your snippet if IFS0bits.T3IF != 0 then it will eventually break out of the loop when count >= RESET_COUNT. After that it will return to where the function was called from.
To answer your second question, while(1) is more like while(true). Therefore, it keeps on looping until it encounters a break.
When a function is called the address the function (or ISR) is called from is put on the top of the stack.
The execution of return will end the function (or ISR). The programm counter (PC) is updated with this address, so the program flow could continue with the statement following the calling address.