查看: 3711|回复: 3

基于单片机的Modbus通讯程序设计(二)

发表于 2016-2-27 15:25:19 | 显示全部楼层 |阅读模式 来自 广东省深圳市罗湖区
/*****************************************************************************
** 函数名称: uint crc16(uchar *puchMsg,uchar usDataLen)         
** 功能描述:  16位CRC校验函数,查表法*
** 输 入:    uchar *puchMsg,uchar usDataLen
**            校验的数组首地址  校验的数据个数   
**        
** 输 出:
**         
** 全局变量:
** 调用模块:
**
** 作 者:    吴鉴鹰
** 日 期:     14.03.26
******************************************************************************/
uint crc16(uchar *puchMsg,uchar usDataLen)         
{                                                                                         
        uchar uchCRCHi = 0xFF ;                                 
        uchar uchCRCLo = 0xFF ;                                 
        uint uIndex ;                                                         
        while (usDataLen--)                                         
        {
                uIndex = uchCRCHi ^ *puchMsg++ ;         
                uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
                uchCRCLo = auchCRCLo[uIndex] ;
        }
        return (((uint)(uchCRCLo) << 8) | uchCRCHi) ;
}


/*****************************************************************************
** 函数名称:  void delay_us(int n)        
** 功能描述:  延时子程序,延迟N us
** 输 入:    int n 延时的时间
**            
**        
** 输 出:
**         
** 全局变量:
** 调用模块:
**
** 作 者:    吴鉴鹰
** 日 期:     15.03.26
******************************************************************************/
/********延时子程序,延迟N us*************/
void delay_us(int n)
{
        int i;
        for(i=n;n>0;n--)
        _nop_();
}


/*****************************************************************************
** 函数名称:  void delay_ms(int n)
** 功能描述:  延时子程序,延迟N ms
** 输 入:    int n 延时的时间
**            
**        
** 输 出:
**         
** 全局变量:
** 调用模块:
**
** 作 者:    吴鉴鹰
** 日 期:     15.03.26
******************************************************************************/
void delay_ms(int n)
{
        int i,j;
        for(i=n;i>0;i--)
                for(j=141;j>0;j--);
}


/*****************************************************************************
** 函数名称:  void Led_Display_Fuc(uint Num)  
** 功能描述:  数码管显示函数,最多显示五位数,数值仅限于0-9,
              该函数根据实际情况选择使用
** 输 入:    uint Num  要显示的数字
**            
**        
** 输 出:
**         
** 全局变量:
** 调用模块:
**
** 作 者:    吴鉴鹰
** 日 期:     15.03.26
******************************************************************************/
void Led_Display_Fuc(uint Num)           
{
        uint i;
        for (i=0;i<5; i++)
        {
                P2 = 0;                       //清除显示,防止数字重影
                P0 = DuanMa[Num%10];           //显示最低一位数
                delay_us(10);
                P2 = 0x80>>i;                         //显示第一位数时点亮第一个数码管,第二位时点亮第二个数码管,以此类推。
                Num = Num/10;                         //将变量除以10处理将显示下一位数        
                   delay_ms(1);
        }
}


复制代码




更多资料可以关注订阅号:单片机精讲吴鉴鹰
随时随地学习在论坛的分享
回复 赏评分 举报
吴鉴鹰


14
主题       
258
帖子       
807
积分
高级技术员
关注TA 发消息
专家等级:
结帖率:12%
打赏:0.00
受赏:1.00
66楼
  楼主| 发表于 2015-4-14 16:44 | 只看该作者 |返回版面
第五讲:基于单片机的Modbus通讯程序设计(二)




