找回密码
 立即注册
查看: 2166|回复: 10

求助关于DS18B20和modbus rtu从站

[复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:765
  • 最近打卡:2026-07-22 08:21:12

11

主题

117

回帖

142

积分

注册会员

积分
142
发表于 2024-1-19 13:40:22 | 显示全部楼层 |阅读模式
STC8读取DS18B20然后在通过串口发送出去挺简单的,没有冲突。

我现在想在modubs rtu从站里面添加读取DS18B20,一直不成功,有没有高手提供个案例。
好像modbus通讯时和读取DS18B20有冲突,会打断读取DS18B20从而导致读取失败
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:765
  • 最近打卡:2026-07-22 08:21:12

11

主题

117

回帖

142

积分

注册会员

积分
142
发表于 2024-1-19 13:46:32 | 显示全部楼层
这个案例如果有了又可以做出好多类似的东西
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:783
  • 最近打卡:2026-07-22 02:22:41
已绑定手机

41

主题

389

回帖

1991

积分

金牌会员

积分
1991
发表于 2024-1-19 15:35:10 | 显示全部楼层
DS18B20读取数据A,完成之后才把A直接传到到另一个地址B,然后MODBUS RTU是关联地址B, 因为可能存在读取数据A的过程中,DS18B20也在更改数据A
纸上得到终觉浅,绝知此事要躬行。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:765
  • 最近打卡:2026-07-22 08:21:12

11

主题

117

回帖

142

积分

注册会员

积分
142
发表于 2024-1-19 15:56:01 | 显示全部楼层
谢谢,不是这个原因。modbus串口通讯时会打断18B20通讯,18B20对时间要求又很严格,导致18B20通讯出错。我知道原因只是不知道怎么解决
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:296
  • 最近打卡:2026-01-08 10:11:26

9

主题

313

回帖

1797

积分

金牌会员

积分
1797
发表于 2024-1-29 13:39:31 | 显示全部楼层
用啥方试收发modbus数据的?
死等?
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:707
  • 最近打卡:2026-07-20 09:09:10

33

主题

3031

回帖

6980

积分

论坛元老

积分
6980
发表于 2024-1-29 14:49:30 | 显示全部楼层
manz*** 发表于 2024-1-19 15:56
谢谢,不是这个原因。modbus串口通讯时会打断18B20通讯,18B20对时间要求又很严格,导致18B20通讯出错。我 ...

不要再串口中断中处理数据,接收完成数据之后在主循环中处理数据
参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:765
  • 最近打卡:2026-07-22 08:21:12

11

主题

117

回帖

142

积分

注册会员

积分
142
发表于 2024-1-30 09:35:58 | 显示全部楼层
大神们帮忙看看什么问题
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:11
  • 最近打卡:2026-02-07 21:44:37

0

主题

3

回帖

100

积分

注册会员

积分
100
发表于 2025-10-18 00:26:29 | 显示全部楼层
DS18B20对时序要求比较严格,DS18B20读取温度过程中,如果串口接收到数据,串口中断会打断DS18B20时序,从而导致读取温度错误或者失败,建议在DS18B20读取的时候暂时关闭串口接收中断,读取完成后再打开中断。如果串口接收中断要求不允许关闭的话,只能用状态机的方式写DS18B20的时序,再要么就更换其它温度传感器,比如IIC类的。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:956
  • 最近打卡:2026-07-21 08:27:41

14

主题

1400

回帖

7694

积分

论坛元老

积分
7694
发表于 2026-7-13 20:09:16 | 显示全部楼层
[quote][size=2][url=forum.php?mod=redirect&goto=findpost&pid=55759&ptid=6349][color=#999999]manzunzu 发表于 2024-1-30 09:35[/color][/url][/size] 大神们帮忙看看什么问题[/quote]

好的,我已经查阅了STC官方论坛和手册资料。下面我来为你分析这个问题,并提供一个完整的解决方案。


问题分析

你遇到的问题非常典型:DS18B20 的单总线通信对时序要求极其严格(微秒级),而 Modbus RTU 的串口中断会随时打断 DS18B20 的读写时序,导致通信失败。

核心矛盾在于:

  • DS18B20 的读写时序需要精确的微秒级延时(如复位脉冲 480μs、读时隙 60μs 等)
  • Modbus 串口中断(尤其是接收中断)随时可能触发,打断正在进行的 DS18B20 时序
  • 即使关中断操作 DS18B20,如果关中断时间过长,又会丢失 Modbus 数据帧

解决方案:状态机 + 分步执行

你的思路完全正确——read_temp() 拆分为多个步骤,用状态机在主循环中分时执行,这样每个步骤的执行时间很短(微秒级),不会长时间关中断影响 Modbus 通信。

整体架构设计

主循环:
  1. 处理 Modbus RTU 协议(串口中断接收,主循环解析)
  2. 调用 DS18B20 状态机(每次执行一个步骤,耗时 < 200μs)
  3. 更新温度值供 Modbus 读取

DS18B20 状态机设计

DS18B20 的一次完整温度转换+读取流程分为以下步骤:

步骤 操作 说明 最大耗时
0 复位 + 发送 Skip ROM (0xCC) + 启动转换 (0x44) 启动温度转换 ~1ms (复位)
1 等待转换完成(约 750ms) 此步骤不阻塞,只是等待定时器计数 750ms
2 复位 + 发送 Skip ROM (0xCC) + 读暂存器 (0xBE) 准备读取温度 ~1ms
3 读取温度低字节 逐个位读取 ~1ms
4 读取温度高字节 逐个位读取 ~1ms
5 计算温度值,标记完成 纯计算 < 10μs

关键优化:步骤 1(等待 750ms 转换)完全不需要阻塞 CPU,只需让状态机等待定时器计数到时间后再进入下一步。


完整代码实现

以下是基于 STC8H 系列(兼容 STC8G/STC8A)的完整代码,包含 Modbus RTU 从站 + DS18B20 非阻塞读取。

文件 1: main.c

/*---------------------------------------------------------------------*/
/* --- STC MCU International Limited ----------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com  -----------------------------------------*/
/* --- 功能: Modbus RTU 从站 + DS18B20 非阻塞读取                      */
/* --- 主时钟: 24MHz (用户可自行修改)                                   */
/* --- MCU: STC8H8K64U / STC8G / STC8A 系列通用                        */
/*---------------------------------------------------------------------*/

#include "stc8h.h"
#include "intrins.h"

#define MAIN_Fosc        24000000L    // 定义主时钟频率

typedef unsigned char   u8;
typedef unsigned int    u16;
typedef unsigned long   u32;

/**************************** 引脚定义 *********************************/
sbit DQ = P3^3;                      // DS18B20 数据引脚

/*************************** Modbus 参数 *******************************/
#define MODBUS_SLAVE_ID    0x01      // 本机 Modbus 地址
#define MODBUS_BAUD        9600      // Modbus 波特率

/*************************** DS18B20 状态机 ****************************/
#define DS18B20_IDLE           0     // 空闲状态
#define DS18B20_START_CONV     1     // 启动温度转换
#define DS18B20_WAIT_CONV      2     // 等待转换完成
#define DS18B20_START_READ     3     // 开始读取温度
#define DS18B20_READ_LSB       4     // 读取低字节
#define DS18B20_READ_MSB       5     // 读取高字节
#define DS18B20_CALC_DONE      6     // 计算完成

#define DS18B20_CONV_TIME      750   // 转换等待时间 (ms)

/**************************** 全局变量 *********************************/
// DS18B20 相关
u8  ds18b20_state = DS18B20_IDLE;    // 状态机当前状态
u8  ds18b20_bit_count;               // 位计数器
u8  ds18b20_byte_val;                // 当前读取的字节值
u8  ds18b20_temp_lsb;                // 温度低字节
u8  ds18b20_temp_msb;                // 温度高字节
u16 ds18b20_conv_timer;              // 转换等待计时器
bit ds18b20_data_ready = 0;          // 温度数据就绪标志
int ds18b20_temperature = 0;         // 最终温度值 (实际温度 * 10)

// Modbus 相关
u8  modbus_rx_buf[32];               // 串口接收缓冲区
u8  modbus_rx_len;                   // 接收数据长度
u8  modbus_rx_index;                 // 接收索引
bit modbus_rx_flag = 0;              // 接收完成标志
u8  modbus_tx_buf[32];               // 串口发送缓冲区
u8  modbus_tx_len;                   // 发送数据长度

// 定时器相关
volatile u16 sys_tick = 0;           // 系统心跳 (1ms)

/************************ 函数声明 *************************************/
void Timer0_Init(void);
void UART1_Init(void);
void DS18B20_StateMachine(void);
void Modbus_Poll(void);
void Modbus_SendResponse(u8 *buf, u8 len);

// DS18B20 底层操作函数
void DS18B20_Delay1us(u8 n);
bit DS18B20_Reset(void);
void DS18B20_WriteByte(u8 dat);
u8  DS18B20_ReadByte(void);

/************************ 主函数 ***************************************/
void main(void)
{
    P0M0 = 0x00; P0M1 = 0x00;       // 设置所有IO为准双向口
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    P4M0 = 0x00; P4M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;
  
    Timer0_Init();                    // 初始化定时器0 (1ms节拍)
    UART1_Init();                     // 初始化串口1 (Modbus)
  
    EA = 1;                           // 开启总中断
  
    while(1)
    {
        DS18B20_StateMachine();       // 处理 DS18B20 状态机 (非阻塞)
        Modbus_Poll();                // 处理 Modbus 协议
    }
}

/*********************** 定时器0初始化 (1ms) ***************************/
void Timer0_Init(void)
{
    // 定时器0, 16位自动重装, 1ms中断
    // 24MHz, 12T模式: 计数脉冲 = 24MHz/12 = 2MHz
    // 重装值 = 65536 - 2000 = 63536 = 0xF830
    AUXR |= 0x80;                     // 定时器0为1T模式 (更快)
    // 若使用12T模式: AUXR &= ~0x80;
  
    TMOD &= ~0x0F;                    // 定时器0模式0 (16位自动重装)
    TMOD |= 0x00;
  
    // 24MHz 1T模式: 计数脉冲 = 24MHz, 1ms = 24000 计数
    // 重装值 = 65536 - 24000 = 41536 = 0xA240
    u16 reload = 65536UL - (MAIN_Fosc / 1000);
    TH0 = (u8)(reload >> 8);
    TL0 = (u8)(reload);
  
    ET0 = 1;                          // 允许定时器0中断
    TR0 = 1;                          // 启动定时器0
}

/*********************** 串口1初始化 (Modbus) **************************/
void UART1_Init(void)
{
    // 串口1, 模式1 (8位UART, 可变波特率), 使用定时器2做波特率发生器
    // 波特率 = 9600bps @ 24MHz
  
    S1_S1 = 0;                        // 串口1选择P3.0/P3.1 (默认)
    S1_S0 = 0;
  
    SCON = 0x50;                      // 模式1, 允许接收
  
    // 定时器2做波特率发生器
    // 波特率 = 定时器2溢出率 / 4
    // 定时器2溢出率 = MAIN_Fosc / (65536 - RL2)
    // RL2 = 65536 - MAIN_Fosc / 4 / 波特率
    // RL2 = 65536 - 24000000 / 4 / 9600 = 65536 - 625 = 64911 = 0xFD8F
    u16 baud_reload = 65536UL - MAIN_Fosc / 4 / MODBUS_BAUD;
  
    T2L = (u8)(baud_reload);
    T2H = (u8)(baud_reload >> 8);
    AUXR |= 0x14;                     // 定时器2开始运行, 1T模式
  
    ES = 1;                           // 允许串口1中断
}

/*********************** 串口1中断服务函数 *****************************/
void UART1_ISR(void) interrupt 4
{
    if(RI)
    {
        RI = 0;
        u8 dat = SBUF;
      
        // 接收超时处理: 如果超过3.5字符时间没有新数据, 认为一帧结束
        // 这里简化处理: 接收缓冲区满或收到足够数据后置标志
        if(modbus_rx_len < sizeof(modbus_rx_buf))
        {
            modbus_rx_buf[modbus_rx_len++] = dat;
        }
      
        // 简单超时判断: 每收到一个字节重置定时器
        // 实际项目中建议使用定时器做3.5字符超时判断
        // 这里使用一个简单的计数方式
        modbus_rx_index = 0;          // 重置超时计数
    }
    if(TI)
    {
        TI = 0;
    }
}

/*********************** 定时器0中断服务函数 ***************************/
void Timer0_ISR(void) interrupt 1
{
    sys_tick++;                       // 1ms 递增
  
    // Modbus 接收超时处理 (3.5字符时间)
    // 9600bps: 1字符 ≈ 1ms, 3.5字符 ≈ 3.5ms
    if(modbus_rx_len > 0)
    {
        modbus_rx_index++;
        if(modbus_rx_index >= 4)      // 约4ms超时
        {
            modbus_rx_flag = 1;       // 标记接收完成
            modbus_rx_index = 0;
        }
    }
}

/********************** DS18B20 微秒延时函数 ***************************/
void DS18B20_Delay1us(u8 n)
{
    // 24MHz下, 1个nop约0.0417us
    // 延时 n 微秒
    while(n--)
    {
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_();
    }
}

/********************** DS18B20 复位 ***********************************/
bit DS18B20_Reset(void)
{
    bit presence;
  
    EA = 0;                           // 关中断保护时序
    DQ = 0;                           // 拉低总线
    DS18B20_Delay1us(480);            // 保持480us
    DQ = 1;                           // 释放总线
    DS18B20_Delay1us(60);             // 等待60us
    presence = DQ;                    // 读取存在脉冲
    DS18B20_Delay1us(420);            // 等待剩余时间
    EA = 1;                           // 开中断
  
    return presence;                  // 0=存在, 1=不存在
}

/********************** DS18B20 写一个字节 *****************************/
void DS18B20_WriteByte(u8 dat)
{
    u8 i;
  
    EA = 0;                           // 关中断保护时序
    for(i = 0; i < 8; i++)
    {
        DQ = 0;                       // 拉低总线
        _nop_();                      // 延时约1us
        DQ = dat & 0x01;              // 输出数据位 (低位在前)
        DS18B20_Delay1us(60);         // 保持60us
        DQ = 1;                       // 释放总线
        _nop_();                      // 恢复时间
        dat >>= 1;
    }
    EA = 1;                           // 开中断
}

/********************** DS18B20 读一个字节 *****************************/
u8 DS18B20_ReadByte(void)
{
    u8 i, dat = 0;
  
    EA = 0;                           // 关中断保护时序
    for(i = 0; i < 8; i++)
    {
        dat >>= 1;
        DQ = 0;                       // 拉低总线启动读时隙
        _nop_();                      // 延时约1us
        DQ = 1;                       // 释放总线
        _nop_();                      // 等待约1us
        if(DQ) dat |= 0x80;           // 读取数据位
        DS18B20_Delay1us(60);         // 等待时隙结束
    }
    EA = 1;                           // 开中断
  
    return dat;
}

/********************** DS18B20 状态机 (非阻塞) ************************/
void DS18B20_StateMachine(void)
{
    static u8 temp_lsb, temp_msb;
  
    switch(ds18b20_state)
    {
        case DS18B20_IDLE:
            // 空闲状态, 启动新一轮温度转换
            ds18b20_state = DS18B20_START_CONV;
            break;
          
        case DS18B20_START_CONV:
            // 步骤1: 复位 + 发送Skip ROM + 启动温度转换
            DS18B20_Reset();
            DS18B20_WriteByte(0xCC);      // Skip ROM
            DS18B20_WriteByte(0x44);      // 启动温度转换
          
            ds18b20_conv_timer = 0;       // 重置等待计时器
            ds18b20_state = DS18B20_WAIT_CONV;
            break;
          
        case DS18B20_WAIT_CONV:
            // 步骤2: 等待转换完成 (非阻塞, 靠定时器计数)
            // 每次进入此状态只检查计时, 不阻塞
            if(ds18b20_conv_timer >= DS18B20_CONV_TIME)
            {
                ds18b20_state = DS18B20_START_READ;
            }
            // 否则什么都不做, 等待下次主循环调用
            break;
          
        case DS18B20_START_READ:
            // 步骤3: 复位 + 发送Skip ROM + 发送读暂存器命令
            DS18B20_Reset();
            DS18B20_WriteByte(0xCC);      // Skip ROM
            DS18B20_WriteByte(0xBE);      // 读暂存器
          
            ds18b20_bit_count = 0;
            ds18b20_byte_val = 0;
            ds18b20_state = DS18B20_READ_LSB;
            break;
          
        case DS18B20_READ_LSB:
            // 步骤4: 读取温度低字节 (8位)
            // 每次主循环读1位, 分8次读完
            if(ds18b20_bit_count < 8)
            {
                u8 bit_val;
              
                EA = 0;
                DQ = 0;                   // 拉低启动读时隙
                _nop_();
                DQ = 1;
                _nop_();
                bit_val = DQ ? 1 : 0;
                DS18B20_Delay1us(60);
                EA = 1;
              
                ds18b20_byte_val >>= 1;
                if(bit_val) ds18b20_byte_val |= 0x80;
              
                ds18b20_bit_count++;
            }
            else
            {
                temp_lsb = ds18b20_byte_val;
                ds18b20_bit_count = 0;
                ds18b20_byte_val = 0;
                ds18b20_state = DS18B20_READ_MSB;
            }
            break;
          
        case DS18B20_READ_MSB:
            // 步骤5: 读取温度高字节 (8位)
            if(ds18b20_bit_count < 8)
            {
                u8 bit_val;
              
                EA = 0;
                DQ = 0;
                _nop_();
                DQ = 1;
                _nop_();
                bit_val = DQ ? 1 : 0;
                DS18B20_Delay1us(60);
                EA = 1;
              
                ds18b20_byte_val >>= 1;
                if(bit_val) ds18b20_byte_val |= 0x80;
              
                ds18b20_bit_count++;
            }
            else
            {
                temp_msb = ds18b20_byte_val;
                ds18b20_state = DS18B20_CALC_DONE;
            }
            break;
          
        case DS18B20_CALC_DONE:
            // 步骤6: 计算温度值
            {
                u16 temp_raw = ((u16)temp_msb << 8) | temp_lsb;
              
                // DS18B20 分辨率12位: 0.0625°C/LSB
                // 温度值 = temp_raw * 0.0625
                // 这里用整数表示, 实际温度 * 10
                if(temp_raw & 0x8000)     // 负温度
                {
                    temp_raw = (~temp_raw) + 1;
                    ds18b20_temperature = -((int)(temp_raw * 625L / 1000));
                }
                else
                {
                    ds18b20_temperature = (int)(temp_raw * 625L / 1000);
                }
              
                ds18b20_data_ready = 1;
            }
          
            ds18b20_state = DS18B20_IDLE; // 回到空闲, 开始新一轮
            break;
          
        default:
            ds18b20_state = DS18B20_IDLE;
            break;
    }
}

/********************** Modbus CRC16 计算 ******************************/
u16 Modbus_CRC16(u8 *buf, u8 len)
{
    u16 crc = 0xFFFF;
    u8 i;
  
    while(len--)
    {
        crc ^= *buf++;
        for(i = 0; i < 8; i++)
        {
            if(crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc >>= 1;
        }
    }
    return crc;
}

/********************** Modbus 协议处理 ********************************/
void Modbus_Poll(void)
{
    u8 i;
    u16 crc, crc_recv;
  
    if(!modbus_rx_flag) return;       // 没有收到完整帧
  
    modbus_rx_flag = 0;
  
    // 检查帧长度 (至少4字节: 地址+功能码+CRC低+CRC高)
    if(modbus_rx_len < 4) 
    {
        modbus_rx_len = 0;
        return;
    }
  
    // 检查CRC
    crc = Modbus_CRC16(modbus_rx_buf, modbus_rx_len - 2);
    crc_recv = (u16)modbus_rx_buf[modbus_rx_len - 1] << 8 |
                modbus_rx_buf[modbus_rx_len - 2];
    if(crc != crc_recv)
    {
        modbus_rx_len = 0;
        return;                       // CRC错误, 丢弃
    }
  
    // 检查从机地址
    if(modbus_rx_buf[0] != MODBUS_SLAVE_ID && modbus_rx_buf[0] != 0x00)
    {
        modbus_rx_len = 0;
        return;                       // 不是发给本机
    }
  
    // 解析功能码
    switch(modbus_rx_buf[1])
    {
        case 0x03:                    // 读保持寄存器
            // 读取 DS18B20 温度值
            {
                u16 reg_addr = (u16)modbus_rx_buf[2] << 8 | modbus_rx_buf[3];
                u16 reg_count = (u16)modbus_rx_buf[4] << 8 | modbus_rx_buf[5];
              
                // 只支持读取地址0x0000的寄存器 (温度值)
                if(reg_addr == 0x0000 && reg_count == 1)
                {
                    modbus_tx_buf[0] = MODBUS_SLAVE_ID;
                    modbus_tx_buf[1] = 0x03;
                    modbus_tx_buf[2] = 2;                    // 数据字节数
                    modbus_tx_buf[3] = (u8)(ds18b20_temperature >> 8);  // 高字节
                    modbus_tx_buf[4] = (u8)(ds18b20_temperature);       // 低字节
                  
                    crc = Modbus_CRC16(modbus_tx_buf, 5);
                    modbus_tx_buf[5] = (u8)(crc);
                    modbus_tx_buf[6] = (u8)(crc >> 8);
                  
                    Modbus_SendResponse(modbus_tx_buf, 7);
                }
                else
                {
                    // 非法地址, 返回异常
                    modbus_tx_buf[0] = MODBUS_SLAVE_ID;
                    modbus_tx_buf[1] = 0x83;                  // 异常功能码
                    modbus_tx_buf[2] = 0x02;                  // 非法数据地址
                  
                    crc = Modbus_CRC16(modbus_tx_buf, 3);
                    modbus_tx_buf[3] = (u8)(crc);
                    modbus_tx_buf[4] = (u8)(crc >> 8);
                  
                    Modbus_SendResponse(modbus_tx_buf, 5);
                }
            }
            break;
          
        case 0x06:                    // 写单个寄存器 (预留)
            // 可在此添加写寄存器功能
            {
                // 返回异常: 不支持
                modbus_tx_buf[0] = MODBUS_SLAVE_ID;
                modbus_tx_buf[1] = 0x86;
                modbus_tx_buf[2] = 0x01;                      // 非法功能码
              
                crc = Modbus_CRC16(modbus_tx_buf, 3);
                modbus_tx_buf[3] = (u8)(crc);
                modbus_tx_buf[4] = (u8)(crc >> 8);
              
                Modbus_SendResponse(modbus_tx_buf, 5);
            }
            break;
          
        default:
            // 不支持的功能码
            modbus_tx_buf[0] = MODBUS_SLAVE_ID;
            modbus_tx_buf[1] = modbus_rx_buf[1] | 0x80;
            modbus_tx_buf[2] = 0x01;                          // 非法功能码
          
            crc = Modbus_CRC16(modbus_tx_buf, 3);
            modbus_tx_buf[3] = (u8)(crc);
            modbus_tx_buf[4] = (u8)(crc >> 8);
          
            Modbus_SendResponse(modbus_tx_buf, 5);
            break;
    }
  
    modbus_rx_len = 0;
}

/********************** Modbus 发送响应 ********************************/
void Modbus_SendResponse(u8 *buf, u8 len)
{
    u8 i;
  
    for(i = 0; i < len; i++)
    {
        SBUF = buf[i];
        while(!TI);                   // 等待发送完成
        TI = 0;
    }
}

文件 2: stc8h.h 头文件

请使用 STC 官方提供的头文件 stc8h.h,可在 STC-ISP 下载软件中找到,或从 STC 官网下载。


关键设计要点说明

1. 状态机分步执行

步骤 每次执行耗时 说明
DS18B20_START_CONV ~1ms (关中断) 复位+写命令,关中断保护
DS18B20_WAIT_CONV < 1μs 仅检查计时器,不阻塞
DS18B20_START_READ ~1ms (关中断) 复位+写命令
DS18B20_READ_LSB ~65μs/位 (关中断) 每次主循环读1位
DS18B20_READ_MSB ~65μs/位 (关中断) 每次主循环读1位
DS18B20_CALC_DONE < 10μs 纯计算,无需关中断

每次关中断时间最长约 65μs(读一个位),远小于 Modbus 的字符间隔(9600bps 下约 1ms),不会导致 Modbus 帧丢失。

2. 主循环周期

DS18B20_START_CONV → (750ms等待) → START_READ → READ_LSB(8次) → READ_MSB(8次) → CALC_DONE
  • 等待转换的 750ms 完全不占用 CPU
  • 读取阶段每次主循环读 1 位,主循环周期约 2~3ms
  • Modbus 随时可以响应,不会被长时间阻塞

3. Modbus 寄存器映射

寄存器地址 内容 类型 说明
0x0000 温度值 只读 实际温度 × 10 (如 25.3°C → 253)

使用说明

  1. 硬件连接

    • DS18B20 的 DQ 接 P3.3(可修改 sbit DQ = P3^3 更改)
    • DS18B20 的 VDD 接 3.3V/5V,GND 接 GND
    • DQ 引脚需接 4.7kΩ 上拉电阻到 VCC
    • 串口1 (P3.0 TXD, P3.1 RXD) 接 Modbus 总线
  2. Modbus 通信参数

    • 波特率:9600
    • 数据位:8
    • 停止位:1
    • 校验:无
    • 从机地址:0x01
  3. 读取温度

    • 发送 Modbus 命令:01 03 00 00 00 01 84 0A
    • 返回:01 03 02 [温度高字节] [温度低字节] [CRC]

注意事项

  1. DS18B20 上拉电阻必须加,否则通信会失败
  2. 关中断时间:每次读位操作关中断约 65μs,在 9600bps 下完全安全。如果使用更高的 Modbus 波特率(如 115200),字符间隔更短,建议进一步优化——将读位操作也拆分为更细的步骤
  3. DS18B20 的 750ms 转换等待是在 WAIT_CONV 状态中通过 sys_tick 计时实现的,完全非阻塞
  4. 如果使用 STC8G/STC8A 系列,代码完全兼容,只需更换对应的头文件

这个架构可以轻松扩展到其他对时序敏感的外设(如 DHT11/DHT22、单总线 LED 等),配合 Modbus 使用。

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-13 22:20:36 | 显示全部楼层
截图202607132220331711.jpg
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-7-22 09:30 , Processed in 0.087419 second(s), 95 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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