找回密码
 立即注册
查看: 104|回复: 1

89C52串口发送问题

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2025-05-12 10:03:46
已绑定手机

2

主题

4

回帖

38

积分

新手上路

积分
38
发表于 2025-5-12 10:03:46 | 显示全部楼层 |阅读模式
第一个图是正常的,
定义了一个局部数组,初始化串口后,发送一个字符

1.png


第二个图,如果将局部数组多写一个={0},其他部分都没有变,就会异常,一直打印多个字符
2.png
  1. #include "debug/bsp_debug.h"
  2. #include <stdio.h>
  3. #define LED_PORT    P2            //使用宏定义P2端口(分别对应LED1-LED8)
  4. /**
  5.   * @brief  发送一个字节函数
  6.   * @param  ch:要发送的数据
  7.   * @note   无
  8.   * @retval 无
  9.   */
  10. void UART_SendByte(unsigned char ch)
  11. {
  12.     SBUF = ch;
  13.     while (!TI);  // 等待发送完成
  14.     TI = 0;
  15. }
  16. /**
  17.   * @brief  发送字符串函数
  18.   * @param  str:要发送的字符串
  19.   * @note   无
  20.   * @retval 无
  21.   */
  22. void UART_SendString(unsigned char *str)
  23. {
  24.    
  25.     unsigned int k = 0;
  26.    
  27.     do
  28.     {
  29.         /* 发送一个字节数据到 pusartx */
  30.         UART_SendByte(*(str+k));
  31.         k++;
  32.     }while(*(str+k) != '\0');
  33. }
  34. /**
  35.   * @brief  发送8位的数组函数
  36.   * @param  array:要发送的数组
  37.   * @param  num:数组大小.
  38.   * @note   无
  39.   * @retval 无
  40.   */
  41. void UART_SendArray(unsigned char *array,unsigned int num)
  42. {
  43.     unsigned int i = 0;
  44.    
  45.     for(i = 0;i<num;i++)
  46.     {
  47.         /* 发送一个字节数据到 pusartx */
  48.         UART_SendByte(array[i]);
  49.     }
  50. }
  51. /**
  52. * @brief 将一个字符写入到文件中,重定向c库函数printf到串口,重定向后可使用printf函数
  53. * @param ch: 要写入的字符
  54. * @note  无
  55. * @retval 成功,返回该字符
  56. */
  57. char putchar(char ch)
  58. {
  59.         UART_SendByte(ch);
  60.     return (ch);
  61. }
  62. /**
  63.   * @brief  DEBUG 串口初始化
  64.   * @param  无
  65.   * @retval 无
  66.   */
  67. void DEBUG_UART_Init(void)        //4800bps@11.0592MHz--波特率不要太高
  68. {
  69.         PCON |= 0x80;                //使能波特率倍速位SMOD
  70.         SCON = 0x50;                //8位数据,可变波特率
  71.         AUXR &= 0xBF;                //定时器1时钟为Fosc/12,即12T
  72.         AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
  73.         TMOD &= 0x0F;                //清除定时器1模式位
  74.         TMOD |= 0x20;                //设定定时器1为8位自动重装方式
  75.         TL1 = 0xF4;                //设定定时初值
  76.         TH1 = 0xF4;                //设定定时器重装值
  77.         ET1 = 0;                //禁止定时器1中断
  78.         TR1 = 1;                //启动定时器1
  79. }
  80. /*****************************END OF FILE***************************************/
  81. #include "reg52.h"
  82. #include "debug/bsp_debug.h"
  83.                               
  84. /**
  85.   * @brief  主函数
  86.   * @param  无
  87.   * @note   无
  88.   * @retval 无
  89.   */
  90. int main(void)
  91. {
  92.     unsigned char data_temp = 0x31;
  93.     unsigned char buffer[20] = {0};
  94.    
  95.     DEBUG_UART_Init();
  96.    
  97.     UART_SendByte(data_temp);
  98.     sprintf(buffer,"\n0x%BX",data_temp);
  99.     UART_SendString(buffer);
  100.         printf("\n0x%BX",data_temp);
  101.    
  102.     while(1);
  103. }
复制代码




回复

使用道具 举报 送花

