hp2025 发表于 2025-5-9 16:17:13

请教下串口打印的问题

我在main()里面想上电的时候打印hell字符串,但是实际串口那边什么也没有或者是乱码(比如一个←的符号),代码如下


#include "main.h"
void main(void)
{       
        u8 current=10;
        u16 p;
        u8 f_scan=scan_start;//扫频值初始化
        io_init();
        pwm_init();
        UART_init();
        OLED_Init();//初始化OLED
        OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
        display_0();//开机界面
        delay_ms(1000);
OLED_Clear();//清屏
        printf("hell");
        while(1)
        {               
//    pwm_sweep(&pwm_f_d13,0.2);
               
//                OLED_DrawBMP(0,0,128,64,BMP1);
    display_mode4();//直径16雾化片固定显示界面
                current++;
                f_scan++;
                p=current*6;
                if(current>254)current=10;
                if(f_scan>scan_end)f_scan=scan_start;
                OLED_ShowNum(25,6,p,4,16);//显示功率值
                OLED_ShowNum(98,6,current,3,16);//显示电流值
                OLED_ShowNum(25,4,f_scan,3,16);//显示扫频终值
               
        }          
}



#include "main.h"
#define FOSC 11059200UL   //系统外部时钟频率(无符号长整型)
#define BAUD 9600         //欲配置的串口通信波特率值

u8 ReceiveByte=0;                                                //全局变量ReceiveByte用于取回串口数据
u16 ReceiveByte_num=0;                  //全局变量ReceiveByte_num用来表示交互次数

/****************************************************************/
//串口初始化函数UART_init(),无形参,无返回值
/****************************************************************/
void UART_init(void)
{
        SCON=0x50;        //8位数据,可变波特率
        AUXR|=0x40;        //定时器1时钟为Fosc,即1T
        AUXR&=0xFE;        //串口1选择定时器1为波特率发生器
        TMOD&=0x0F;        //设定定时器1为16位自动重装方式
        TL1=(65536-(FOSC/4/BAUD));   //设置波特率重装值
TH1=(65536-(FOSC/4/BAUD))>>8;//设置波特率重装值
        ET1=0;                        //禁止定时器1中断
        TR1=1;                        //启动定时器1
ES=1;                                //使能UART中断开关ES
EA=1;                                //使能单片机总中断开关EA
}

/****************************************************************/
//串口发送单字节数据SendData(),有形参dat用于接收欲发送的单字节数据,
//无返回值
/****************************************************************/
void SendData(u8 dat)
{
        SBUF=dat;                        //发送数据到发送缓冲区内
        while(TI==0);        //等待串口数据发送完毕
        TI=0;                                        //清除发送完成标志位TI       
}
/****************************************************************/
//发送字符重定向函数putchar(),有形参ch有返回值char
/****************************************************************/
char putchar(char ch)
{
SendData((u8)ch);                //将Printf内容发往串口
return (ch);
}

/****************************************************************/
//串口中断服务函数UART_ISR(),无形参,无返回值
/****************************************************************/
void UART_ISR() interrupt 4 using 1
{
        if(RI)                                                                //如果串口接收到数据
        {
                RI=0;                                                         //清除接收数据标志位RI
                ReceiveByte=SBUF;                //从接收数据缓冲区取出数据赋值给变量
                ReceiveByte_num++;        //ReceiveByte_num用于指示串口交互次数
    switch(ReceiveByte)        //判断串口接收命令值
    {
      case '1':
      {
//         printf("|第%d次命令,",ReceiveByte_num);
        //                                printf("CMD=【1】,RED is ON!\r\n");
         
                                        //用户编写的程序
//         printf("|*************************************************\r\n");
      }break;
      case '2':
      {
//          printf("|第%d次命令,",ReceiveByte_num);
//                                        printf("CMD=【2】,GREEN is ON!\r\n");
         
                                        //用户编写的程序
//         printf("|*************************************************\r\n");
      }break;
                        case '3':
      {
//          printf("|第%d次命令,",ReceiveByte_num);
//                                        printf("CMD=【2】,BLUE is ON!\r\n");
      
                                        //用户编写的程序
//         printf("|*************************************************\r\n");
      }break;
      case '?':
      {
//         printf("|第%d次命令,",ReceiveByte_num);
//                                        printf("CMD=【?】,Welcome to use system help!\r\n");
         
                                        //用户编写的程序体
//          printf("|*************************************************\r\n");
      }break;
      default:
      {
//          printf("|第%d次命令,",ReceiveByte_num);
//                                        printf("【ERROR】The command is illegal,ALL OFF!\r\n");
                                       
                                        //用户编写的程序体
//         printf("|*************************************************\r\n");
      }
    }
        }
}


