找回密码
 立即注册
查看: 35|回复: 2

STC8H4K64TL通讯问题,急急急

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:6
  • 最近打卡:2026-04-27 18:35:57
已绑定手机

6

主题

2

回帖

34

积分

新手上路

积分
34
发表于 4 天前 | 显示全部楼层 |阅读模式
利用STC8H4K64TL,显示TVOC传感器发出的数据,为什么通讯不上?
传感器的RX接了单片机的P3.1, TX接了单片机的P3.0,  
并且是统一供电与接地。以下是测试程序


//<<AICUBE_USER_HEADER_REMARK_BEGIN>>
////////////////////////////////////////
// 文件名称: main.c
// 文件描述: TVOC传感器数据显示
////////////////////////////////////////
//<<AICUBE_USER_HEADER_REMARK_END>>

#include "config.h"

//<<AICUBE_USER_INCLUDE_BEGIN>>
//<<AICUBE_USER_INCLUDE_END>>

//<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>

// 共阴数码管段码表
code unsigned char SEG_CODE[] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

code unsigned char SEG_CODE_DOT[] = {
    0xBF, 0x86, 0xDB, 0xCF, 0xE6, 0xED, 0xFD, 0x87, 0xFF, 0xEF
};

#define FRAME_HEADER    0x2C
#define FRAME_LENGTH    6

// 变量
unsigned char rxBuffer[6];
unsigned char rxIndex = 0;
unsigned int tvoc_value = 0;
unsigned char comm_ok = 0;
unsigned int display_value = 0;
unsigned char timeout_flag = 0;

// 函数声明
void UpdateDisplay(void);
void SetDisplayNumber(unsigned int num);
void SetDisplayError(void);
void ProcessTVOCData(void);
void LED_Display_Init(void);
void Timer0_Init(void);

//<<AICUBE_USER_GLOBAL_DEFINE_END>>

////////////////////////////////////////
// 项目主函数
////////////////////////////////////////
void main(void)
{
    SYS_Init();
   
    EA = 1;
    SetDisplayNumber(0);
   
    while (1)
    {
        if(comm_ok)
        {
            comm_ok = 0;
            timeout_flag = 0;
            SetDisplayNumber(tvoc_value);
        }
        
        if(timeout_flag == 1)
        {
            SetDisplayError();
        }
    }
}

////////////////////////////////////////
// 系统初始化函数
////////////////////////////////////////
void SYS_Init(void)
{
    EnableAccessXFR();
    IAP_SetTimeBase();

    // === P3口初始化(直接配置)===
    P3M0 &= ~0x03;  // P3.0/P3.1 准双向口
    P3M1 &= ~0x03;
   
    // === LED数码管显示初始化 ===
    COMEN |= 0x07;
    SEGENL = 0xFF;
    SEGENH = 0x00;
    LEDCKS = 0x07;
    LEDCTRL = 0x80;
   
    // === 串口1初始化 (9600, 24MHz) ===
    #define BAUDRATE (9600UL)
    #define T2_RELOAD (65536 - (SYSCLK / BAUDRATE + 2) / 4)
   
    AUXR |= 0x01;   // 定时器2开始运行
    AUXR |= 0x04;   // 定时器2为1T模式
    T2L = (unsigned char)(T2_RELOAD);
    T2H = (unsigned char)(T2_RELOAD >> 8);
    SCON = 0x50;    // 模式1, 8位UART, 允许接收
   
    // === 定时器0初始化 ===
    AUXR &= ~0x80;  // 12T模式
    TMOD &= 0xF0;
    TMOD |= 0x01;   // 16位模式
    TL0 = 0x00;
    TH0 = 0x4C;     // 50ms初值
    TF0 = 0;
    TR0 = 1;
   
    // === 使能中断 ===
    ES = 1;
    ET0 = 1;
}

////////////////////////////////////////
// 定时器0中断 - 50ms
////////////////////////////////////////
void Timer0_ISR(void) interrupt 1
{
    static unsigned char count_50ms = 0;
   
    TL0 = 0x00;
    TH0 = 0x4C;
   
    count_50ms++;
    if(count_50ms >= 60)  // 3秒
    {
        count_50ms = 0;
        timeout_flag = 1;
    }
}

