Atmega168a RX Interrup doesnt appear to trigger

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

I have been trying to get the USART RX interrupt trigger working on my ATmega168A using the tutorial here (updating my registers as per the datasheet): Tutorial

I have setup the system to use a baud of 4800 at a freq of 1MHz (8Mhz internal oscillator div/8), prescale value of 12 came from the microchip datasheet.

My hardware setup has the TX and RX lines crossed to an arduino usb2serial module (pc comms) and an LED connection on PD6. nothing else is connected to the ATmega168a.

My software setup is ATmel Studio 7 with WinAvr to code, compile and program the chip.

 

Using the normal serial communications (inside my main loop), the system works as expected and echos the transmitted char back to the pc console. I have tested the LED toggling code as well and i know that works.

I then added the lines at the end of the function USART_Init() which should enable the RX interrupt and the global interrpt in the main code, but when typing over the same serial connection which worked, nothing happens (no light change) which suggests to me that either its not triggering or theres an error. I havent got a debug connection to the chip to test it.

 

Ive tried to simulate it and as far as i can see the registers setup correctly but i have been unable to see if it recieves data from the serial connection.

 

Ive been looking alover the web at different tutorials and other forum posts but i have been unable to find something which has shown the interrupt to be working.

 

Have I missed something or have I got a fault in something?

 

