UART Transmission via Interrupt on the 8051 Microcontroller

My platform is the c8051F120 microcontroller. I would like to send (= tx) bytes via UART0 using interrupts. So far, my project is as follows:

#define UART0_TX_SIZE 16 char UART0_tx[UART0_TX_SIZE]; short UART0_tx_uart = 0; short UART0_tx_main = 0; short UART0_tx_available = 0; void UART0_putChar(char value) { char SAVE_SFRPAGE; bit EA_SAVE = EA; // potentially blocking code while (UART0_tx_available == UART0_TX_SIZE) ; // disable interrupts EA = 0; EA = 0; if (UART0_tx_available) { UART0_tx[UART0_tx_main] = value; ++UART0_tx_main; if (UART0_tx_main == UART0_TX_SIZE) UART0_tx_main = 0; ++UART0_tx_available; } else { SAVE_SFRPAGE = SFRPAGE; SFRPAGE = UART0_PAGE; SBUF0 = value; SFRPAGE = SAVE_SFRPAGE; } // reenable if necessary EA = EA_SAVE; } // (return void works for other interrupts) void UART0_Interrupt() interrupt (4) { if (RI0 == 1) { RI0 = 0; } if (TI0 == 1) { // cause of interrupt: previous tx is finished TI0 = 0; // Q: Should this clear tx interrupt flag be further down? if (SSTA0 & 0x20) { // Errors tx collision SSTA0 &= 0xDF; } if (UART0_tx_available) { // If buffer not empty --UART0_tx_available; // Decrease array size SBUF0 = UART0_tx[UART0_tx_uart]; //Transmit ++UART0_tx_uart; //Update counter if (UART0_tx_uart == UART0_TX_SIZE) UART0_tx_uart = 0; } } } 

I am sure that the initialization regarding the UART0 registers and synchronization via Timer2 (not part of the above code) is correct, because I can use the lock function:

 char putchar_Blocking(char value) { char SFRPAGE_SAVE = SFRPAGE; SFRPAGE = UART0_PAGE; while (!TI0) // while TI0 == 1 wait for transmit complete ; TI0 = 0; SBUF0 = value; SFRPAGE = SFRPAGE_SAVE; return value; } 

When I want to switch to interrupt design, of course, I also set

 ES0 = 1; 

Does anyone find a flaw in my design that is trying to use interupt? Or does anyone have some sample code for this? Thanks! And a big cry for jszakmeister that answered my question regarding reading the TCNT register.

+4
source share
2 answers

My colleague Guo Xiong discovered an error: the variable UART0_tx_available did not increase or decrease in the right place. The following is the corrected and verified version:

 #define UART0_TX_SIZE 16 char UART0_tx[UART0_TX_SIZE]; short UART0_tx_uart = 0; short UART0_tx_main = 0; short UART0_tx_available = 0; void UART0_putChar(char value) { char SAVE_SFRPAGE; bit EA_SAVE = EA; // potentially blocking code while (UART0_tx_available == UART0_TX_SIZE) ; // disable interrupts EA = 0; EA = 0; if (UART0_tx_available) { UART0_tx[UART0_tx_main] = value; ++UART0_tx_main; if (UART0_tx_main == UART0_TX_SIZE) UART0_tx_main = 0; } else { SAVE_SFRPAGE = SFRPAGE; SFRPAGE = UART0_PAGE; SBUF0 = value; SFRPAGE = SAVE_SFRPAGE; } ++UART0_tx_available; // reenable if necessary EA = EA_SAVE; } // (return void works for other interrupts) void UART0_Interrupt() interrupt (4) { if (RI0 == 1) { RI0 = 0; } if (TI0 == 1) { // cause of interrupt: previous tx is finished TI0 = 0; // Q: Should this clear tx interrupt flag be further down? if (SSTA0 & 0x20) { // Errors tx collision SSTA0 &= 0xDF; } --UART0_tx_available; // Decrease array size if (UART0_tx_available) { // If buffer not empty SBUF0 = UART0_tx[UART0_tx_uart]; //Transmit ++UART0_tx_uart; //Update counter if (UART0_tx_uart == UART0_TX_SIZE) UART0_tx_uart = 0; } } } 
0
source

The biggest drawback that I see is that you should not have any variables (for example: UART0_tx_available) that are changed by the main code and the interrupt code.

Usually I implement UART with interruption using a circular buffer and two pointers.

Here is a simple C example for an AVR microcontroller. My code 8051 is an assembly.

 /* size of RX/TX buffers */ #define UART_RX_BUFFER_SIZE 16 #define UART_TX_BUFFER_SIZE 16 #define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1) #define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1) #if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK ) #error RX buffer size is not a power of 2 #endif #if ( UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK ) #error TX buffer size is not a power of 2 #endif /* * module global variables */ static volatile unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE]; static volatile unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE]; static volatile unsigned char UART_TxHead; static volatile unsigned char UART_TxTail; static volatile unsigned char UART_RxHead; static volatile unsigned char UART_RxTail; static volatile unsigned char UART_LastRxError; SIGNAL(UART0_TRANSMIT_INTERRUPT) /************************************************************************* Function: UART Data Register Empty interrupt Purpose: called when the UART is ready to transmit the next byte **************************************************************************/ { unsigned char tmptail; if ( UART_TxHead != UART_TxTail) { /* calculate and store new buffer index */ tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK; /* get one byte from buffer and write it to UART */ UART0_DATA = UART_TxBuf[tmptail]; /* start transmission */ UART_TxTail = tmptail; }else{ /* tx buffer empty, disable UDRE interrupt */ UART0_CONTROL &= ~_BV(UART0_UDRIE); } } /************************************************************************* Function: uart_putc() Purpose: write byte to ringbuffer for transmitting via UART Input: byte to be transmitted Returns: none **************************************************************************/ void uart_putc(unsigned char data) { unsigned char tmphead; tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK; while ( tmphead == UART_TxTail ){ ;/* wait for free space in buffer */ } UART_TxBuf[tmphead] = data; UART_TxHead = tmphead; /* enable UDRE interrupt */ UART0_CONTROL |= _BV(UART0_UDRIE); }/* uart_putc */ 

Special thanks to Peter Fleury http://jump.to/fleury for the library from which these routines came from.

+3
source

All Articles