请选择 进入手机版 | 继续访问电脑版

硬汉嵌入式论坛

 找回密码
 立即注册
楼主: eric2013
收起左侧

[客户分享] 【年末福利】安富莱电子发布Modbus教程,配套STM32F103,F407,F429和H7主从机例子,含HAL和标准库两版(2022-10-03)

  [复制链接]

29

主题

514

回帖

606

积分

金牌会员

积分
606
QQ
发表于 2020-12-18 17:42:02 | 显示全部楼层
感谢分享。
Releasing your creativity
回复

使用道具 举报

34

主题

111

回帖

213

积分

高级会员

程序小白

积分
213
QQ
发表于 2021-1-6 14:35:26 | 显示全部楼层
您好  看了Modbus从站例子 ,还是有疑问。
比如举例     01 03 3006 0001 6B0B   --- 读 3006H , 1个字节数据
                  01 03 4000 0010 51C6   --- 读 4000H , 16个字节数据
                  01 03 4001 0010 0006   --- 读 4001H , 16个字节数据
                  。。。。。。
在哪里还要添加判断及代码? 样例中只是接收到 03 调用 bsp_PutMsg()

回复

使用道具 举报

34

主题

111

回帖

213

积分

高级会员

程序小白

积分
213
QQ
发表于 2021-1-6 14:59:21 | 显示全部楼层
看到了 是通过 MODS_ReadRegValue(uint16_t reg_addr, uint8_t *reg_value) 实现的。
可是,当地址不是连续的,而且数量又很多时,这样的处理不是很复杂?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2021-1-7 08:01:51 | 显示全部楼层
清风徐来 发表于 2021-1-6 14:59
看到了 是通过 MODS_ReadRegValue(uint16_t reg_addr, uint8_t *reg_value) 实现的。
可是,当地址不是连 ...

最好是分批,方便一次错误了重传
回复

使用道具 举报

34

主题

111

回帖

213

积分

高级会员

程序小白

积分
213
QQ
发表于 2021-1-7 10:19:12 | 显示全部楼层
eric2013 发表于 2021-1-7 08:01
最好是分批,方便一次错误了重传

好的。 移植modbus 从站模块 到 RTX4上,有什么要注意的地方吗? 样例是主站模块
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2021-1-8 08:36:55 | 显示全部楼层
清风徐来 发表于 2021-1-7 10:19
好的。 移植modbus 从站模块 到 RTX4上,有什么要注意的地方吗? 样例是主站模块

跟主站一样。

【安富莱】F429开发板RTOS+emWin+FS+TCP/IP+USB+CAN+Modbus综合Demo已发布(2017-03-21)
http://www.armbbs.cn/forum.php?m ... 7330&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
发表于 2021-2-4 03:21:44 | 显示全部楼层
谢谢大侠!
回复

使用道具 举报

0

主题

15

回帖

15

积分

新手上路

积分
15
发表于 2021-4-10 14:14:29 | 显示全部楼层
谢谢,学习中
回复

使用道具 举报

1

主题

7

回帖

10

积分

新手上路

积分
10
发表于 2021-6-25 15:25:02 | 显示全部楼层
好资料,谢谢分享
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2021-6-27 22:27:06 | 显示全部楼层
感谢,收益很大   必须大力支持

回复

使用道具 举报

0

主题

3

回帖

3

积分

新手上路

积分
3
发表于 2021-7-2 15:52:07 | 显示全部楼层


谢谢楼主,非常 的!
回复

使用道具 举报

2

主题

63

回帖

69

积分

初级会员

积分
69
发表于 2021-8-26 11:52:55 | 显示全部楼层
为什么下载例程会提示与网站不是私密链接,弹出登录对话框?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2021-8-28 08:59:41 | 显示全部楼层
banzhangzzw 发表于 2021-8-26 11:52
为什么下载例程会提示与网站不是私密链接,弹出登录对话框?

估计你下载的时候服务器出问题了,你现在下载应该没问题了。
回复

使用道具 举报

2

主题

63

回帖

69

积分

初级会员

积分
69
发表于 2021-8-29 17:02:39 | 显示全部楼层
eric2013 发表于 2021-8-28 08:59
估计你下载的时候服务器出问题了,你现在下载应该没问题了。

可以下载了
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2021-9-13 15:10:17 | 显示全部楼层
太感谢了,最近要做modbus主站,翻翻旧贴
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2021-10-21 13:54:48 | 显示全部楼层
无私奉献,牛牛牛
回复

