[AI8051U入门第五步]modbus_RTU主机
前言
学习目标
1、学习Modbus rtu 主机程序编写
2、学习Modbus slave软件使用
完整程序可在博主下载资源进行下载
Modbus rtu介绍可以看添加链接描述[AI8051U入门第四步]modbus_RTU从机了解
一、Modbus slave软件使用
STEP1:打开Modbus slave软件
STEP2:点击connect
STEP3:按照下面进行配置,串口号根据自己的配置
STEP4:按照下面进行配置
STEP5:按照下面进行配置
二、部分程序展示
以下是Modbus 发送函数,
/**********************************
*函数名:发送数据给从机 0x03
*形参:形参:unsigned char Device, //从机地址
*unsigned char Function //功能码
*unsigned short Frist_addr, //读取的寄存器起始地址
*unsigned short number, //读取的寄存器个数
*时间:2025/7/18
*作者:单片有机机
**********************************/
void Modbus_Rtu_Pull_Write(unsigned char Device,unsigned char Function,unsigned short Frist_addr,unsigned short number){unsigned char buff[8];unsigned short crc16=0;buff[0]=Device; //从机地址buff[1]=Function; //功能码buff[2]=Frist_addr>>8; //寄存器地址高位 buff[3]=Frist_addr; //寄存器地址低位buff[4]=number>>8; //读取寄存器个数高位 buff[5]=number; //读取寄存器个数低位 crc16=GetCRC16(buff,6);buff[6]=crc16>>8; //读取寄存器个数高位 buff[7]=crc16; //读取寄存器个数低位 //发送指令Uart1_Putc_Num(buff,sizeof(buff));
}
解析数据帧,按下P33控制P2LED电亮
/**********************************
*函数名:解析从机发送来的数据
*形参:形参:无
*时间:2025/7/18
*作者:单片有机机
**********************************/
unsigned char Modbus_Rtu_Pull_Parsing(void){int j=0;unsigned short crc=0;unsigned short crc2=0;unsigned short sum=0; unsigned short addr=0; unsigned short dat=0;unsigned short return_dat=0;if(UAR1.timeOut==1){UAR1.timeOut=0;if(UAR1.rx1_len >=5){crc =GetCRC16((unsigned char*)UAR1.buff1,UAR1.rx1_len-2);//计算接收数据的CRC16校验crc2=((UAR1.buff1[UAR1.rx1_len-2]<<8)+UAR1.buff1[UAR1.rx1_len-1]);//读取串口接收的数据的CRC校验位 if(crc == crc2){ //判断CRC16校验位是否正确if(UAR1.buff1[0]==Modbus_Slave_Device) //判断modebus从机地址{ if(UAR1.buff1[1] ==Modbus_PULL_Keep_Read){//判断是0x03 sum = UAR1.buff1[2]/2; //读取从机上传数量 for(j=0;j<sum;j++){ //将数据提取出来info.datas[j]= (UAR1.buff1[3+2 * j] << 8) | UAR1.buff1[3+2 * j + 1]; }return_dat =1;}else if(UAR1.buff1[1] ==Modbus_PULL_Write){addr = UAR1.buff1[2]<<8|UAR1.buff1[3];if(addr == Modbus_PULL_Write_addr){ //读取地址dat = UAR1.buff1[4]<<8|UAR1.buff1[5]; if(dat == 0x0000){P27=1; P21=0;} if(dat == 0x0001){P21=1; P22=0;}if(dat == 0x0002){P22=1; P23=0;}if(dat == 0x0003){P23=1; P24=0;}if(dat == 0x0004){P24=1; P25=0;} if(dat == 0x0005){P25=1; P26=0;} if(dat == 0x0006){P26=1; P27=0;} return_dat =2; }} }}UAR1.rx1_len =0; }} return return_dat;
}
运行函数
void Modbus_Rtu_Pull_loop(void){//循环发送数据给从机if(Modbus_cnt>=Modbus_Read_Time){Modbus_Rtu_Pull_Write(Modbus_Slave_Device,Modbus_PULL_Keep_Read,0x0000,0x0001); //读从机地址为0x01,起始地址0x0000,读取数量1个 Modbus_cnt =0;//从新计数 }if((Modbus_cnt >= 50 && Modbus_cnt <= Modbus_Read_Time-50) &&wait_Send_flag ==1){ //避免粘包wait_Send_flag =0;Modbus_Rtu_Pull_Write(Modbus_Slave_Device,Modbus_PULL_Write,Modbus_PULL_Write_addr,tast);} if(KEY ==0){delay_ms(5);if(KEY ==0){ wait_Send_flag =1;tast++; if(tast >6){tast=0;}} while(KEY ==0); }//解析从机发送来的数据Modbus_Rtu_Pull_Parsing();if(rc->CtrlBits.LED_P20 ==1){P20=0; }else if(rc->CtrlBits.LED_P20 ==0){P20=1;}}
三、完整程序展示
Modbus_RTU_P.c
#include "Modbus_RTU_P.h"
#include "crc16.h"
#include "uart.h"
#include "string.h"
#include "system.h"#define Modbus_Read_Time 200 //每100ms发送一次0x03寄存器数据
#define Modbus_Slave_Device 0x01 //从机地址
#define Modbus_PULL_Write 0x06
#define Modbus_PULL_Keep_Read 0x03#define Modbus_PULL_Write_addr 0x0001#define KEY P33struct tagINFO info;unsigned long Modbus_cnt=0;unsigned short tast=0;
unsigned short wait_Send_flag=0;void Modbus_Rtu_Pull_loop(void){//循环发送数据给从机if(Modbus_cnt>=Modbus_Read_Time){Modbus_Rtu_Pull_Write(Modbus_Slave_Device,Modbus_PULL_Keep_Read,0x0000,0x0001); //读从机地址为0x01,起始地址0x0000,读取数量1个 Modbus_cnt =0;//从新计数 }if((Modbus_cnt >= 50 && Modbus_cnt <= Modbus_Read_Time-50) &&wait_Send_flag ==1){ //避免粘包wait_Send_flag =0;Modbus_Rtu_Pull_Write(Modbus_Slave_Device,Modbus_PULL_Write,Modbus_PULL_Write_addr,tast);} if(KEY ==0){delay_ms(5);if(KEY ==0){ wait_Send_flag =1;tast++; if(tast >6){tast=0;}} while(KEY ==0); }//解析从机发送来的数据Modbus_Rtu_Pull_Parsing();if(rc->CtrlBits.LED_P20 ==1){P20=0; }else if(rc->CtrlBits.LED_P20 ==0){P20=1;}}/**********************************
*函数名:发送数据给从机 0x03
*形参:形参:unsigned char Device, //从机地址
*unsigned char Function //功能码
*unsigned short Frist_addr, //读取的寄存器起始地址
*unsigned short number, //读取的寄存器个数
*时间:2025/7/18
*作者:单片有机机
**********************************/
void Modbus_Rtu_Pull_Write(unsigned char Device,unsigned char Function,unsigned short Frist_addr,unsigned short number){unsigned char buff[8];unsigned short crc16=0;buff[0]=Device; //从机地址buff[1]=Function; //功能码buff[2]=Frist_addr>>8; //寄存器地址高位 buff[3]=Frist_addr; //寄存器地址低位buff[4]=number>>8; //读取寄存器个数高位 buff[5]=number; //读取寄存器个数低位 crc16=GetCRC16(buff,6);buff[6]=crc16>>8; //读取寄存器个数高位 buff[7]=crc16; //读取寄存器个数低位 //发送指令Uart1_Putc_Num(buff,sizeof(buff));
}/**********************************
*函数名:解析从机发送来的数据
*形参:形参:无
*时间:2025/7/18
*作者:单片有机机
**********************************/
unsigned char Modbus_Rtu_Pull_Parsing(void){int j=0;unsigned short crc=0;unsigned short crc2=0;unsigned short sum=0; unsigned short addr=0; unsigned short dat=0;unsigned short return_dat=0;if(UAR1.timeOut==1){UAR1.timeOut=0;if(UAR1.rx1_len >=5){crc =GetCRC16((unsigned char*)UAR1.buff1,UAR1.rx1_len-2);//计算接收数据的CRC16校验crc2=((UAR1.buff1[UAR1.rx1_len-2]<<8)+UAR1.buff1[UAR1.rx1_len-1]);//读取串口接收的数据的CRC校验位 if(crc == crc2){ //判断CRC16校验位是否正确if(UAR1.buff1[0]==Modbus_Slave_Device) //判断modebus从机地址{ if(UAR1.buff1[1] ==Modbus_PULL_Keep_Read){//判断是0x03 sum = UAR1.buff1[2]/2; //读取从机上传数量 for(j=0;j<sum;j++){ //将数据提取出来info.datas[j]= (UAR1.buff1[3+2 * j] << 8) | UAR1.buff1[3+2 * j + 1]; }return_dat =1;}else if(UAR1.buff1[1] ==Modbus_PULL_Write){addr = UAR1.buff1[2]<<8|UAR1.buff1[3];if(addr == Modbus_PULL_Write_addr){ //读取地址dat = UAR1.buff1[4]<<8|UAR1.buff1[5]; if(dat == 0x0000){P27=1; P21=0;} if(dat == 0x0001){P21=1; P22=0;}if(dat == 0x0002){P22=1; P23=0;}if(dat == 0x0003){P23=1; P24=0;}if(dat == 0x0004){P24=1; P25=0;} if(dat == 0x0005){P25=1; P26=0;} if(dat == 0x0006){P26=1; P27=0;} return_dat =2; }} }}UAR1.rx1_len =0; }} return return_dat;
}
Modbus_RTU_P.h
#ifndef __Modbus_Rtu_P
#define __Modbus_Rtu_P
#include <AI8051U.H>typedef struct tagINFO
{unsigned short datas[4];
}*pINFO;
typedef struct
{struct{unsigned LED_P20 :1; //0unsigned LED_P21 :1;unsigned LED_P22 :1;unsigned LED_P23 :1;unsigned LED_P24 :1;unsigned LED_P25 :1; //5unsigned LED_P26 :1;unsigned LED_P27 :1;unsigned NULL8 :1;unsigned NULL9 :1;unsigned NULL10 :1; //10unsigned NULL11 :1;unsigned NULL12 :1;unsigned NULL13 :1;unsigned NULL14 :1;unsigned NULL15 :1; //15}CtrlBits;unsigned short PTC100_N0_AD;
}RegCtrl;
extern struct tagINFO info;
#define rc ((RegCtrl *) info.datas)extern unsigned long Modbus_cnt;void Modbus_Rtu_Pull_loop(void);
void Modbus_Rtu_Pull_Write(unsigned char Device,unsigned char Function,unsigned short Frist_addr,unsigned short number);
unsigned char Modbus_Rtu_Pull_Parsing(void);
#endif
Timer定时器
timer.c
#include "timer.h"
#include "uart.h"
#include "Modbus_RTU_P.h"#define LED P20
unsigned long timer3_cnt=0;/**********************************
*功能:定时器3初始化
*形参:无
*时间:2025/7/14
*作者:单片有机机
**********************************/
void Timer3Init(void) //@33.1776Mhz 定时1ms
{T4T3M &= 0xFD; T3L = 0x33; T3H = 0xF5; ET3 =1; EA = 1;T4T3M |= 0x08; //开始计时
}/**********************************
*功能:定时器3中断处理函数
*形参:无
*时间:2025/7/14
*作者:单片有机机
**********************************/
void t3int() interrupt 19
{ timer3_cnt++;if(timer3_cnt>=1){if(UAR1.timeOut>1)UAR1.timeOut--;if(UAR1.timeOut>1)UAR1.timeOut--;if(UAR1.timeOut>1)UAR1.timeOut--;timer3_cnt=0;}Modbus_cnt ++;
}
timer.h
#ifndef __timer_H
#define __timer_H
#include <AI8051U.H>
void Timer3Init(void);#endif
main.c
/********************************** (C) COPYRIGHT ******************************** File Name : Main.c* Author : 单片有机机* Version : V1.0* Date : 2025/07/15* Description : modbus Rtu从机程序* Hardware : RX P30TX P31 * Frequency : 33.1776Mhz
*******************************************************************************/
#include <AI8051U.H>
#include "system.h"
#include "uart.h"
#include "timer.h"
#include "crc16.h"
#include "Modbus_RTU_P.h"
#include "modbus_rtu_s.h"#define Modbus_Slave_Mode 0
#define Modbus_PULL_Mode 1void main(void){GPIO_Init();Timer3Init(); Uart1_Init(115200); printf("hello,AI8051U、单片有机机\r\n");while(1){#if Modbus_PULL_ModeModbus_Rtu_Pull_loop(); #endif#if Modbus_Slave_ModeModbus_Rtu_Slave_loop();#endif}
}
总结
这就是Modbus RTU PULL程序