通过 8051 微控制器上的中断进行 UART 传输

Posted

技术标签:

【中文标题】通过 8051 微控制器上的中断进行 UART 传输【英文标题】:UART transmission via interrupt on a 8051 microcontroller 【发布时间】:2012-09-02 08:06:28 【问题描述】:

我的平台是 c8051F120 微控制器。我想使用中断通过 UART0 发送(=tx)字节。到目前为止,我的设计如下:

#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;
        
  

我很确定通过 Timer2(不是上述代码的一部分)对 UART0 寄存器和计时进行的初始化是正确的,因为我可以使用阻塞功能:

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;

当我要切换到中断设计的时候,当然我也设置了

ES0 = 1;

有人在我的设计中发现了试图使用中断的缺陷吗?或者,有人有这方面的示例代码吗?谢谢!非常感谢 jszakmeister,他回答了我关于读取 TCNT 寄存器的问题。

【问题讨论】:

如果我的回答有帮助,如果您接受,我将不胜感激。 亲爱的 Jeff,我有 ATmega128CAN 和其他 ATMEL 芯片的代码可以解决这个问题(你可以在我的网站上找到代码卫星/Cansat)。不幸的是,您对我的情况的回答中的代码“翻译”不会导致我从我的代码中得到任何其他行为:字符发送得太快。你知道如何修复我的示例代码吗?你只是说“最大的缺陷”,但只要我禁用中断,共享变量是完全安全的......我将再花一个周末试图找出我的代码有什么问题:计数中断调用等...... 告诉我更多关于您的问题....字符的速度只能与波特率一样快,还是您需要添加字符间延迟? 问题是UART0_tx_available的更新位置 在我的世界里,禁用中断是不行的。 【参考方案1】:

我看到的最大缺陷是你不应该有任何变量(例如:UART0_tx_available)被主代码和中断代码修改。

通常我使用一个循环缓冲区和两个指针来实现一个中断驱动的 UART。

这是 AVR micro 的简单 C 示例。我的 8051 代码都是汇编代码。

/* 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 */

特别感谢 Peter Fleury http://jump.to/fleury 提供这些例程来自的库。

【讨论】:

非常感谢!看起来很有前途。我将查找某些行背后的含义,例如 UART0_CONTROL |= _BV(UART0_UDRIE);之前我以为开启UartTx中断位,也会导致进入中断函数/向量……所以还是不明白你的解决方案…… @datahaki 我认为将 AVR C 转换为 8051 C 然后将 8051 汇编转换为 8051 C 会更容易。 UART0_CONTROL |= _BV(UART0_UDRIE);是用于设置 UART 接收使能位的便携式方法。我相信相应的 8051 代码将是 EA=1;或者 ES0=1;? @datahaki 哎呀,UART0_CONTROL |= _BV(UART0_UDRIE);是用于设置 UART 中断启用位的便携式技术 @datahaki 关于启用 UartTX 并进入中断,这是设计的操作,因此它将开始传输字符。您需要验证这些位将如何在您的各种 8051 中工作。 如果 UART0_CONTROL |= _BV(UART0_UDRIE);只使能 uart 中断,即类似于 ES0=1;那么第一个字节是如何发送的。毕竟中断只有在发送一个字节后才会触发,或者通过设置TI0=1;【参考方案2】:

我的同事郭雄发现了错误:变量UART0_tx_available没有在正确的地方增加和减少。以下是经过修正和测试的版本:

#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;
        
  

【讨论】:

如果在您的 if (UART0_tx_available) 块内任何时候发生 tx 中断并且 UART0_tx_available 的值 == 1,则中断例程会将值降为零并关闭中断,从而导致传输停止.这将是需要数天(数周)才能解决的随机故障类型。 刚刚意识到您正在禁用中断,因此如果出现这种情况,您的 while 将无限期阻塞,因为您的中断已关闭。 “如果 tx 中断在您的 if (UART0_tx_available) 块(UART0_putChar() 函数(?))内随时发生......”因为中断被禁用,所以不会发生。代码运行良好。 “你正在禁用中断,你的 while 将无限期地阻塞......”。在 while 循环结束之前会启用中断。 太棒了!我分析过度了。

以上是关于通过 8051 微控制器上的中断进行 UART 传输的主要内容,如果未能解决你的问题,请参考以下文章

串口中断优先级最高的是

微控制器的环形缓冲区

将 BLE 模块连接到微控制器

如何在 8051 程序集中的内存字段中右对齐 ASCII 字符串

CC2530微控制器与IAR开发环境

LPC2138微控制器之I2C