头文件

#ifndef __Interrupt_H
#define __Interrupt_H

void UART_init(void);
void SendData(unsigned char dat);
char putchar(char ch);


#endif





#ifndef __main_H
#define __main_H


#include "STC8h.h"       
#include "oled.h"
#include "oledfont.h"       
#include "bmp.h"
#include "io.h"
#include "intrins.h"
#include "stdio.h"//串口打印函数包含 打印调试信息
#include "pwm.h"
#include "ADC.h"
#include "Interrupt.h"
#include "Serial_print.h"

#defineu8 unsigned char
#defineu16 unsigned int
#defineu32 unsigned int

#endif

国芯人工智能 发表于 2025-5-9 16:18:00

针对您遇到的串口打印异常问题,结合代码结构和常见故障场景,建议从以下几个方面进行系统性排查:

一、串口初始化配置验证
1. 波特率一致性检查
   确认UARTinit()中设置的波特率与接收端(PC串口工具/逻辑分析仪)完全一致。常见误差源包括:
时钟源配置错误(如误用HSI未校准时钟)
分频系数计算误差(建议使用波特率计算公式验证)
   c
   // 示例:UART时钟分频计算(需适配具体主频)
   #define FCPU 8000000UL// 假设系统主频8MHz
   #define BAUD 115200
   uartdiv = (FCPU + (BAUD/2)) / BAUD;// 四舍五入计算分频值

2. 数据帧格式匹配性
   检查数据位(8/9bit)、停止位(1/2bit)、校验位(None/Even/Odd)是否与接收端严格匹配。典型配置示例:
   c
   USARTInitTypeDef USARTInitStruct;
   USARTInitStruct.USARTBaudRate = 115200;
   USARTInitStruct.USARTWordLength = USARTWordLength8b;
   USARTInitStruct.USARTStopBits = USARTStopBits1;
   USARTInitStruct.USARTParity = USARTParityNo;
   USARTInitStruct.USARTMode = USARTModeTx;// 需显式使能发送模式

二、标准输出重定向实现
1. 重定向printf到串口
   需在工程中重写write或fputc函数(不同编译器实现方式不同):
   c
   // GCC编译器重定向示例
   int write(int fd, char ptr, int len) {
       (void)fd;
       while(ptr && len--) {
         USARTSendData(USART1, ptr++);
         while(USARTGetFlagStatus(USART1, USARTFLAGTXE) == RESET);
       }
       return len;
   }

2. MicroLib优化支持
   若使用Keil MDK,请确认勾选Use MicroLib选项(Project -> Options -> Target)。此优化库可显著减少printf内存占用。

三、硬件链路诊断
1. 物理层信号检测
使用示波器测量TX引脚波形,验证实际波特率(测量1bit时间=1/BAUD)
检查电平标准是否匹配(3.3V TTL与PC串口需经过电平转换)
交叉验证TX/RX线路(尝试交换测试)

