Atmega32 without delay

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

 

I'm using an Atmega32 with an external crystal 7.3728MHz, I'm trying to NOT use the _delay_ms() I want to set an LED on for 10 seconds without the delay.h. I'm using a counter and an if condition but I can't determine the correct value for the counter that will give me an approximate 10 seconds. The reason I can't use the _delay_ms is that at anytime the microcontroller is  going to receive some data at the RX pin and if I used the _delay_ms for 10 seconds I'll miss the data that should be received on the RX pin. Below is the code I'm testing on

 

#include <avr/io.h>
#define F_CPU 7372800UL

void InitUART( unsigned char baudrate );
void TransmitByte( unsigned char data );
char ReceiveChar(void);
char IsCharAvailable();

char RecivedChar;
char Recived_Data[800];
int Count = 0;
int Function_ID = 0;
int Output_Flag = 0;
int Output_Counter = 0;

int main(void)
{
    InitUART(3); /* Set the baudrate to 115200 bps using a 7.37280MHz crystal */
    while(1)
    {
        if( IsCharAvailable() == 1 )
        {
            for (int i=0;i<100;i++)
            {
                RecivedChar = ReceiveChar();
                Recived_Data[Count]=RecivedChar;
                Count=Count+1;
            }
        }
​       Count=0;
        
        //Parse received data
        for(int i=0;i<Count;i++)
        {
            if(Recived_Data[i] == '3')
            {
                Function_ID = 3;
            }
            
            if(Recived_Data[i] == '2')
            {
                //Do some other work
                Function_ID = 2;
            }
        }
        
        if(Function_ID == 2)
        {
            //Do some other work - I didn't post the reset of the code here because I transmit some data through the TX PIN of the microcontroller
        }
        
        if(Function_ID == 3)
        {
            DDRB |= 1 << PINB0;
            PORTB |= 1 << PINB0; //Set PINB0 to a high reading - Turn LED ON and wait for 10 seconds using the below if conditions and the increment of Output_Counter
            Output_Flag = 1;
        }
        
        if(Output_Flag == 1)
        {
            Output_Counter++;
        }
        
        /*
         when I used the 10000 it gave me about 1 seconds, I need to know what value I should put in order for it to give me approximately 10 seconds
         I've tried 35000 it gave me more than 45 minutes. I don't know this should be calculated.
         
         The point is to keep the code looping in the while(1) loop because within the 10 seconds that PINB0 is set to 1 I'll be receiving other data 
         for example Function_ID==2 and I'll be doing other work I just need PINB0 to be set to 1 for 10 seconds when I receive FUnction_ID == 3 without 
         using the _delay_ms because I'll be waiting 10 seconds and I will receive other data within the 10 seconds delay that I would be able to receive at the RX PIN of the microcontroller
        */
        if(Output_Counter > 10000) //Wait for 10 seconds then set PINB0 OFF
        {
            DDRB |= 1 << PINB0;
            PORTB &= ~(1 << PINB0);
            Output_Flag = 0;
            Output_Counter = 0;
        }
        
    }
}

void InitUART( unsigned char baudrate )
{
    UBRRL =  baudrate;                   /*  Set the baud rate */
    UCSRB = (UCSRB | _BV(RXEN) | _BV(TXEN) );  /* Enable UART   receiver and transmitter */
}

char ReceiveChar()
{
    return UDR;/* Return the  data */
}

char IsCharAvailable()
{
    // indicate a char has been received?
    if ( (UCSRA & (0x80)) ) return 1;
    else return 0;
}

 

Last Edited: Tue. Oct 28, 2014 - 10:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you don't want to just sit inside delay() loops then you have a couple of options. Both use interrupts:

 

1) just put the UART stuff on an interrupt. In this case if something is received the code immediately switches to the RXC handler to collect it so nothing is lost. The usual technique here is to then employ a ring/circular buffer (aka a "FIFO") and you just put characters that are received into the FIFO. When the delay ends you then process the characters that were received while you were tied up in the delay(). Of course if the delay is 10 seconds a LOT of characters might be received in that time and the buffer might need to be quite big.

 