////////////////////////////////////////
// 处理TVOC传感器数据
////////////////////////////////////////
void ProcessTVOCData(void)
{
    unsigned int raw_value;
   
    if(rxBuffer[0] != FRAME_HEADER) return;
   
    raw_value = (unsigned int)(rxBuffer[1] << 8) | rxBuffer[2];
    tvoc_value = raw_value;
    if(tvoc_value > 999) tvoc_value = 999;
    comm_ok = 1;
}

////////////////////////////////////////
// 串口1中断服务程序
////////////////////////////////////////
void UART1_ISR(void) interrupt 4
{
    unsigned char ch;
   
    if(RI)
    {
        RI = 0;
        ch = SBUF;
        
        if(rxIndex == 0 && ch == FRAME_HEADER)
        {
            rxBuffer[rxIndex++] = ch;
        }
        else if(rxIndex >= 1 && rxIndex < FRAME_LENGTH)
        {
            rxBuffer[rxIndex++] = ch;
            if(rxIndex == FRAME_LENGTH)
            {
                ProcessTVOCData();
                rxIndex = 0;
            }
        }
        else
        {
            rxIndex = 0;
        }
    }
   
    if(TI)
    {
        TI = 0;
    }
}

////////////////////////////////////////
// 数码管显示函数
////////////////////////////////////////
void UpdateDisplay(void)
{
    unsigned int num = display_value;
    unsigned char hundreds, tens, units;
   
    if(num > 999) num = 999;
   
    hundreds = num / 100;
    tens = (num % 100) / 10;
    units = num % 10;
   
    COM0_DC_L = SEG_CODE_DOT[hundreds];
    COM1_DC_L = SEG_CODE[tens];
    COM2_DC_L = SEG_CODE[units];
}

void SetDisplayNumber(unsigned int num)
{
    display_value = num;
    UpdateDisplay();
}

void SetDisplayError(void)
{
    COM0_DC_L = SEG_CODE[1];
    COM1_DC_L = SEG_CODE[2];
    COM2_DC_L = SEG_CODE[3];
}

//<<AICUBE_USER_FUNCTION_IMPLEMENT_BEGIN>>
//<<AICUBE_USER_FUNCTION_IMPLEMENT_END>>


回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:393
  • 最近打卡:2026-04-30 08:56:47

849

主题

1万

回帖

2万

积分

管理员

积分
23131
发表于 4 天前 | 显示全部楼层
DEEPSEEK 的回答,不知 如何


从你的描述和代码来看,通讯不上很可能是**接收逻辑处理有问题**。你的中断服务程序里,用了一种比较严格的帧同步方式:只有连续收到的6个字节都符合预期,才会处理数据。这种方法在收到的帧数据有错位或丢失时,很容易卡死。

### 🔍 首先,推荐一种更可靠的接收方法

一种更稳健的做法是:**在中断里只负责“收数据”,在主循环里进行“找帧头、校验、解析”**。这种方法容错性更好,能自己从任何错位的数据流里重新同步。

可以参考下面的代码逻辑,它可以代替你那部分接收和解析的代码:

```c
// 全局变量
#define RX_BUF_SIZE    32          // 缓冲区大小,留点余量
unsigned char rxBuffer[RX_BUF_SIZE]; // 接收缓冲区
unsigned char rxCount = 0;           // 当前接收到的字节数
bit frameComplete = 0;               // 接收到一帧完整的原始数据

// 串口1中断服务程序
void UART1_ISR(void) interrupt 4
{
    if(RI)
    {
        RI = 0;
        if(rxCount < RX_BUF_SIZE)    // 防止溢出
        {
            rxBuffer[rxCount++] = SBUF;
        }
        else
        {
            rxCount = 0;              // 溢出则丢弃
        }
    }
    // TI 部分保持不变...
}

// 在主循环中调用的数据处理函数
void ProcessReceivedData(void)
{
    unsigned char i;
    unsigned int raw_value = 0;

    // 在缓冲区中查找帧头 0x2C
    for(i = 0; i < rxCount; i++)
    {
        if(rxBuffer[i] == FRAME_HEADER)
        {
            // 检查从帧头开始,是否还有足够的数据
            if(i + FRAME_LENGTH <= rxCount)
            {
                // 可选:在这里添加校验和验证
                // if(rxBuffer[i+5] != (rxBuffer[i] + ... ) ) break;

                // 提取数据 (大端模式)
                raw_value = ((unsigned int)rxBuffer[i+1] << 8) | rxBuffer[i+2];
                tvoc_value = (raw_value > 999) ? 999 : raw_value;
                comm_ok = 1;           // 通知主循环刷新显示
                timeout_flag = 0;      // 重置超时计时

                // 清除已处理的数据,避免重复处理
                // 移动缓冲区剩余数据到开头
                unsigned char remaining = rxCount - (i + FRAME_LENGTH);
                if(remaining > 0)
                {
                    for(unsigned char j = 0; j < remaining; j++)
                        rxBuffer[j] = rxBuffer[i + FRAME_LENGTH + j];
                }
                rxCount = remaining;
                return;
            }
            break;
        }
    }

    // [可选] 如果缓冲区快满了还找不到有效帧,为避免溢出,进行部分清理
    if(rxCount >= RX_BUF_SIZE - FRAME_LENGTH)
    {
        rxCount = 0; // 强制清空,从头开始
    }
}
```

