我目前面临一个特殊问题,即使用 Keil C51 在 89C52 微控制器上进行 UART 传输并在 Proteus 中进行模拟。代码片段如下:
#include <reg52.h>
#include <intrins.h>
// HERE: When an array is declared then uart will send the first uart character twice,
unsigned long myArrayDeclaration[4] = { 0x00FFF789, };
// If the line above exists , UART will wrongly send "xxyz", instead of "xyz", nonsense.
sbit UART_RXD = P3^0; // RXD pin
sbit UART_TXD = P3^1; // TXD pin
void UART_Init() {
TMOD |= 0x20; // Timer 1, Mode 2 (8-bit auto-reload)
// TH1 = 0xFD; // Set baud rate for 9600 bps at 11.0592 MHz crystal
TH1 = 0xFA; // Set baud rate for 9600 bps at 22.1184 MHz crystal
TL1 = TH1;
TR1 = 1; // Start Timer 1
SCON = 0x50; // Set serial mode 1 (8-bit UART)
EA = 1; // Enable global interrupts
ES = 1; // Enable serial interrupt
}
void UART_TxString(const char *string) {
while (*string != '\0') {
SBUF = *string; // Send character
while (!TI); // Wait for transmit complete
TI = 0; // Clear transmit interrupt flag
string++; // Move to the next character
//_nop_(); // This makes no difference
}
}
void main() {
UART_Init(); // Init uart
// _nop_(); // This makes no difference
UART_TxString("xyz"); // Send 3 chars
while (1) {
}
}
我遇到的问题是,当声明一个数组时,例如
unsigned long myArrayDeclaration[4] = { 0x00FFF789, };
,UART 传输将第一个字符发送两次,导致“xxyz”而不是预期的“xyz”。如果我删除数组声明,传输就会正常工作。
任何类型的数组都会引发问题。
我使用 Keil C51 环境和 uVision 4 IDE 并在 Proteus 中模拟代码。微控制器是89C52芯片。
这里是一个错误再现最少的存储库,以及 Proteus 模拟:GITHUB REPO
我感谢任何关于如何解决 UART 传输异常的见解或建议。预先感谢您的帮助。
我已经尝试过:
UART_Init()
之后等待(很多),没有区别。错误是在没有提供中断服务程序(ISR)的情况下启用串行中断。
我是怎么找到这个的?
源码清楚地显示了串行中断的启用:
EA = 1; // Enable global interrupts
ES = 1; // Enable serial interrupt
但是源码没有定义中断服务程序。它看起来像这样:
void serial_isr(void) interrupt 4 {
/* statements */
}
89C52 的串行中断 ISR 在固定地址 0x0023 处启动,请参见数据手册。
下一步是查看链接器生成的映射文件以查找此处的内容。相关部分是这样的(缩短):
LINK MAP OF MODULE: firmware (?C_STARTUP)
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 008CH UNIT ?C_C51STARTUP
CODE 008FH 0032H UNIT ?PR?_UART_TXSTRING?MAIN
这个(缩短):
SYMBOL TABLE OF MODULE: firmware (?C_STARTUP)
VALUE TYPE NAME
----------------------------------
------- MODULE ?C_STARTUP
C:0003H SEGMENT ?C_C51STARTUP
C:0000H PUBLIC ?C_STARTUP
C:0006H SYMBOL IDATALOOP
C:0003H SYMBOL STARTUP1
C:0000H LINE# 126
C:0003H LINE# 133
C:0005H LINE# 134
C:0006H LINE# 135
C:0007H LINE# 136
C:0009H LINE# 185
C:000CH LINE# 196
------- ENDMOD ?C_STARTUP
------- MODULE MAIN
C:0000H SYMBOL _ICE_DUMMY_
C:00DAH PUBLIC UART_Init
C:008FH PUBLIC _UART_TxString
D:0018H SYMBOL string
0x0C 处的模块结尾
?C_STARTUP
和 0x8F 处的模块开头 MAIN
之间存在间隙。由于没有其他模块使用此空间,因此它用零填充。
零字节是
NOP
的机器代码指令。处理器不执行任何操作并前进到下一条指令。
事情是这样的:
TI
。UART_TxString()
在其 while
循环中被中断,没有机会看到 TI
设置。UART_TxString()
的第一条指令(请参阅映射文件的摘录)。string
位于静态固定位置0x18(请参阅映射文件的摘录)。string
仍然指向第一个字符,因此 SBUF
再次接收到 第一个字符。TI
仍处于设置状态,因为它不会自动重置。while
循环立即退出。 TI
由软件重置。SBUF
留在UART的移位寄存器中。所以下一个字符不会覆盖它。TI
时,不会触发中断服务例程,因为ISR不会中断自身。 (注:是的,你可以这样做,但需要相应的说明,这里没有。)
UART_TxString()
做了应该做的事情,发送
string
的所有字符。
while
循环。由于
TI
永远不会再次设置,因此它会永远循环。