2) perhaps the "better" solution is a timer interrupt. Just set a timer to interrupt on overflow or compare at some regular rate like every 10ms or every 100ms or something. Keep incrementing a counter each time the interrupt occurs. In the main() code you keep checking for UARt activity but also at the start you take note of the current counter. You then keep checking if it has reached (passed) some value that is current+N where N is the number of 10ms/100ms you wanted to delay for. If, for example you are "ticking" at 100ms then to measure a 10s period you look for the counter being more than current+100. Or you could just set the counter to 0 at the start of the "delay" then in amongst the UART activity you keep checking until it has reached 100 or more. (if your tick s 10ms not 100ms then you are checking for 1,000).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your reply,

 

I've heard of the timer interrupts but I've never used it before, would it be too much if I asked if you could guide me more with this currently I'm reading about the timer interrupts.

Thank you,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would agree that a Timer interupt is a 'neater' solution.

 

However,   you can achieve 10s by calling delay_ms(1) 10000 times.

So you just check your UART in the polling loop.  e.g.

 

       timeout = 10000;        // loop for 10s
       while (timeout-- ) {
           if (UART_available)  ...
           delay_ms(1);
       }
       do_next_thing();

Polling a peripheral or flag inside a 'timeout' counter is a simple technique.

 

David.
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are articles in the Tutorial Forum about using timer interrupts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you do a forum search on the legacy site legacy.avrfreaks.net for "TICKS_PER_TOCK" you will find discussions about having a "heartbeat" timer tick for internal timekeeping.  (My most common is 10ms.)

Example: http://legacy.avrfreaks.net/inde...

(and that entire thread deals with your query)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are 2 things that people have a hard time with when writing embedded software, time and state.

Embedded software almost always controls hardware. That means it needs to know what is going on in the outside world.

This involves keeping track of time and state. How long has button x been pressed and what button was pressed before that?

 

Time is usually measured in milliseconds. Every control system I have written ( hundreds ) has tracked time in milliseconds. Maybe 1 of them required microseconds. You need to know the current time and know that something has happened for xx ms, then you need to do something about it.

 

* Set up a timer interrupt at 1 ms. All the interrupt does is increment a 32 bit tick counter, specifically a uint32_t.

  see this post for more info; https://www.avrfreaks.net/forum/t...

* In your code you can then get the current tick count and later see how many ms have elapsed so you can make a decision on what to do next.

 

Definitely get used to using interrupts, they make the system work much better. The UART is another place an interrupt is very important. UARTS are Slow slow slow and waiting for a string to be sent will wreak havoc on your system.

With interrupts you stuff your string to be sent into a buffer and the tx interrupt sends it as fast as it can while you go on your merry way.

The rx interrupt just grabs each char as it is recieved and again stuffs it into a buffer. You do need to check for received data on a regular basis of course

Keith Vasilakes

Firmware engineer

Minnesota

Last Edited: Tue. Oct 28, 2014 - 03:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Embedded software is almost always controls software.

Don't you mean "controls hardware'?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:

Embedded software is almost always controls software.

Don't you mean "controls hardware'?

 

Hmm, thats not worded well. too many ways to read that. I meant 'controls software' like I am a 'controls engineer'...

you're way is more clear.

 

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all for you replies,

 

I've tried the interrupts with timers, but for some reason I'm not getting the 10 seconds correct I think it's my OCR1A number that is incorrect I tried a couple of numbers that I calculated but I still can't get it to work for a complete 10 seconds. In other words how do I calculate the OCR1A number when I'm using an external 7.3728MHz crystal, because the current number that  I have now '115200' I got it from the same calculation but using the internal 1MHz oscillator of the micorcontroller. What I've done is that I divided the 7.3728MHz by 64 and I got the 115200 but this technique seems to be incorrect. 

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 7372800UL

void InitUART( unsigned char baudrate );
void TransmitByte( unsigned char data );
char ReceiveChar(void);
char IsCharAvailable();
void PowerLed(int falg);

