Modbus协议51单片机C语言实现
51单片机的串口通信程序(C语言)

51单片机的串口通信程序(C语言) 51单片机的串口通信程序(C语言)在嵌入式系统中,串口通信是一种常见的数据传输方式,也是单片机与外部设备进行通信的重要手段之一。
本文将介绍使用C语言编写51单片机的串口通信程序。
1. 硬件准备在开始编写串口通信程序之前,需要准备好相应的硬件设备。
首先,我们需要一块51单片机开发板,内置了串口通信功能。
另外,我们还需要连接一个与单片机通信的外部设备,例如计算机或其他单片机。
2. 引入头文件在C语言中,我们需要引入相应的头文件来使用串口通信相关的函数。
在51单片机中,我们需要引入reg51.h头文件,以便使用单片机的寄存器操作相关函数。
同时,我们还需要引入头文件来定义串口通信的相关寄存器。
3. 配置串口参数在使用串口通信之前,我们需要配置串口的参数,例如波特率、数据位、停止位等。
这些参数的配置需要根据实际需要进行调整。
在51单片机中,我们可以通过写入相应的寄存器来配置串口参数。
4. 初始化串口在配置完串口参数之后,我们需要初始化串口,以便开始进行数据的发送和接收。
初始化串口的过程包括打开串口、设置中断等。
5. 数据发送在串口通信中,数据的发送通常分为两种方式:阻塞发送和非阻塞发送。
阻塞发送是指程序在发送完数据之后才会继续执行下面的代码,而非阻塞发送是指程序在发送数据的同时可以继续执行其他代码。
6. 数据接收数据的接收与数据的发送类似,同样有阻塞接收和非阻塞接收两种方式。
在接收数据时,需要不断地检测是否有数据到达,并及时进行处理。
7. 中断处理在串口通信中,中断是一种常见的处理方式。
通过使用中断,可以及时地响应串口数据的到达或者发送完成等事件,提高程序的处理效率。
8. 串口通信实例下面是一个简单的串口通信实例,用于在51单片机与计算机之间进行数据的传输。
```c#include <reg51.h>#include <stdio.h>#define BAUDRATE 9600#define FOSC 11059200void UART_init(){TMOD = 0x20; // 设置定时器1为模式2SCON = 0x50; // 设置串口为模式1,允许接收TH1 = 256 - FOSC / 12 / 32 / BAUDRATE; // 计算波特率定时器重载值TR1 = 1; // 启动定时器1EA = 1; // 允许中断ES = 1; // 允许串口中断}void UART_send_byte(unsigned char byte){SBUF = byte;while (!TI); // 等待发送完成TI = 0; // 清除发送完成标志位}unsigned char UART_receive_byte(){while (!RI); // 等待接收完成RI = 0; // 清除接收完成标志位return SBUF;}void UART_send_string(char *s){while (*s){UART_send_byte(*s);s++;}}void main(){UART_init();UART_send_string("Hello, World!"); while (1){unsigned char data = UART_receive_byte();// 对接收到的数据进行处理}}```总结:通过以上步骤,我们可以编写出简单的51单片机串口通信程序。
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协议与C语言开发1.4

内存地址 小端模式存放内容 大端模式存放内容
0x4000
0x78
0x12
0x4001
0x56
0x34
0x4002
0x34
0x560x780x403如何判断机器的字节序(2)
联合体union的存放顺序是所有成员都从低地址开始存放,利用该 特性可以轻松地获得了CPU对内存采用Little-endian还是Big-endian 模式读写:
BOOL IsBigEndian() { union NUM { int a; char b; }num; num.a = 0x1234; if( num.b == 0x12 ) { return TRUE; } return FALSE }
大端和小端模式的内存定义-16位
16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU 内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
小端模式存放内容
大端模式存放内容
0x4000
0x34
0x12
0x4001
0x12
0x34
大端和小端模式的内存定义-32位
0x12
大端和小端模式的优缺点
一般操作系统都是小端,而通讯协议是大端的 • 小端模式 :强制转换数据不需要调整字节内容,1、2、4字节的 存储方式一样 • 大端模式 :符号位的判定固定为第一个字节,容易判断正负
数组在大端小端情况下的存储
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value: Big-Endian: 低地址存放高位,如下: 高地址 --------------buf[3] (0x78) -- 低位 buf[2] (0x56) buf[1] (0x34) buf[0] (0x12) -- 高位 --------------低地址 Little-Endian: 低地址存放低位,如下: 高地址 --------------buf[3] (0x12) -- 高位 buf[2] (0x34) buf[1] (0x56) buf[0] (0x78) -- 低位 --------------
modbus(C语言程序)

