张旗红 发表于 2025-9-29 10:30:09

STC15W408AS串口调试不通

使用的芯片是STC15W408AS,使用ISP的CDC/HID串口助手,只有发送没有接收,串口调不通。软件代码如下:


#include <STC15.h>
#include <intrins.h>

typedef unsigned int u16;       
typedef unsigned char u8;

sbit Beep = P1^6;// 定义LED控制引脚
void UART_SendByte(u8 dat);
void UART_SendString(const char *s);

bit tx_busy = 0;// 发送忙标志

void Uart1_Init(void)        //115200bps@24.000MHz
{
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x40;                //定时器时钟1T模式
        AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                //设置定时器模式
        TL1 = 0xCC;                        //设置定时初始值
        TH1 = 0xFF;                        //设置定时初始值
        ET1 = 0;                        //禁止定时器中断
        TR1 = 1;                        //定时器1开始计时
        ES = 1;                          //打开接收中断
        EA = 1;                          //打开总中断
}

void main()
{       
        u16 i;
        Beep=0;
        Uart1_Init();
       
        while(1)
        {               
                Beep=0;       
                UART_SendString("System Started!\r\n");
                for(i = 0; i < 30000; i++)
                {
                  _nop_();
                }
                Beep=1;
               
        }       
       
}

// 发送字符串函数
void UART_SendString(const char *s)
{
    while(*s)          // 遍历字符串直到结束符
    {
      UART_SendByte(*s++); // 发送每个字符
    }
}

// 发送单字节函数
void UART_SendByte(u8 dat)
{
    while(tx_busy);   // 等待前一个字节发送完成
    tx_busy = 1;      // 设置发送忙标志
    SBUF = dat;       // 启动发送

}


//=======================串口中断========================
void uart() interrupt 4 // 串口通信中断函数
{
    u8 rec_data;
        if(RI)
    {
                RI = 0;                          // 清除接收中断标志位
                rec_data = SBUF;        // 存储接收到的数据
                SBUF = rec_data;        // 将接收到的数据放入发送寄存器
        }
       
        if(TI)
    {
                TI = 0;                          // 清除发送完成标志位
                tx_busy = 0;
        }
}


国芯人工智能 发表于 2025-9-29 10:33:00

你好,关于你使用 STC15W408AS 芯片进行串口通信调试过程中出现“只有发送没有接收”的问题,下面我将从硬件、软件、配置、调试工具等多个方面进行分析,并给出可能的排查建议和优化方向。

一、问题描述总结

你当前使用的芯片为 STC15W408AS,使用 ISP 的 CDC/HID 串口助手进行串口通信调试,发现:
发送功能正常(可以打印“System Started!”);
接收无响应(没有收到任何数据);
已开启接收中断(ES = 1),也开启了总中断(EA = 1);
使用的是 串口1(UART1),波特率为 115200 bps,晶振为 24MHz。

二、代码分析

我们先来看你的串口初始化函数:

c
void Uart1Init(void)        //115200bps@24000MHz
{
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x40;                //定时器时钟1T模式
        AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                //设置定时器模式
        TL1 = 0xCC;                        //设置定时初始值
        TH1 = 0xFF;                        //设置定时初始值
        ET1 = 0;                        //禁止定时器中断
        TR1 = 1;                        //定时器1开始计时
        ES = 1;                          //打开接收中断
        EA = 1;                          //打开总中断
}

这段代码看起来是标准的 STC15 系列 UART 初始化配置,但有几个细节需要进一步确认:

1. 波特率是否准确?

你使用的是 24MHz 晶振,波特率为 115200 bps,使用定时器1在1T模式下作为波特率发生器。

计算公式如下:

Baud = Fosc / (3212(256 - TH1))

代入你设置的 TH1 = 0xFF, TL1 = 0xCC:

TH1 = 0xFF, TL1 = 0xCC → 重载值为 0xFFCC → 十进制为 65484
256 - (65484 / 256) ≈ 256 - 255.816 ≈ 0.184
Baud = 24000000 / (32120.184) ≈ 33690 bps

结论:波特率设置错误!

你当前设置的定时器初值无法达到 115200 bps,实际波特率约为 33k bps,与串口助手设置的 115200 不匹配,会导致接收失败。

