# modbus **Repository Path**: currybro/modbus ## Basic Information - **Project Name**: modbus - **Description**: 全新的、可裁剪的modbus。一定是你没见过的版本。易移植易上手,大量注释爱不释手。 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 260 - **Created**: 2021-08-09 - **Last Updated**: 2021-08-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # modbus ## 介绍 全新的、可裁剪的modbus。一定是你没见过的版本。易移植易上手,大量注释爱不释手。 ## 软件架构 本modbus基于状态机框架实现,因此具有极快的相应速度。不过状态机没有自己的缓存可以保存信息,因此需要额外的变量来存放对应的数据,所以该框架占用的内存会比缓存对比框架要多。 ## 安装教程 1. 先准备好一个工程,可以是新建的空的工程也可以是旧的工程。这里以新工程来举例。双击“新建工程.bat”,名字可以任意,只要是英文就行。这里随便写个modbus。 ![新建工程](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-1.gif) 2. 复制modbus.h和modbus.c到工程文件夹。 ![复制文件到工程](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-2.png) 3. 打开工程,在任意工程文件夹里双击,比如双击DEVICE文件夹,在弹出的选择框中双击modbus.c将其添加到工程中。 ![加入文件](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-3.gif) 4. 打开main.c,加载modbus的头文件。如下第二句: ```c #include "ecbm_core.h" //加载库函数的头文件。 #include "modbus.h" //加载modbus的头文件。 void main(){ //main函数,必须的。 system_init(); //系统初始化函数,也是必须的。 while(1){ } } ``` 至此modbus组件已完整的添加到工程中。但只是添加到工程中还不能使用modbus。modbus是基于串口的协议,因此我们还需要初始化串口。 5. 好在ECBM默认就是打开串口的,先到ecbm_core.h去设置当前使用单片机型号。当然设置的时候别忘了使用ECBM强大的图形化配置界面,只需要点击窗口左下角的【Configuration Wizard】标签就行。实例的单片机我用的是STC8F2K32S2,于是按下图的步骤设置。确保单片机时钟设置是【内部高速时钟HSI(标准)】,这样设置的话ECBM库会自己识别出你在stc-isp工具上设置的时钟频率。有了这个自动识别,你就不会因为时钟没调好、波特率不对而收不到数据啦。然后确保【自动下载功能】是打开的,一方面自动下载会让你的调试更加方便,另一方面该功能只要开启就会自动初始化串口,省事。 ![设置工程](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-4.gif) 6. 打开uart.h,进入到图形化配置界面。将波特率修改成实际使用的波特率,比如115200。使能接收并打开串口1的接收回调函数。 ![设置串口](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-5.png) 7. 在main.c中定义串口1回调函数,函数名为uart1_receive_callback。然后将modbus的接收函数放到串口1回调函数中。接着定义modbus读写串口的两个函数ecbm_modbus_rtu_set_data和ecbm_modbus_rtu_get_data。 ```c #include "ecbm_core.h" //加载库函数的头文件。 #include "modbus.h" //加载modbus的头文件。 void main(){ //main函数,必须的。 system_init(); //系统初始化函数,也是必须的。 while(1){ } } void uart1_receive_callback(void){//接收处理部分。 ecbm_modbus_rtu_receive(); } void ecbm_modbus_rtu_set_data(emu8 dat){//发送数据部分。 uart_char(1,dat);//ECBM库的发送函数。 } emu8 ecbm_modbus_rtu_get_data(void){//获取串口值部分。 return SBUF;//串口1的寄存器 } ``` 8. modbus还有接收超时的设定,因此还需要定时器的帮助。打开timer.h,进入图形化设置界面,任选一个定时器,比如定时器0。设置成定时器模式,然后定时时间为1mS。因为在实验平台上单片机工作在24MHz,所以定时1mS的初值是24000。 ![设置定时器](https://gitee.com/ecbm/modbus/raw/master/%E5%9B%BE%E7%89%87/317-6.png) 9. 回到main.c,添加初始化定时器和运行定时器的代码。最后把modbus的运行函数写到循环中就行了。此时modbus已安装完毕,可以使用了。 ```c #include "ecbm_core.h" //加载库函数的头文件。 #include "modbus.h" //加载modbus的头文件。 void main(){ //main函数,必须的。 system_init(); //系统初始化函数,也是必须的。 timer_init(); //初始化定时器。 timer_start(0); //开启定时器0。 while(1){ ecbm_modbus_rtu_run();//运行modbus函数。 } } void uart1_receive_callback(void){//接收处理部分。 ecbm_modbus_rtu_receive(); } void ecbm_modbus_rtu_set_data(emu8 dat){//发送数据部分。 uart_char(1,dat);//ECBM库的发送函数。 } emu8 ecbm_modbus_rtu_get_data(void){//获取串口值部分。 return SBUF;//串口1的寄存器 } void tim0_fun(void)TIMER0_IT_NUM {//定时器处理部分 ECBM_MODBUS_RTU_TIMEOUT_RUN(); } ``` ## 使用说明 modbus是基于串口的通信协议,用于电脑访问设备的寄存器来完成设置或者执行某些动作。其固定的数据格式为:【设备地址】+【功能码】+【起始地址】+【功能码相关】+【CRC校验】。本库目前支持01,02,03,04,05,06,10共7个功能码。 ### 功能码详解 #### 【01】读线圈 | 设备地址 | 功能码 | 起始地址 | 线圈数量 | CRC | | -------- | ------ | ------------- | -------- | ------------ | | 1~247 | 01 | 0x0000~0xFFFF | 1~2000 | 先低位后高位 | 举例:主机发送【01 01 00 00 00 01 FD CA】。意思是读取地址为01的设备中0000号线圈的值。 #### 【02】读离散量输入 | 设备地址 | 功能码 | 起始地址 | 线圈数量 | CRC | | -------- | ------ | ------------- | -------- | ------------ | | 1~247 | 02 | 0x0000~0xFFFF | 1~2000 | 先低位后高位 | 举例:主机发送【01 02 00 00 00 03 38 0B】。意思是读取地址为01的设备中0000~0002号3个离散量的值。 #### 【03】读保持寄存器 | 设备地址 | 功能码 | 起始地址 | 寄存器数量 | CRC | | -------- | ------ | ------------- | ---------- | ------------ | | 1~247 | 03 | 0x0000~0xFFFF | 1~125 | 先低位后高位 | 举例:主机发送【01 03 00 0A 00 03 25 C9 】。意思是读取地址为01的设备中000A~000C号3个寄存器的值。 #### 【04】读输入寄存器 | 设备地址 | 功能码 | 起始地址 | 寄存器数量 | CRC | | -------- | ------ | ------------- | ---------- | ------------ | | 1~247 | 04 | 0x0000~0xFFFF | 1~125 | 先低位后高位 | 举例:主机发送【01 04 00 00 00 01 31 CA 】。意思是读取地址为01的设备中0000号输入寄存器的值。 #### 【05】写单个线圈 | 设备地址 | 功能码 | 起始地址 | 线圈数量 | CRC | | -------- | ------ | ------------- | -------------- | ------------ | | 1~247 | 05 | 0x0000~0xFFFF | 0x0000或0xFF00 | 先低位后高位 | 举例:主机发送【01 05 00 0A FF 00 AC 38 】。意思是将地址为01的设备中000A号线圈的值设置为1。 #### 【06】写单个寄存器 | 设备地址 | 功能码 | 起始地址 | 寄存器值 | CRC | | -------- | ------ | ------------- | ------------- | ------------ | | 1~247 | 06 | 0x0000~0xFFFF | 0x0000~0xFFFF | 先低位后高位 | 举例:主机发送【01 06 00 01 12 34 D5 7D 】。意思是将地址为01的设备中0001号线圈的值设置为0x1234。 #### 【10】写多个寄存器 | 设备地址 | 功能码 | 起始地址 | 寄存器数量 | 字节计数 | 寄存器值 | CRC | | -------- | ------ | ------------- | ---------- | ------------ | ------------- | ------------ | | 1~247 | 10 | 0x0000~0xFFFF | 1~78 | 寄存器数量*2 | 0x0000~0xFFFF | 先低位后高位 | 举例:主机发送【01 10 00 0A 00 04 08 11 11 22 22 33 33 44 44 5D 5E 】。意思是将地址为01的设备中000A~000D号4个寄存器的值分别设置为0x1111、0x2222、0x3333、0x4444。 ## 更新历史 ### V1.0.2(2021-03-18) 1. 修改u8、u16、u32数据类型的名字,解决了重定义错误的出现。 2. 新增安装说明。 3. 更新图片。 ### V1.0.1(2021-03-16) 1. 优化了图形界面的逻辑。 2. 扩大了宏定义的作用范围,现在支持自定义读写函数和共用逻辑块。 3. 验证了01、03、05、06功能码。 4. 新增了0xFF的广播地址。 5. modbus自组头文件,成为独立的库。 6. 02和04指令暂时移出,需要用户自己填写。同时他们的异常码04无法使用。 ### V1.0.0(2021-03-15) 1. 更新了modbus第一版,支持01、02、03、04、05、06、16功能码。 2. 增加了基于硬件(STC8F2K32S2)和软件(ECBM V3库)的示例工程。