modbus(C语言程序)2009-10-15 09:31:28| 分类:默认分类|字号订阅#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,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,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}。
51单片机MODBUS协议汇编-蚂蚁叼骨头

51单片机MODBUS协议汇编-蚂蚁叼骨头51单片机汇编—MODBUS协议(带写入功能)//读数据帧:01设备地址03功能码0000起始地址0001数据长度840A校验码//读返回帧:01设备地址03功能码02数据个数12 数据34数据B533校验码//写数据帧:01设备地址10功能码0001起始地址0001数据长度02数据字节数12数据34数据AAF6校验码//写返回帧:以写帧相同。
//带此‘-----’标志,且后无任何备注的可以去除或用空指令代替,此些地方时是为了测试时使用的状态显示位。
ORG 0000HLJMP MAIN;ORG 0023HLJMP PCTX;ORG 0100HMAIN: MOV SP,#30H;MOV P0,#00H;MOV 60H,#12H;---------------MOV 61H,#34H;----------------------MOV 62H,#56H; --------MOV 63H,#78H;---MOV TMOD,#20H;MOV TL1,#0FDH;MOV TH1,#0FDH;MOV SCON,#50H;MOV PCON,#00H;;/MOV R3,#00H;存入数据个数(未用)MOV P1,#00H;MOV R3,#00H;MOV R2,#08H;MOV R1,#40H;数据首地址(未用)//MOV 0B8H,#10H; 串口优先级SETB TR1;SETB ES;//MOV DPTR,#0100H;SETB EA;MOV P0,#00H;MOV R4,#00H;LOO:MOV A,R3;JNZ LOP;MOV A,R4;MOV P1,A;INC R4;LCALL DELAY1S;DJNZ R5,LOO;JMP LOON;PCTX: SETB P0.0;------------------ TX: JB RI,LOOP;LCALL DELAY10;延时10ms等待;JNB RI,LOP;SETB P0.1;----------------- LJMP OUT;LOOP: MOV A,SBUF;MOV 3EH,A;CLR RI;MOV A,R1;ADD A,R3;MOV R0,A;MOV @R0,3EH;INC R3;存入数据个数(未用)MOV A,R3;-------------MOV P2,A;-------------LCALL DELAY10;LJMP OUT;//DJNZ R2,OUT;LOP:CLR ES;//LCALL DELAY1S;延时10msMOV R1,#40H;MOV A,@R1;(数据地址送A比较器)SUBB A,#01H;(地址为01响应)JNZ RESET;SETB P0.2;----------MOV A,41H;SUBB A,#03;功能码为03,对数据读操作。
手把手教你学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通讯本文和另一篇----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协议下51系列单片机与eView触摸屏的通讯方法-图文

Modbus协议下51系列单片机与eView触摸屏的通讯方法-图文以下是为大家整理的modbus协议下51系列单片机与eView触摸屏的通讯方法-图文的相关范文,本文关键词为modbus,协议,系列,单片机,eView,触摸屏,通讯,,您可以从右上方搜索框检索更多相关文章,如果您觉得有用,请继续关注我们并推荐给您的好友,您可以在综合文库中查看更多范文。
modbus协议下51系列单片机与eView触摸屏的通讯方法(组图)modbus协议由于其具有开放性、透明性、本钱低、易于开发等特点,已成为当今产业领域通讯协议的首选。
本文介绍了一种基于modbus通讯协议的eView触摸屏与常用的51单片机的通讯方法。
该方法通过c51编程实现modbus通讯,在51系列单片机上具有通用性,有一定的鉴戒作用。
产业控制中经常需要观察系统的运行状态或者修改运行参数。
触摸屏能够直观、生动地显示运行参数和运行状态,而且通过触摸屏画面可以直接修改系统运行参数,人机交互性好。
单片机广泛应用于工控领域中,与触摸屏配合,可组成良好的人机交互环境。
触摸屏与单片机通讯,需要根据触摸屏采用的通讯协议为单片机编写相应的通讯程序。
modbus协议是美国modicon公司推出的,一种有效支持控制器之间以及控制器经过网络(如以太网)与其他设备之间进行通讯的协议。
本文以sTc89c51单片机和人机电子有限公司的eView触摸屏为例,介绍其通讯程序的开发过程。
1系统结构实现触摸屏与单片机的通讯,主要是解决通讯协议的题目。
本文使用开放的modbus通讯协议,以触摸屏作主站,单片机作从站。
eView 触摸屏本身支持modbus通讯协议,假如单片机也支持modbus协议,就可以进行通讯了。
eview触摸屏支持Rs-232和Rs-485两种通讯接口。
在产业控制领域,由于Rs-485具有可靠性高、传输间隔远、抗干扰能力强等优点,所以在本系统中触摸屏与单片机通讯采用Rs-485连接,传输速率设置为9600kbps。