使用道具 举报

2

主题

9

回帖

15

积分

新手上路

积分
15
发表于 2021-10-28 08:39:29 | 显示全部楼层
老师,这个GUI是用什么写的 MFC吗?
回复

使用道具 举报

4

主题

12

回帖

24

积分

新手上路

积分
24
发表于 2021-10-29 16:30:36 | 显示全部楼层
有407的吗
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2021-10-30 08:55:34 | 显示全部楼层

V5就是F407
回复

使用道具 举报

0

主题

5

回帖

5

积分

新手上路

积分
5
发表于 2021-11-1 21:52:28 | 显示全部楼层
你好  楼主,我用stm32 做主机 pc做从机, 05 分别发送 0x 01 05 00 00 FF 00 8C 3A    0x 01 05 00 01 FF 00 DD FA,但是pc接收机 都是一样 寄存器值 1 1 0 1 1 1 0 0 1 01,不知大为何
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2021-11-2 09:08:18 | 显示全部楼层
书的那页 发表于 2021-11-1 21:52
你好  楼主,我用stm32 做主机 pc做从机, 05 分别发送 0x 01 05 00 00 FF 00 8C 3A    0x 01 05 00 01 FF  ...

1234.png
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
发表于 2021-11-6 17:47:47 | 显示全部楼层
你好 看了主机程序 static void MODH_Read_01H(void)
{
        uint8_t bytes;
        uint8_t *p;
       
        if (g_tModH.RxCount > 0)
        {
                bytes = g_tModH.RxBuf[2];        /* 数据长度 字节数 */                               
                switch (g_tModH.Reg01H)
                {
                        case REG_D01:
                                if (bytes == 8)
                                {
                                        p = &g_tModH.RxBuf[3];       
                                       
                                        g_tVar.D01 = BEBufToUint16(p); p += 2;        /* 寄存器 */       
                                        g_tVar.D02 = BEBufToUint16(p); p += 2;        /* 寄存器 */       
                                        g_tVar.D03 = BEBufToUint16(p); p += 2;        /* 寄存器 */       
                                        g_tVar.D04 = BEBufToUint16(p); p += 2;        /* 寄存器 */
                                       
                                        g_tModH.fAck01H = 1;
                                }
                                break;
                }
        }
}


对bytes = g_tModH.RxBuf[2];        /* 数据长度 字节数 */         对 bytes == 8  这个数据是怎样计算出来的
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2022-2-21 11:21:29 | 显示全部楼层
老大,这个主站例程适合多从机的模式下吗?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2022-2-21 11:22:01 | 显示全部楼层
a513247209 发表于 2022-2-21 11:21
老大,这个主站例程适合多从机的模式下吗?

没问题。
回复

使用道具 举报

6

主题

126

回帖

144

积分

初级会员

积分
144
发表于 2022-2-23 15:14:08 | 显示全部楼层
硬汉大哥,我也遇到142楼的问题
按下K1发送的指令是读取4个线圈的值,4个线圈回来的就是1个字节

发送的指令.png
这个是实验结果
接收到的数值.png

所以MODH_Read_01H函数里面, if (bytes == 8)是不是应该是 if (bytes == 1)才对,要么一直超时,返回的字节数不够8位

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2022-2-23 15:25:02 | 显示全部楼层
role_2099 发表于 2022-2-23 15:14
硬汉大哥,我也遇到142楼的问题
按下K1发送的指令是读取4个线圈的值,4个线圈回来的就是1个字节

这部分属于应用代码,
大家自己根据自己的应用做适配,我们这里是我们项目里面的,有些地方没有清除干净。
回复

使用道具 举报

6

主题

126

回帖

144

积分

初级会员

积分
144
发表于 2022-2-23 15:41:34 | 显示全部楼层
eric2013 发表于 2022-2-23 15:25
这部分属于应用代码,
大家自己根据自己的应用做适配,我们这里是我们项目里面的,有些地方没有清除干净 ...

谢谢硬汉大哥,了解了,刚才看了下H7-TOOL里面就是空函数
回复

使用道具 举报

0

主题

5

回帖

5

积分

新手上路

积分
5
发表于 2022-2-28 11:39:44 | 显示全部楼层
硬汉大哥,想请问一下如果modbus里的字节接收回调函数:
回复