✅ 建议:重新计算正确的波特率初值

推荐使用 STC 官方提供的 波特率计算工具或以下公式重新设置:

TH1 = TL1 = 256 - Fosc / (3212Baud)

代入 Fosc = 24MHz,Baud = 115200:

TH1 = TL1 = 256 - 24000000 / (3212115200)
   ≈ 256 - 24000000 / 4423680
   ≈ 256 - 5.426 ≈ 250.574 → 取 250 → 0xFA

因此,应修改为:

c
TL1 = 0xFA;
TH1 = 0xFA;

三、接收中断未定义处理函数

你在代码中开启了接收中断:

c
ES = 1;
EA = 1;

但你没有提供接收中断服务函数,如:

c
void UART1ISR(void) interrupt 4
{
    if(RI)
    {
      RI = 0;
      // 处理接收数据
    }
    if(TI)
    {
      TI = 0;
      txbusy = 0;
    }
}

问题: 没有定义中断服务函数,会导致中断触发后无法处理,甚至程序跑飞。

✅ 建议:补充串口接收中断服务函数

例如:

c
void UART1ISR(void) interrupt 4
{
    if(RI)
    {
      RI = 0;
      u8 dat = SBUF;
      // 可以回显或者处理接收到的数据
      UARTSendByte(dat);
    }
    if(TI)
    {
      TI = 0;
      txbusy = 0;
    }
}

四、硬件连接问题排查

1. 串口引脚是否正确连接?

STC15W408AS 的串口1引脚为:
P3.0(RXD):接收;
P3.1(TXD):发送。

请确认:
你是否将 TXD 接到 USB 转 TTL 模块的 RX;
RXD 是否接到 USB 模块的 TX;
GND 是否共地;
电平是否匹配(3.3V / 5V);

2. 使用的是 ISP 下载口自带的串口?

有些 ISP 下载器(如 STC-USB)内部使用的是虚拟串口(CDC),在使用时需注意:
驱动是否安装;
串口号是否正确;
是否与其他串口冲突;
是否支持全双工通信(有些只支持下载,不支持调试);

五、调试建议

1. 使用逻辑分析仪或示波器查看 TXD 波形
确认 TXD 是否有发送数据;
查看波特率是否与设置一致;
判断是否因电平、波特率不匹配导致接收失败。

2. 使用串口助手发送数据测试
手动发送“HELLO”等字符;
观察是否能触发接收中断;
在中断中添加 LED 闪烁或打印回显判断是否收到数据。

六、完整修正后的代码示例

c
include
include

typedef unsigned int u16;       
typedef unsigned char u8;

sbit Beep = P1^6;

bit txbusy = 0;

void UARTSendByte(u8 dat)
{
    while(txbusy);      // 等待上次发送完成
    SBUF = dat;
    txbusy = 1;
}

void UARTSendString(const char s)
{
    while(s)
    {
      UARTSendByte(s++);
    }
}

void Uart1Init(void)
{
    SCON = 0x50;         // 8位数据,可变波特率
    AUXR |= 0x40;          // 定时器时钟为1T模式
    AUXR &= 0xFE;          // 串口1选择定时器1为波特率发生器
    TMOD &= 0x0F;          // 设置定时器模式
    TL1 = 0xFA;            // 115200@24MHz
    TH1 = 0xFA;
    ET1 = 0;               // 禁止定时器1中断
    TR1 = 1;               // 启动定时器1
    ES = 1;                // 使能串口中断
    EA = 1;                // 使能全局中断
}

void UART1ISR(void) interrupt 4
{
    if(RI)
    {
      RI = 0;
      u8 dat = SBUF;
      UARTSendByte(dat);// 回显
    }
    if(TI)
    {
      TI = 0;
      txbusy = 0;
    }
}

void main()
{
    u16 i;
    Beep = 0;
    Uart1Init();

    while(1)
    {
      Beep = 0;       
      UARTSendString("System Started!\r\n");
      for(i = 0; i < 30000; i++);
    }
}

七、总结

| 问题点 | 原因 | 解决方案 |
|--------|------|-----------|
| 波特率设置错误 | TH1/TL1 初值不匹配 115200 | 修改为 0xFA |
| 无接收中断处理 | 没有定义中断函数 | 添加 UART1ISR 函数 |
| 硬件连接问题 | 引脚接错或电平不匹配 | 检查串口连接、电平、共地 |
| 调试工具问题 | ISP 串口不支持接收 | 使用独立 USB 转 TTL 模块 |

