Interrupts, volatile, byte data

Thread Starter

AlbertHall

Joined Jun 4, 2014
9,882
If I have a program with an interrupt routine (reading and writing UART data using a circular buffer) and a main program acessing that data and the shared variables are bytes and declared volatile will I need to disable interrupts while the main program is accessing those bytes?
 

Papabravo

Joined Feb 24, 2006
13,743
A small but important distinction. You can read any variable you want all the time. What you must not do is change ANY variable in both the interrupt routine and the main program. For example you can read both the "put" and the "get" pointers to the circular buffer to decide if there is data there. When you go to "get" a byte out of the circular buffer you can modify the "get" pointer, because the interrupt routine will never touch it. If you are keeping a count of data in the buffer and bot the main and the interrupt routine can potentially modify it then you must disable interrupts while you make the change.

Another thing to keep in mind is the concept of atomic access. If the variable in question requires more than one sequential access and an interrupt could occur between the two read operations then there could be a problem.

https://courses.cs.washington.edu/courses/cse378/07au/lectures/L25-Atomic-Operations.pdf
 
Last edited:

Thread Starter

AlbertHall

Joined Jun 4, 2014
9,882
It might help if you told us which micro you are using. DMA? Hardware UART? Purpose of FIFO circular buffer.
PIC16F, the buffer is there so if the main program is busy, received bytes will not be lost. Also the main program can write data to the buffer and get on with whatever while the interrupt sends it to the UART.
 

jpanhalt

Joined Jan 18, 2008
9,435
PIC16F, the buffer is there so if the main program is busy, received bytes will not be lost. Also the main program can write data to the buffer and get on with whatever while the interrupt sends it to the UART.
I have not noticed that my 16F chips can do that. That is, the ISR and Main cannot execute program instructions at the time time.
 

Papabravo

Joined Feb 24, 2006
13,743
I don't think he is saying that. A UART has both a transmitter and a receiver. There can be interrupts for both. Normally you leave the receive interrupt on all the time. The transmit interrupt is only enabled if there are characters in the transmit queue. I knew what he was talking about. Apparently you missed that.
 

jpanhalt

Joined Jan 18, 2008
9,435
The black and white of what he wrote is pretty clear. In plain English, "Main program can write data to the buffer" and "interrupt sends it to the UART" certainly seems like simultaneous execution of code to me.

I am sure AlbertHall can explain what he meant by the underlined text himself.

I am flattered that you have taken the time to read my posts. Please try to understand them.
 

Ian Rogers

Joined Dec 12, 2012
774
If I have a program with an interrupt routine (reading and writing UART data using a circular buffer) and a main program acessing that data and the shared variables are bytes and declared volatile will I need to disable interrupts while the main program is accessing those bytes?
I don't.. The reason why is serial communication isn't the fastest.. I find once the data arrives and the buffer head doesn't match the tail. The processing time outside the interrupt easily takes care of it before the next byte... If you disable the interrupt you may miss bytes... The interrupt updates the head and the main should update the tail.. If a byte arrives whilst updating the tail, it'll just do another read next pass..

I can't remember ever having an issue and my main is 70mS or so...
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
9,882
Here is what I have so far (shamelessly stolen from www). The question comes down to do all those declaration need to be volatile, and are the disable interrupts necessary.
For considerations of time and speed, the PIC will be running from an 18.432 MHz crystal (4.608MHz instruction cycle) and I hope to run this at 115,200 baud.

C:
#define RXBUFSIZE 64
#define TXBUFSIZE 64

unsigned volatile char volatile rx_buff[RXBUFSIZE];
unsigned char volatile rx_begin = 0;
unsigned char volatile rx_next_free = 0;
unsigned char volatile rx_count = 0;

unsigned volatile char volatile tx_buff[TXBUFSIZE];
unsigned char volatile tx_begin = 0;
unsigned char volatile tx_next_free = 0;
unsigned char volatile tx_count = 0;

