51单片机与力控modbus通信
51单片机与力控modbus通信

网上关于单片机和力控modbus通信的实例很少,关键并不是modbus协议而是力控与单片机连接的设置,下面的程序是基于51单片机做的。
在正个调试过程中由于我们不清楚力控发送给单片机的请求数据格式,我们可以用串口调试工具进行串口调试,将力控发送请求显示在串口调试工具中,其中单片机的程序如下#include<reg52.h>#include<string.h>#define uchar unsigned char //一个字节#define uint unsigned int // 两个字节void send_char(unsigned char txd);uint rece_count=0;uint send_flag=0;uchar rece_buf[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};uint i;/*串行口初始化*/void chushi(){SCON=0x50; //串口工作方式1,即10位异步PCON=0x00;//波特率不倍增TMOD=0x20;TH1=TL1=0xfa;TR1=1;}/*主程序*/main(){chushi();rece_count=0;while(1){if(RI){RI=0;if(rece_count<8){rece_buf[rece_count]=SBUF;rece_count++;}}if(rece_count==8){send_flag=1;}while(send_flag==1){ for(i=0;i<8;i++){SBUF=rece_buf[i];while(TI==0) ;TI=0;}}}}打开力控运行,将发送的数据保存到单片机中,然后打开串口调试工具,显示如下:我们看力控发送请求的格式:01 03 ff ff 00 01 84 2e,最后的两位crc是高位在前,低位在后。
51单片机新手入门之Modbus通讯

51单片机新手入门之Modbus通讯本文和另一篇----C# WPF新手入门之Modbus通讯为一个系列,所用模块均是气体分析模块,之前用C3做的上位机(主要是方便调试,单片机不好搞),既然电脑上串口通讯代码没问题,那么放到单片机稍加修改也就理所当然肯定可行O(∩_∩)O~。
本文包括单片机的电路设计和软件设计两部分(单片机采用STC12C5A60S2双串口),至此单片机和上位机的串口通讯均成功实现,供需要的同学参考。
1.电路设计Altium Designer原理图如下:电源部分:24V转5V及6VAD及滤波Modbus通讯电脑串口通讯lcd屏显示预留接口,可以另接MAX232用来调试发送给模块数据正确性实际效果如下:电脑串口接收到的数据,和LCD 屏显示的是一样的下载步骤1. 选择芯片下载步骤2. 打开串口 下载步骤3. 打开hex 文件 下载步骤4. MCU 先断电,点击下载,看到提示后上电2.软件设计这里最麻烦的在于模块的Modbus通讯需要偶校验,我是手动添加的校验位,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,把所有需要发送的数据一个个手动添加了再发送^_^;另外lcd12864屏也搞半天,主要照着时序图写必须sid先发送,然后sclk,不然不行 ̄□ ̄。
气体模块详细的通讯协议参考和上位机通讯那篇,此处不再介绍。
图中电路板没有焊AD芯片和温度芯片,引脚备用,相关代码注释掉了。
代码如下:#include "STC12C5A60S2.H"#include <intrins.H>.//头文件#define uchar unsigned char#define uint unsigned intuchar sendbuffer[17];//发送数据uchar flag=0;uchar re_buffer[32];uchar count=0;uint ad_data=0;double ad_vol=0;uint con_mid=0;uint gascon=0;long sum_o2=0;uint average_counter=20;uint idata oldtemp[21];uchar idata Send_Buff[20]; //moduleuint pre_contemp=0;uint O2_con=0;uchar idata test[21]; //moduleuint temperature=0;uint dat;uint testlcd=0;uchar c[]={0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9};//MAX1241 模数芯片引脚设置,此电路板我没焊^_^//sbit ADC_CS=P0^1;//sbit ADC_CLK=P0^0;//sbit ADC_DOUT=P0^2;sbit ADC_CS=P3^6;sbit ADC_CLK=P3^5;sbit ADC_DOUT=P3^7;//LCD12864 LCD屏幕引脚设置sbit cs=P2^2;sbit sid=P2^1;sbit sclk=P2^0;sbit DQ=P0^1; //DS18B20 温度引脚设置,依然没有…#define N 11#define N2 20void delayms(unsigned char t){unsigned char i;unsigned char j;for(j=t;j;j--)for(i=192;i;i--);/*1ms延时*/}void delayus(uint t){uint i;for(i=0;i<t;i++){_nop_();}}void delay(float sec){unsigned int i;unsigned int j;j=sec*100;while(j--){i=1561;while(--i);}}void UART1_init() //串口1初始化,此串口和电脑通讯{TMOD=0x20;/设置定时器工作方式2TH1=0xfd; //波特率9600TL1=0xfd;TR1=1;REN=1;SM0=0;SM1=1;//ES=1;}void UART2_init()//串口2初始化,和模块通讯 Modbus{S2CON= 0x50; //方式1,允许接收BRT = 0xf4; //波特率2400AUXR = AUXR |0X10; //允许独立波特率允许AUXR1 = AUXR1&0xef; //将uart2切换到P1口IE2 = IE2|0X01; //允许串口2中断}void UART1_Send (unsigned char UART_data)//{//ES=0;SBUF = UART_data; //将接收的数据发送回去while(TI!=1); //检查发送中断标志位TI = 0; //另发送中断标志位为0//ES=1;}void UART2_Send(unsigned char UART_data)//串口2发送{//ES = 0 ;S2BUF = UART_data;while((S2CON&0x02)!=0x02);S2CON &= ~0x02;//ES = 1 ;}void UART1_Send_String (char *str, char len)//串口1发送字符串{unsigned char i;for(i=0;i<=len;i++){UART1_Send(str[i]);}}void UART2_Send_String (char *str, char len) //串口2发送字符串{unsigned char i;for(i=0;i<=len;i++){UART2_Send(str[i]);}}unsigned char Creat_Addr(unsigned char adr, unsigned char position)//计算模块地址{unsigned char hich;unsigned char loch;hich = adr/16;loch = adr%16;if(hich>9)hich+=7;if(loch>9)loch+=7;if(position == 1){return hich+0x30;}else if(position == 0){return loch+0x30;}}unsigned char CheckSum(unsigned char *str, unsigned char position, uchar len)/计算校验码{uchar i;unsigned int sum=0;uchar hi, lo;//uchar len = 12;for(i = 1; i <= len; i ++){*str ++;sum += *str;}sum = 256-(sum%256);hi = sum/16;lo = sum%16;if(hi > 9)hi += 7;if(lo > 9)lo += 7;hi += 0x30;lo += 0x30;if(sum == 256)hi = lo = 0x30;if(position == 1){return hi;}else if(position == 0){return lo;}}void data_init(){sendbuffer[0]=0x5B;sendbuffer[1]=0x30;sendbuffer[2]=0x30;sendbuffer[3]=0x30;sendbuffer[4]=0x30;sendbuffer[5]=0x30;sendbuffer[6]=0x7C;sendbuffer[7]=0x30;sendbuffer[8]=0x30;sendbuffer[9]=0x30;sendbuffer[10]=0x30;sendbuffer[11]=0x30;//TEMsendbuffer[12]=0x30;sendbuffer[13]=0x30;sendbuffer[14]=0x5D;sendbuffer[15]=0x0D;sendbuffer[16]=0x0A;}void calculate_module(unsigned char str[])//lcd屏显示{unsigned int concen;uchar wan,qian,bai,shi,ge;/*uchar d4 = str[7]-48;uchar d3 = str[8]-48;uchar d2 = str[9]-48;uchar d1 = str[10]-48;*/ //浓度只需要后面部分 uchar d4 = str[24]-48;uchar d3 = str[25]-48;uchar d2 = str[26]-48;uchar d1 = str[27]-48;if(d4>9)d4-=7;if(d3>9)d3-=7;if(d2>9)d2-=7;if(d1>9)d1-=7;concen = d4*4096+d3*256+d2*16+d1;gascon=concen;wan=concen/10000;qian=concen%10000/1000;bai=concen%1000/100;shi=concen%100/10;ge=concen%10;//sendbuffer[6]=wan+0x30; //最终显示XXX.X%sendbuffer[7]=qian+0x30;sendbuffer[8]=bai+0x30;sendbuffer[9]=shi+0x30;sendbuffer[10]=ge+0x30;}void module_init()//气体模块初始化{Send_Buff[0] = ':';Send_Buff[3] = '0';Send_Buff[4] = '3';Send_Buff[5] = '0';Send_Buff[6] = '0';Send_Buff[7] = '0';Send_Buff[8] = 'A';Send_Buff[9] = '0';Send_Buff[10] = '0';Send_Buff[11] = '0';Send_Buff[12] = '1';Send_Buff[1] = Creat_Addr(31, 1);Send_Buff[2] = Creat_Addr(31, 0);Send_Buff[13] = CheckSum(Send_Buff, 1, 12);Send_Buff[14] = CheckSum(Send_Buff, 0, 12);Send_Buff[15] = 0x0D;Send_Buff[16] = 0x0A;//手动添加校验,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,最终发送给模块的以下数据,地址被写死,这个不像C#做的一目了然O(∩_∩)Otest[0]=0X3A;test[1]=0XB1;test[2]=0XC6;test[3]=0X30;test[4]=0X33;test[5]=0X30;test[6]=0X30;test[7]=0X30;test[8]=0X41;test[9]=0X30;test[10]=0X30;test[11]=0X30;test[12]=0XB1;test[13]=0X39;test[14]=0XB4;test[15]=0X8D;test[16]=0X0A;}/*uint read_max1241() AD芯片处理{uint ADC_Data;uchar i;ADC_CLK=0;ADC_CS=0;ADC_Data=0;while(!ADC_DOUT);ADC_CLK=1;ADC_CLK=0;for(i=0;i<12;i++){ADC_CLK=1;ADC_Data<<=1;ADC_Data |= ADC_DOUT;ADC_CLK=0;}ADC_CS=1;ADC_CLK=0;return ADC_Data;}unsigned int ad_filter()//滤波{unsigned int count1,i,j;unsigned int value_buf[N];unsigned int temp;unsigned int sum=0;for (count1=0;count1<N;count1++){value_buf[count1] =read_max1241(); delayms(20);}for (j=0;j<(N-1);j++){for (i=0;i<(N-j);i++){if ( value_buf[i]>value_buf[i+1] ) {temp = value_buf[i];value_buf[i] = value_buf[i+1]; value_buf[i+1] = temp;}}}for(count1=3;count1<(N-3);count1++){sum += value_buf[count1];}return (unsigned int)(sum/(N-6));}void calculate_o2()//测试用{uchar wan,qian,bai,shi,ge;uint con_fin=0;uchar i;ad_data=ad_filter();ad_vol=(ad_data/4096.0)*2500.0;//Voltage ad_data=(uint)ad_vol; //concentrationdelayms(2);if(average_counter>0){sum_o2+= ad_data;oldtemp[average_counter-1]=ad_data;average_counter--;}else{sum_o2 -=oldtemp[19];for(i=20;i>0;i--){oldtemp[i]=oldtemp[i-1];}oldtemp[0]=ad_data;sum_o2+=oldtemp[0];con_fin=(uint)(sum_o2/N2);//O2_con=0.8*con_fin+0.2*pre_contemp;//pre_contemp=O2_con;wan=con_fin/10000;qian=con_fin%10000/1000;bai=con_fin%1000/100;shi=con_fin%100/10;ge=con_fin%10;sendbuffer[1]=wan+0x30;sendbuffer[2]=qian+0x30;sendbuffer[3]=bai+0x30;sendbuffer[4]=shi+0x30;sendbuffer[5]=ge+0x30;delayms(5);}}//-------------18B20 温度显示复位函数--------------- void ow_reset(void){char presence=1;while(presence){while(presence){DQ=1;delayus(2);DQ=0; //delayus(550); // 550usDQ=1; //delayus(66); // 66uspresence=DQ;}delayus(500); //延时500uspresence = ~DQ;}DQ=1;}//-----------18B20写命令函数------------void write_byte(uchar val){uchar i;for (i=8; i>0; i--) //{DQ=1;delayus(2);DQ = 0;delayus(5);//5usDQ = val&0x01;delayus(66); //66usval=val/2;}DQ = 1;delayus(11);}//--------------18B20读一个字节函数---------uchar read_byte(void){uchar i;uchar value = 0;for (i=8;i>0;i--){DQ=1;delayus(2);value>>=1;DQ = 0;delayus(4); //4usDQ = 1;delayus(4); //4usif(DQ)value|=0x80;delayus(66); //66us}DQ=1;return(value);}void Read_Temperature(void){unsigned int Temp1,Temp2;uchar bai,shi,ge;ow_reset(); //DS18B20write_byte(0xCC);write_byte(0x44);ow_reset(); //DS1302复位write_byte(0xCC);write_byte(0xbe);Temp1=read_byte();Temp2=read_byte();ow_reset();temperature=(((Temp2<<8)|Temp1)*0.625); //0.0625=xx, 0.625=xx.x, 6.25=xx.xxbai=temperature/100;shi=temperature%100/10;ge=temperature%10;sendbuffer[11]=bai+0x30;sendbuffer[12]=shi+0x30;sendbuffer[13]=ge+0x30;delayms(5);}* //图中电路板此部分没焊,此部分代码不使用^_^void writecmd_lcd(uchar cmd) //lcd屏写指令{uchar i;uchar cmd1;cmd1=cmd;//----------先写控制,选择写指令,还是写数据11111000 for(i=0;i<5;i++) //必须sid先发送,然后sclk,不然不行 {sid=1;sclk=1;sclk=0;}for(i=0;i<3;i++){sid=0;sclk=1;sclk=0;}//delayms(10);cmd=cmd&0xf0; //先高4位for(i=0;i<8;i++){if(cmd&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd=cmd<<1;}//delayms(10);cmd1=((cmd1<<4)&0xf0); //低4位 for(i=0;i<8;i++){if(cmd1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd1=cmd1<<1;}}void writedata_lcd(uchar dat) {uchar i;uchar dat1;dat1=dat;//11111010for(i=0;i<5;i++){sid=1;sclk=1;sclk=0;}sid=0;sclk=1;sclk=0;sid=1;sclk=1;sclk=0;sid=0;sclk=1;sclk=0;//delayms(10);dat=dat&0xf0;for(i=0;i<8;i++)if(dat&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat=dat<<1;}//delayms(10);dat1=((dat1<<4)&0xf0);for(i=0;i<8;i++){if(dat1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat1=dat1<<1;}}void init_lcd()//初始化lcd屏{cs=1;writecmd_lcd(0x30);//设定为8位控制writecmd_lcd(0x0c);//显示打开writecmd_lcd(0x01);//清屏}void gotoxy(uint row, uint col){switch(row){case 1: writecmd_lcd(0x80+col);break; case 2: writecmd_lcd(0x90+col);break; case 3: writecmd_lcd(0x88+col);break; case 4: writecmd_lcd(0x98+col);break;}void clear(){writecmd_lcd(0x01);delayms(10);}void SendStr(uchar *str){uchar i;for(i=0;str[i]!='\0';i++){writedata_lcd(str[i]);}}void lcd_display(uint lcddata)//lcd屏显示浓度{uchar wan,qian,bai,shi,ge;wan=lcddata/10000;qian=lcddata%10000/1000;bai=lcddata%1000/100;shi=lcddata%100/10;ge=lcddata%10;gotoxy(2,1);writedata_lcd(0xa3);writedata_lcd(c[qian]);gotoxy(2,2);writedata_lcd(0xa3);writedata_lcd(c[bai]);gotoxy(2,3);writedata_lcd(0xa3);writedata_lcd(c[shi]);gotoxy(2,4);SendStr(".");gotoxy(2,5);writedata_lcd(0xa3);writedata_lcd(c[ge]);gotoxy(2,6);SendStr("%");}void main()//主函数{delay(2.1);UART1_init();UART2_init();data_init();module_init();init_lcd();EA=1;delay(2.1);while(1){/*calculate_o2();//测试用Read_Temperature();*/ //温度芯片没焊UART2_Send_String(test,16);//串口2:和模块通讯delay(0.8);if(flag==1){calculate_module(re_buffer);delayms(5);UART1_Send_String(sendbuffer,16);//串口1:电脑上可以接收发送的数据 flag=0;delay(0.8);}lcd_display(gascon);//lcd显示浓度delay(0.8);}}/*void uart1_in() interrupt 4/串口1中断,不使用,因为只是发送{RI=0;re_buffer[count]=SBUF;if(re_buffer[0]!=':'){count=0;}else{count++;if(count==10){flag=1;count=0;}}}*/void uart2_in() interrupt 8//串口2中断,需要接受模块返回的数据{if(S2CON&0X01){re_buffer[count]=S2BUF;re_buffer[count]&=0x7f;count++;S2CON&=0XFE;}if(count==32){count=0;flag=1;}}。
力控组态软件怎么和支持modbus协议的设备通信