char RecivedChar;
char Recived_Data[800];
int Count = 0;
int Function_ID = 0;
int Output_Flag = 0;
int Output_Counter = 0;

int main(void)
{
	PowerLed(1);
	InitUART(3); /* Set the baudrate to 115200 bps using a 7.37280MHz crystal */
	
	sei(); // Enable the global interrupts
	
	while(1)
	{
		if( IsCharAvailable() == 1 )
		{
			for (int i=0;i<100;i++)
			{
				RecivedChar = ReceiveChar();
				Recived_Data[Count]=RecivedChar;
				Count=Count+1;
			}
		}
		
		//Parse received data
		for(int i=0;i<Count;i++)
		{
			if(Recived_Data[i] == '3')
			{
				Function_ID = 3;
			}
			
			if(Recived_Data[i] == '2')
			{
				//Do some other work
				Function_ID = 2;
			}
		}
		Count=0;
		
		if(Function_ID == 2)
		{
			//Do some other work - I didn't post the reset of the code here because I transmit some data through the TX PIN of the microcontroller
		}
		
		if(Function_ID == 3)
		{
			DDRB |= 1 << PINB0;
			PORTB |= 1 << PINB0; //Set PINB0 to a high reading - Turn LED ON and wait for 10 seconds using the below if conditions and the increment of Output_Counter
			Output_Flag = 1;
			TCCR1B |= 1<<CS10 | 1<<CS11 | 1<<WGM12; //The WGM12 resets the TCNT1 back to '0' when the OCR1A is reached
			TIMSK |= 1<<OCIE1A; //If using atmega324, this register is TIMSK1
			OCR1A = 115200;//15624;//57600;//230400;;
		}
		
		//if(TCNT1 > 7200 && Output_Flag == 1)
		//{
			//Output_Counter++;
			////if(Output_Counter >10)
			////{
				//DDRB |= 1 << PINB0;
				//PORTB &= ~(1 << PINB0);
				//Output_Flag = 0;
				//Output_Counter = 0;
				//Function_ID = 0;
			////}
		//}
		
		/*
		 when I used the 10000 it gave me about 1 seconds, I need to know what value I should put in order for it to give me approximately 10 seconds
		 I've tried 35000 it gave me more than 45 minutes. I don't know this should be calculated.
		 
		 The point is to keep the code looping in the while(1) loop because within the 10 seconds that PINB0 is set to 1 I'll be receiving other data 
		 for example Function_ID==2 and I'll be doing other work I just need PINB0 to be set to 1 for 10 seconds when I receive FUnction_ID == 3 without 
		 using the _delay_ms because I'll be waiting 10 seconds and I will receive other data within the 10 seconds delay that I would be able to receive at the RX PIN of the microcontroller
		*/
		//if(Output_Counter > 10000) //Wait for 10 seconds then set PINB0 OFF
		//{
			//DDRB |= 1 << PINB0;
			//PORTB &= ~(1 << PINB0);
			//Output_Flag = 0;
			//Output_Counter = 0;
		//}
		
	}
}

ISR(TIMER1_COMPA_vect)
{
	Output_Counter++;
	if(Output_Counter > 10)
	{
		//Turn LED OFF - Se t PINB0 to low
		DDRB |= 1 << PINB0;
		PORTB &= ~(1 << PINB0);
		Output_Flag = 0;
		Output_Counter = 0;
		Function_ID = 0;
	}
}


void InitUART( unsigned char baudrate )
{
	UBRRL =  baudrate;                   /*  Set the baud rate */
	UCSRB = (UCSRB | _BV(RXEN) | _BV(TXEN) );  /* Enable UART   receiver and transmitter */
}

char ReceiveChar()
{
	return UDR;/* Return the  data */
}

char IsCharAvailable()
{
	// indicate a char has been received?
	if ( (UCSRA & (0x80)) ) return 1;
	else return 0;
}