使用道具 举报

0

主题

5

回帖

5

积分

新手上路

积分
5
发表于 2022-2-28 11:59:12 | 显示全部楼层
硬汉大哥,想请问下这个回调函数:
----------------------------------------------------------------------------
void MODS_ReciveNew(uint8_t _byte)
{
        /*
                3.5个字符的时间间隔,只是用在RTU模式下面,因为RTU模式没有开始符和结束符,
                两个数据包之间只能靠时间间隔来区分,Modbus定义在不同的波特率下,间隔时间是不一样的,
                所以就是3.5个字符的时间,波特率高,这个时间间隔就小,波特率低,这个时间间隔相应就大

                4800  = 7.297ms
                9600  = 3.646ms
                19200  = 1.771ms
                38400  = 0.885ms
        */
        uint32_t timeout;

        g_mods_timeout = 0;
       
        timeout = 35000000 / SBAUD485;                        /* 计算超时时间,单位us 35000000*/
       
        /* 硬件定时中断,定时精度us 硬件定时器1用于ADC, 定时器2用于Modbus */
        bsp_StartHardTimer(1, timeout, (void *)MODS_RxTimeOut);

        if (g_tModS.RxCount < S_RX_BUF_SIZE)
        {
                g_tModS.RxBuf[g_tModS.RxCount++] = _byte;
        }
}
--------------------------------------------------------------------------------------
如果在每个中断里面都进行了数据写入,那么串口FIFO里的接收数据FIFO缓冲是不是失去了意义呢(想着节省内存)?因为没有用到从串口接收缓冲里以FIFO的方式去取数据而是直接中断里面取了数据。

还有一个问题,是否会出现当执行轮询函数POLL:
-------------------------------------------------------------
void MODS_Poll(void)
{
        uint16_t addr;
        uint16_t crc1;
        /* 超过3.5个字符时间后执行MODH_RxTimeOut()函数。全局变量 g_rtu_timeout = 1; 通知主程序开始解码 */
        if (g_mods_timeout == 0)       
        {
                return;                                                                /* 没有超时,继续接收。不要清零 g_tModS.RxCount */
        }
       
        g_mods_timeout = 0;                                                 /* 清标志 */

        if (g_tModS.RxCount < 4)                                /* 接收到的数据小于4个字节就认为错误 */
        {
                goto err_ret;
        }

        /* 计算CRC校验和 */
        crc1 = CRC16_Modbus(g_tModS.RxBuf, g_tModS.RxCount);
        if (crc1 != 0)
        {
                goto err_ret;
        }

        /* 站地址 (1字节) */
        addr = g_tModS.RxBuf[0];                                /* 第1字节 站号 */
        if (addr != SADDR485)                                         /* 判断主机发送的命令地址是否符合 */
        {
                goto err_ret;
        }

        /* 分析应用层协议 */
        MODS_AnalyzeApp();                                               
       
err_ret:
#if 1                                                                                /* 此部分为了串口打印结果,实际运用中可不要 */
        g_tPrint.Rxlen = g_tModS.RxCount;
        memcpy(g_tPrint.RxBuf, g_tModS.RxBuf, g_tModS.RxCount);
#endif
       
        g_tModS.RxCount = 0;                                        /* 必须清零计数器,方便下次帧同步 */
}
-----------------------------------------------------------------------
在执行过程中,下一个接收中断来了,改变了g_tModS.RxCount的值,导致后续的以g_tModS.RxCount值为判断的分支进不去而丢弃这个帧呢?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2022-2-28 12:16:38 | 显示全部楼层
碧蓝深邃 发表于 2022-2-28 11:59
硬汉大哥,想请问下这个回调函数:
------------------------------------------------------------------ ...

1、485是独立的buf管理的。
2、不会,rtu是主从式,从机应答了才会下一帧。
回复

使用道具 举报

0

主题

5

回帖

5

积分

新手上路

积分
5
发表于 2022-2-28 15:26:25 | 显示全部楼层
eric2013 发表于 2022-2-28 12:16
1、485是独立的buf管理的。
2、不会,rtu是主从式,从机应答了才会下一帧。

非常感谢硬汉大哥!
第二个问题明白了!但是第一个问题还是想请问一下:
modbus确实在头文件开辟了自己的收发buf如下
-------------------------------------------------------
#define S_RX_BUF_SIZE                30
#define S_TX_BUF_SIZE                128

