发新帖我要提问
12
返回列表
打印
[STM32F1]

STM32F103单片机modbus通信示例

[复制链接]
手机看帖
扫描二维码
随时随地手机跟帖
21
modbus.h
#ifndef __MODBUS_H
#define __MODBUS_H
#include "sys.h"


#define SlaveID  0x01     //从机地址

void DisposeReceive( void );
void Modbus_03_Slave(void);
void Modbus_06_Slave(void);
void Modbus_16_Slave(void);
#endif

使用特权

评论回复
22
花间一壶酒sd|  楼主 | 2023-4-22 00:19 | 只看该作者
modbus.c
#include "modbus.h"
#include "crc16.h"
#include "uart2.h"

#define HoldRegStartAddr    0x0000                                                                                                                                                                                //保持寄存器起始地址
#define HoldRegCount        8                                                                                                                                                                                                        //保持寄存器数量
#define HoldMaxValue        1000                                                                                                                                                    //寄存器最大值

extern u8 data_backup[DMA_REC_LEN];                                                                                                                                                                   //modbus接收到数据备份
extern u16 dataLen_backup;                                                                                                                                                                                                                //modbus接收数据长度备份


u8 SendBuf[DMA_REC_LEN] = {0};
u16 StartRegAddr = HoldRegStartAddr;
u8 err = 0;                                                                                                        //错误代码

u8 HoldReg[HoldRegCount*2] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8}; //存储8路数据值


/*
错误代码说明:
0x01  不是支持的功能码
0x02  起始地址不在规定范围内
0x03  寄存器数量不在规定范围内
0x04  数据校验错误
*/
//处理接收到的数据
// 接收: [地址][功能码][起始地址高][起始地址低][总寄存器数高][总寄存器数低][CRC低][CRC高]
void DisposeReceive( void )
{
    u16 CRC16 = 0, CRC16Temp = 0;
    if( data_backup[0] == SlaveID )                                                                         //地址等于本机地址 地址范围:1 - 32
    {
        CRC16 = App_Tab_Get_CRC16( data_backup, dataLen_backup - 2 );                                  //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
        CRC16Temp = ( ( u16 )( data_backup[dataLen_backup - 1] << 8 ) | data_backup[dataLen_backup - 2] );
        if( CRC16 != CRC16Temp )
        {
            err = 4;                                                                                               //CRC校验错误
        }
        StartRegAddr = ( u16 )( data_backup[2] << 8 ) | data_backup[3];
        if( StartRegAddr > (HoldRegStartAddr + HoldRegCount - 1) )
        {
            err = 2;                                                                                               //起始地址不在规定范围内 00 - 07    1 - 8号通道
        }
        if( err == 0 )
        {
            switch( data_backup[1] )                                                                        //功能码
            {
                case 3:                                                                                            //读多个寄存器
                {
                    Modbus_03_Slave();
                    break;
                }
                case 6:                                                                                            //写单个寄存器
                {
                    Modbus_06_Slave();
                    break;
                }
                case 16:                                                                                           //写多个寄存器
                {
                    Modbus_16_Slave();
                    break;
                }
                default:
                {
                    err = 1;                                                                                       //不支持该功能码
                    break;
                }
            }
        }
        if( err > 0 )
        {
            SendBuf[0] = data_backup[0];
            SendBuf[1] = data_backup[1] | 0x80;
            SendBuf[2] = err;                                                                                      //发送错误代码
            CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 );                                                           //计算CRC校验值
            SendBuf[3] = CRC16Temp & 0xFF;                                                                         //CRC低位
            SendBuf[4] = ( CRC16Temp >> 8 );                                                                       //CRC高位
            uart2_Send( SendBuf, 5 );                                       
            err = 0;                                                                                               //发送完数据后清除错误标志
        }
    }
}