这样修改后,在主循环的 `while(1)` 里调用 `ProcessReceivedData()` 即可,它能更灵活地应对数据错位的问题。

### 🛠️ 其次,建议从这几个方面排查硬件

如果修改代码后还是没反应,问题很可能出在硬件或物理连接上。

1.  **检查波特率误差**
    *   你的系统时钟是 `SYSCLK` 吗?你在代码中用 `#define T2_RELOAD (65536 - (SYSCLK / BAUDRATE + 2) / 4)` 来计算定时器重载值,这个公式对不同的系统时钟精度要求很高。
    *   以常见的 `24MHz` 系统时钟为例,产生 `9600` 波特率的理论重载值是 `65536 - (24000000 / 9600 + 2) / 4 = 65536 - (2500 + 2) / 4 = 65536 - 625 = 64911`。请务必确认你用的 `SYSCLK` 宏定义是 `24000000UL` 或实际频率。
    *   **强烈建议**:如果你的传感器支持,请将单片机的主时钟改为 `22.1184MHz`,这样能实现 `9600` 波特率的零误差。否则,请用示波器测量单片机 `P3.1` 引脚的波形,确认其波特率与传感器是否匹配。

2.  **确认IO口模式**
    *   STC8H系列是增强型51,其IO口默认是高阻模式。虽然你在 `SYS_Init` 里将 `P3.0` 和 `P3.1` 设置为了准双向口,但请确认这两行代码**在串口初始化之前被执行**,并且确实生效了。
    *   可以尝试将 `P3M0` 和 `P3M1` 的设置改为更明确的推挽输出/高阻输入组合,但这通常不是首要原因。

3.  **用串口助手验证单片机**
    *   **这是最关键的一步。** 断开传感器与单片机的连接,将单片机的 `P3.0 (RXD)` 和 `P3.1 (TXD)` 通过USB转串口模块连接到电脑。
    *   用串口助手给单片机发送一帧完整的数据(例如:`2C 01 2C 03 FF E0`),看数码管上能否正确显示 `0x012C = 300`。这能直接排除单片机程序本身的故障。
    *   同时,也可以用串口助手直接监听传感器发送的数据,看它是否按照协议(帧头 `0x2C`,6字节数据)稳定输出。

4.  **检查传感器本身**
    *   **预热时间**:很多TVOC传感器(如SGP30)上电后需要一定时间预热(几十秒甚至几分钟)才会输出有效数据,在此之前可能会输出默认值或无效值。请耐心等待并观察。
    *   **I²C vs UART**:你使用的传感器具体型号是什么?很多TVOC传感器(如SGP30)是 **I²C** 接口的。请再次确认传感器数据手册,它是否真的支持 **UART(串口)** 通讯?

5.  **检查连接线**
    *   **TX/RX交叉连接**:请确保 `传感器TX` → `单片机P3.0 (RXD)`,`传感器RX` → `单片机P3.1 (TXD)`。交叉连接是串口通讯最容易犯的错误,建议用万用表蜂鸣器档确认。
    *   **共用参考地**:请再次确认单片机和传感器是否已经**共地**(GND连GND)。不共地是无法通讯的。

建议你先用**串口助手验证单片机**,这是最快定位问题的方法。等确认单片机接收数据没问题后,再连上传感器进行调试。如果还是不行,可以提供传感器具体的型号和更多信息,我再帮你分析。

回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:60
  • 最近打卡:2026-05-01 05:53:11
已绑定手机

3

主题

94

回帖

505

积分

高级会员

积分
505
发表于 3 天前 | 显示全部楼层
要提供此款TVOC传感器的用户手册,了解通讯协议才能查错。
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-5-1 10:31 , Processed in 0.124179 second(s), 56 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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