#include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    #define F_CPU 1000000UL
    #define BAUD 4800
    #define BAUD_PRESCALE 12
    
    
    void USART_Transmit( unsigned char data )
    {
      /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;
    /* Put data into buffer, sends the data */
    UDR0 = data;
    }
    
    unsigned char USART_Receive( void )
    {
    /* Wait for data to be received */
    while ( !(UCSR0A & (1<<RXC0)) )
    ;
    /* Get and return received data from buffer */
    return UDR0;
    }
    
    void USART_Init( unsigned int ubrr)
    {
    /*Set baud rate */
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    /* Enable receiver and transmitter */
    UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 1stop bit */
    UCSR0C |= (0<<USBS0)|(1<<UCSZ00)|(1<<UCSZ01);
    /*enable RX interrupt */
    UCSR0B |= (1 << RXCIE0);
    }
    
    int main(void)
    {
    USART_Init(BAUD_PRESCALE);
    sei();
    while (1) 
    {
        //a = USART_Receive();
        //USART_Transmit(a);
        _delay_ms(100);
    }
    }

    ISR(USART_RX_vect, ISR_BLOCK){
    //char ReceivedByte ;
    //ReceivedByte = UDR0 ; // Fetch the received byte value into the variable " ByteReceived "
    //UDR0 = ReceivedByte ; // Echo back the received byte back to the computer
    PORTD ^= (1<<PD6);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Forget RXCIE for the time being. The chances are that if it doesn't work the timing is wrong (FAQ#3). So verify that first. Just use something simple like:

    void USART_Transmit( unsigned char data )
    {
          /* Wait for empty transmit buffer */
        while ( !( UCSR0A & (1<<UDRE0)) )
        ;
        /* Put data into buffer, sends the data */
        UDR0 = data;
    }
    
    void USART_Init( unsigned int ubrr)
    {
    /*Set baud rate */
    UBRR0 = ubrr;
    /* Enable transmitter */
    UCSR0B = (1<<TXEN0);
    /* Set frame format: 8data, 1stop bit */
    UCSR0C |= (0<<USBS0)|(1<<UCSZ00)|(1<<UCSZ01);
    /*enable RX interrupt */
    }
    
    int main(void)
    {
        USART_Init(BAUD_PRESCALE);
        while (1) 
        {
            USART_Transmit('U');
        }
    }

With 8N1 framing the character 'U' is "special" in that it leads to a TXD output that is just 1010101010101010.. which makes it very easy to use a scope or logic analyser to chack that the pulses are 208us wide (which is the bit width at 4800 baud). Alteratively connect to a PC and just see if you get "UUUUUUUUUUUUUUUU..."

 

When Transmit works then you can extend to receive. Still don't go for the complexity of interrupts but just do something like:

while(1) {
    USART_Transmit(USART_Receive() + 1);
}

if you type "HAL" at this you should get "IBM" back.

 

Only when all this works should you then enable RXCIE.

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

I started out testing normal serial communication with the RX and TX within the main loop, i had it echoing back the characters as i typed them and they were all coming back correct (I have just tried your method of increasing the numbers by 1 and it cam back as expected HAL->IBM). Unfortunately I dont have a scope or analyser available to have a look at the exact timings.

 

From this test I believe I have got the correct baud and clock speeds set up, and once I confirmed the serial was working I moved on to the RX interrupt.

I left the lines of code commented out in the main loop which I used. The only things I added from the working configuration was:

  • UCSR0B |= (1 << RXCIE0);
  • sei();
  • ISR(USART_RX_vect, ISR_BLOCK){
        //char ReceivedByte ;
        //ReceivedByte = UDR0 ; // Fetch the received byte value into the variable " ByteReceived "
        //UDR0 = ReceivedByte ; // Echo back the received byte back to the computer
        PORTD ^= (1<<PD6);
        }

Everything else was the same from when I had it working without interrupts

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

Ah OK, then the RXCIE should work.

 

Only thing is that if you have:

    PORTD ^= (1<<PD6);

then your main() needs:

    int main(void)
    {
    DDRD = (1 << 6); // make PD6 output !!
    USART_Init(BAUD_PRESCALE);
    sei();
    while (1) 

 

Oh and just out of interest, why are you using ISR_BLOCK ? It's the default anyway so:

ISR(USART_RX_vect){

achieves the same in a less complex way.

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

Thanks you for spotting that, ive added the port direction definition in and ive also added to turn the light on so i could see when the program started (code below). This still hasnt done anything, the light comes on at the start of the code but doesnt toggle when a character is sent from the pc serial terminal.

 

int main(void)
{
	DDRD = (1<<PD6);
	USART_Init(BAUD_PRESCALE);
	sei();
	PORTD = (1<<PD6);
	while (1)
	{

I put in the ISR_BLOCK as it was suggested as something to try on a forum which i was serching before i posted here (part of me trying to debug it).

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

As it's a 168 do you have a debugWire debugger ?

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

no i dont, im using a usbasp programmer

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

On the whole, if synchronous comms works then simply setting RXCIE, creating an ISR() and doing sei() should "just work" for asynchronous so it's not immediately obvious to me what, if anything you are doing wrong here.

 

That's why a $15 Microchip Snap could be a good investment so you can see what's going on "inside" the 168.

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

This is why ive been a little stumped at whats going on, everything says it should work but it isnt. I dont think ive done something stuipid with it, or missed anything out.

 

I will look at getting a microchip snap and see if that can help me try and work out what on earth is happening

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

Could you post a zip of your project, perhaps someone can help you trouble shoot your project who has a debugger / logic analyzer.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274

 

 

 

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

This is the zip

 

 

Attachment(s): 

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

I'm not 100% sure but I belive that you will need to read UDR0 in the ISR, (can't just flop the bit for test) .

But you should at least get 1 ISR

 

And depending on the level at start perhaps you have a hanging ISR that block you!

 

After SEI try a dummy read of UDR0

 

Add:

I never use seriel ISR's, I pull the status in a timer ISR.

 

Add:

I see this :

return UDR0;

I would look at the generated code to make sure that code is correct.

 

 

 

Add:

and does this work:

 while ( !(UCSR0A & (1<<RXC0)) )

When you just got a receive ISR ! (I fear you hang here) 

 

 

Last Edited: Mon. Mar 25, 2019 - 02:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have added in a read of UDR0 in the ISR and directly after sei() and still no visible response on the LED

 

Add:

I see this :

return UDR0;

 

This has worked when i was reading in the main code loop. that isnt used in the interrupt trigger

 

Add:

and does this work:

 while ( !(UCSR0A & (1<<RXC0)) )

When you just got a receive ISR ! (I fear you hang here) 

 

The functions of USAER TX and RX are there from when I tested it and gotr it working from the main code. when using the interrypt i have commented out the code for them so they shouldnt run

Last Edited: Mon. Mar 25, 2019 - 02:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You did get Sparrow's point about reading UDR to clear the interrupt flag? You can do this several ways. Either just change:

    ISR(USART_RX_vect, ISR_BLOCK){
    //char ReceivedByte ;
    //ReceivedByte = UDR0 ; // Fetch the received byte value into the variable " ByteReceived "
    //UDR0 = ReceivedByte ; // Echo back the received byte back to the computer
    PORTD ^= (1<<PD6);
    }

to be:

    ISR(USART_RX_vect) {
      volatile char ReceivedByte ;
      ReceivedByte = UDR0 ; // Fetch the received byte value into the variable " ByteReceived " - clears RXCIF

      PORTD ^= (1<<PD6);
    }

or even just:

    ISR(USART_RX_vect) {

      UDR0; // A register access alone is enough to force a read (it's "volatile" so it must be read when mentioned)

      PORTD ^= (1<<PD6);
    }

Last Edited: Mon. Mar 25, 2019 - 03:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes i got sparrows point, make sure the flag has been "reset" by reading from the register. that way the system is in a known state when you try to run again.

I used your second code snippit and still no luck.

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

clawson wrote:
You did get Sparrow's point about reading UDR to clear the interrupt flag?

Hmmm--if you don't clear the flag, then the interrupt will fire AGAIN, right?

 

So perhaps flip it around:  OP doesn't have debugging equipment to tell for sure, so the constantly-firing ISR may look like the AVR is "stucked"?

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

theusch wrote:
Hmmm--if you don't clear the flag, then the interrupt will fire AGAIN, right?
Yeah and I imagine so fast that the LED will toggle at more than 20Hz so the Human eye/brain will see an LED that shows as " the light comes on at the start of the code but doesnt toggle when a character is sent  "

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

what happens if you use this code:

int main(void)
{
	DDRD = (1<<PD6);
	USART_Init(BAUD_PRESCALE);
	sei();
	PORTD = (1<<PD6);
	char a;
	while (1)
	{
             PORTD ^= (1<<PD6);
		_delay_ms(500);
	}
}

ISR(USART_RX_vect){
	UDR0 = UDR0+1;
}

And also put the F_CPU define before the #include statements. That should also reduce the warnings by 1 and ensure that if you ever change the cpu frequency you are not having wrong delay times.

 

This code should just flash the led 1 time per second and again when writing HAL it should return IBM to you.

 

In addition you also should not make the first write to the UCSR0B & UCSR0C registers an OR write, but a hard write. You do not know at what garbage state these registers are left and you want to make sure they have a specific setting. Note that they should be 0 after reset something else might have put garbage in accidentally......

 

 

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

clawson wrote:

theusch wrote:
Hmmm--if you don't clear the flag, then the interrupt will fire AGAIN, right?
Yeah and I imagine so fast that the LED will toggle at more than 20Hz so the Human eye/brain will see an LED that shows as " the light comes on at the start of the code but doesnt toggle when a character is sent  "

The datasheet indeed indicates that a new interrupt will be generated upon exit when the UDR register is not read:

 

So the LED might be flashing so fast that it hardly lits.

 

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

I tried using your code however the LED gets "stuck" after i send a char over serial. 

meslomp wrote:

what happens if you use this code:

int main(void)
{
	DDRD = (1<<PD6);
	USART_Init(BAUD_PRESCALE);
	sei();
	PORTD = (1<<PD6);
	char a;
	while (1)
	{
             PORTD ^= (1<<PD6);
		_delay_ms(500);
	}
}

ISR(USART_RX_vect){
	UDR0 = UDR0+1;
}

And also put the F_CPU define before the #include statements. That should also reduce the warnings by 1 and ensure that if you ever change the cpu frequency you are not having wrong delay times.

 

This code should just flash the led 1 time per second and again when writing HAL it should return IBM to you.

 

In addition you also should not make the first write to the UCSR0B & UCSR0C registers an OR write, but a hard write. You do not know at what garbage state these registers are left and you want to make sure they have a specific setting. Note that they should be 0 after reset something else might have put garbage in accidentally......

 

 

 

I have not been able to get the microchip snap working on mplab x ide, it doesnt show up. However I have been modifying my code to see if i could get a more useful output (without the debug wire). I have added a second LED on PD7 which will just toggle in the main code every second. When I send multiple chars over serial, the led stops flashing in whatever state it is until I stop sending chars. This tells me that an interrupt is working as expected BUT my interrupt code doesnt seem to run.

 

int main(void)
{
	DDRD = (1<<PD6)|(1<<PD7);
	USART_Init(BAUD_PRESCALE);
	sei();
	UDR0;
	PORTD |= (0<<PD6);
	while (1)
	{
		//USART_Transmit(USART_Receive() +1);
		_delay_ms(1000);
		PORTD ^= (1<<PD7);
	}
}

ISR(USART_RX_vect){
	UDR0 ; // Fetch the received byte value into the variable " ByteReceived "
	//UDR0 = ReceivedByte ; // Echo back the received byte back to the computer
	PORTD = (1<<PD6);
}