问题:力控组态软件怎么和支持modbus协议的设备通信回答:Modbus是应用于电子控制器上的一种通用协议。
通过此协议,控制器相互之间、控制器经由网络(例如以太网)或者串口和其它设备之间可以通信。
力控科技的开发平台,既可以作为modbus主站从设备采集数据,也可以作为modbus从站将数据转发给其他的modbus 主站,下面以力控作为modbus(rtu串行口)主站为例讲解1、力控作为modbus主站访问其他modbus从站的方法概括来讲可以分为两个步骤:(1)、进入io设备组态,找到modbus(rtu串行口),组设备结合现场情况填写以上参数,设备地址和实际连接设备的地址一致。
(2)、串口配置(3)、采集配置(1)写单个寄存器:HR保持寄存器单寄存的写入操作,一般设备支持这个命令。
一条命令只能写一个寄存器。
简单的数据类型可以用这条命令支持,如8位、16位的数据类型可以用此命令写入。
(2)写多个寄存器:HR保持寄存器多寄存器的写入操作,一条命令可以写多个寄存器的值。
复杂的数据类型用这条命令支持,如32位,64位的数据类型。
(3)屏蔽写寄存器:HR保持寄存器的模拟量按位操作命令(设备要支持屏蔽写命令22功能码)。
(4)最大长度:是指MODBUS中一条数据所读取的字(两个字节)数,包的长度越长,一次读取的数据就越多,通讯效率就越快。
MODBUS协议中规定数据包最大长度不能超过255个字节。
另外有些PLC对包长还有限制,请根据具体设备进行填写。
(5)组包间隔:两个相邻采集点的字偏移地址如果大于等于“组包间隔”就重新打一个包。
他可以控制包中数据点的密度,如上图配置包中相邻数据的地址间距不会大于10。
(6)05功能码:选中后线圈下置命令用05功能码执行。
(7)15功能码:选中后线圈下置命令用15功能码执行。
2、数据连接打开数据库组态,选择数据库新建点:选择相应的点类型,点击继续填写点名,其他可以默认,点击数据连接点参数:常用的是PV(现场实时值)和DESC,当要读取字符串时要用DESC连接IO设备:选择要连接的的设备名称连接项:点击“增加”增加链接项根据实际的工程需要设置相对应的内存区以及偏移地址、数据格式、读写属性,就完成了数据连接的全过程。
Modbus ASCⅡ协议在51单片机上的实现

