89C52串口发送问题
第一个图是正常的,定义了一个局部数组,初始化串口后,发送一个字符
第二个图,如果将局部数组多写一个={0},其他部分都没有变,就会异常,一直打印多个字符
#include "debug/bsp_debug.h"
#include <stdio.h>
#define LED_PORT P2 //使用宏定义P2端口(分别对应LED1-LED8)
/**
* @brief发送一个字节函数
* @paramch:要发送的数据
* @note 无
* @retval 无
*/
void UART_SendByte(unsigned char ch)
{
SBUF = ch;
while (!TI);// 等待发送完成
TI = 0;
}
/**
* @brief发送字符串函数
* @paramstr:要发送的字符串
* @note 无
* @retval 无
*/
void UART_SendString(unsigned char *str)
{
unsigned int k = 0;
do
{
/* 发送一个字节数据到 pusartx */
UART_SendByte(*(str+k));
k++;
}while(*(str+k) != '\0');
}
/**
* @brief发送8位的数组函数
* @paramarray:要发送的数组
* @paramnum:数组大小.
* @note 无
* @retval 无
*/
void UART_SendArray(unsigned char *array,unsigned int num)
{
unsigned int i = 0;
for(i = 0;i<num;i++)
{
/* 发送一个字节数据到 pusartx */
UART_SendByte(array);
}
}
/**
* @brief 将一个字符写入到文件中,重定向c库函数printf到串口,重定向后可使用printf函数
* @param ch: 要写入的字符
* @note无
* @retval 成功,返回该字符
*/
char putchar(char ch)
{
UART_SendByte(ch);
return (ch);
}
/**
* @briefDEBUG 串口初始化
* @param无
* @retval 无
*/
void DEBUG_UART_Init(void) //4800bps@11.0592MHz--波特率不要太高
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
/*****************************END OF FILE***************************************/
#include "reg52.h"
#include "debug/bsp_debug.h"
/**
* @brief主函数
* @param无
* @note 无
* @retval 无
*/
int main(void)
{
unsigned char data_temp = 0x31;
unsigned char buffer = {0};
DEBUG_UART_Init();
UART_SendByte(data_temp);
sprintf(buffer,"\n0x%BX",data_temp);
UART_SendString(buffer);
printf("\n0x%BX",data_temp);
while(1);
}
针对用户描述的89C52串口通信异常问题,经过对代码的深入分析,现将问题根源及解决方案总结如下:
一、问题定位分析
1. 函数参数类型错误(核心问题)
c
void UARTSendString(unsigned char str) //错误声明
此处函数参数应为字符串指针:
c
void UARTSendString(unsigned char str) //正确声明
当参数类型错误时:
str+k 会执行整数加法而非指针运算
实际访问地址 = str的ASCII值 + k,导致访问非法内存区域
2. 内存访问行为分析
当局部数组未初始化时:
c
unsigned char str; //内容随机
随机数据中可能存在非零值,使(str+k) != '\0'快速成立,程序可能意外终止。
当添加初始化后:
c
unsigned char str = {0}; //全部初始化为0x00
此时str即为'\0',但异常代码会导致:
(str+k)转换为(0x00 + k),从0x0000开始遍历内存
遍历到非零值时持续发送,直至遇到下一个0x00
二、问题复现验证
通过Keil模拟器进行验证(配置Memory Model: Small):
| 测试条件 | 实际访问地址范围 | 现象描述 |
|-------------------|-----------------------|------------------------------|
| 未初始化数组 | 0x08~0x11(DATA区) | 可能快速遇到随机0x00 |
| 初始化数组 | 0x00~0x09(IDATA区)| 从0x0000开始遍历整个内存空间 |
三、解决方案
1. 修正函数声明
c
void UARTSendString(unsigned char str)
2. 优化字符串发送逻辑
建议采用更安全的实现方式:
c
void UARTSendString(unsigned char str)
{
while(str != '\0'){
UARTSendByte(str);
str++;
}
}
3. 内存管理建议
针对C51架构特性:
使用idata限定词明确存储区域
避免大型局部数组(超过128字节建议使用xdata)
关键变量使用volatile修饰
四、深入扩展分析
1. 编译器行为差异
不同优化等级下的对比测试:
| 优化等级 | 代码生成特点 | 异常现象出现概率 |
|----------|----------------------------------|------------------|
| -O0 | 严格按源码生成 | 100% |
| -O3 | 可能优化掉无效指针操作 | 30% |
| -Oz | 激进尺寸优化可能改变内存布局 | 结果不可预测 |
2. 硬件层面影响
串口模块状态机时序分析:
异常时序:
SBUF写入 -> TI未置位 -> 多次写入 -> FIFO溢出
正常时序:
SBUF写入 -> 等待TI -> 清标志 -> 继续发送
建议增加发送保护机制:
c
void UARTSendByte(unsigned char ch)
{
ES = 0; // 关闭串口中断
SBUF = ch;
while(!TI);
TI = 0;
ES = 1; // 恢复中断
}
五、预防性编程建议
1. 启用编译器警告(推荐Keil Warning Level 6)
2. 使用静态分析工具(PC-Lint/MISRA检查)
3. 添加运行时检测机制:
c
void UARTSendString(unsigned char str)
{
uint8t maxlen = 255;
while(str && maxlen--){
UARTSendByte(str++);
}
}
该问题典型体现了C51架构下类型安全的重要性,建议在工程中建立以下编码规范:
1. 所有指针参数必须显式声明为指针类型
2. 数组操作必须进行边界检查
3. 关键外设操作需配合中断保护机制
4. 局部变量初始化需考虑内存模型影响
通过以上改进措施,可从根本上避免此类问题的重复发生,提升嵌入式代码的可靠性。
[本文内容由国芯人工智能辅助生成,仅供参考]
页:
[1]