八、扩展建议
使用串口调试助手(如 XCOM、SSCOM)进行收发测试;
使用串口缓冲区实现更复杂的接收逻辑;
使用定时器或延时函数控制发送频率;
若需更高波特率或稳定通信,可考虑使用 定时器2 或 独立波特率发生器模块(部分 STC15 支持)。

如仍有问题,欢迎提供更详细的硬件连接图、串口助手截图或波形图,我可以进一步协助排查。

祝调试顺利!

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

ercircle 发表于 2025-9-29 11:15:29

注意此型号无定时器1

SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x01;                //串口1选择定时器2为波特率发生器
        AUXR |= 0x04;                //定时器时钟1T模式
        T2L = 0xCC;                        //设置定时初始值
        T2H = 0xFF;                        //设置定时初始值
        AUXR |= 0x10;                //定时器2开始计时
        ES = 1;                                //使能串口1中断
       
      EA = 1;                            //打开总中断

张旗红 发表于 2025-9-29 14:47:01

ercircle 发表于 2025-9-29 11:15
注意此型号无定时器1
我把定时器改为T2了,重新修改了代码,但是和之前现象一样,也没有发送数据的回显,也没有接收,能帮我再看下代码吗,有劳您?代码如下:

#include <STC15.h>
#include <intrins.h>

typedef unsigned int u16;      
typedef unsigned char u8;

sbit Beep = P1^6;// 定义LED控制引脚
void UART_SendByte(u8 dat);
void UART_SendString(const char *s);

bit tx_busy = 0;// 发送忙标志

void Uart1_Init(void)      //9600bps @24.000MHz
{
      SCON = 0x50;                //8位数据,可变波特率
      //PCON = 0x00;                //波特率不加倍
      AUXR = 0x05;                //定时器时钟1T模式 串口1选择定时器2为波特率发生器
      T2L = 0x8F;                        //设置定时初始值
      T2H = 0xFD;                        //设置定时初始值
      AUXR |= 0x10;                //启动定时器2
      ES = 1;                            //打开接收中断
      EA = 1;                            //打开总中断
}

void main()
{      
      u16 i;
      Beep=0;
      Uart1_Init();
      
      while(1)
      {               
                Beep=0;      
                UART_SendString("System Started!\r\n");
                for(i = 0; i < 30000; i++)
                {
                  _nop_();
                }
                Beep=1;
               
      }      
      
}

// 发送字符串函数
void UART_SendString(const char *s)
{
    while(*s)          // 遍历字符串直到结束符
    {
      UART_SendByte(*s++); // 发送每个字符
    }
}

// 发送单字节函数
void UART_SendByte(u8 dat)
{
    while(tx_busy);   // 等待前一个字节发送完成
    tx_busy = 1;      // 设置发送忙标志
    SBUF = dat;       // 启动发送

}


//=======================串口中断========================
void uart() interrupt 4 // 串口通信中断函数
{
    u8 rec_data;
      if(RI)
    {
                RI = 0;                            // 清除接收中断标志位
                rec_data = SBUF;      // 存储接收到的数据
                UART_SendByte(rec_data);
                SBUF = rec_data;      // 将接收到的数据放入发送寄存器
      }
      
      if(TI)
    {
                TI = 0;                            // 清除发送完成标志位
                tx_busy = 0;
      }
}
串口情况如下:



ercircle 发表于 2025-9-29 15:13:34

张旗红 发表于 2025-9-29 14:47
我把定时器改为T2了,重新修改了代码,但是和之前现象一样,也没有发送数据的回显,也没有接收,能帮我再 ...

早上在15W408AS测过的代码,24M下载,波特率115200,还不行的话检查下硬件连接:

#include <STC15.h>
#include <intrins.h>

typedef unsigned int u16;      
typedef unsigned char u8;

sbit Beep = P1^6;// 定义LED控制引脚
void UART_SendByte(u8 dat);
void UART_SendString(const char *s);

bit tx_busy = 0;// 发送忙标志