/*
函数功能:读保持寄存器  03
主站请求报文:      0x01 0x03   0x0000  0x0001  0x840A     读从0开始的1个保持寄存器
从站正常响应报文:  0x01 0x03   0x02    0x09C4  0xBF87     读到的2字节数据为 0x09C4
*/
void Modbus_03_Slave( void )
{
    u16 RegNum = 0;
    u16 CRC16Temp = 0;
    u8 i = 0;
    RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
    if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )      //寄存器地址+寄存器数量 在规定范围内 <=8
    {
        SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址       
        SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
        SendBuf[2] = RegNum * 2;                                                                                                                                                                                        //返回字节数量
        for( i = 0; i < RegNum; i++ )                                                                             //循环读取保持寄存器内的值
        {
            SendBuf[3 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2];
            SendBuf[4 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2 + 1];
        }
        CRC16Temp = App_Tab_Get_CRC16( SendBuf, RegNum * 2 + 3 );                                                  //获取CRC校验值
        SendBuf[RegNum * 2 + 3] = CRC16Temp & 0xFF;                                                                //CRC低位
        SendBuf[RegNum * 2 + 4] = ( CRC16Temp >> 8 );                                                              //CRC高位
        uart2_Send( SendBuf, RegNum * 2 + 5 );
    }
    else
    {
        err = 3;                                                                                                   //寄存器数量不在规定范围内
    }
}
/*
函数功能:写单个保持寄存器 06
主站请求报文:      0x01 0x06    0x0000  0x1388    0x849C   写0号寄存器的值为0x1388
从站正常响应报文:  0x01 0x06    0x0000  0x1388    0x849C    0号寄存器的值为0x1388
*/
void Modbus_06_Slave( void )
{
    u16  RegValue = 0;
    u16 CRC16Temp = 0;
    RegValue = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                              //获取寄存器值
    if( RegValue <= HoldMaxValue )                                                  //寄存器值不超过1000
    {
        HoldReg[StartRegAddr * 2] = data_backup[4];                                                         //存储寄存器值
        HoldReg[StartRegAddr * 2 + 1] = data_backup[5];
        SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址
        SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
        SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
        SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
        SendBuf[4] = data_backup[4];                                                                                                                                                                        //值高位
        SendBuf[5] = data_backup[5];                                                                                                                                                                        //值低位
        CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                              //获取CRC校验值
        SendBuf[6] = CRC16Temp & 0xFF;                                                                            //CRC低位
        SendBuf[7] = ( CRC16Temp >> 8 );                                                                          //CRC高位
        uart2_Send( SendBuf, 8 );
    }
    else
    {
        err =  3;                                                                                                  //寄存器数值不在规定范围内
    }
}
/*
函数功能:写多个连续保持寄存器值 16
主站请求报文:       0x01 0x10    0x7540  0x0002  0x04  0x0000 0x2710    0xB731  写从0x7540地址开始的2个保持寄存器值 共4字节
从站正常响应报文:   0x01 0x10    0x7540  0x0002  0x5A10                         写从0x7540地址开始的2个保持寄存器值
*/
void Modbus_16_Slave( void )
{
    u16 RegNum = 0;
    u16 CRC16Temp = 0;
    u8 i = 0;
    RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
    if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )             //寄存器地址+寄存器数量 在规定范围内 <=8
    {
        for( i = 0; i < RegNum; i++ )                                                                              //存储寄存器设置值
        {
            HoldReg[StartRegAddr * 2 + i * 2] = data_backup[i * 2 + 7];
            HoldReg[StartRegAddr * 2 + 1 + i * 2] = data_backup[i * 2 + 8];
        }
        SendBuf[0] = data_backup[0];                                                                                                                                                                        //起始地址
        SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
        SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
        SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
        SendBuf[4] = data_backup[4];                                                                                                                                                                        //寄存器数量高位
        SendBuf[5] = data_backup[5];                                                                                                                                                                        //寄存器数量低位
        CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                        //获取CRC校验值
        SendBuf[6] = CRC16Temp & 0xFF;                                                                      //CRC低位
        SendBuf[7] = ( CRC16Temp >> 8 );                                                                    //CRC高位
        uart2_Send( SendBuf, 8 );
    }
    else
    {
        err = 3;                                                                                                   //寄存器数量不在规定范围内
    }
}

使用特权

评论回复
23
花间一壶酒sd|  楼主 | 2023-4-22 00:19 | 只看该作者
测试效果

测试功能码 0x03  读一个或者多个保持寄存器值

使用特权

评论回复
24
花间一壶酒sd|  楼主 | 2023-4-22 00:20 | 只看该作者
测试功能码 0x10 写多个保持寄存器值

使用特权

评论回复
25
花间一壶酒sd|  楼主 | 2023-4-22 00:20 | 只看该作者
再次读取所有寄存器值



说明寄存器值修改成功。

工程下载地址 https://download.csdn.net/download/qq_20222919/12937240

串口调试软件下载地址: https://download.csdn.net/download/qq_20222919/18798579?spm=1001.2014.3001.5501

使用特权

评论回复
26
周半梅| | 2024-6-6 07:03 | 只看该作者

需要在做项目的过程中经历磨难

使用特权

评论回复
27
Pulitzer| | 2024-6-6 08:06 | 只看该作者

硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得

使用特权

评论回复
28
童雨竹| | 2024-6-6 10:02 | 只看该作者

结构化模块化的程序设计的思想,使最基本的要求

使用特权

评论回复
29
Wordsworth| | 2024-6-6 11:05 | 只看该作者

确定好硬件原理图,硬件布线,最后才是软件的开发

使用特权

评论回复
30
Clyde011| | 2024-6-6 12:08 | 只看该作者

没有发现有哪本是介绍设计思想的

使用特权

评论回复
31
公羊子丹| | 2024-6-6 13:01 | 只看该作者

写程序不难,但是程序怎么样才能写的好,写的快,那是需要点经验积累的

使用特权

评论回复
32
万图| | 2024-6-6 14:04 | 只看该作者

在实际的项目应用当中,单片机引脚的复用相当厉害

使用特权

评论回复
33
Uriah| | 2024-6-6 15:07 | 只看该作者

但是如果不懂程序设计的思想的话,会给你做项目的过程中带来很多很多的困惑。

使用特权

评论回复
34
帛灿灿| | 2024-6-6 17:03 | 只看该作者

时间片轮的设计思想

使用特权

评论回复
35
Bblythe| | 2024-6-6 18:06 | 只看该作者

事实上很多做项目的工程师本身自己也会在用

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则