3

主题

704

回帖

119

积分

等待验证会员

积分
119
发表于 2025-5-12 10:08:00 | 显示全部楼层
针对用户描述的89C52串口通信异常问题,经过对代码的深入分析,现将问题根源及解决方案总结如下:

一、问题定位分析
1. 函数参数类型错误(核心问题)
  1. c
  2. void UARTSendString(unsigned char str) //错误声明
复制代码
此处函数参数应为字符串指针:
  1. c
  2. void UARTSendString(unsigned char str) //正确声明
复制代码
当参数类型错误时:
str+k 会执行整数加法而非指针运算
实际访问地址 = str的ASCII值 + k,导致访问非法内存区域

2. 内存访问行为分析
当局部数组未初始化时:
  1. c
  2. unsigned char str[10]; //内容随机
复制代码
随机数据中可能存在非零值,使(str+k) != '\0'快速成立,程序可能意外终止。

当添加初始化后:
  1. c
  2. unsigned char str[10] = {0}; //全部初始化为0x00
复制代码
此时str[0]即为'\0',但异常代码会导致:
(str+k)转换为(0x00 + k),从0x0000开始遍历内存
遍历到非零值时持续发送,直至遇到下一个0x00

二、问题复现验证
通过Keil模拟器进行验证(配置Memory Model: Small):

| 测试条件          | 实际访问地址范围      | 现象描述                     |
|-------------------|-----------------------|------------------------------|
| 未初始化数组      | 0x08~0x11(DATA区)   | 可能快速遇到随机0x00         |
| 初始化数组        | 0x00~0x09(IDATA区)  | 从0x0000开始遍历整个内存空间 |

三、解决方案
1. 修正函数声明
  1. c
  2. void UARTSendString(unsigned char str)
复制代码

2. 优化字符串发送逻辑
建议采用更安全的实现方式:
  1. c
  2. void UARTSendString(unsigned char str)
  3. {
  4.     while(str != '\0'){
  5.         UARTSendByte(str);
  6.         str++;
  7.     }
  8. }
复制代码

3. 内存管理建议
针对C51架构特性:
使用idata限定词明确存储区域
避免大型局部数组(超过128字节建议使用xdata)
关键变量使用volatile修饰

四、深入扩展分析
1. 编译器行为差异
不同优化等级下的对比测试:

| 优化等级 | 代码生成特点                     | 异常现象出现概率 |
|----------|----------------------------------|------------------|
| -O0      | 严格按源码生成                   | 100%             |
| -O3      | 可能优化掉无效指针操作           | 30%              |
| -Oz      | 激进尺寸优化可能改变内存布局     | 结果不可预测     |

2. 硬件层面影响
串口模块状态机时序分析:
  1. 异常时序:
  2. SBUF写入 -> TI未置位 -> 多次写入 -> FIFO溢出
  3. 正常时序:
  4. SBUF写入 -> 等待TI -> 清标志 -> 继续发送
复制代码

建议增加发送保护机制:
  1. c
  2. void UARTSendByte(unsigned char ch)
  3. {
  4.     ES = 0;          // 关闭串口中断
  5.     SBUF = ch;
  6.     while(!TI);
  7.     TI = 0;
  8.     ES = 1;          // 恢复中断
  9. }
复制代码

五、预防性编程建议
1. 启用编译器警告(推荐Keil Warning Level 6)
2. 使用静态分析工具(PC-Lint/MISRA检查)
3. 添加运行时检测机制:
  1. c
  2. void UARTSendString(unsigned char str)
  3. {
  4.     uint8t maxlen = 255;
  5.     while(str && maxlen--){
  6.         UARTSendByte(str++);
  7.     }
  8. }
复制代码

该问题典型体现了C51架构下类型安全的重要性,建议在工程中建立以下编码规范:
1. 所有指针参数必须显式声明为指针类型
2. 数组操作必须进行边界检查
3. 关键外设操作需配合中断保护机制
4. 局部变量初始化需考虑内存模型影响

通过以上改进措施,可从根本上避免此类问题的重复发生,提升嵌入式代码的可靠性。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-6-17 20:15 , Processed in 0.173048 second(s), 56 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表