通过 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 传输的主要内容,如果未能解决你的问题,请参考以下文章