/*****************************************************************************
** 函数名称: void init_uart(void)
** 功能描述: 串口初始化函数
** 输 入: int n 延时的时间
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void init_uart(void)
{
TH1 = 0xfa;
TL1 = 0xfa; // 波特率设为9600
TMOD = 0x21; // 使用T1定时器,模式2
PCON = PCON | 0x80; // 启用波特率加强位
SCON = 0x50; // 串口模式为2,即8位数据位,停止位为1,无校验
PS = 1; // 设定串口中断优先级为最高
TR1 = 1; // 开始定时
ES = 1;         // 打开串口中断
EA = 1; // 打开总中断
}


/*****************************************************************************
** 函数名称: void Send_Data_Fuc(uchar num)
** 功能描述: 发送函数
发送一个字符即一个字节
** 输 入: uchar num 发送的数据或者字符
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Send_Data_Fuc(uchar num)
{
SBUF=num;         //把字符放到发送数据缓存区SBUF,num可以是字符也可以是一个字节数据
while(!TI);         //当发送完之后,TI自动置位,即TI=1,表示发送完成
TI = 0;         //当发送完之后TI不会自动置0,所以需手动将其置0,表示还没发送完数据
}




/*****************************************************************************
** 函数名称: void send_word(uchar *mydata,uchar num)
** 功能描述: 发送函数
发送字符串或者一连串的字节数据
** 输 入: uchar *mydata,uchar num
** mydata表示要发送的字符串数组,num表示数组长度
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void send_word(uchar *mydata,uchar num)
{
int i;
for(i=0;i<num;i++)
{
Send_Data_Fuc(*(mydata+i));
}
Data_Count = 0;
}


/*****************************************************************************
** 函数名称: void Com_Data_Receive() interrupt 4 using 1
** 功能描述: 中断接收函数
当数据接收完时,进入串口中断将接收的值放入数组
** 输 入:
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Com_Data_Receive() interrupt 4 using 1
{
if(RI)         //当接收完一个字节的数据后,RI会自动置位,即RI=1
{
uchar a;
RI=0;         //接受中断标志软件清零
a=SBUF;         //把缓存在单片机的数据给a
Receive_buf[Data_Count]=a;        //将接受数据放入预置数组
Data_Count++;         //数组自动递增,用于存储下一个数据
if(Data_Count == 8)         //当接收完8个MODBUS的功能码之后,自动清0,从新接收(支持的01、05、03、06命令读取或者写入每次都发送8个字节)
{
Data_Count = 0;
Receive_Finish_Flag = 1;         //接受完8个MODBUS功能码的标志位
}
}
}


/*****************************************************************************
** 函数名称: void Com_Data_Mng(void)
** 功能描述: MODBUS功能码处理
能识别01,03,05,06功能码,可以实现一对多通讯
** 输 入:
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Com_Data_Mng(void)
{
//uint begin_address=0,address_leg=0,crc_end;
uint crc_end,legg;         //CEC校验值和03功能码发送的位的长度
char hi_type,low_type;         //03、06功能码发送字节高低位
if(Receive_buf[0]== 0xa1) //判断机器号码
{
if(Receive_buf[1] == 0x01)         //01功能码
{
//begin_address=Receive_buf[2]<<8+Receive_buf[3];
//address_leg=Receive_buf[4]<<8+Receive_buf[5];
crc_end = crc16(Receive_buf,6);         //校验
if(crc_end == Receive_buf[6]<<8 | Receive_buf[7])//当校验通过时时
{
hi_type=0;         //高位状态
low_type=Pd_Flag_0 | Pd_Flag_1 | Pd_Flag_2 | Pd_Flag_3 | Pd_Flag_4 | Pd_Flag_5 | Pd_Flag_6 | Pd_Flag_7;         //低位状态
//low_type=~P0;
Send_buf[0] = Receive_buf[0];         //站号
Send_buf[1] = Receive_buf[1];         //功能码
Send_buf[2] = 0x02;         //字节数
Send_buf[3] = low_type;         //低位状态,即LED开关情况
Send_buf[4] = hi_type;         //高位状态
crc_end = crc16(Send_buf,5);         //校验
Send_buf[5] = crc_end%256;         //校验低位
Send_buf[6] = crc_end>>8;         //校验高位
send_word(Send_buf,7);         //发送返回屏
}
}
if(Receive_buf[1] == 0x05)         //05功能码
{
begin_address=Receive_buf[3];         //开始地址
crc_end=crc16(Receive_buf,6);         //校验
if(crc_end==Receive_buf[6]<<8|Receive_buf[7])         //校验正确时
{
if(Receive_buf[4]==0xff)         //当为强制打开时
{
switch(begin_address)         //对应的地址
{
case 0x00: LED_OUT_0 = 0; Pd_Flag_0 = 0x01; break;        //相应的LED点亮,记录下相应的P0位状态
case 0x01: LED_OUT_1 = 0; Pd_Flag_1 = 0x02; break;
case 0x02: LED_OUT_2 = 0; Pd_Flag_2 = 0x04; break;
case 0x03: LED_OUT_3 = 0; Pd_Flag_3 = 0x08; break;
case 0x04: LED_OUT_4 = 0; Pd_Flag_4 = 0x10; break;
case 0x05: LED_OUT_5 = 0; Pd_Flag_5 = 0x20; break;
case 0x06: LED_OUT_6 = 0; Pd_Flag_6 = 0x40; break;
case 0x07: LED_OUT_7 = 0; Pd_Flag_7 = 0x80; break;
}        
}
else         //当强制为OFF时
{
//flag_led=0;
switch(begin_address)
{
case 0x00: LED_OUT_0=1; Pd_Flag_0=0; break;         //相应的LED熄灭,记录下相应的P0位状态
case 0x01: LED_OUT_1=1; Pd_Flag_1=0; break;
case 0x02: LED_OUT_2=1; Pd_Flag_2=0; break;
case 0x03: LED_OUT_3=1; Pd_Flag_3=0; break;
case 0x04: LED_OUT_4=1; Pd_Flag_4=0; break;
case 0x05: LED_OUT_5=1; Pd_Flag_5=0; break;
case 0x06: LED_OUT_6=1; Pd_Flag_6=0; break;
case 0x07: LED_OUT_7=1; Pd_Flag_7=0; break;
}
}        
Send_buf[0]=Receive_buf[0];         //站号
Send_buf[1]=Receive_buf[1];         //功能码
Send_buf[2]=Receive_buf[2];         //起始高位
Send_buf[3]=Receive_buf[3];         //起始低位
Send_buf[4]=Receive_buf[4];         //返回状态
Send_buf[5]=Receive_buf[5];         //返回状态
crc_end=crc16(Send_buf,6);         //校验
Send_buf[6]=crc_end%256;         //校验低位
Send_buf[7]=crc_end>>8;         //校验高位
send_word(Send_buf,8);         //发送返回屏
}
}        
if(Receive_buf[1]==0x03)         //03功能码
{
crc_end=crc16(Receive_buf,6);         //校验
if(crc_end == Receive_buf[6]<<8|Receive_buf[7])        //校验一致
{
begin_address=Receive_buf[2]<<8|Receive_buf[3];
address_leg=Receive_buf[4]<<8 | Receive_buf[5];         //总寄存器长度
hi_type=0;
Send_buf[0]=Receive_buf[0];         //站号
Send_buf[1]=Receive_buf[1];         //功能码
legg=(uint)address_leg*2;         //字节数
Send_buf[2]=address_leg*2;;         //字节数
for(time=0;time<legg;time++)         //发送相应字节
{
if(!(time%2))         //字高位为0
Send_buf[time+3] = disp[time/2+begin_address]/256;         //字高字节
else
Send_buf[time+3] = disp[time/2+begin_address]%256;         //字低字节
}
crc_end=crc16(Send_buf,legg+3);         //校验
Send_buf[legg+3]=crc_end%256;         //校验低位
Send_buf[legg+4]=crc_end>>8;         //校验高位
send_word(Send_buf,legg+5);         //返回屏
}
}
if(Receive_buf[1]==0x06)         //06功能码
{
begin_address=Receive_buf[2]<<8|Receive_buf[3]; //写入地址
address_num=Receive_buf[4]<<8|Receive_buf[5];        //寄存器值
crc_end=crc16(Receive_buf,6);         //校验
if(crc_end==Receive_buf[6]<<8|Receive_buf[7])        //校验正确
{
disp[begin_address]=address_num;         //存入数组,给03功能码调用数值
Send_buf[0]=Receive_buf[0];         //站号
Send_buf[1]=Receive_buf[1];         //功能码
Send_buf[2]=Receive_buf[2];         //开始高位
Send_buf[3]=Receive_buf[3];         //开始低位
Send_buf[4]=Receive_buf[4];         //字高位
Send_buf[5]=Receive_buf[5];         //字低位
crc_end=crc16(Send_buf,6);         //校验
Send_buf[6]=crc_end%256;         //校验低位
Send_buf[7]=crc_end>>8;         //校验高位
send_word(Send_buf,8);         //返回屏


}
}
}
}


/*****************************************************************************
** 函数名称: void main(void)
** 功能描述: 主函数


** 输 入:
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void main(void)
{
init_uart();         //初始化串口
while(1)
{
Led_Display_Fuc(address_num);         //在开发板数码管上显示相应位的数值,发送或者接受数据会暂停显示(数码管闪烁)
//按钮输入
if(!KEY_OUT_3) //判断按钮是否按下
{
delay_ms(2);         //延时
while (!KEY_OUT_3);
//delay_ms(2);
LED_OUT_2=!LED_OUT_2;
if(LED_OUT_2)
{
Pd_Flag_2=0x0;
}
else
{
Pd_Flag_2=0x4;
}        
}
if(!KEY_OUT_4)
{
delay_ms(2);
while (!KEY_OUT_4);
//delay_ms(2);
LED_OUT_3=!LED_OUT_3;
if(LED_OUT_3)
{
Pd_Flag_3=0x0;
}
else
{
Pd_Flag_3=0x8;
}
}
while(Receive_Finish_Flag)         //当接收完成时
{
Com_Data_Mng();         //识别相应的功能码
Receive_Finish_Flag=0;         //重新接收        
}        
}        
}


已绑定手机
发表于 2016-3-22 16:03:10 | 显示全部楼层 来自 北京市
帅呆了
 楼主| 发表于 2016-3-23 14:04:50 | 显示全部楼层 来自 广东省深圳市

有用就好!
已绑定手机
发表于 2020-12-31 08:52:47 | 显示全部楼层 来自 安徽省合肥市
牛,赞赞赞赞~~~~~~~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

合作/建议

TEL: 19168984579

工作时间:
周一到周五 9:00-11:30 13:30-19:30
  • 扫一扫关注公众号
  • 扫一扫打开小程序
Copyright © 2013-2024 一牛网 版权所有 All Rights Reserved. 帮助中心|隐私声明|联系我们|手机版|粤ICP备13053961号|营业执照|EDI证
在本版发帖搜索
微信客服扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表