STC8H4K64TL通讯问题,急急急
利用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;
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 != FRAME_HEADER) return;
raw_value = (unsigned int)(rxBuffer << 8) | rxBuffer;
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 = ch;
}
else if(rxIndex >= 1 && rxIndex < FRAME_LENGTH)
{
rxBuffer = 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;
COM1_DC_L = SEG_CODE;
COM2_DC_L = SEG_CODE;
}
void SetDisplayNumber(unsigned int num)
{
display_value = num;
UpdateDisplay();
}
void SetDisplayError(void)
{
COM0_DC_L = SEG_CODE;
COM1_DC_L = SEG_CODE;
COM2_DC_L = SEG_CODE;
}
//<<AICUBE_USER_FUNCTION_IMPLEMENT_BEGIN>>
//<<AICUBE_USER_FUNCTION_IMPLEMENT_END>>
DEEPSEEK 的回答,不知 如何
从你的描述和代码来看,通讯不上很可能是**接收逻辑处理有问题**。你的中断服务程序里,用了一种比较严格的帧同步方式:只有连续收到的6个字节都符合预期,才会处理数据。这种方法在收到的帧数据有错位或丢失时,很容易卡死。
### 🔍 首先,推荐一种更可靠的接收方法
一种更稳健的做法是:**在中断里只负责“收数据”,在主循环里进行“找帧头、校验、解析”**。这种方法容错性更好,能自己从任何错位的数据流里重新同步。
可以参考下面的代码逻辑,它可以代替你那部分接收和解析的代码:
```c
// 全局变量
#define RX_BUF_SIZE 32 // 缓冲区大小,留点余量
unsigned char rxBuffer; // 接收缓冲区
unsigned char rxCount = 0; // 当前接收到的字节数
bit frameComplete = 0; // 接收到一帧完整的原始数据
// 串口1中断服务程序
void UART1_ISR(void) interrupt 4
{
if(RI)
{
RI = 0;
if(rxCount < RX_BUF_SIZE) // 防止溢出
{
rxBuffer = 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 == FRAME_HEADER)
{
// 检查从帧头开始,是否还有足够的数据
if(i + FRAME_LENGTH <= rxCount)
{
// 可选:在这里添加校验和验证
// if(rxBuffer != (rxBuffer + ... ) ) break;
// 提取数据 (大端模式)
raw_value = ((unsigned int)rxBuffer << 8) | rxBuffer;
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 = rxBuffer;
}
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)。不共地是无法通讯的。
建议你先用**串口助手验证单片机**,这是最快定位问题的方法。等确认单片机接收数据没问题后,再连上传感器进行调试。如果还是不行,可以提供传感器具体的型号和更多信息,我再帮你分析。
要提供此款TVOC传感器的用户手册,了解通讯协议才能查错。
页:
[1]