0
\$\begingroup\$

I only have some limited knowledge in interrupts.

This is what I understand about following ISR functions

ISR(USART_RX_vect) will get called when a new data becomes available at the receive buffer (RXC0 of USCR0A will set to 1) and it gets cleared when data is read

ISR(USART_UDRE_vect) will get called when UDRE0 becomes 1 indicating transmit buffer is empty and ready to receive data

I set up an UART interrupt code as below, not sure is this the proper way ISR function should look like but it works

/*
 * uart_interrupt.c
 *
 * Created: 12-10-2018 04:54:04 PM
 * Author : Athul
 */ 
#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
//#include <util/setbaud.h>

#define BAUD 9600
#define SIZE 500
int i = 0, j = 0;

uint8_t data_int;

/* For U2X0 = 0 */
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD)) - 1)/2;

/* For U2X0 = 1 */
//uint16_t UBRR0_value = ((F_CPU / (8L * BAUD)) - 1)/2;


void uart_init(void)
{
    /* For a baud rate of 9600 (F_CPU = 1000000), error is 7% when U2X0 = 0. Writing this bit to one will
     reduce the divisor of the baud rate divider from 16 to 8 effectively doubling the 
     transfer rate for asynchronous communication and reduce error to 0.2% */
    UCSR0A |= (1 << U2X0);

    /*set (global) interrupt enable bit*/
    sei();

    /* transmit enable, receive enable & RX Complete Interrupt Enable */
    UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);

    /* 8 bit mode */
    UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);

    /* Baud rate registers */
    UBRR0H = UBRR0_value >> 8;
    UBRR0L = UBRR0_value;

}

void uart_tx(uint8_t data)
{
    /*wait till USART Receive Complete*/
    loop_until_bit_is_set(UCSR0A, UDRE0); // same as while(!(UCSR0A & (1 << UDRE0)));
    UDR0 = data;
}

uint8_t uart_rx(void) {

    /*wait till USART Data Register Empty*/
    loop_until_bit_is_set(UCSR0A, RXC0); // same as while(!(UCSR0A & (1 << RXC0)));
    return UDR0;
}

void printString(uint8_t *ptr)
{
    while(*ptr != '\0')
    {
        uart_tx(*ptr);
        ptr++;
    }
}

/*Interrupt sub-routine for RX Complete Interrupt Enable*/
ISR(USART_RX_vect)
{
    data_int = UDR0;
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = data_int;
}

int main(void)
{
    /*Initialize uart*/
    uart_init();
    /*Test*/
    uint8_t *p = "Hello";
    printString(p);

    /*Set PORTB pins as output*/
    DDRB = 0XFF;

    /*event loop*/
    while (1) 
    {
        /*Toggle PORTB pins*/
        PORTB = 0XFF;
        _delay_ms(100);
        PORTB = 0X00;
        _delay_ms(100);
    }
}

To know how to implement UDRIE0, I made following changes,

//USART Data Register Empty Interrupt Enable
UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0) | (1 << UDRIE0);

ISR(USART_RX_vect)
{
    data_int = UDR0;
}

ISR(USART_UDRE_vect)
{
    UDR0 = data_int;
}

It keeps printing the data that I last entered

Then I decided to create an array and store received characters in it and the ISR(USART_UDRE_vect) would read each character and transmit it.

#define SIZE 64
int i = 0, j = 0;

uint8_t data_int[SIZE];

ISR(USART_RX_vect)
{
    data_int[i] = UDR0;
    i++;
    if(i > 64)
    {
        i = 0;
    }
}

ISR(USART_UDRE_vect)
{
    UDR0 = data_int[j];
    j++;
    if(j == 64)
    {
        UCSR0B &= ~(1 << UDRIE0); //disable UDRIE0 interrupt
    }
}

It prints "hello" and nothing else.

Can anyone tell why isn't it working. A small example of UDRIE0 if possible?

Also compiler gives a warning pointer targets in initialization differ in signedness [-Wpointer-sign] What does this mean?

\$\endgroup\$
-1
\$\begingroup\$

Interrupts are managed by hardware and fired immediately when any type of event occurs, e.g. when data register is empty, transmission of data is complete, etc.

If you implement ISR sub-routine for given type of interrupt an actual executing application code is interrupted immediatelly when the interrupt is fired and this sub-routine is executed, I.e. a long jump in program memory. Before execution of the sub-routine all relevant registers are pushed to a stack and restored when the sub-routine is finished. After serving the interrupt the interrupted application code continues from the address where it was interrupted.

A code of sub-routines must be as small as possible and must be executed quickly, so there should not be any delays or long loops. If you want to process received UART data you should put them to a buffer in the sub-routine and process in the main loop.

In case of atmel microcontrollers the interrupt flags, e.g. UDRE, are automatically cleared when the sub-routines is finished. No need to clear them by software.

Hope my explanation of interrupts is clear and will help you to write correct code.

\$\endgroup\$
  • 1
    \$\begingroup\$ While your explanation of how interrupts work is ok, you don't really answer OP's question of what's wrong with his ISR, or what a UART ISR should look like. \$\endgroup\$ – C_Elegans Nov 24 '18 at 2:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.