用stm32库函数编写的modbus源代码.docx

上传人:b****3 文档编号:4146884 上传时间:2022-11-28 格式:DOCX 页数:33 大小:25.31KB
下载 相关 举报
用stm32库函数编写的modbus源代码.docx_第1页
第1页 / 共33页
用stm32库函数编写的modbus源代码.docx_第2页
第2页 / 共33页
用stm32库函数编写的modbus源代码.docx_第3页
第3页 / 共33页
用stm32库函数编写的modbus源代码.docx_第4页
第4页 / 共33页
用stm32库函数编写的modbus源代码.docx_第5页
第5页 / 共33页
点击查看更多>>
下载资源
资源描述

用stm32库函数编写的modbus源代码.docx

《用stm32库函数编写的modbus源代码.docx》由会员分享,可在线阅读,更多相关《用stm32库函数编写的modbus源代码.docx(33页珍藏版)》请在冰豆网上搜索。

用stm32库函数编写的modbus源代码.docx

用stm32库函数编写的modbus源代码

用stm32库函数编写的modbus源代码

说在前面的话:

1.请勿盲目抄袭。

这个协议使用了一个定时器,所以在别处请不要再使用,如果定时器不够用,可以做虚拟定时器。

也就是采用一个物理的定时器产生时基。

在这个定时器的中断函数中可以给相应的多个定时器自加1.每个虚拟定时器可以用两个变量分别控制打开关闭,和计时。

这个已经试验通过了可行的。

其实就跟我们使用物理的定时器一样,只不过物理的定时器是用晶振产生时基。

2.这段代码已经调试通过了,也硬件试验过,没有问题,如果你出现问题了,看看你在主函数的的各种基本配置有没有完成。

如果要使用06和10号功能,你还需要在主函数中建立一个100个元素的数组,每个元素是16位。

3.写这个文档的时候,这个协议已经是半年前完成的了。

所以有些东西记得不是很清楚了,如果说错了,请以实际为准。

只是不想让这份代码死在电脑中了,所以才想起来要拿出来分享,支持开源精神。

4.如果实在实在是没有弄出来,请联系我,可以共同交流,我的邮箱:

 

#include"stm32f10x.h"

/*此Modbus协议暂时只支持RTU模式,只支持作为Modbus从设备。

暂时支持的功能码(16进制)如下表所示:

01.读线圈状态(读多个输出位的状态,有效地位为0-31)

02.读输入位状态(读多个输入位的状态,有效地位为0-31)

03.读保持寄存器(读多个保持寄存器的数值,有效地位为0-99)

04.读输入寄存器(读多个输入寄存器的数值,有效地址为0-1)

05.强制单个线圈(强制单个输出位的状态,有效地位为0-31)

06.预制单个寄存器(设定一个寄存器的数值,有效地址为0-99)

0F.强制多个线圈(强制多个输出位的状态,有效地址为0-31)

10.预制多个寄存器(设定多个寄存器的数值,有效地址为0-99)

暂时支持的错误代码为:

01不合法功能代码从机接收的是一种不能执行功能代码。

发出查询命令后,该代码指示无程序功能。

(不支持的功能代码)

02不合法数据地址接收的数据地址,是从机不允许的地址。

(起始地址不在有效范围内)

03不合法数据查询数据区的值是从机不允许的值。

(在起始地址的基础上,这个数量是不合法的)

供用户调用的函数有:

1.voidModInit(u8Id);//用于Modbus初始化,在函数调用前,必须初始化函数,用于Main函数中

2.voidModRcv(void);//用于modbus信息接收,放在串口接收中断

3.voidModSend(void);//用于modbus信息接收,放在串口发送中断

例如:

voidUSART1_IRQHandler(void)//USART1中断

{

if(USART_GetITStatus(USART1,USART_IT_RXNE)!

=RESET)

{

voidModRcv(void);

……

……

……

}

if(USART_GetITStatus(USART1,USART_IT_TC)!

=RESET)

{

voidModSend(void);//用于modbus信息接收

……

……

……

}

}*/

 

//modbus用通讯参数

u8Tim_Out;//大于3.5个字符时间,保守取3ms(波特率9600的时候大约2点几毫秒)

u8Rcv_Complete;//一帧是否已经接受完成

u8Send_Complete;//一帧是否已经发送完成

u8Com_busy;//通讯繁忙,表示上一帧还未处理结束

u8Rcv_Buffer[210];//用来存放接收到的完整的一帧数据(第一个字节用来存放接收到的有效字节数,也就是数组中的有效字节数)