2. 电源稳定性分析
   测量MCU供电电压纹波(建议

[本文内容由国芯人工智能辅助生成,仅供参考]

国芯人工智能 发表于 2025-5-9 16:18:00

针对您描述的串口通信异常问题,结合提供的代码片段分析,可能涉及以下几个技术要点需要排查:

一、基础配置检查
1. UART初始化验证
确认UARTinit()函数正确配置以下参数:
   c
   //示例配置参数(需根据实际硬件调整)
   #define BAUDRATE 115200       // 必须与接收端一致
   #define DATABITS 8            // 数据位
   #define STOPBITS 1            // 停止位
   #define PARITY    UARTParityNone // 校验位
检查时钟源配置(HSE/HSI),波特率计算公式是否与主频匹配:
   波特率 = APBxCLK / (USARTDIV16)
   若时钟分频系数计算错误,将直接导致通信失败

2. 硬件连接完整性
确认TX/RX引脚与串口模块物理连接正确(避免交叉错接)
使用示波器测量TX引脚波形,验证实际波特率是否与配置一致

二、输出重定向实现
1. 标准库重定向缺失
   c
   //需在工程中添加以下重定向代码(以ARMCC为例)
   int fputc(int ch, FILE f) {
       while(USARTGetFlagStatus(UARTx, USARTFLAGTXE) == RESET);
       USARTSendData(UARTx, (uint8t)ch);
       return ch;
   }
若未实现重定向,printf将无法通过串口输出
检查编译工具链是否启用微库(Microlib)特性

2. 缓冲区刷新机制
   c
   setvbuf(stdout, NULL, IONBF, 0); // 禁用缓冲区
添加此语句可避免因缓冲区未满导致的输出延迟

三、系统时序分析
1. 初始化顺序优化
   c
   int main(void) {
       SystemClockConfig(); // 必须优先初始化时钟
       HALInit();         // HAL库初始化
       UARTinit();          // 串口初始化需在printf前完成
       printf("Boot Message\r\n");
   }
确保时钟树配置正确,特别是USART外设时钟使能

2. 电源稳定性验证
测量MCU供电电压是否稳定(推荐范围:2.7-3.6V)
检查复位电路是否正常,异常复位可能导致外设状态异常

四、故障现象专项排查
1. 乱码定位方法
发送固定字节序列(如0x55/0xAA)验证波形
使用以下公式计算实际波特率偏差:
   误差百分比 = |(理论周期 - 实测周期)/理论周期| × 100%
   建议偏差控制在2%以内

2. 特殊字符解析异常
接收端软件(如Putty)需配置为相同编码格式(推荐UTF-8)
检查流控设置是否匹配(RTS/CTS使能状态)

五、增强代码健壮性
1. 添加状态检测机制
   c
   void SafePrintf(const char msg) {
       if(USARTGetFlagStatus(UARTx, USARTFLAGTXE)) {
         printf("%s", msg);
       }
   }
2. 异常处理建议
   c
   if(HALUARTGetState(&huart1) != HALUARTSTATEREADY) {
       ErrorHandler();
   }

六、推荐调试流程
1. 使用最小系统测试(仅保留UART相关功能)
2. 通过寄存器直接操作验证发送功能:
   c
   USARTx->DR = 'A'; // 直接写入数据寄存器
   while(!(USARTx->SR & USARTSRTC)); // 等待发送完成
3. 交叉验证硬件:更换串口模块或测试PC端接收软件

建议按照上述步骤进行系统性排查。若问题仍未解决,可进一步提供UART初始化代码段及硬件连接示意图以便深入分析。

[本文内容由国芯人工智能辅助生成,仅供参考]

国芯人工智能 发表于 2025-5-9 16:18:00

针对您描述的串口通信问题,结合提供的代码片段,以下是可能的原因分析及排查建议:

一、基础问题排查
1. 硬件连接验证
确认TX/RX引脚与USB转串口工具的接线方向正确(MCU TX→转接器RX,MCU RX→转接器TX)
检查串口工具供电电压是否与MCU电平匹配(3.3V/5V)
使用万用表测量TX引脚电压,发送数据时应能观察到电平跳变

2. 波特率一致性验证
检查UARTinit()函数中设置的波特率是否与上位机接收端完全一致
建议使用115200、9600等标准波特率进行测试
可通过示波器测量实际波特率误差(误差应DR寄存器的写入值
检查NVIC中相关中断是否被意外屏蔽

2. DMA冲突排查
   若使用DMA传输:
检查DMA通道配置是否正确
确认DMA优先级设置
查看DMA中断标志位状态

3. 电源完整性测试
   使用示波器捕获MCU供电电压:
上电瞬间电压跌落情况
发送数据时的纹波幅度(应

[本文内容由国芯人工智能辅助生成,仅供参考]

国芯人工智能 发表于 2025-5-9 16:18:00

针对您遇到的串口打印异常问题,结合代码及常见嵌入式开发经验,可从以下几个方面进行系统性排查:

一、串口初始化问题
1. 波特率一致性验证
   确保UARTinit()函数中设置的波特率与上位机工具(如串口助手、Putty等)的接收波特率完全一致。若存在±5%以上的偏差,将导致数据采样错误,表现为乱码或数据丢失。

2. 时钟源配置核查
   串口模块的时钟源(如HSE、HSI、PLL)必须与初始化代码中的时钟树配置匹配。若实际时钟频率与预设值不符,将导致波特率计算错误。建议通过以下方式验证:
   c
   // 在初始化后添加时钟频率输出
   printf("System Clock: %d Hz", SystemCoreClock);
   输出值应与预期时钟频率一致。

3. 数据帧格式检查
   确认UARTinit()中数据位(8/9位)、停止位(1/2位)、校验位(奇/偶/无)与上位机设置完全一致。典型配置示例:
   c
   // 8位数据位、无校验、1位停止位
   USARTInitStructure.USARTWordLength = USARTWordLength8b;
   USARTInitStructure.USARTParity = USARTParityNo;
   USARTInitStructure.USARTStopBits = USARTStopBits1;

二、标准输出重定向问题
1. 检查fputc重定向
   printf依赖fputc函数将字符发送至串口。若未重定向或重定向错误,将无输出。需在代码中添加:
   c
   #include
   int fputc(int ch, FILE f) {
       while (USARTGetFlagStatus(USART1, USARTFLAGTXE) == RESET);
       USARTSendData(USART1, (uint8t)ch);
       return ch;
   }
   注意替换USART1为实际使用的串口实例。

2. 启用MicroLib优化
   在Keil等IDE中,勾选Use MicroLib选项以适配嵌入式环境的标准库精简版本。未启用可能导致printf无法正常工作。

三、硬件链路排查
1. 物理连接验证
交叉检查TX/RX引脚是否与调试器连接正确(MCU TX接调试器RX,RX接TX)。
确认串口电平兼容性(如3.3V TTL与USB转串口模块匹配)。

2. 信号质量测试
   使用示波器或逻辑分析仪捕获串口TX引脚波形,验证实际波特率与数据帧格式。以115200bps为例,单个位宽应为8.68μs。

四、代码执行逻辑优化
1. 初始化顺序调整
   确保外设初始化顺序符合数据手册要求。建议按以下顺序:
   c
   void main(void) {
       SystemClockConfig();// 时钟配置优先
       ioinit();             // GPIO初始化
       UARTinit();         // 串口初始化
       // 其他外设初始化
       printf("hell");      // 初始化完成后输出
   }

2. 添加延时稳定性处理
   部分MCU需在初始化后等待时钟稳定,可添加短暂延时:
   c
   UARTinit();
   delayms(50);       // 等待串口模块稳定
   printf("hell");

3. 简化测试代码
   暂时屏蔽OLED、PWM等无关功能,构建最小测试系统:
   c
   void main(void) {
       UARTinit();
       while(1) {
         printf("test\r\n");
         delayms(1000);
       }
   }

五、编译环境配置
1. 检查优化等级
   避免使用-O3等高优化等级导致代码被异常裁剪。建议调试阶段设置为-O0。

2. 链接脚本验证
   确认堆栈空间充足(至少128字节),避免printf因栈溢出导致异常。

六、扩展调试建议
1. 中断冲突排查
   若使用中断接收数据,需检查中断优先级配置,避免与定时器、PWM等外设中断冲突。

2. DMA发送模式检查
   若使用DMA传输,需验证通道配置、传输完成标志及内存地址对齐。

通过以上步骤逐步排查,可定位问题根源。若仍无法解决,请提供UARTinit()函数实现及硬件型号,以便进一步分析。

[本文内容由国芯人工智能辅助生成,仅供参考]

ercircle 发表于 2025-5-9 17:43:37

上传完整工程和硬件原理图看看。
还有用例程试下。

王昱顺 发表于 2025-5-9 18:07:45

存在一个小问题,可能导致你所说的上电不打印


void SendData(u8 dat)
{
      SBUF=dat;                        //发送数据到发送缓冲区内
      while(TI==0);      //等待串口数据发送完毕
      TI=0;                                        //清除发送完成标志位TI      
}
你的发送串口数据使用的是等待TI标志位,但是你又使能了串口中断,同时
串口中断里没有清除TI标志位。

这样就会导致,发送第一个数据“SBUF=dat;”后,在“while(TI==0);”语句之前,就进入了串口中断,
因为进入了串口中断后并没有清除TI标志位,所以就会导致执行完串口中断程序后反复进入串口中断
进而执行不到TI=0;导致卡死
解决方法是使用一个发送忙标志位,SBUF赋值后将发送忙标志位置1,然后用while等待忙标志变成0。
同时在中断程序中判断TI状态,为1则清除TI状态并且将发送忙标志位清零
这样应该就可以正常了,这部分具体程序可以参考实验箱例程和核心板例程部分,里面的串口使用printf发送都是使用的这种方式

hp2025 发表于 2025-5-12 09:51:07

王昱顺 发表于 2025-5-9 18:07
存在一个小问题,可能导致你所说的上电不打印




感谢大师的经验分享,问题已经解决

hp2025 发表于 2025-5-12 10:26:34

国芯人工智能 发表于 2025-5-9 16:18
针对您遇到的串口打印异常问题,结合代码及常见嵌入式开发经验,可从以下几个方面进行系统性排查:

一、串 ...

写的很详细,问题已经解决

_奶咖君_ 发表于 2025-5-12 10:41:56

王昱顺 发表于 2025-5-9 18:07
存在一个小问题,可能导致你所说的上电不打印




emmm 看了眼自己的程序,,一直都是这样使用的,,中断中不处理TI标志位,主函数里轮询TI标志然后清零,,

按照你的说法,这样写是一定会死在反复进入中断这步骤,,那为什么我似乎一直没有遇到这个情况呢0.0
页: [1] 2
查看完整版本: 请教下串口打印的问题