Question

My platform is a c8051F120 microcontroller. I would like to send (=tx) bytes via UART0 using interrupts. My design so far is the following:

#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 pretty sure that the initialization regarding UART0 registers and timing via Timer2 (not part of the above code) is correct, because I am able to use the blocking 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 the interrupt design, of course, I also set

ES0 = 1;

Does anybody find a flaw in my design that attempts to use the interupt? Or, does anybody have sample code for this? Thank you! And a big shout-out to jszakmeister, who answered my question regarding reading the TCNT register.

Was it helpful?

Solution 2

My colleague Guo Xiong found the mistake: The variable UART0_tx_available was not incremented and decremented at the right place. Below is the corrected and tested 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;
    }    
  }
}

OTHER TIPS

The biggest flaw I see is that you should not have any variable (for example: UART0_tx_available) being modified by the main code and the interrupt code.

Usually I implement an interrupt driven UART using a circular buffer and two pointers.

Here is a simple C example for the AVR micro. My 8051 code is all 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 */

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top