u8Send_Buffer[210];//用来存放待发送的完整的一帧数据(第一个字节用来存放待发送的有效字节数,也就是数组中的有效字节数)

u8Rcv_Data;//用来存放接收的一个字节

u8Send_Data;//用来存放要发送的一字节

u8Mod_Id;//用来标志作为从站的站号

u8Rcv_Num;//用来表示接收的一帧的有效字节数(从功能码到CRC校验)

u8Send_Num;//用来表示待发送的一帧的字节数

u8*PointToRcvBuf;//用来指向接收的数据缓存

u8*PointToSendBuf;//用来指向带发送的数据缓存

u8Comu_Busy;//用来表示能否接收下一帧数据

u8HaveMes;

externu16HoldReg[100];

 

//CRC校验查表用参数

/*CRC高位字节值表*/

staticu8auchCRCHi[]={

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低位字节值表*/

staticu8auchCRCLo[]={

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

};

//声明modbus的函数

voidModInit(u8Id);//用于Modbus初始化,参数Id为站号(1-255)

voidModRcv(void);//用于modbus信息接收

voidModSend(void);//用于modbus信息接收

voidMessageHandle(u8*pointer_in,u8*pointer_out);//处理收到的信息帧

voidReadOutputBit(u8*pointer_1,u8*pointer_2);//读线圈

voidReadInputBit(u8*pointer_1,u8*pointer_2);//读输入位

voidReadHoldingReg(u8*pointer_1,u8*pointer_2);//读保持寄存器

voidReadInputReg(u8*pointer_1,u8*pointer_2);//读输入寄存器

voidForceSingleCoil(u8*pointer_1,u8*pointer_2);//强制单个线圈

voidPresetSingleReg(u8*pointer_1,u8*pointer_2);//预制单个寄存器

voidForceMulCoil(u8*pointer_1,u8*pointer_2);//强制多个线圈

voidPresetMulReg(u8*pointer_1,u8*pointer_2);//预制多个寄存器

voidErrorHandle(u8Mode,u8*Pointer);//错误信息帧处理

u16CRC16(u8*puchMsgg,u8usDataLen);//用于计算CRC校验码

 

/*函数功能:

用于Modbus初始化

函数输入:

Id为Modbus站号。

函数输出:

无。

*/

voidModInit(u8Id)

{

//modbus参数初始化

PointToRcvBuf=Rcv_Buffer;

PointToSendBuf=Send_Buffer;

Send_Num=1;//发送的数据顺序(输出数组的第几个数)

Mod_Id=Id;//站号设置

Rcv_Buffer[1]=Mod_Id;

Send_Buffer[1]=Mod_Id;

Comu_Busy=0;

}

/*函数功能:

用于Modbus信息接收

函数输入:

无。

函数输出:

无。

*/

voidModRcv(void)

{

HaveMes=1;//表示接收到了信息

Rcv_Data=USART_ReceiveData(USART1);

if(Comu_Busy!

=1)//如果不忙,可以接收下一帧信息

{

TIM_Cmd(TIM2,DISABLE);

TIM_SetCounter(TIM2,0);

if((Tim_Out!

=0)&&(Rcv_Data==Mod_Id))//如果间隔时间超过了3.5个字符,同时接受的字节和自己的站号一致,则认为接收开始

{

Rcv_Complete=0;//表示数据帧接收开始

Rcv_Num=0;//接收数据个数初始化

Rcv_Num++;//同时个数加一

}

if((0==Tim_Out)&&(0==Rcv_Complete))//如果处于接收一帧的正常过程中

{

if(Rcv_Num<100)

{

Rcv_Buffer[Rcv_Num+1]=Rcv_Data;//将数据放入接收数组中

Rcv_Num++;//同时个数加一

}

else

{

Rcv_Complete=1;

Comu_Busy=1;

Rcv_Buffer[0]=Rcv_Num;

*(PointToSendBuf+2)=*(PointToRcvBuf+2);//获取功能码

ErrorHandle(6,PointToSendBuf);//表示超出了字节数(从机设备忙碌)

Rcv_Num=0;

}

}

Tim_Out=0;

TIM_Cmd(TIM2,ENABLE);//开启4.5ms计时(三个半字符的保守估计)

}

}

/*函数功能:

用于Modbus信息发送

函数输入:

无。

函数输出:

无。

*/

voidModSend(void)

{

Send_Data=*(PointToSendBuf+Send_Num);

USART_SendData(USART1,Send_Data);

Send_Num++;

if(Send_Num>(*PointToSendBuf))//发送已经完成

{

Comu_Busy=0;

*PointToSendBuf=0;

Rcv_Num=0;

Send_Num=1;

//启动数据发送

USART_ITConfig(USART1,USART_IT_TC,DISABLE);//关闭数据发送中断

}

}

/*函数功能:

Modbus专用定时器(TIM2)

函数输入:

无。

函数输出:

无。

*/

voidTIM2_IRQHandler(void)

{

if(TIM_GetITStatus(TIM2,TIM_IT_Update)!

=RESET)//检查指定的TIM中断发生与否:

TIM中断源

{

TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除TIMx的中断待处理位:

TIM中断源

Tim_Out=1;

TIM_Cmd(TIM2,DISABLE);

TIM_SetCounter(TIM2,0);

Rcv_Complete=1;

Rcv_Buffer[0]=Rcv_Num;

if(HaveMes!

=0&&Rcv_Num>4)

{

Comu_Busy=1;

MessageHandle(PointToRcvBuf,PointToSendBuf);

}

}

}

 

/*函数功能:

CRC校验用函数

函数输入:

puchMsgg是要进行CRC校验的消息,usDataLen是消息中字节数

函数输出:

计算出来的CRC校验码。

*/

u16CRC16(u8*puchMsgg,u8usDataLen)//puchMsgg是要进行CRC校验的消息,usDataLen是消息中字节数

{

u8uchCRCHi=0xFF;/*高CRC字节初始化*/

u8uchCRCLo=0xFF;/*低CRC字节初始化*/

u8uIndex;/*CRC循环中的索引*/

while(usDataLen--)/*传输消息缓冲区*/

{

uIndex=uchCRCHi^*puchMsgg++;/*计算CRC*/

uchCRCHi=uchCRCLo^auchCRCHi[uIndex];

uchCRCLo=auchCRCLo[uIndex];

}

return((uchCRCHi<<8)|uchCRCLo);

}

 

/*函数功能:

对输入的信息帧进行处理,按照功能码不同,调用不同的函数处理

函数输入:

两个指针,pointer_1指向用来存放输入信息帧的数组,

pointer_2用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)

后面的元素按照Modbus协议组织。

函数输出:

无。

*/

voidMessageHandle(u8*pointer_in,u8*pointer_out)

{

u16CalKey;//计算出来的校验值

u16RcvKey;//接收到的校验值

HaveMes=0;//清除信息位

//获取接收到的校验值

RcvKey=(u16)*(pointer_in+(*pointer_in-1));

RcvKey=RcvKey<<8;

RcvKey=RcvKey|(u16)*(pointer_in+(*pointer_in));

CalKey=CRC16(pointer_in+1,*pointer_in-2);

if(CalKey==RcvKey)

{

switch(*(pointer_in+2))//第二个字节为功能码

{

case0x01:

ReadOutputBit(pointer_in,pointer_out);//读输出线圈

break;

case0x02:

ReadInputBit(pointer_in,pointer_out);//读输入位

break;

case0x03:

ReadHoldingReg(pointer_in,pointer_out);//读保持寄存器

break;

case0x04:

ReadInputReg(pointer_in,pointer_out);//读输入寄存器

break;

case0x05:

ForceSingleCoil(pointer_in,pointer_out);//强制单个线圈状态

break;

case0x06:

PresetSingleReg(pointer_in,pointer_out);//预制单个寄存器

break;

case0x0F:

ForceMulCoil(pointer_in,pointer_out);//强制多个线圈

break;

case0x10:

PresetMulReg(pointer_in,pointer_out);//预制多个寄存器

break;

default:

{

*(pointer_out+2)=*(pointer_in+2);//获取功能码

ErrorHandle(1,pointer_out);//功能码错误

}

break;

}

}

else

{

Comu_Busy=0;

}

}

 

/*函数功能:

读取线圈状态

函数输入:

两个指针,pointer_1指向用来存放输入信息帧的数组,

pointer_2用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)

后面的元素按照Modbus协议组织。

函数输出:

无。

*/

voidReadOutputBit(u8*pointer_1,u8*pointer_2)//pointer_1用作输入,pointer_2用作输出

{

u16Address=0;//待读取线圈起始地址(GPIO_X,X为A,B两个端口,每个端口16位,对应地址0——31)

u16Num=0;//要读取的线圈个数

u8Byte=0;//要读取的线圈个数总共占用的字节数;

u32PortTemp;//用来存

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1