生 反馈 消息 并用 Mo b s d u 协议 发 出 在其 它 网络 上包 含 息 中每个 字节
代 码系统 : 位二 进制 , 八 十六 进制 数 0 , …9 A…F 消 息 中每 个 8位域 都 由两个 十六 进制 字符组 成
结 位 束
ED N
消 息格式 : Mo b s 信分 为 两种 . du 通 一种 经 由 R 2 2兼 容 串行 起 位 S3 始 地 f 功 码 数 位 C校 码 址e 码 能代 据 R 验 c TR ADES DRs FNTO DT Uc I N AA CCC K R 腿C 接 口组成 Mob s d u 网络 进行通 信 . 另一 种 通过 以太 网结 SAT
( ) C I 式 1AS I 模
字 节 。起始 位 默认 设 置 O 3 (, 位 默认 设置 O O x A :结束 1 xD ( 回车)O O ( 行) ,x A 换 。用 户 通过 设 置地 址 位可 实 现多 机 机 设定 功能 代码 为 O O , l , 表单 片机 检 测, 能代 x0O O代 x 功
Mo b s d u 是全球 第 一个真 正用 于工业 现场 的 总线 协 中每个 A CI S I 字符 都是~ 个 十六进 制字符 。 议、 通过 此协 议 , 制 器相互 之 间 、 制器 经 由 网络 ( 控 控 例 消 息格式 : 如以太 网 ) 和其 它设 备之间 可 以通信 。 已经 成 为一种 起始 它 位 T R 通 用 的工业 标准 。 了它 , 有 不同厂 商生 产 的控 制设 备 之 S AT
包 含两个 4 i 的十六进 制 的字符 bt
制 器须 要 知道 它们 的设 备地 址 . 别按 地址 发 来 的 消 识 息, 决定 要产 生何 种行 动 。如果需 要 回应 . 制器 将产 控 了 Mo b s消息转 换为 在此 网络上使 用 的帧或 包 结构 . du 这种 转换 也 扩展 了根 据具体 的网络解 决节 地址 、路 由 路 径及错 误 检测 的方 法
51单片机程序(modbus通信)

modbus通信1、LCD.c#include <reg51.h>#include<LCD.h>unsigned char code number_X[]={ //宽x高=8x16,纵向字节倒序0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00, //00x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00, //10x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00, //20x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00, //30x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00, //40x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00, //50x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00, //60x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00, //70x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00, //80x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00, //90x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // .0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00, //-0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //nop 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00, //:0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00};void LCD_WriteCommandE1(unsigned char com) {while(CRADD1 & 0x80);CWADD1 = com;}void LCD_WriteDataE1(unsigned char dat)while(CRADD1 & 0x80);DWADD1 = dat;}void LCD_WriteCommandE2(unsigned char com) {while(CRADD2 & 0x80);CWADD2 = com;}void LCD_WriteDataE2(unsigned char dat){while(CRADD2 & 0x80);DWADD2 = dat;}void LCD_Init(){LCD_WriteCommandE1(0xe2);LCD_WriteCommandE2(0xe2);LCD_WriteCommandE1(0xa4);LCD_WriteCommandE2(0xa4);LCD_WriteCommandE1(0xa9);LCD_WriteCommandE2(0xa9);LCD_WriteCommandE1(0xa0);LCD_WriteCommandE2(0xa0);LCD_WriteCommandE1(0xc0);LCD_WriteCommandE2(0xc0);LCD_WriteCommandE1(0xaf);LCD_WriteCommandE2(0xaf);}void LCD_Clear(void){unsigned char i,j;for(i=0;i<4;i++){LCD_WriteCommandE1(i+0xb8);LCD_WriteCommandE2(i+0xb8);LCD_WriteCommandE1(0x00);LCD_WriteCommandE2(0x00);for(j=0;j<0x50;j++){LCD_WriteDataE1(0x00);LCD_WriteDataE2(0x00);}}void display_cn(unsigned char lin,unsigned int col,unsigned int len,unsigned char *p) {unsigned int seg,i,j;unsigned char a,L,n;switch(lin){case 0: n=0xba;break;case 1: n=0xb8;break;}for(i=0;i<len;i++){for(j=0;j<2;j++){L=col;LCD_WriteCommandE1(n+j);LCD_WriteCommandE2(n+j);for(seg=0;seg<16;seg++){if (L < 61){a = L;LCD_WriteCommandE1(a);LCD_WriteDataE1(*p++);}else{a = L-61;LCD_WriteCommandE2(a);LCD_WriteDataE2(*p++);}L++;}}col=col+16;}}void display_number(unsigned char lin,unsigned int col,unsigned char num){unsigned int seg,i,j;unsigned char a,L,n,k;switch(lin){case 0: n=0xba;break;case 1: n=0xb8;break;}k=num*16;for(j=0;j<2;j++){L=col;LCD_WriteCommandE1(n+j);LCD_WriteCommandE2(n+j);for(seg=0;seg<8;seg++){if (L < 61){a = L;LCD_WriteCommandE1(a);LCD_WriteDataE1(number_X[k++]);}else{a = L-61;LCD_WriteCommandE2(a);LCD_WriteDataE2(number_X[k++]);}L++;}}}void display_unsigned_int(unsigned char lin,unsigned int col,unsigned int dat) {unsigned int seg;unsigned char k[4];k[3]=dat%10;k[2]=((dat/10)%10);k[1]=((dat/100)%10);k[0]=((dat/1000)%10);if(k[0]==0) {k[0]=12;}if((k[0]==12)&&(k[1]==0)){ k[0]=12;k[1]=12;}if((k[0]==12)&&(k[1]==12)&&(k[2]==0)){k[0]=12;k[1]=12;k[2]=12;}for(seg=0;seg<4;seg++){display_number(lin,col,k[seg]);col=col+10;}}void display_signed_int(unsigned char lin,unsigned int col,signed int dat){unsigned int seg;unsigned char k[5],a;k[0]=12;if(dat<0){dat=(~dat)+1;k[0]=11;}k[4]=dat%10;k[3]=((dat/10)%10);k[2]=((dat/100)%10);k[1]=((dat/1000)%10);a=k[0];if(k[1]==0) {k[0]=12;k[1]=a;}if((k[1]==a)&&(k[2]==0)){ k[0]=12;k[1]=12;k[2]=a;}if((k[1]==12)&&(k[2]==a)&&(k[3]==0)){k[0]=12;k[1]=12;k[2]=12;k[3]=a;}for(seg=0;seg<5;seg++){display_number(lin,col,k[seg]);col=col+10;}}void display_unsigned_char(unsigned char lin,unsigned int col,unsigned char dat) {unsigned int seg;unsigned char k[3];k[1]=dat%10;k[0]=((dat/10)%10);for(seg=0;seg<2;seg++){display_number(lin,col,k[seg]);col=col+10;}}2、LCD.h#include <reg51.h>#include <absacc.h>#ifndef __LCD__#define __LCD__#define CWADD1 XBYTE[0x8000]#define DWADD1 XBYTE[0x8001]#define CRADD1 XBYTE[0x8002]#define DRADD1 XBYTE[0x8003]#define CWADD2 XBYTE[0x8004]#define DWADD2 XBYTE[0x8005]#define CRADD2 XBYTE[0x8006]#define DRADD2 XBYTE[0x8007]extern void LCD_Init();extern void display_cn(unsigned char lin,unsigned int col,unsigned int len,unsigned char *p);extern void display_signed_int(unsigned char lin,unsigned int col,signed int dat);extern void display_unsigned_int(unsigned char lin,unsigned int col,unsigned int dat);extern void display_unsigned_char(unsigned char lin,unsigned int col,unsigned char dat);extern void LCD_Clear(void);#endif1、MCU.c#include <reg51.h>#include "MCU.h"#define oscillator 12000000void timer0_init(void){TMOD|=0x01;//Timer0 is 16 bits timer, timer1 is Baud rate generatorTH0=0x97;TL0=0xd5;TR0=1;ET0=1;}void uart_init(unsigned int baud_rate){PCON=0x00;SCON=0x50;TMOD|=0x20;TL1=256-(oscillator/384)/baud_rate;TH1=256-(oscillator/384)/baud_rate;TR1=1;ES=1;}2、MCU.h#ifndef __MCU__#define __MCU__extern void timer0_init(void);extern void uart_init(unsigned int baud_rate);#endif3、MODBUSASC.c#include <reg51.h>unsigned char xdata TX_buf[200]_at_ 0x9100;unsigned char xdata Dat[200] _at_ 0x9200;void Data_ASCII(unsigned char num,unsigned char * buf ){unsigned char a;a=(num>>4)&0x0f;if(a>9) buf[0]=a+0x37;else buf[0]=a+0x30;a=num&0x0f;if(a>9) buf[1]=a+0x37;else buf[1]=a+0x30;}unsigned char ASCII_Data(unsigned char* buf ){unsigned char a,b;a=buf[0]-0x30;if(a>9) a=a-0x07;b=buf[1]-0x30;if(b>9) b=b-0x07;a=(a<<4)|b;return (a);}unsigned char LRC(unsigned char*Msg_buf,unsigned char MsgLen){unsigned char LRC_buf = 0,i ;for(i=0;i<MsgLen;i++){LRC_buf=Msg_buf[i]+LRC_buf;}i=~LRC_buf;LRC_buf=i+1;return LRC_buf ;}void Send_modbusASC_UART(unsigned char* SendData,unsigned char DataLen) {unsigned char buf[2];unsigned int i,j,k;j=1;TX_buf[0]=0x3a;for(i=0;i<DataLen;i++){Data_ASCII(SendData[i],buf );TX_buf[j]=buf[0];j++;TX_buf[j]=buf[1];j++;}TX_buf[j]=0x0d;j++;TX_buf[j]=0x0a;j++;for(i=0;i<j;i++){TI=0;SBUF=TX_buf[i];while(!TI);TI=0;for(k=0;k<30;k++);}}void Send_modbusASC_Err(unsigned char FunNum,unsigned char ErrNum,unsigned char ID) {unsigned char temp[4];temp[0]=ID;temp[1]=FunNum+0x80;temp[2]=ErrNum;temp[3]=LRC(temp,3);Send_modbusASC_UART(temp,4);}void FunctionASC_NO3(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID) {unsigned char a,i,j,n,temp[2];unsigned int Fun_addr,Fun_num;j=1;for(i=0;i<7;i++){temp[0]=Msg_buf[j];j++;temp[1]=Msg_buf[j];j++;Dat[i]=ASCII_Data(temp );}a=LRC(Dat,6);if(a!=Dat[6]) return;Fun_addr=(Dat[2]<<8)|Dat[3];Fun_num=(Dat[4]<<8)|Dat[5];if((Fun_num==0)||(Fun_num>64)){Send_modbusASC_Err(1,3,ID);return;}if(Fun_addr>63){Send_modbusASC_Err(1,2,ID);return;}Dat[0]=ID;Dat[1]=3;Dat[2]=Fun_num*2;j=3;n=Fun_addr;for(i=0;i<Fun_num;i++){Dat[j]=(unsigned char)(AO_state[n]>>8);j++;Dat[j]=(unsigned char)AO_state[n];j++;n++;}Dat[j]=LRC(Dat,j);Send_modbusASC_UART(Dat,j+1);}void FunctionASC_NO6(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID) {unsigned char a,i,j,temp[2];unsigned int Fun_addr,Fun_out;j=1;for(i=0;i<7;i++){temp[0]=Msg_buf[j];j++;temp[1]=Msg_buf[j];j++;Dat[i]=ASCII_Data(temp );}a=LRC(Dat,6);if(a!=Dat[6]) return;Fun_addr=(Dat[2]<<8)|Dat[3];Fun_out=(Dat[4]<<8)|Dat[5];if(Fun_out>0xff00){Send_modbusASC_Err(1,3,ID);return;}if(Fun_addr>64){Send_modbusASC_Err(1,2,ID);return;}AO_state[Fun_addr]=Fun_out;j=1;for(i=0;i<5;i++){Dat[j]=Msg_buf[j];j++;}Dat[j]=LRC(Dat,j);Send_modbusASC_UART(Msg_buf,j+1);}4、MODBUSASC.h#ifndef _MODBUSASC_#define _MODBUSASC_#ifdef __cplusplusextern "C"{#endifextern void Data_ASCII(unsigned char num,unsigned char * buf );extern unsigned char ASCII_Data(unsigned char* buf );extern void FunctionASC_NO3(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID);extern void FunctionASC_NO4(unsigned char* Msg_buf,unsigned int* AI_state,unsigned char ID);extern void FunctionASC_NO6(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID);#ifdef __cplusplus}#endif5、MAIN.c#include <reg51.h>#include<LCD.h>#include<main.h>#include "mcu.h"#include "modbusASC.h"void wait(unsigned int x){unsigned int i;i=0;for(i=0;i<x;i++);}void timer0_isr(void)interrupt 1{TH0=0x3c;TL0=0xb0;ms_count++;if(ms_count>=20){ms_count=0;LED=~LED;S_flag=1;}}void UART_isr(void)interrupt 4{unsigned char a;RI=0;a=SBUF;if(a==0x3a)UART_offset=0;if(a==0x0a)UART_message=1;RX_buf[UART_offset]=a;UART_offset++;}void display(void){unsigned char i,col;col=28;display_unsigned_int(0,col,modbus_buf[0]);display_unsigned_int(1,col,modbus_buf[1]); }void ModbusASC_Msg(unsigned char* modmus_buf){unsigned char temp[2];unsigned char a;temp[0]=modmus_buf[1];temp[1]=modmus_buf[2];a=ASCII_Data(temp );if(a!=Mode_ID) return;temp[0]=modmus_buf[3];temp[1]=modmus_buf[4];a=ASCII_Data(temp );switch(a){case 0x03:{FunctionASC_NO3(modmus_buf,modbus_buf,Mode_ID);break;}case 0x06:{FunctionASC_NO6(modmus_buf,modbus_buf,Mode_ID);break;}}}void main(void){unsigned char i;LCD_Init();LCD_Clear();display_cn(0,0,1,RD_dat);display_cn(1,0,1,TD_dat);modbus_buf[0]=18;modbus_buf[1]=28;timer0_init();uart_init(2400);EA=1;Mode_ID=1;while(1){display( );if(UART_message==1){ModbusASC_Msg(RX_buf);UART_message=0;}if(S_flag==1){modbus_buf[0]++;S_flag=0;}}}6、MAIN.h#include <reg51.h>#ifndef MAIN_H__#define MAIN_H__code unsigned char RD_dat[]={ //纵向字节倒序。
51单片机modbus协议程序

51单片机modbus协议程序#include ;#define uint8 unsigned char#define uint16 unsigned int#define FOSC 16000000uint16 BAUD=9600;uint16 TEMP_Alert=1000;//字地址 0 - 255 (只取低8位)//位地址 0 - 255 (只取低8位)uint16 TempRegister; //用于测试字址址16 uint8 localAddr = 0x01; //单片机控制板的地址uint8 sendCount; //发送字节个数uint8 receCount;//接收到的字节个数//uint8 sendPosi;//发送位置uint8 xdata receBuf[1];uint8 xdata sendBuf[1];void checkComm0Modbus(void);uint16 getRegisterVal(uint16 addr,uint16*tempData);uint16 setRegisterVal(uint16 addr,uint16 tempData); void switch_BAUD(uint16 value);/*****************************波特率调整函数********************************/////函数功能:调整串口通信波特率////串口工作在工作方式1,即8位波特率可变模式/************************************************ ****************************/void switch_BAUD(uint16 value){switch(value){case 0x0001: { BAUD=9600;break; }case 0x0002: { BAUD=14400;break; }case 0x0003: { BAUD=19200;break; }}TR1=0;//停止定时器1ES=0;//关闭串口中断TH1=TL1=-(FOSC/12/32/BAUD);//设置波特率TR1=1;//开启定时器1ES=1;//使能串口中断}/***************************CRC校验码生成函数********************************/////函数功能:生成CRC校验码////本代码中使用查表法,以提高运算速度/****************************************************************************/uint16 crc16(uint8 *puchMsg, uint16 usDataLen){uint8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */uint8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */uint16 uIndex ; /* CRC循环中的索引 */while (usDataLen--) /* 传输消息缓冲区 */{uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */ uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return (uchCRCLo ;>; 8;sendBuf[i+4] = tempData & 0xff;}sendBuf[0] = localAddr;sendBuf[1] = 3; //function code : 03sendBuf[2] = byteCount;byteCount += 3;//加上前面的地址,功能码,地址共3+byteCount 个字节crcData = crc16(sendBuf,byteCount);sendBuf[byteCount] = crcData & 0xff;// CRC代码低位在前byteCount++;sendBuf[byteCount] = crcData >;>; 8 ;//高位在后sendCount = byteCount + 1;//例如byteCount=49,则sendBuf[]中实际上有49+1个元素待发Begin_send();}//void readRegisters(void)/********从机响应主机问询函数,function code : 16,设置多个寄存器值 *********/////函数功能:丛机根据串口接收到的数据包receBuf[]里面的内容,根据被强制寄存器////的起始地址,去设置相应寄存器的值,响应数据包同询问数据包////的内容相同,经过串口发送到主机。
关于51单片机上实现modbus协议

关于51单片机上实现modbus协议你找一个MODBUS的协议详细资料好好看看,就是一种通讯约定,你按照它规定的格式通讯就可以了协议发送给询问方。
Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。
此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。
标准的Modicon控制器使用RS232C实现串行的Modbus。
Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。
Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验,但TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。
另外,Modbus采用主从方式定时收发数据,在实际使用中如果某Slave站点断开后(如故障或关机),Master端可以诊断出来,而当故障修复后,网络又可自动接通。
因此,Modbus协议的可靠性较好。
下面我来简单的给大家介绍一下,对于Modbus的ASCII、RTU和TCP协议来说,其中TCP和RTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。
所以在这里我仅介绍一下Modbus的ASCII和RTU协议。
下表是ASCII协议和RTU协议进行的比较:协议开始标记结束标记校验传输效率程序处理ASCII :(冒号)CR,LF LRC 低直观,简单,易调试RTU 无无CRC 高不直观,稍复杂通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。
51单片机的modbus

#include <intrins.h>
#define uchar unsigned char
#define length 8
unsigned char sum=0;
unsigned int crc;
unsigned char crc_tmp=0;
/* CRC 高位字节值表 */
const uint8 code auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
TH1=0xFA;
TL1=0xFA;
TR1=1;
//PS=1;
}
unsigned char delay(unsigned char j)
{
unsigned char k,l;
for(l=0;l<=j;l++ )
for(k=0;k<=10;k++);
return 0;
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
uint16 crc16(uint8 *puchMsg, uint16 usDataLen);
sbit Send_Rcv=P3^2;
sbit bdog = P3^4;
力控modbus通信方法modbus-tcp、modbus-rtu莫迪康通信配置步骤

⼒控modbus通信⽅法modbus-tcp、modbus-rtu莫迪康通信配置步骤⽬录第 1 章 485 设备与⼒控通讯1.1DI/DO/AI/AO 接⼊1.1.1添加设备1.1.2测试设备1.2读取 DI 状态1.3输出/读取 DO 状态1.4读取 AI 值第 2 章以太⽹设备与⼒控通讯2.1DI/DO/AI/AO 接⼊2.1.1添加设备2.1.2测试设备2.2读取 DI 状态2.3输出/读取 DO 状态2.4读取 AI 值第 1章 485 设备与⼒控通讯1.1DI/DO/AI/AO 接⼊以 ZKA-4088-RS485 为例,介绍综科智控 485 设备与⼒控进⾏通讯的步骤设置。
ZKA-4088-RS485 是8 路数字量输⼊(D I),8 路数字量输出(DO),4 路模拟量输⼊设备(AI,0/4-20mA),485 通信总线。
以 ZKA-4088-RS485 出⼚默认通讯参数如下:站号:01波特率:115200数据格式:8,N,1与电脑连接的端⼝:客户查看电脑设备管理器中的COM⼝,选择实际使⽤的端⼝1.1.1添加设备选择:IO 设备组态>[PLC] > [莫迪康] > [ModbusRTU] 如下图所⽰:为设备取⼀个名称,在此输⼊ TEST,单击“下⼀步”,如下图所⽰。
为设备选择连接串⼝,选择COM4,根据实际对应COM 参数设置,并保存选择 6 和 16 号命令,选择 32 位数数据存放格式:1.1.2测试设备在设备上右键> [TEST],如下图所⽰:1.2读取 DI 状态ZKA-4088-RS485的 8路 DI 寄存器为 10001~10008:如上图所⽰,寄存器 DI001 变量值为 1,表⽰此时第 1 个输⼊通道X1闭合。
1.3输出/读取 DO 状态ZKA-4088-RS485 的 8路 DO 寄存器为 00001~00008:新建 IO 点,选择可读可写建⽴ IO 点完成后,点击运⾏按,即可读取 DO 状态在对应的 IO 点上右键>写数据写⼊ 1,则通道 DO1 输出,设备上 Y1灯亮1.4读取 AI 值ZKA-4088-RS485 的 4 路 AI(模拟量输⼊)寄存器为 30001~30008,因为ZKA模块采⽤的是32位AD芯⽚,所以每个模拟量通道占⽤ 2 个寄存器,共4 个字节(32bit),其中⾼两个字节为采集结果的⾼16bit,低两个字为采集结果的低16bit,模块出⼚时为保证⾼精度,滤波后输出16bit,所以⽤户实际使⽤时只需要读取低16bit采集结果即可,,也就是读取偏置0002,0004,0006,0008地址上的寄存器数据,数据类型为⽆符号整型。
手把手教你学51单片机之十八 RS485通信与Modbus协议

在工业控制、电力通讯、智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换。
最初采用的方式是RS232接口,由于工业现场比较复杂,各种电气设备会在环境中产生比较多的电磁干扰,会导致信号传输错误。
除此之外,RS232接口只能实现点对点通信,不具备联网功能,最大传输距离也只能达到几十米,不能满足远距离通信要求。
而RS485则解决了这些问题,数据信号采用差分传输方式,可以有效的解决共模干扰问题,最大距离可以到1200米,并且允许多个收发设备接到同一条总线上。
随着工业应用通信越来越多,1979年施耐德电气制定了一个用于工业现场的总线协议Modbus协议,现在工业中使用RS485通信场合很多都采用Modbus协议,本节课我们要讲解一下RS485通信和Modbus协议。
单单使用一块KST-51开发板是不能够进行RS485实验的,应很多同学的要求,把这节课作为扩展课程讲一下,如果要做本课相关实验,需要自行购买USB转485通信模块。
18.1 RS485通信实际上在RS485之前RS232就已经诞生,但是RS232有几处不足的地方:1、接口的信号电平值较高,达到十几V,容易损坏接口电路的芯片,而且和TTL电平不兼容,因此和单片机电路接起来的话必须加转换电路。
2、传输速率有局限,不可以过高,一般到几十Kb/s就到极限了。
3、接口使用信号线和GND与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。
4、传输距离有限,最多只能通信几十米。
5、通信的时候只能两点之间进行通信,不能够实现多机联网通信。
针对RS232接口的不足,就不断出现了一些新的接口标准,RS485就是其中之一,他具备以下的特点:1、我们在讲A/D的时候,讲过差分信号输入的概念,同时也介绍了差分输入的好处,最大的优势是可以抑制共模干扰。
尤其工业现场的环境比较复杂,干扰比较多,所以通信如果采用的是差分方式,就可以有效的抑制共模干扰。
51单片机实现Modbus从机程序

51单片机实现M o d b u s从机程序(总11页)本页仅作为文档封面,使用时可以删除This document is for reference only-rar21year.March自己用单片机做的Modbus从机,可以使用STC89C52。
实现了命令码为1、2、3、4、5、6的功能,程序中有些是我们部分其他功能的函数和数据,希望大家参考下编程的思想。
uint Switch=0xbc95;//开关状态uchar bdata Coil1=0xff,Coil2=0xbc;//16位线圈状态sbit Coil1_bit0=Coil1^0;sbit Coil1_bit1=Coil1^1;sbit Coil1_bit2=Coil1^2;sbit Coil1_bit3=Coil1^3;sbit Coil1_bit4=Coil1^4;sbit Coil1_bit5=Coil1^5;sbit Coil1_bit6=Coil1^6;sbit Coil1_bit7=Coil1^7;sbit Coil2_bit8=Coil2^0;sbit Coil2_bit9=Coil2^1;sbit Coil2_bit10=Coil2^2;sbit Coil2_bit11=Coil2^3;sbit Coil2_bit12=Coil2^4;sbit Coil2_bit13=Coil2^5;sbit Coil2_bit14=Coil2^6;sbit Coil2_bit15=Coil2^7;uint idataReOnlybuf[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0 x0c,0x0d,0x0e,0x0f};uint idataReWrbuf[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x 0c,0x0d,0x0e,0x0f};//CRC校验查表码值const uchar code auchCRCHi[] ={0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40};const uchar code auchCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40};/******************************************** Function:CRC校验子函数Input:要校验的数组起始地址长度Output:16位校验码(高位在前)********************************************/uint crccheck(uchar *puchMsg, uchar usDataLen){uchar uchCRCHi = 0xFF ;uchar uchCRCLo = 0xFF ;uchar uIndex ;while (usDataLen--){uIndex = uchCRCHi ^ *puchMsg++ ;uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return (uchCRCHi << 8 | uchCRCLo) ;}/******************************************** Function:读线圈状态子函数Input:无Output:无********************************************/void ReadCoil(void){uint StartAddress,tempAddress;uchar CoilNum,i,ByteNum,j;uchar CoilVal;bit exit=0;StartAddress=resvbuf[2];StartAddress=StartAddress|resvbuf[3];tempAddress=StartAddress;CoilNum=resvbuf[5];//读取的位数ByteNum=CoilNum/8;if(CoilNum%8!=0)ByteNum++;Sendbuf[2]=ByteNum;//返回的字节数if(resvbuf[1]==0x01){for(i=0;i<ByteNum;i++){Sendbuf[i+3]=0;for(j=0;j<8;j++){CoilVal=GetCoilVal(StartAddress);Sendbuf[i+3]|=CoilVal<<j;StartAddress++;if(StartAddress>=tempAddress+CoilNum){exit=1;break;}}if(exit==1)break;}}else if(resvbuf[1]==0x02){for(i=0;i<ByteNum;i++){Sendbuf[i+3]=0;for(j=0;j<8;j++){CoilVal=GetSWVal(StartAddress);Sendbuf[i+3]|=CoilVal<<j;StartAddress++;if(StartAddress>=tempAddress+CoilNum){exit=1;break;}}if(exit==1)break;}}SendCount=5+ByteNum;SendData();}/******************************************** Function:读寄存器状态子函数Input:无Output:无********************************************/void ReadRegisters(void){uint StartAddress;uchar ByteCount,i;StartAddress=resvbuf[2];StartAddress=StartAddress<<8|resvbuf[3];ByteCount=resvbuf[5]*2;Sendbuf[2]=resvbuf[5];if(resvbuf[1]==0x03)for(i=0;i<ByteCount;i+=2){Sendbuf[i+4]=ReWrbuf[StartAddress]&0xff;Sendbuf[i+3]=ReWrbuf[StartAddress]>>8;StartAddress++;}else if(resvbuf[1]==0x04)for(i=0;i<ByteCount;i+=2){Sendbuf[i+4]=ReOnlybuf[StartAddress];Sendbuf[i+3]=ReOnlybuf[StartAddress];StartAddress++;}SendCount=ByteCount+5;SendData();}/******************************************** Function:强制单线圈子函数Input:无Output:无********************************************/ void ForceSingalCoil(void){uint Address,OnOff;bit temp,CoilVal;Address=resvbuf[2]<<8|resvbuf[3];OnOff=resvbuf[4]<<8|resvbuf[5];if(OnOff==0x0000){CoilVal=0;temp=SetCoilVal(Address,CoilVal);}else if(OnOff==0xFF00){CoilVal=1;temp=SetCoilVal(Address,CoilVal);}if(temp==1){Sendbuf[2]=resvbuf[2];Sendbuf[3]=resvbuf[3];Sendbuf[4]=resvbuf[4];Sendbuf[5]=resvbuf[5];SendCount=8;SendData();}elseError=1;}/******************************************** Function:强制单寄存器子函数Input:无Output:无********************************************/ void SetOneRegisterVal(){uint R_Address,RegisterVal;bit temp1;R_Address=resvbuf[2];R_Address=R_Address<<8|resvbuf[3];RegisterVal=resvbuf[4];RegisterVal=RegisterVal<<8|resvbuf[5];temp1=SetRegisterVal(R_Address,RegisterVal);if(temp1==1){Sendbuf[2]=resvbuf[2];Sendbuf[3]=resvbuf[3];Sendbuf[4]=resvbuf[4];Sendbuf[5]=resvbuf[5];SendCount=8;SendData();}elseError=1;}/******************************************** Function:读线圈值子函数Input:线圈地址Output:线圈的值********************************************/ uchar GetCoilVal(uint Address){uint CoilAddress;uchar CoilVal=0;CoilAddress=Address;switch(CoilAddress&0x0f){case 0: CoilVal=Coil1_bit0;break;case 1: CoilVal=Coil1_bit1;break;case 2: CoilVal=Coil1_bit2;break;case 3: CoilVal=Coil1_bit3;break;case 4: CoilVal=Coil1_bit4;break;case 5: CoilVal=Coil1_bit5;break;case 6: CoilVal=Coil1_bit6;break;case 7: CoilVal=Coil1_bit7;break;case 8: CoilVal=Coil2_bit8;break;case 9: CoilVal=Coil2_bit9;break;case 10: CoilVal=Coil2_bit10;break;case 11: CoilVal=Coil2_bit11;break;case 12: CoilVal=Coil2_bit12;break;case 13: CoilVal=Coil2_bit13;break;case 14: CoilVal=Coil2_bit14;break;case 15: CoilVal=Coil2_bit15;break;default: break;}return CoilVal;}/******************************************** Function:读开关值子函数Input:开关地址Output:开关值********************************************/ uchar GetSWVal(uint Address1){uchar CoilVal;uchar SW1,SW2=0xff;SW1=P2;Switch=SW2<<8|SW1;switch(Address1&0x0f){case 0: CoilVal=Switch&0x01;break;case 1: CoilVal=Switch>>1&0x01;break;case 2: CoilVal=Switch>>2&0x01;break;case 3: CoilVal=Switch>>3&0x01;break;case 4: CoilVal=Switch>>4&0x01;break;case 5: CoilVal=Switch>>5&0x01;break;case 6: CoilVal=Switch>>6&0x01;break;case 7: CoilVal=Switch>>7&0x01;break;case 8: CoilVal=Switch>>8&0x01;break;case 9: CoilVal=Switch>>9&0x01;break;case 10: CoilVal=Switch>>10&0x01;break;case 11: CoilVal=Switch>>11&0x01;break;case 12: CoilVal=Switch>>12&0x01;break;case 13: CoilVal=Switch>>13&0x01;break;case 14: CoilVal=Switch>>14&0x01;break;case 15: CoilVal=Switch>>15&0x01;break;default: break;}return CoilVal;}/******************************************** Function:强制单线圈值子函数Input:线圈地址,强制值Output:结果值********************************************/bit SetCoilVal(uint Address,bit Val){bit result=1;switch(Address&0x0f){case 0: Coil1_bit0=Val;break;case 1: Coil1_bit1=Val;break;case 2: Coil1_bit2=Val;break;case 3: Coil1_bit3=Val;break;case 4: Coil1_bit4=Val;break;case 5: Coil1_bit5=Val;break;case 6: Coil1_bit6=Val;break;case 7: Coil1_bit7=Val;break;case 8: Coil2_bit8=Val;break;case 9: Coil2_bit9=Val;break;case 10: Coil2_bit10=Val;break;case 11: Coil2_bit11=Val;break;case 12: Coil2_bit12=Val;break;case 13: Coil2_bit13=Val;break;case 14: Coil2_bit14=Val;break;case 15: Coil2_bit15=Val;break;default: result=0;break;}P1=Coil1;return result;}/******************************************** Function:设置单寄存器值子函数Input:寄存器地址,值Output:结果值********************************************/ bit SetRegisterVal(uint Address1,uint Val1){uchar Addr;bit result1=1;Addr=Address1&0x0f;if(Addr<16)ReWrbuf[Addr]=Val1;elseresult1=0;return result1;}。
51单片机新手入门之Modbus通讯

51单片机新手入门之Modbus通讯本文和另一篇----C# WPF新手入门之Modbus通讯为一个系列,所用模块均是气体分析模块,之前用C3做的上位机(主要是方便调试,单片机不好搞),既然电脑上串口通讯代码没问题,那么放到单片机稍加修改也就理所当然肯定可行O(∩_∩)O~。
本文包括单片机的电路设计和软件设计两部分(单片机采用STC12C5A60S2双串口),至此单片机和上位机的串口通讯均成功实现,供需要的同学参考。
1.电路设计Altium Designer原理图如下:电源部分:24V转5V及6VAD及滤波Modbus通讯电脑串口通讯lcd屏显示预留接口,可以另接MAX232用来调试发送给模块数据正确性实际效果如下:电脑串口接收到的数据,和LCD 屏显示的是一样的下载步骤1. 选择芯片下载步骤2. 打开串口 下载步骤3. 打开hex 文件 下载步骤4. MCU 先断电,点击下载,看到提示后上电2.软件设计这里最麻烦的在于模块的Modbus通讯需要偶校验,我是手动添加的校验位,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,把所有需要发送的数据一个个手动添加了再发送^_^;另外lcd12864屏也搞半天,主要照着时序图写必须sid先发送,然后sclk,不然不行 ̄□ ̄。
气体模块详细的通讯协议参考和上位机通讯那篇,此处不再介绍。
图中电路板没有焊AD芯片和温度芯片,引脚备用,相关代码注释掉了。
代码如下:#include "STC12C5A60S2.H"#include <intrins.H>.//头文件#define uchar unsigned char#define uint unsigned intuchar sendbuffer[17];//发送数据uchar flag=0;uchar re_buffer[32];uchar count=0;uint ad_data=0;double ad_vol=0;uint con_mid=0;uint gascon=0;long sum_o2=0;uint average_counter=20;uint idata oldtemp[21];uchar idata Send_Buff[20]; //moduleuint pre_contemp=0;uint O2_con=0;uchar idata test[21]; //moduleuint temperature=0;uint dat;uint testlcd=0;uchar c[]={0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9};//MAX1241 模数芯片引脚设置,此电路板我没焊^_^//sbit ADC_CS=P0^1;//sbit ADC_CLK=P0^0;//sbit ADC_DOUT=P0^2;sbit ADC_CS=P3^6;sbit ADC_CLK=P3^5;sbit ADC_DOUT=P3^7;//LCD12864 LCD屏幕引脚设置sbit cs=P2^2;sbit sid=P2^1;sbit sclk=P2^0;sbit DQ=P0^1; //DS18B20 温度引脚设置,依然没有…#define N 11#define N2 20void delayms(unsigned char t){unsigned char i;unsigned char j;for(j=t;j;j--)for(i=192;i;i--);/*1ms延时*/}void delayus(uint t){uint i;for(i=0;i<t;i++){_nop_();}}void delay(float sec){unsigned int i;unsigned int j;j=sec*100;while(j--){i=1561;while(--i);}}void UART1_init() //串口1初始化,此串口和电脑通讯{TMOD=0x20;/设置定时器工作方式2TH1=0xfd; //波特率9600TL1=0xfd;TR1=1;REN=1;SM0=0;SM1=1;//ES=1;}void UART2_init()//串口2初始化,和模块通讯 Modbus{S2CON= 0x50; //方式1,允许接收BRT = 0xf4; //波特率2400AUXR = AUXR |0X10; //允许独立波特率允许AUXR1 = AUXR1&0xef; //将uart2切换到P1口IE2 = IE2|0X01; //允许串口2中断}void UART1_Send (unsigned char UART_data)//{//ES=0;SBUF = UART_data; //将接收的数据发送回去while(TI!=1); //检查发送中断标志位TI = 0; //另发送中断标志位为0//ES=1;}void UART2_Send(unsigned char UART_data)//串口2发送{//ES = 0 ;S2BUF = UART_data;while((S2CON&0x02)!=0x02);S2CON &= ~0x02;//ES = 1 ;}void UART1_Send_String (char *str, char len)//串口1发送字符串{unsigned char i;for(i=0;i<=len;i++){UART1_Send(str[i]);}}void UART2_Send_String (char *str, char len) //串口2发送字符串{unsigned char i;for(i=0;i<=len;i++){UART2_Send(str[i]);}}unsigned char Creat_Addr(unsigned char adr, unsigned char position)//计算模块地址{unsigned char hich;unsigned char loch;hich = adr/16;loch = adr%16;if(hich>9)hich+=7;if(loch>9)loch+=7;if(position == 1){return hich+0x30;}else if(position == 0){return loch+0x30;}}unsigned char CheckSum(unsigned char *str, unsigned char position, uchar len)/计算校验码{uchar i;unsigned int sum=0;uchar hi, lo;//uchar len = 12;for(i = 1; i <= len; i ++){*str ++;sum += *str;}sum = 256-(sum%256);hi = sum/16;lo = sum%16;if(hi > 9)hi += 7;if(lo > 9)lo += 7;hi += 0x30;lo += 0x30;if(sum == 256)hi = lo = 0x30;if(position == 1){return hi;}else if(position == 0){return lo;}}void data_init(){sendbuffer[0]=0x5B;sendbuffer[1]=0x30;sendbuffer[2]=0x30;sendbuffer[3]=0x30;sendbuffer[4]=0x30;sendbuffer[5]=0x30;sendbuffer[6]=0x7C;sendbuffer[7]=0x30;sendbuffer[8]=0x30;sendbuffer[9]=0x30;sendbuffer[10]=0x30;sendbuffer[11]=0x30;//TEMsendbuffer[12]=0x30;sendbuffer[13]=0x30;sendbuffer[14]=0x5D;sendbuffer[15]=0x0D;sendbuffer[16]=0x0A;}void calculate_module(unsigned char str[])//lcd屏显示{unsigned int concen;uchar wan,qian,bai,shi,ge;/*uchar d4 = str[7]-48;uchar d3 = str[8]-48;uchar d2 = str[9]-48;uchar d1 = str[10]-48;*/ //浓度只需要后面部分 uchar d4 = str[24]-48;uchar d3 = str[25]-48;uchar d2 = str[26]-48;uchar d1 = str[27]-48;if(d4>9)d4-=7;if(d3>9)d3-=7;if(d2>9)d2-=7;if(d1>9)d1-=7;concen = d4*4096+d3*256+d2*16+d1;gascon=concen;wan=concen/10000;qian=concen%10000/1000;bai=concen%1000/100;shi=concen%100/10;ge=concen%10;//sendbuffer[6]=wan+0x30; //最终显示XXX.X%sendbuffer[7]=qian+0x30;sendbuffer[8]=bai+0x30;sendbuffer[9]=shi+0x30;sendbuffer[10]=ge+0x30;}void module_init()//气体模块初始化{Send_Buff[0] = ':';Send_Buff[3] = '0';Send_Buff[4] = '3';Send_Buff[5] = '0';Send_Buff[6] = '0';Send_Buff[7] = '0';Send_Buff[8] = 'A';Send_Buff[9] = '0';Send_Buff[10] = '0';Send_Buff[11] = '0';Send_Buff[12] = '1';Send_Buff[1] = Creat_Addr(31, 1);Send_Buff[2] = Creat_Addr(31, 0);Send_Buff[13] = CheckSum(Send_Buff, 1, 12);Send_Buff[14] = CheckSum(Send_Buff, 0, 12);Send_Buff[15] = 0x0D;Send_Buff[16] = 0x0A;//手动添加校验,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,最终发送给模块的以下数据,地址被写死,这个不像C#做的一目了然O(∩_∩)Otest[0]=0X3A;test[1]=0XB1;test[2]=0XC6;test[3]=0X30;test[4]=0X33;test[5]=0X30;test[6]=0X30;test[7]=0X30;test[8]=0X41;test[9]=0X30;test[10]=0X30;test[11]=0X30;test[12]=0XB1;test[13]=0X39;test[14]=0XB4;test[15]=0X8D;test[16]=0X0A;}/*uint read_max1241() AD芯片处理{uint ADC_Data;uchar i;ADC_CLK=0;ADC_CS=0;ADC_Data=0;while(!ADC_DOUT);ADC_CLK=1;ADC_CLK=0;for(i=0;i<12;i++){ADC_CLK=1;ADC_Data<<=1;ADC_Data |= ADC_DOUT;ADC_CLK=0;}ADC_CS=1;ADC_CLK=0;return ADC_Data;}unsigned int ad_filter()//滤波{unsigned int count1,i,j;unsigned int value_buf[N];unsigned int temp;unsigned int sum=0;for (count1=0;count1<N;count1++){value_buf[count1] =read_max1241(); delayms(20);}for (j=0;j<(N-1);j++){for (i=0;i<(N-j);i++){if ( value_buf[i]>value_buf[i+1] ) {temp = value_buf[i];value_buf[i] = value_buf[i+1]; value_buf[i+1] = temp;}}}for(count1=3;count1<(N-3);count1++){sum += value_buf[count1];}return (unsigned int)(sum/(N-6));}void calculate_o2()//测试用{uchar wan,qian,bai,shi,ge;uint con_fin=0;uchar i;ad_data=ad_filter();ad_vol=(ad_data/4096.0)*2500.0;//Voltage ad_data=(uint)ad_vol; //concentrationdelayms(2);if(average_counter>0){sum_o2+= ad_data;oldtemp[average_counter-1]=ad_data;average_counter--;}else{sum_o2 -=oldtemp[19];for(i=20;i>0;i--){oldtemp[i]=oldtemp[i-1];}oldtemp[0]=ad_data;sum_o2+=oldtemp[0];con_fin=(uint)(sum_o2/N2);//O2_con=0.8*con_fin+0.2*pre_contemp;//pre_contemp=O2_con;wan=con_fin/10000;qian=con_fin%10000/1000;bai=con_fin%1000/100;shi=con_fin%100/10;ge=con_fin%10;sendbuffer[1]=wan+0x30;sendbuffer[2]=qian+0x30;sendbuffer[3]=bai+0x30;sendbuffer[4]=shi+0x30;sendbuffer[5]=ge+0x30;delayms(5);}}//-------------18B20 温度显示复位函数--------------- void ow_reset(void){char presence=1;while(presence){while(presence){DQ=1;delayus(2);DQ=0; //delayus(550); // 550usDQ=1; //delayus(66); // 66uspresence=DQ;}delayus(500); //延时500uspresence = ~DQ;}DQ=1;}//-----------18B20写命令函数------------void write_byte(uchar val){uchar i;for (i=8; i>0; i--) //{DQ=1;delayus(2);DQ = 0;delayus(5);//5usDQ = val&0x01;delayus(66); //66usval=val/2;}DQ = 1;delayus(11);}//--------------18B20读一个字节函数---------uchar read_byte(void){uchar i;uchar value = 0;for (i=8;i>0;i--){DQ=1;delayus(2);value>>=1;DQ = 0;delayus(4); //4usDQ = 1;delayus(4); //4usif(DQ)value|=0x80;delayus(66); //66us}DQ=1;return(value);}void Read_Temperature(void){unsigned int Temp1,Temp2;uchar bai,shi,ge;ow_reset(); //DS18B20write_byte(0xCC);write_byte(0x44);ow_reset(); //DS1302复位write_byte(0xCC);write_byte(0xbe);Temp1=read_byte();Temp2=read_byte();ow_reset();temperature=(((Temp2<<8)|Temp1)*0.625); //0.0625=xx, 0.625=xx.x, 6.25=xx.xxbai=temperature/100;shi=temperature%100/10;ge=temperature%10;sendbuffer[11]=bai+0x30;sendbuffer[12]=shi+0x30;sendbuffer[13]=ge+0x30;delayms(5);}* //图中电路板此部分没焊,此部分代码不使用^_^void writecmd_lcd(uchar cmd) //lcd屏写指令{uchar i;uchar cmd1;cmd1=cmd;//----------先写控制,选择写指令,还是写数据11111000 for(i=0;i<5;i++) //必须sid先发送,然后sclk,不然不行 {sid=1;sclk=1;sclk=0;}for(i=0;i<3;i++){sid=0;sclk=1;sclk=0;}//delayms(10);cmd=cmd&0xf0; //先高4位for(i=0;i<8;i++){if(cmd&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd=cmd<<1;}//delayms(10);cmd1=((cmd1<<4)&0xf0); //低4位 for(i=0;i<8;i++){if(cmd1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd1=cmd1<<1;}}void writedata_lcd(uchar dat) {uchar i;uchar dat1;dat1=dat;//11111010for(i=0;i<5;i++){sid=1;sclk=1;sclk=0;}sid=0;sclk=1;sclk=0;sid=1;sclk=1;sclk=0;sid=0;sclk=1;sclk=0;//delayms(10);dat=dat&0xf0;for(i=0;i<8;i++)if(dat&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat=dat<<1;}//delayms(10);dat1=((dat1<<4)&0xf0);for(i=0;i<8;i++){if(dat1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat1=dat1<<1;}}void init_lcd()//初始化lcd屏{cs=1;writecmd_lcd(0x30);//设定为8位控制writecmd_lcd(0x0c);//显示打开writecmd_lcd(0x01);//清屏}void gotoxy(uint row, uint col){switch(row){case 1: writecmd_lcd(0x80+col);break; case 2: writecmd_lcd(0x90+col);break; case 3: writecmd_lcd(0x88+col);break; case 4: writecmd_lcd(0x98+col);break;}void clear(){writecmd_lcd(0x01);delayms(10);}void SendStr(uchar *str){uchar i;for(i=0;str[i]!='\0';i++){writedata_lcd(str[i]);}}void lcd_display(uint lcddata)//lcd屏显示浓度{uchar wan,qian,bai,shi,ge;wan=lcddata/10000;qian=lcddata%10000/1000;bai=lcddata%1000/100;shi=lcddata%100/10;ge=lcddata%10;gotoxy(2,1);writedata_lcd(0xa3);writedata_lcd(c[qian]);gotoxy(2,2);writedata_lcd(0xa3);writedata_lcd(c[bai]);gotoxy(2,3);writedata_lcd(0xa3);writedata_lcd(c[shi]);gotoxy(2,4);SendStr(".");gotoxy(2,5);writedata_lcd(0xa3);writedata_lcd(c[ge]);gotoxy(2,6);SendStr("%");}void main()//主函数{delay(2.1);UART1_init();UART2_init();data_init();module_init();init_lcd();EA=1;delay(2.1);while(1){/*calculate_o2();//测试用Read_Temperature();*/ //温度芯片没焊UART2_Send_String(test,16);//串口2:和模块通讯delay(0.8);if(flag==1){calculate_module(re_buffer);delayms(5);UART1_Send_String(sendbuffer,16);//串口1:电脑上可以接收发送的数据 flag=0;delay(0.8);}lcd_display(gascon);//lcd显示浓度delay(0.8);}}/*void uart1_in() interrupt 4/串口1中断,不使用,因为只是发送{RI=0;re_buffer[count]=SBUF;if(re_buffer[0]!=':'){count=0;}else{count++;if(count==10){flag=1;count=0;}}}*/void uart2_in() interrupt 8//串口2中断,需要接受模块返回的数据{if(S2CON&0X01){re_buffer[count]=S2BUF;re_buffer[count]&=0x7f;count++;S2CON&=0XFE;}if(count==32){count=0;flag=1;}}。
51单片机ModbusRTU串口通信超级开发板

51单片机ModbusRTU串口通信超级开发板51单片机Modbus RTU串口通信超级开发板一、开发板简介:01、4路数字量输入,TLP521光耦隔离;02、4路继电器输出,TLP521光耦隔离,达林顿管ULN2803驱动;03、4路模拟电压输入,TLC0834核心芯片,采用LM324运放;04、4路模拟电压输出,TLC5620核心芯片,采用LM324运放;05、2路独立串行通信口,均可在RS232和RS485之间进行切换;06、1路有源蜂鸣器输出;07、6组数码管显示输出,可实时显示DI、DO、AI、AO当前状态;08、开发板采用一路外接直流12V供电;09、CPU核心芯片采用宏晶STC12C5A60S2双串口芯片;10、单片机程序支持标准Modbus RTU之01、02、03、04、05、06、15、16等功能码;11、开发板实物如下图所示:二、可实现的单机串口通信如下所示:01、14种主流组态软件(主站)和单片机(从站)1:1之Modbus RTU串口通信;02、24种主流触摸屏(主站)和单片机(从站)1:1之Modbus RTU串口通信;03、20种主流PLC(主站)和单片机(从站)1:1之Modbus RTU串口通信;04、单片机(主站)和20种主流PLC(从站)1:1之Modbus RTU串口通信;05、单片机(主站)和单片机(从站)1:1之Modbus RTU串口通信;06、单片机(主站)和20种主流变频器(从站)1:1之Modbus RTU串口通信。
三、可实现的多机串口通信如下所示:01、14种主流组态软件(主站)和单片机(从站)1:N之Modbus RTU串口通信;02、24种主流触摸屏(主站)和单片机(从站)1:N之Modbus RTU串口通信;03、20种主流PLC(主站)和单片机(从站)1:N之Modbus RTU串口通信;04、单片机(主站)和20种主流PLC(从站)1:N之Modbus RTU串口通信;05、单片机(主站)和单片机(从站)1:N之Modbus RTU串口通信;06、单片机(主站)和20种主流变频器(从站)1:N之Modbus RTU串口通信。
Modbus协议51单片机C语言实现

#include <REGX52.H>#include<intrins.h>//_nop_();#define OSFREQ 11059200char TimeInterval;char MyAddress;char data DI[6]={1,2,3,4,5,6};char data DO[6]={6,7,8,9,10,11};char data AI[6]={11,12,13,14,15,16};char data AO[6]={16,17,18,19,20,21};unsigned char data ReceiveData[14];unsigned char data countnumber;sbit Recenable =P1^6;//控制端sbit led =P2^7;//控制端/*---------------------------------------------------------------------------函数说明:CRC 高位字节值表---------------------------------------------------------------------------*/ static unsigned char code auchCRCHi[] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40} ;/*---------------------------------------------------------------------------函数说明:CRC低位字节值表---------------------------------------------------------------------------*/ static unsigned char code auchCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40};/*---------------------------------------------------------------------------调用方式:unsigned int CRC16(unsigned char *puchMsg, unsigned int usDataLen) 函数说明:CRC校验---------------------------------------------------------------------------*/unsigned int CRC16(unsigned char *puchMsg, unsigned int usDataLen){unsigned char uchCRCHi = 0xFF ; // 高CRC字节初始化unsigned char uchCRCLo = 0xFF ; // 低CRC 字节初始化unsigned uIndex ; // CRC循环中的索引while (usDataLen--) // 传输消息缓冲区{uIndex = uchCRCHi ^ *puchMsg++ ; // 计算CRCuchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return (uchCRCHi << 8 | uchCRCLo) ;}/*---------------------------------------------------------------------------调用方式:unsigned char getbit(unsigned int address,unsigned char function)函数说明:取出所给地址的位值---------------------------------------------------------------------------*/unsigned char getbit(unsigned int address,unsigned char function){unsigned char data Offset;unsigned char data temp;unsigned char data *Myaddress;Offset=(char)address&0x07;switch (function) //根据功能码不同进行相应处理{case 2:Myaddress=DI;break;case 1:Myaddress=DO;break;default:return 2;}temp=(char)(address>>3);temp=*(Myaddress+temp);temp>>=(Offset);if (temp&0x01)return 1;elsereturn 0;}/*---------------------------------------------------------------------------调用方式:void SendData(unsigned char *output,unsigned char Outlength) 函数说明:发送数据至窗口---------------------------------------------------------------------------*/void SendData(unsigned char *output,unsigned char Outlength){ES=0;while(Outlength--){TI=0;ACC=*output;TB8=P;SBUF=*(output++);while(!TI);TI=0;}ES=1;}/*---------------------------------------------------------------------------调用方式:void Function12(unsigned char address,unsigned char len)函数说明:功能码1,2处理---------------------------------------------------------------------------*/void Function12(unsigned char address,unsigned char len){unsigned int data i;unsigned char data j;unsigned char data length;unsigned char data *send;unsigned int data temp;unsigned char data function;length=0;send=ReceiveData;function=send[1];for (i=1;i<=len;i++){length++;*(send+2+length)=0;for (j=0;j<8;j++){*(send+2+length)=*(send+2+length)|getbit(address,function)<<j; address++;i++;if(i>len)break;}i--;}*(send+2)=length; //数据长度temp=CRC16(send,length+3); //DI状态数据*(send+3+length)=(char)(temp>>8); //CRC校验高*(send+4+length)=(char)temp; //CRC校验低SendData(send,length+5); //调用A发送程序}/*---------------------------------------------------------------------------调用方式:void Function3(unsigned char address,unsigned char len) 函数说明:功能码3处理-----读取寄存器---------------------------------------------------------------------------*/void Function3(unsigned char address,unsigned char len){unsigned char data i;unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+2)=2*len; //数据长度高address=2*address;for(i=0;i<len;i++) //取输入寄存器数据{*(send+3+2*i)=AO[address++];*(send+4+2*i)=AO[address++];}temp=CRC16(send,2*len+3); //CRC校验*(send+3+2*len)=(char)(temp>>8);*(send+4+2*len)=(char)temp;SendData(send,2*len+5); //调用A发送程序}/*---------------------------------------------------------------------------调用方式:void Function4(unsigned char address,unsigned char len) 函数说明:功能码处理4----读取输入寄存器---------------------------------------------------------------------------*/void Function4(unsigned char address,unsigned char len){unsigned char data i;unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+2)=2*len;address=2*address;for(i=0;i<len;i++) //取数据{*(send+4+2*i)=AI[address++];*(send+3+2*i)=AI[address++];}temp=CRC16(send,2*len+3);*(send+3+2*len)=(char)(temp>>8);*(send+4+2*len)=(char)temp;SendData(send,2*len+5);}/*---------------------------------------------------------------------------调用方式:void Function6(unsigned char address)函数说明:写单路寄存器---------------------------------------------------------------------------*/void Function6(unsigned char address){int temp;unsigned char data *WriteData;temp=2*address;WriteData=AO;//将写入的数据进行处理//your code to add here to deal with the write value*(WriteData+temp)=ReceiveData[4];*(WriteData+temp+1)=ReceiveData[5];SendData(ReceiveData,countnumber); //调用发送程序,返回与主机相同的报文}/*---------------------------------------------------------------------------调用方式:void SendError(char ErrorCode)函数说明:---------------------------------------------------------------------------*/void SendError(char ErrorCode){unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+1)=ReceiveData[1]|0x01;//最高位置1*(send+2)=ErrorCode;temp=CRC16(send,3);*(send+3)=(char)(temp>>8);*(send+4)=(char)temp;SendData(send,countnumber); //调用发送程序,返回错误代码}/*---------------------------------------------------------------------------调用方式:void Deal()函数说明:接收数据处理---------------------------------------------------------------------------*/void Deal(){unsigned int data temp;unsigned char data address;unsigned char data length;unsigned char data counter;unsigned char data *Pointer;Pointer=ReceiveData;counter=countnumber;if (counter<=3 ) return;temp=Pointer[counter-2]<<8;temp= temp|Pointer[counter-1];if( temp==CRC16(Pointer,counter-2)) //较验正确{address=Pointer[3];length=Pointer[5];if(address<48){switch (Pointer[1]) //根据功能码不同进行相应处理*/{case 1:Function12(address,length);break;case 2:Function12(address,length);break;case 3:address=address-1;Function3(address,length);break;case 4:address=address-1;Function4(address,length);break;case 6:address=address-1;Function6(address);default:SendError(0x81);break;}}else{ SendError(0x82);}}countnumber=0;}/*bit Chargetbit(unsigned char character,unsigned char Number){if(character>>Number&0x01==1)return 1;elsereturn 0;}//设定某一位的值*********************************************************** unsigned char SetBit(unsigned char Character,unsigned char num,bit boolen){unsigned char code bit_value[]={1,2,4,8,16,32,64,128};if(boolen)return Character|bit_value[num];elsereturn Character&~bit_value[num];}*//*---------------------------------------------------------------------------调用方式:void Init_timer2(unsigned int baudrate)函数说明:定时器2的初始化---------------------------------------------------------------------------*/void Init_timer2(unsigned int baudrate){unsigned int data TimReg2;T2CON = 0x00;T2MOD = 0x00;TimReg2=65536-(OSFREQ/384/baudrate*11);RCAP2L=TimReg2&0x00ff;RCAP2H=(TimReg2>8); //RCAP2H=TimReg2/256RCAP2L=0;RCAP2H=0;TL2 = RCAP2L; //TH2 = RCAP2H; //TR2=1; // T2CON.2 start timerET2=0; // 开定时器2中断}/*---------------------------------------------------------------------------调用方式:void Init_SerialPort(unsigned int baudrate)函数说明:串口初始化---------------------------------------------------------------------------*/void Init_SerialPort(unsigned int baudrate){unsigned char TimReg2;SCON = 0xd0; // 方式3, 8-bit UART, enable rcvrTMOD = 0x21; // T0方式1(16位), T1方式2,8位重装TimReg2=256-(OSFREQ/384/baudrate);TH1 = TimReg2; // 定时器1,在11.0592MHz下,波特率为9600 TL1 = TH1;TR1= 1; // TR1: 定时开始//PS=1; // 串行中断优先ES=1; // 接收中断}/*---------------------------------------------------------------------------函数说明:主函数---------------------------------------------------------------------------*/main(){EA=1; //开总中断Init_SerialPort(9600);Init_timer2(9600);Recenable=0; //接收允许MyAddress=0x01; //本机地址while(1){}}/*---------------------------------------------------------------------------调用方式:void SeiralA() interrupt 4 using 0函数说明:通讯中断---------------------------------------------------------------------------*/void SeiralA() interrupt 4 using 0{if(RI){if((TimeInterval>2)&&(TimeInterval<=4)){ countnumber=0;}if(countnumber>60){ countnumber=0;}ReceiveData[countnumber++]=SBUF; //将接收到的数据到缓冲区内TimeInterval=0;RI=0;ET2=1; //当接收到数据后,将定时器2开启}}/*---------------------------------------------------------------------------调用方式:void SerialPortTime() interrupt 5 using 0函数说明:定时器2中断程序---------------------------------------------------------------------------*/void SerialPortTime() interrupt 5 using 0{ET2=0; //定时器2中断允许控制TimeInterval=TimeInterval+1;if((TimeInterval>4) && countnumber){Recenable=1; //发送Deal(); //将接收到的数据进行处理Recenable=0; //接收countnumber=0;}else{ ET2=1;} //将定时器2打开TF2=0; //定时器2溢出标志,软件清0}。
单片机与PLC之间的通讯模式的Modbus协议的有效应用

单片机与PLC之间的通讯模式的Modbus协议的有效应用摘要;随着经济的发展,Modbus协议得到了广泛的应用,这满足了单片机和PLC通讯模式的应用,在此过程中HMI由于其自身的高实时性、高性能、小体积化得到了广泛的普及在一些工业自动系统设备中,都能看到它的影子。
其通过对图片、汉字、图形的有效应用,确保相关工作环节的稳定运行,大大满足了单片机与PLC通讯环节运作需要。
标签:波特;地址接收;代码;通讯管理;方案深化1 Modbus通讯协议的系统分析1.1 为了实现对单片机与PLC通讯环节的有效分析,我们首先要进行Modbus协议的深入分析,以分析目前工作中存在的不足,通过相应措施的应用,确保实际工作环节的稳定运行。
Modbus是施耐德电气公司的一种总线协议运作模式,有利于实现Modbus在以太网上的分布式应用,满足实际场景的需要。
随着经济技术的不断发展,Modbus协议的所有权也在发生着改变,目前主要由Modbus-IDA组织所应用,这极大推动了该协议模式的发展,随着Modbus的节点安装数量的不断提升,Modbus协议的应用范围更加广泛,目前已经成为电子控制器的广泛应用的语言。
Modbus协议应用于各个协议控制器之间,确保了网络之间的良好通信,比如以太网与其他设备的良好通信。
随着经济的发展,它已经成为一种通用的工业标准模式。
实现了对相关控制设备的有效应用,促进了工业网络的集中监控化,促进网络通信系统的不断完善。
这种模式通过对RS-232C兼容串行接口等环节的应用,实现日常信号位环节、奇偶校验等环节的稳定运行,实现了Modbus协议模式的正确应用。
1.2 为了确保单片机与PLC之间的通讯模式的深化,我们需要进行Modbus 协议的应用深化,这种协议模式提供了一种主从原则,就是主设备的初始化传输,该协议系统的其他相关环节都是围绕这个主从原则进行具体运用的,其他设备通过对主设备的反应,进行相关环节的运行,它主要是对主设备提供的相关数据信息进行反映。
关于51单片机上实现modbus协议

关于51单片机上实现modbus协议你找一个MODBUS的协议详细资料好好看看,就是一种通讯约定,你按照它规定的格式通讯就可以了协议发送给询问方。
Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。
此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。
标准的Modicon控制器使用RS232C实现串行的Modbus。
Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。
Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验,但TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。
另外,Modbus采用主从方式定时收发数据,在实际使用中如果某Slave站点断开后(如故障或关机),Master端可以诊断出来,而当故障修复后,网络又可自动接通。
因此,Modbus协议的可靠性较好。
下面我来简单的给大家介绍一下,对于Modbus的ASCII、RTU和TCP协议来说,其中TCP和RTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。
所以在这里我仅介绍一下Modbus的ASCII和RTU协议。
下表是ASCII协议和RTU协议进行的比较:协议开始标记结束标记校验传输效率程序处理ASCII :(冒号)CR,LF LRC 低直观,简单,易调试RTU 无无CRC 高不直观,稍复杂通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。
Modbus协议下实现单片机与PLC之间的通讯 - 单片机

Modbus协议下实现单片机与PLC之间的通讯 - 单片机1引言 hmi(人机界面)以其体积小,高性能,强实时等特点,越来越多的应用于工业自动化系统和设备中。
它有字母、汉字、图形和图片等不同的显示,界面简单友好。
配有长寿命的薄膜按钮键盘,操作简单。
它一般采用具有集成度高、速度快、高可靠且价格低等优点的单片机[1]作为其核心控制器,以实现实时快速处理。
plc和单片机结合不仅可以提PLC的数据处理能力,还可以给用户带来友好简洁的界面。
本文以Modbus通讯协议为例,详细讨论了一个人机系统中,如何用C51实现单片机和PLC之间通讯的实例。
2Modbus通讯协议 Modbus协议是应用于电子控制器上的一种通用语言。
通过此协议,控制器相互之间、控制器经由网络和其它设备之间可以通信。
Modbus协议提供了主—从原则,即仅一设备(主设备)能初始化传输(查询)。
其它设备(从设备)根据主设备查询提供的数据作出相应反应。
主设备查询的格式:设备地址(或广播,此时不需要回应)、功能代码、所有要发送的数据、和一错误检测域。
从设备回应消息包括确认地址、功能码、任何要返回的数据、和一错误检测域。
如果在消息接收过程中发生一错误,或从设备不能执行其命令,从设备将建立一错误消息并把它作为回应发送出去。
控制器能设置为两种传输模式:ASCII 和RTU,在同样的波特率下,RTU可比ASCII方式传送更多的数据,所以采用KTU模式。
(1)典型的RTU消息帧典型的RTU消息帧如表1所示。
RTU消息帧的地址域包含8bit。
可能的从设备地址是0...127(十进制)。
其中地址0是用作广播地址,以使所有的从设备都能认识。
主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。
当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。
RTU消息帧中的功能代码域包含了8bits,当消息从主设备发往从设备时,功能代码域将告之从设备需要执行哪些行为;当从设备回应时,它使用功能代码域来指示是正常回应(无误)还是有某种错误发生(称作异议回应,一般是将功能码的最高位由0改为1)。
手把手教你学51单片机之十八RS485通信与Modbus协议

在工业控制、电力通讯、智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换。
最初采用的方式是 RS232 接口,由于工业现场比较复杂,各种电气设备会在环境中产生比较多的电磁干扰,会导致信号传输错误。
除此之外,RS232 接口只能实现点对点通信,不具备联网功能,最大传输距离也只能达到几十米,不能满足远距离通信要求。
而 RS485 则解决了这些问题,数据信号采用差分传输方式,可以有效的解决共模干扰问题,最大距离可以到1200 米,并且允许多个收发设备接到同一条总线上。
随着工业应用通信越来越多, 1979 年施耐德电气制定了一个用于工业现场的总线协议 Modbus 协议,现在工业中使用RS485 通信场合很多都采用 Modbus 协议,本节课我们要讲解一下 RS485 通信和Modbus 协议。
单单使用一块KST-51 开发板是不能够进行RS485 实验的,应很多同学的要求,把这节课作为扩展课程讲一下,如果要做本课相关实验,需要自行购买USB 转 485 通信模块。
18.1 RS485通信实际上在 RS485 之前 RS232 就已经诞生,但是RS232 有几处不足的地方:1、接口的信号电平值较高,达到十几V ,容易损坏接口电路的芯片,而且和TTL 电平不兼容,因此和单片机电路接起来的话必须加转换电路。
2、传输速率有局限,不可以过高,一般到几十Kb/s 就到极限了。
3、接口使用信号线和GND 与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。
4、传输距离有限,最多只能通信几十米。
5、通信的时候只能两点之间进行通信,不能够实现多机联网通信。
针对 RS232 接口的不足,就不断出现了一些新的接口标准,RS485 就是其中之一,他具备以下的特点:1、我们在讲A/D 的时候,讲过差分信号输入的概念,同时也介绍了差分输入的好处,最大的优势是可以抑制共模干扰。
尤其工业现场的环境比较复杂,干扰比较多,所以通信如果采用的是差分方式,就可以有效的抑制共模干扰。
基于51单片机实现modbus协议通信单片机原理与应用课程设计论文

Ⅰ、课程设计(报告)题目:基于51单片机实现modbus协议通信Ⅱ、课程设计(论文)工作内容一、课程设计目标1、培养综合运用知识和独立开展实践创新的能力;2、通过编写实现modbus协议的程序,学生不但能够更加熟悉modbus协议,而且能够增强编程能力,为以后走上工作岗位奠定基础。
二、研究方法及手段应用1、上网查阅有关MODBUS协议的资料,分层次阅读协议;2、确定系统设计结构和编写方案;3、将功能分解为各个函数,分别编写每个函数。
三、课程设计预期效果1、给开发板上电;2、运行mbpoll调试软件:选择01功能实现读线圈,选择03功能实现读寄存器,05功能实现写单个线圈,06功能实现写单个寄存器,15功能实现写多个线圈,16功能实现写多个寄存器。
摘要目前,工业现场总线使用modbus协议已经非常普遍。
本次课程设计,使用开发板实现modbus协议通信,模拟工业现场。
通过本次试验,我们进一步知道了编程能力的重要性,更好地学习如何使用C语言编写单片机程序。
首先,我先通过网上查资料了解modbus协议的内容,分层次理解协议。
先大体确定整个程序的结构,然后分层次编写,然后将程序烧到单片机上,来实现功能。
老师让我们基于51单片机实现modbus协议通讯的目的就是让同学们在理论学习的基础上,通过对工业现场总线使用的一种协议的理解进而编程实现,使学生不但能够将课堂上学到的理论知识与实际应用结合起来,而且能够对分析、解决实际工业上的通信问题进一步加深认识,为今后能够独立进行实现通信协议程序的编写工作打下一定的基础。
本次课程设计的主要问题在于将modbus协议用C语言解读出来。
在程序开始设计之前,必须反复推敲设计方案并设计每个函数的方框图。
如果一切都设计好之后,底层协议的编写就变得容易得多。
【关键词】modbus协议函数 51单片机程序设计第一章系统设计第一节课题目标及总体方案一、课题目标实现Modbus协议部分内容用ASCII模式完成以上功能是本设计的基本要求。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
网上关于单片机和力控modbus通信的实例很少,关键并不是modbus协议而是力控与单片机连接的设置,下面的程序是基于51单片机做的。
在正个调试过程中由于我们不清楚力控发送给单片机的请求数据格式,我们可以用串口调试工具进行串口调试,将力控发送请求显示在串口调试工具中,其中单片机的程序如下#include<reg52.h>#include<string.h>#define uchar unsigned char //一个字节#define uint unsigned int // 两个字节void send_char(unsigned char txd);uint rece_count=0;uint send_flag=0;uchar rece_buf[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};uint i;/*串行口初始化*/void chushi(){SCON=0x50; //串口工作方式1,即10位异步PCON=0x00;//波特率不倍增TMOD=0x20;TH1=TL1=0xfa;TR1=1;}/*主程序*/main(){chushi();rece_count=0;while(1){if(RI){RI=0;if(rece_count<8){rece_buf[rece_count]=SBUF;rece_count++;}}if(rece_count==8){send_flag=1;}while(send_flag==1){ for(i=0;i<8;i++){SBUF=rece_buf[i];while(TI==0) ;TI=0;}}}}打开力控运行,将发送的数据保存到单片机中,然后打开串口调试工具,显示如下:我们看力控发送请求的格式:01 03 ff ff 00 01 84 2e,最后的两位crc是高位在前,低位在后。
Crc的高低位的前后顺序一定要注意,尤其在单片机编程中。
单片机程序:#include<reg52.h>#include<string.h>#define uchar unsigned char //一个字节#define uint unsigned int // 两个字节void send_char(unsigned char txd);uchar send_buf[7];uchar rece_buf[8];uint i;uint crc_data;uchar rece_count=0;uchar send_flag=0;/*串行口初始化*/void chushi(){SCON=0x50; //串口工作方式1,即10位异步PCON=0x00;//波特率不倍增TMOD=0x20;TH1=TL1=0xfa; //22.1184M晶振TR1=1;}/*CRC校验表*/const uchar code auchCRCHi[] = { //数组定义必须加code ,否则出现空间不够用的情况0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40} ;const uchar code auchCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,0x40} ;/*CRC计算程序*/uint crc16 (uchar *puchMsg, uint usDatalen){uchar uchCRCHi=0xff;uchar uchCRCLo=0xff;uint uindex;uint crcdata;while(usDatalen--){uindex=uchCRCHi^*puchMsg++;uchCRCHi=uchCRCLo^auchCRCHi[uindex];uchCRCLo=auchCRCLo[uindex];;}crcdata=uchCRCLo; //低位在前高位在后crcdata=crcdata<<8;crcdata=crcdata+uchCRCHi;return(crcdata);}/*主程序*/main(){chushi();send_buf[0]=0x01;//站址send_buf[1]=0x03;//读操作send_buf[2]=0x02;//字节数send_buf[3]=0x00;//第一个寄存器数据高字节send_buf[4]=0x01;//第一个寄存器数据低字节/*计算crc*/crc_data=crc16(send_buf,5);send_buf[6]=crc_data>>8; //低位send_buf[5]=crc_data; //高位while(1){if(RI){RI=0;if(rece_count<8){rece_buf[rece_count]=SBUF;rece_count++;}}if(rece_count==8){ if(rece_buf[0]==0x01){send_flag=1;}}if(send_flag==1){ for(i=0;i<7;i++){SBUF=send_buf[i];while(TI==0) ;TI=0;}send_flag=0;rece_count=0;}}}力控设置如下:Com口可以在电脑设备管理器中查找到这里面必须是03号命令,这在modbus中代表读操作,对于偏置我不是很明白,数据格式按这样选择是正确的。