__interrupt isr(){
    // If a character has arrived in the hardware buffer
    if (RCIF){
        // Put it in the software buffer
        if (rx_count >= RXBUFSIZE) die(ERROR_rx_OVERFLOW);
        rx_buff[rx_next_free] = RCREG;
        rx_next_free = (rx_next_free + 1) % RXBUFSIZE;
        rx_count++;
    }
    // If there is space in hardware FIFO, and interrupt
    // has been enabled because stuff in software FIFO needs to be sent.
    if (TXIE && TXIF){
        // Put a byte from s/w fifo to h/w fifo.
        // (Here, tx_count is always > 0 (in theory))
        TXREG = tx_buff[tx_begin];
        tx_count--;
        tx_begin = (tx_begin + 1) % TXBUFSIZE;
        // If this was the last byte in the s/w FIFO,
        // disable the interrupt: we don't care
        // when it has finished sending.
        if(tx_count==0) TXIE = 0;
    }
}
void send_char(char c){
    // disable interrupts to avoid bad things happening
    di();
    // if the hardware buffer is empty,
    if (TXIF && tx_count == 0){
        // put a byte directly into the hardware FIFO
        TXREG = c;
    } else {
        // cannot send byte directly so put in the software FIFO
        if (tx_count >= TXBUFSIZE) die(ERROR_TX_OVERFLOW);
        tx_buff[tx_next_free] = c;
        tx_next_free = (tx_next_free + 1) % TXBUFSIZE;
        tx_count++;
        // Enable TX interrupt since it now has something
        // it needs to transfer from the s/w FIFO to the h/w FIFO
        TXIE = 1;
    }
    ei();
}
char get_char(){
    // wait for a byte to appear in the s/w buffer
    while (!rx_count) {
        // If the h/w buffer overflowed, die with error
        if (OERR) die(ERROR_rx_HW_OVERFLOW)
    }
    // disable interrupts to avoid bad things happening
    di();
    unsigned char c = rx_buff[rx_begin];
    rx_count--;
    rx_begin = (rx_begin + 1) % RXBUFSIZE;
    ei();
    return c;
}
void send_str(const unsigned char * str){
    unsigned char char_idx = 0;
    // until we reach the end-of-string null character,
    while (str[char_idx]){
        // queue a character for sending
        send_char(str[char_idx++]);
    }
}
 

Ian Rogers

Joined Dec 12, 2012
774
Its not shameless….. I write oodles... If I post on T' web then its for everyone... Its open sourced ( so to speak )

Now! I've never used interrupt driven Tx….. Never seen the point.... But circular buffers only need a size, a head and a tail.. all the rest can be calculated variables.. I wrote ( with help from t' web..) a small circular buffer and the difference between head and tail never gets bigger than 5 ~ 6 at the most.. This keeps the volatile count to two..

BTW.... Even one volatile variable will direct the compiler NOT to optimize all the other variables in the ISR.. I do think folk over think things... Once upon a time I looked at serial communication and ran for the hills... Nearly all examples were really OTT..

I now use Can a lot... I now know that serial comms is more robust than I first thought... Especially if you are using baud calculated crystals..
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
9,882
But circular buffers only need a size, a head and a tail.. all the rest can be calculated variables.. I wrote ( with help from t' web..) a small circular buffer and the difference between head and tail never gets bigger than 5 ~ 6 at the most.. This keeps the volatile count to two..
Given that, do the send_char and get_char functions need the interrupt disabled?
 

JohnInTX

Joined Jun 26, 2012
4,085
If I have a program with an interrupt routine (reading and writing UART data using a circular buffer) and a main program acessing that data and the shared variables are bytes and declared volatile will I need to disable interrupts while the main program is accessing those bytes?
FWIW, I always disable interrupts when reading a buffer driven by interrupt. Regardless of the language, I use mostly assembler, there comes a point when you need to access the shared data between interrupt and main program and you just don't want to have to deal with all of the various ways to mix up foreground and background processing of pointers, indexes, counters etc. In the RX interrupt the access to the buffer system is atomic i.e. guaranteed not to be interfered with by the main routines. The only way to ensure that for the main (non-interrupt) routines is to disable the receive interrupt so that you never mix the put and get operations. If your 'get' routines are written correctly they'll check the buffer and only if there is a character to be gotten, disable the RX interrupt, get the character with all of the pointer/index/wrap necessary then re-enable the interrupt. As long as your code can complete that critical section in less than one character time, the double-buffering of the UART will take care of any timing issues.

If you have buffered TX, disable the TX interrupt when writing to the buffer as well. I use buffered TX a lot.. just attach the buffer to printf and let 'er rip. The timing issues transmitting are a bit different but if you ever have to transmit more than two characters (in a PIC) you'll wind up delaying the rest of your program while the characters are emitted by the UART. Don't like that.

Others will doubtless offer many other ways to deal with the issue.. My learn-it-once philosophy says to disable interrupts when accessing ANY foreground/background common areas. That goes for any other apps as well. Buffered in, buffered out.
Keep interrupt routines short and succinct. YMMV

Have fun!
 
Last edited:

Ian Rogers

Joined Dec 12, 2012
774
Not really, because my "getchar" is in the interrupt and sending never gets problematic..

When the interrupt on a pic occurs, whilst in the interrupt you tend not to be interrupted until a retfie occurs.. especially if you only have RCIE on the go.... If you have TXIE on the go as well then maybe... But as I said.... I always send..

Look at it this way.... Send a string... Whilst sending without interrupts you can receive into the circular buffer.. once sendstring is done.. Then you'll see the head far from the tail and deal with it.. Does your code need the TXIE type transmission?
 

Ian Rogers

Joined Dec 12, 2012
774
Well to answer the original question.. The pic16 has one interrupt and cannot be interrupted.. If the TXIF happens during a RCIF it has to wait anyway.. As for the volatile variables... There is no harm in declaring volatile variables, but results in a slightly slower code as there is no optimisation ..
 
Top