51单片机新手入门之Modbus通讯.docx
- 文档编号:5246680
- 上传时间:2022-12-14
- 格式:DOCX
- 页数:26
- 大小:1.08MB
51单片机新手入门之Modbus通讯.docx
《51单片机新手入门之Modbus通讯.docx》由会员分享,可在线阅读,更多相关《51单片机新手入门之Modbus通讯.docx(26页珍藏版)》请在冰豆网上搜索。
51单片机新手入门之Modbus通讯
51单片机新手入门之Modbus通讯
本文和另一篇----C#WPF新手入门之Modbus通讯为一个系列,所用模块均是气体分析模块,之前用C3做的上位机(主要是方便调试,单片机不好搞),既然电脑上串口通讯代码没问题,那么放到单片机稍加修改也就理所当然肯定可行O(∩_∩)O~。
本文包括单片机的电路设计和软件设计两部分(单片机采用STC12C5A60S2双串口),至此单片机和上位机的串口通讯均成功实现,供需要的同学参考。
1.电路设计
AltiumDesigner原理图如下:
实际效果如下:
下载步骤1.选择芯片
下载步骤3.打开hex文件
电脑串口接收到的数据,和LCD屏显示的是一样的
下载步骤2.打开串口
下载步骤4.MCU先断电,点击下载,看到提示后上电
2.软件设计
这里最麻烦的在于模块的Modbus通讯需要偶校验,我是手动添加的校验位,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,把所有需要发送的数据一个个手动添加了再发送^_^;另外lcd12864屏也搞半天,主要照着时序图写必须sid先发送,然后sclk,不然不行 ̄□ ̄。
气体模块详细的通讯协议参考和上位机通讯那篇,此处不再介绍。
图中电路板没有焊AD芯片和温度芯片,引脚备用,相关代码注释掉了。
代码如下:
#include"STC12C5A60S2.H"
#include
#defineucharunsignedchar
#defineuintunsignedint
ucharsendbuffer[17];//发送数据
ucharflag=0;
ucharre_buffer[32];
ucharcount=0;
uintad_data=0;
doublead_vol=0;
uintcon_mid=0;
uintgascon=0;
longsum_o2=0;
uintaverage_counter=20;
uintidataoldtemp[21];
ucharidataSend_Buff[20];//module
uintpre_contemp=0;
uintO2_con=0;
ucharidatatest[21];//module
uinttemperature=0;
uintdat;
uinttestlcd=0;
ucharc[]={0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9};
//MAX1241模数芯片引脚设置,此电路板我没焊^_^
//sbitADC_CS=P0^1;
//sbitADC_CLK=P0^0;
//sbitADC_DOUT=P0^2;
sbitADC_CS=P3^6;
sbitADC_CLK=P3^5;
sbitADC_DOUT=P3^7;
//LCD12864LCD屏幕引脚设置
sbitcs=P2^2;
sbitsid=P2^1;
sbitsclk=P2^0;
sbitDQ=P0^1;//DS18B20温度引脚设置,依然没有…
#defineN11
#defineN220
voiddelayms(unsignedchart)
{
unsignedchari;
unsignedcharj;
for(j=t;j;j--)
for(i=192;i;i--);/*1ms延时*/
}
voiddelayus(uintt)
{
uinti;
for(i=0;i { _nop_(); } } voiddelay(floatsec) { unsignedinti; unsignedintj; j=sec*100; while(j--) { i=1561; while(--i); } } voidUART1_init()//串口1初始化,此串口和电脑通讯 { TMOD=0x20;/设置定时器工作方式2 TH1=0xfd;//波特率9600 TL1=0xfd; TR1=1; REN=1; SM0=0; SM1=1; //ES=1; } voidUART2_init()//串口2初始化,和模块通讯Modbus { S2CON=0x50;//方式1,允许接收 BRT=0xf4;//波特率2400 AUXR=AUXR|0X10;//允许独立波特率允许 AUXR1=AUXR1&0xef;//将uart2切换到P1口 IE2=IE2|0X01;//允许串口2中断 } voidUART1_Send(unsignedcharUART_data)// { //ES=0; SBUF=UART_data;//将接收的数据发送回去 while(TI! =1);//检查发送中断标志位 TI=0;//另发送中断标志位为0 //ES=1; } voidUART2_Send(unsignedcharUART_data)//串口2发送 { //ES=0; S2BUF=UART_data; while((S2CON&0x02)! =0x02); S2CON&=~0x02; //ES=1; } voidUART1_Send_String(char*str,charlen)//串口1发送字符串 { unsignedchari; for(i=0;i<=len;i++) { UART1_Send(str[i]); } } voidUART2_Send_String(char*str,charlen)//串口2发送字符串 { unsignedchari; for(i=0;i<=len;i++) { UART2_Send(str[i]); } } unsignedcharCreat_Addr(unsignedcharadr,unsignedcharposition)//计算模块地址 { unsignedcharhich; unsignedcharloch; hich=adr/16; loch=adr%16; if(hich>9) hich+=7; if(loch>9) loch+=7; if(position==1) { returnhich+0x30; } elseif(position==0) { returnloch+0x30; } } unsignedcharCheckSum(unsignedchar*str,unsignedcharposition,ucharlen)/计算校验码 { uchari; unsignedintsum=0; ucharhi,lo; //ucharlen=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) { returnhi; } elseif(position==0) { returnlo; } } voiddata_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;//TEM sendbuffer[12]=0x30; sendbuffer[13]=0x30; sendbuffer[14]=0x5D; sendbuffer[15]=0x0D; sendbuffer[16]=0x0A; } voidcalculate_module(unsignedcharstr[])//lcd屏显示 { unsignedintconcen; ucharwan,qian,bai,shi,ge; /*uchard4=str[7]-48; uchard3=str[8]-48; uchard2=str[9]-48; uchard1=str[10]-48;*///浓度只需要后面部分 uchard4=str[24]-48; uchard3=str[25]-48; uchard2=str[26]-48; uchard1=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; } voidmodule_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(∩_∩)O test[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; } /*uintread_max1241()AD芯片处理 { uintADC_Data; uchari; 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; returnADC_Data; } unsignedintad_filter()//滤波 { unsignedintcount1,i,j; unsignedintvalue_buf[N]; unsignedinttemp; unsignedintsum=0; for(count1=0;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(unsignedint)(sum/(N-6)); } voidcalculate_o2()//测试用 { ucharwan,qian,bai,shi,ge; uintcon_fin=0; uchari; ad_data=ad_filter(); ad_vol=(ad_data/4096.0)*2500.0;//Voltage ad_data=(uint)ad_vol;//concentration delayms (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温度显示复位函数--------------- voidow_reset(void) { charpresence=1; while(presence) { while(presence) { DQ=1; delayus (2); DQ=0;// delayus(550);//550us DQ=1;// delayus(66);//66us presence=DQ; } delayus(500);//延时500us presence=~DQ; } DQ=1; } //-----------18B20写命令函数------------ voidwrite_byte(ucharval) { uchari; for(i=8;i>0;i--)// { DQ=1; delayus (2); DQ=0; delayus(5);//5us DQ=val&0x01; delayus(66);//66us val=val/2; } DQ=1; delayus(11); } //--------------18B20读一个字节函数--------- ucharread_byte(void) { uchari; ucharvalue=0; for(i=8;i>0;i--) { DQ=1; delayus (2); value>>=1; DQ=0; delayus(4);//4us DQ=1; delayus(4);//4us if(DQ)value|=0x80; delayus(66);//66us } DQ=1; return(value); } voidRead_Temperature(void) { unsignedintTemp1,Temp2; ucharbai,shi,ge; ow_reset();//DS18B20 write_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.xx bai=temperature/100; shi=temperature%100/10; ge=temperature%10; sendbuffer[11]=bai+0x30; sendbuffer[12]=shi+0x30; sendbuffer[13]=ge+0x30; delayms(5); }*//图中电路板此部分没焊,此部分代码不使用^_^ voidwritecmd_lcd(ucharcmd)//lcd屏写指令 { uchari; ucharcmd1; 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; } elsesid=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; } elsesid=0; sclk=1; sclk=0; cmd1=cmd1<<1; } } voidwritedata_lcd(uchardat) { uchari; uchardat1; dat1=dat; //11111010 for(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; } elsesid=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; } elsesid=0; sclk=1; sclk=0; dat1=dat1<<1; } } voidinit_lcd()//初始化lcd屏 { cs=1; writecmd_lcd(0x30);//设定为8位控制 writecmd_lcd(0x0c);//显示打开 writecmd_lcd(0x01);//清屏 } voidgotoxy(uintrow,uintcol) { switch(row) { case1: writecmd_lcd(0x80+col);break; ca
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 51 单片机 新手入门 Modbus 通讯