typedef struct
{
        uint8_t RxBuf[S_RX_BUF_SIZE];
        uint8_t RxCount;
        uint8_t RxStatus;
        uint8_t RxNewFlag;

        uint8_t RspCode;

        uint8_t TxBuf[S_TX_BUF_SIZE];
        uint8_t TxCount;
}MODS_T;
---------------------------------------------------
但是串口FIFO里也开辟了串口本身的收发buf如下:
-----------------------------------------------------
#if UART3_FIFO_EN == 1
        #define UART3_BAUD                        9600
        #define UART3_TX_BUF_SIZE        1*1024
        #define UART3_RX_BUF_SIZE        1*1024
---------------------------------------------------------------------------------
想请问的是modbus是独立的buf,并且在中断里有自己的写buf回调,并没有从串口FIFO的读buf里去取数据,那么是否可以释放掉大部分的串口本身的RXbuf呢?因为没用到。(RAM小,为了节省RAM。。。)
再次谢谢硬汉大哥!
回复

使用道具 举报

4

主题

280

回帖

292

积分

高级会员

积分
292
发表于 2022-4-11 09:09:14 | 显示全部楼层
非常好的资料。谢谢楼主。
回复

使用道具 举报

2

主题

9

回帖

15

积分

新手上路

积分
15
发表于 2022-5-3 14:00:09 | 显示全部楼层
硬汉哥 啥时候来一起modbus 主从站的教学视频?
回复

使用道具 举报

0

主题

13

回帖

13

积分

新手上路

积分
13
发表于 2022-5-12 14:15:02 | 显示全部楼层
文档里的CRC校验高字节与CRC校验低字节的描述文字顺序反了,但modbus帧的CRC的值的顺序是对的
回复

使用道具 举报

0

主题

13

回帖

13

积分

新手上路

积分
13
发表于 2022-5-12 14:17:37 | 显示全部楼层
文档里面表格的CRC校验高字节和CRC校验低字节描述文字反了,但modbus帧的CRC值顺序是对的
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2022-5-12 14:29:08 | 显示全部楼层
quar 发表于 2022-5-12 14:17
文档里面表格的CRC校验高字节和CRC校验低字节描述文字反了,但modbus帧的CRC值顺序是对的

modbus是大端,注意这一点就行。
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2022-6-19 22:14:29 | 显示全部楼层
void MODS_ReciveNew(uint8_t _byte)
{
        /*
                3.5个字符的时间间隔,只是用在RTU模式下面,因为RTU模式没有开始符和结束符,
                两个数据包之间只能靠时间间隔来区分,Modbus定义在不同的波特率下,间隔时间是不一样的,
                所以就是3.5个字符的时间,波特率高,这个时间间隔就小,波特率低,这个时间间隔相应就大

                4800  = 7.297ms
                9600  = 3.646ms
                19200  = 1.771ms
                38400  = 0.885ms
        */
        uint32_t timeout;

        g_mods_timeout = 0;
      
        timeout = 35000000 / SBAUD485;                        /* 计算超时时间,单位us 35000000*/
      
        /* 硬件定时中断,定时精度us 硬件定时器1用于ADC, 定时器2用于Modbus */
        bsp_StartHardTimer(1, timeout, (void *)MODS_RxTimeOut);

        if (g_tModS.RxCount < S_RX_BUF_SIZE)
        {
                g_tModS.RxBuf[g_tModS.RxCount++] = _byte;
        }
}

此处的接收超时时间不符合 MODBUS 标准, 标准是"当波特率小于等于 19200bps 时, 接收超时为 3.5 个字节, 否则固定为 1750us"
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106395
QQ
 楼主| 发表于 2022-6-20 00:15:31 | 显示全部楼层
wtj7603 发表于 2022-6-19 22:14
void MODS_ReciveNew(uint8_t _byte)
{
        /*

是的
image.png
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2022-8-17 12:00:29 | 显示全部楼层
image.png     这的寄存器数量是不是错了
回复

使用道具 举报

21

主题

48

回帖

111

积分

初级会员

积分
111
发表于 2022-8-19 15:37:46 | 显示全部楼层
eric2013,
modbus协议,V4版本的在103上已经调通了。感谢~分享的福利。
现在正在调试V5版本的在407上的协议。
有HAL版本的modbus协议吗?
回复

使用道具 举报

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

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-18 16:11 , Processed in 0.331335 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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