void PowerLed(int falg)
{
	// enable  PD6 as ACK
	DDRD |= (1<<PD6);
	
	// PIN5 PORTD set -> LED on
	if(falg ==1)
	{
		PORTD |= (1 << PD6);
	}else
	{
		//delay_ms(50);
		
		// PIN5 PORTD clear -> LED off
		PORTD &= ~(1 << PD6);
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've also though why not just use the timer without the interrupts and please correct me if this is a bad idea. I've tried the following code but still can't get a correct 10 seconds for PINB0 to be set to high then after the 10 seconds drops to low.

 

***UPDATE***: 

I've made a mistake I used the I've used the CS10 and CS12 therefore I should have divided the 7372800 by 1024 not 64 which will give me 7200, and I've tested it with protus simulator ISIS and it seems to be working correct. But is this a good way to do it because I still don't want my code to be stuck for 10 seconds because within those 10 seconds I will be receiving and transmitting data through the RX PIN and the TX PIN. Than you

#include <avr/io.h>
#define F_CPU 7372800UL

void InitUART( unsigned char baudrate );
void TransmitByte( unsigned char data );
char ReceiveChar(void);
char IsCharAvailable();
void PowerLed(int falg);

char RecivedChar;
char Recived_Data[800];
int Count = 0;
int Function_ID = 0;
int Output_Flag = 0;
int Output_Counter = 0;

int main(void)
{
    PowerLed(1);
    InitUART(3); /* Set the baudrate to 115200 bps using a 7.37280MHz crystal */
    
    TCCR1B |= 1<<CS10 | 1<<CS12;
    
    while(1)
    {
        if( IsCharAvailable() == 1 )
        {
            for (int i=0;i<100;i++)
            {
                RecivedChar = ReceiveChar();
                Recived_Data[Count]=RecivedChar;
                Count=Count+1;
            }
        }
        
        //Parse received data
        for(int i=0;i<Count;i++)
        {
            if(Recived_Data[i] == '3')
            {
                Function_ID = 3;
            }
            
            if(Recived_Data[i] == '2')
            {
                //Do some other work
                Function_ID = 2;
            }
        }
        Count=0;
        
        if(Function_ID == 2)
        {
            //Do some other work - I didn't post the reset of the code here because I transmit some data through the TX PIN of the microcontroller
        }
        
        if(Function_ID == 3)
        {
            DDRB |= 1 << PINB0;
            PORTB |= 1 << PINB0; //Set PINB0 to a high reading - Turn LED ON and wait for 10 seconds using the below if conditions and the increment of Output_Counter
            Output_Flag = 1;
        }
        
        if(TCNT1 > 115200 && Output_Flag == 1) //115200 = 7.3728mega divided by 64 is NOT giving me the correct 1 seconds
        {
            TCNT1 = 0;
            Output_Counter++; // loop 10 times, 1 second each to get the 10 seconds I need
            if(Output_Counter >10)
            {
                DDRB |= 1 << PINB0;
                PORTB &= ~(1 << PINB0);
                Output_Flag = 0;
                Output_Counter = 0;
                Function_ID = 0;
            }
        }
        
        /*
         when I used the 10000 it gave me about 1 seconds, I need to know what value I should put in order for it to give me approximately 10 seconds
         I've tried 35000 it gave me more than 45 minutes. I don't know this should be calculated.
         
         The point is to keep the code looping in the while(1) loop because within the 10 seconds that PINB0 is set to 1 I'll be receiving other data 
         for example Function_ID==2 and I'll be doing other work I just need PINB0 to be set to 1 for 10 seconds when I receive FUnction_ID == 3 without 
         using the _delay_ms because I'll be waiting 10 seconds and I will receive other data within the 10 seconds delay that I would be able to receive at the RX PIN of the microcontroller
        */
        //if(Output_Counter > 10000) //Wait for 10 seconds then set PINB0 OFF
        //{
            //DDRB |= 1 << PINB0;
            //PORTB &= ~(1 << PINB0);
            //Output_Flag = 0;
            //Output_Counter = 0;
        //}
        
    }
}

void InitUART( unsigned char baudrate )
{
    UBRRL =  baudrate;                   /*  Set the baud rate */
    UCSRB = (UCSRB | _BV(RXEN) | _BV(TXEN) );  /* Enable UART   receiver and transmitter */
}

char ReceiveChar()
{
    return UDR;/* Return the  data */
}

char IsCharAvailable()
{
    // indicate a char has been received?
    if ( (UCSRA & (0x80)) ) return 1;
    else return 0;
}

void PowerLed(int falg)
{
    // enable  PD6 as ACK
    DDRD |= (1<<PD6);
    
    // PIN5 PORTD set -> LED on
    if(falg ==1)
    {
        PORTD |= (1 << PD6);
    }else
    {
        //delay_ms(50);
        
        // PIN5 PORTD clear -> LED off
        PORTD &= ~(1 << PD6);
    }
}

 

Last Edited: Wed. Oct 29, 2014 - 09:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
			TCCR1B |= 1<<CS10 | 1<<CS11 | 1<<WGM12; //The WGM12 resets the TCNT1 back to '0' when the OCR1A is reached
			TIMSK |= 1<<OCIE1A; //If using atmega324, this register is TIMSK1
			OCR1A = 115200;//15624;//57600;//230400;;

1.   I am always happier with using parentheses round each bitmask.     Your expression will work fine.   But other combinations will go wrong with precedence etc.

2.  OCR1A is a 16-bit quantity.    115200 is more than 16-bits.    And you should calculate it correctly if you want a 1 second IRQ.

 

It is always worth adding a comment that explains what you intend a sequence to do.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
        if(Function_ID == 3)
        {
            DDRB |= 1 << PINB0;
            PORTB |= 1 << PINB0; //Set PINB0 to a high reading - Turn LED ON and wait for 10 seconds using the below if conditions and the increment of Output_Counter
            Output_Flag = 1;
        }

Surely when you identify "function 3" you have to then set TCNT1 to 0 to start the count? As it stands your code starts TCNT1 counting when it does:

    TCCR1B |= 1<<CS10 | 1<<CS12;

So TCNt1 might have any value at the moment that you have received the 100 characters and then find that there is a '3'. Also this code:

        for(int i=0;i<Count;i++)
        {
            if(Recived_Data[i] == '3')
            {
                Function_ID = 3;
            }
            
            if(Recived_Data[i] == '2')
            {
                //Do some other work
                Function_ID = 2;
            }
        }

is a bit suspicious. You know the data you are going to be receiving better than anyone here but suppose the 100 characters includes both '2's and '3's or just several '2's or several '3's. Once you have found a "Function_ID" don't you want to "break;" from this parsing loop? Also you are in the  middle of a while(1) so while you wait for TCNT1 to get to 115200 (an interesting value for a 16 bit register!!!) won't it keep going back and doing the parsing for() loop over and over again?

 

This whole program smacks of bad design. Software should be designed THEN implemented. Don't just make it up and try and bolt things on as you go along. It's a recipe for disaster. Go back and write out a specification of what it is you need this program to do "receiver 100 charactes, scan them for a function indicator, start a timer, wait for completion, do something else...". When you have a specification (perhaps even a flow chart) for what you want to achieve THEN implement the code from that design. You'll get a much better solution for having thought about the design before implementing it.

 

I cannot help thinking that what you really need here is a "finite state machine". The program clearly has "phases of operation". At first you are receiving characters, then you are parsing them, then you are acting on the instructions. In C you would have something like:

typedef enum {
    RECEIVE,
    PARSE,
    ACT
} state_type;

state_type current_state;

int main(void) {
    current_state = RECEIVE;
    while (1) {
        switch (current_state) {
            case RECEIVE: 
                // handle the character receive
                current_state = PARSE;
                break;
            case PARSE:
                // do the parsing
                current_state = ACT;
                break;
            case ACT:
                // do what's to be done
                current_state = RECEIVE;
                break;
        }
    }
}

That's just sketchy but it shows how the code moves from one state to the next, doing whatever is to be done at each stage. Far better than just putting it all together in a big while(1) loop and setting various flags to try and get it form one state to the next. It can only be in one distinct state at any one time so keep one such state variable and have it always set to what's being done right now. Then move it on to the next state (or back to the start) once that current work is finished.