void Uart1_Init(void)      //115200bps@24.000MHz
{
//      SCON = 0x50;                //8位数据,可变波特率
//      AUXR |= 0x40;                //定时器时钟1T模式
//                                AUXR |= 0x01;                //串口1选择定时器2为波特率发生器

//      //AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
//      TMOD &= 0x0F;                //设置定时器模式
//      TL1 = 0xCC;                        //设置定时初始值
//      TH1 = 0xFF;                        //设置定时初始值
//      ET1 = 0;                        //禁止定时器中断
//      TR1 = 1;                        //定时器1开始计时
//      ES = 1;                            //打开接收中断
       
                SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x01;                //串口1选择定时器2为波特率发生器
        AUXR |= 0x04;                //定时器时钟1T模式
        T2L = 0xCC;                        //设置定时初始值
        T2H = 0xFF;                        //设置定时初始值
        AUXR |= 0x10;                //定时器2开始计时
        ES = 1;                                //使能串口1中断
       
      EA = 1;                            //打开总中断
}

void main()
{      
      u16 i;
      Beep=0;
      Uart1_Init();
      
      while(1)
      {               
                Beep=0;      
                UART_SendString("System Started!\r\n");
                for(i = 0; i < 30000; i++)
                {
                  _nop_();
                }
                Beep=1;
               
      }      
      
}

// 发送字符串函数
void UART_SendString(const char *s)
{
    while(*s)          // 遍历字符串直到结束符
    {
      UART_SendByte(*s++); // 发送每个字符
    }
}

// 发送单字节函数
void UART_SendByte(u8 dat)
{
    while(tx_busy);   // 等待前一个字节发送完成
    tx_busy = 1;      // 设置发送忙标志
    SBUF = dat;       // 启动发送

}


//=======================串口中断========================
void uart() interrupt 4 // 串口通信中断函数
{
    u8 rec_data;
      if(RI)
    {
                RI = 0;                            // 清除接收中断标志位
                rec_data = SBUF;      // 存储接收到的数据
                SBUF = rec_data;      // 将接收到的数据放入发送寄存器
      }
      
      if(TI)
    {
                TI = 0;                            // 清除发送完成标志位
                tx_busy = 0;
      }
}

张旗红 发表于 2025-9-29 17:06:41

ercircle 发表于 2025-9-29 15:13
早上在15W408AS测过的代码,24M下载,波特率115200,还不行的话检查下硬件连接:



我用你测试的这个代码也测了一下,我只发了数字9,能正常进入中断和发送波形,但是串口助手选择了串口,设置波特率,打开串口后,却没有任何接收。麻烦帮我再看下哈,

串口界面如下(帮看下是否有设置不对的地方):

ercircle 发表于 2025-9-29 17:12:49

张旗红 发表于 2025-9-29 17:06
我用你测试的这个代码也测了一下,我只发了数字9,能正常进入中断和发送波形,但是串口助手选择了串口,设 ...

按你编写的逻辑这个代码正常运行应一直在发送:

System Started!\r\n


收到PC消息后原文回传。


可以发下硬件原理图和实物连接看看

张旗红 发表于 2025-9-29 17:28:24

硬件原理图:P31和P30复用为IIC的时钟线和数据线,但现在应该不影响串口,

实物连接如下:



另外打开串口前,使用外部电源给板子供电。

ercircle 发表于 2025-9-29 17:57:37

张旗红 发表于 2025-9-29 17:28
硬件原理图:P31和P30复用为IIC的时钟线和数据线,但现在应该不影响串口,

实物连接如下:



这工具看着是短接的I2C当前不在串口模式?

先换个不复用的板子和常规串口工具测吧,测正常了再替换图里这个工具和板子,对比差异。

张旗红 发表于 2025-9-29 18:01:31

ercircle 发表于 2025-9-29 17:57
这工具看着是短接的I2C当前不在串口模式?

先换个不复用的板子和常规串口工具测吧,测正常了再替换图里 ...

换了一个串口工具测试,串口助手可以正常接收mcu发来的数据,在接收数据之前需要在串口助手的发送窗预先发送一个信令,以启动串口助手的接收。
所以,ISP的CDC/HID助手是否也需要一个类似的启动接收信令啊{:wunai:}
页: [1] 2
查看完整版本: STC15W408AS串口调试不通