《用stm32库函数编写的modbus源代码.pdf》由会员分享,可在线阅读,更多相关《用stm32库函数编写的modbus源代码.pdf(38页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、用 stm32 库函数编写的 modbus 源代码 说在前面的话:1.请勿盲目抄袭。这个协议使用了一个定时器,所以在别处请不要再使用,如果定时器不够用,可以做虚拟定时器。也就是采用一个物理的定时器产生时基。在这个定时器的中断函数中可以给相应的多个定时器自加 1.每个虚拟定时器可以用两个变量分别控制打开关闭,和计时。这个已经试验通过了可行的。其实就跟我们使用物理的定时器一样,只不过物理的定时器是用晶振产生时基。2.这段代码已经调试通过了,也硬件试验过,没有问题,如果你出现问题了,看看你在主函数的的各种基本配置有没有完成。如果要使用 06 和10 号功能,你还需要在主函数中建立一个 100 个元素
2、的数组,每个元素是 16位。3.写这个文档的时候,这个协议已经是半年前完成的了。所以有些东西记得不是很清楚了,如果说错了,请以实际为准。只是不想让这份代码死在电脑中了,所以才想起来要拿出来分享,支持开源精神。4.如果实在实在是没有弄出来,请联系我,可以共同交流,我的邮箱:*#includestm32f10 x.h/*此 Modbus 协议暂时只支持 RTU 模式,只支持作为 Modbus 从设备。暂时支持的功能码(16 进制)如下表所示:01.读线圈状态(读多个输出位的状态,有效地位为0-31)02.读输入位状态(读多个输入位的状态,有效地位为0-31)03.读保持寄存器(读多个保持寄存器的数
3、值,有效地位为0-99)04.读输入寄存器(读多个输入寄存器的数值,有效地址为0-1)05.强制单个线圈(强制单个输出位的状态,有效地位为0-31)06.预制单个寄存器(设定一个寄存器的数值,有效地址为0-99)0F.强制多个线圈(强制多个输出位的状态,有效地址为0-31)10.预制多个寄存器(设定多个寄存器的数值,有效地址为0-99)暂时支持的错误代码为:01 不合法功能代码从机接收的是一种不能执行功能代码。发出查询命令后,该代码指示无程序功能。(不支持的功能代码)02 不合法数据地址接收的数据地址,是从机不允许的地址。(起始地址不在有效范围内)03 不合法数据查询数据区的值是从机不允许的值
4、。(在起始地址的基础上,这个数量是不合法的)供用户调用的函数有:1.void ModInit(u8 Id);/用于 Modbus 初始化,在函数调用前,必须初始化函数,用于 Main 函数中 2.void ModRcv(void);/用于 modbus 信息接收,放在串口接收中断 3.void ModSend(void);/用于 modbus 信息接收,放在串口发送中断 例如:void USART1_IRQHandler(void)/USART1 中断 if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)void ModRcv(void);if(
5、USART_GetITStatus(USART1,USART_IT_TC)!=RESET)void ModSend(void);/用于 modbus 信息接收 */modbus 用通讯参数 u8 Tim_Out;/大于 3.5 个字符时间,保守取 3ms(波特率 9600 的时候大约 2 点几毫秒)u8 Rcv_Complete;/一帧是否已经接受完成 u8 Send_Complete;/一帧是否已经发送完成 u8 Com_busy;/通讯繁忙,表示上一帧还未处理结束 u8 Rcv_Buffer210;/用来存放接收到的完整的一帧数据(第一个字节用来存放接收到的有效字节数,也就是数组中的有效字
6、节数)u8 Send_Buffer210;/用来存放待发送的完整的一帧数据(第一个字节用来存放待发送的有效字节数,也就是数组中的有效字节数)u8 Rcv_Data;/用来存放接收的一个字节 u8 Send_Data;/用来存放要发送的一字节 u8 Mod_Id;/用来标志作为从站的站号 u8 Rcv_Num;/用来表示接收的一帧的有效字节数(从功能码到 CRC 校验)u8 Send_Num;/用来表示待发送的一帧的字节数 u8*PointToRcvBuf;/用来指向接收的数据缓存 u8*PointToSendBuf;/用来指向带发送的数据缓存 u8 Comu_Busy;/用来表示能否接收下一帧
7、数据 u8 HaveMes;extern u16 HoldReg100;/CRC 校验查表用参数/*CRC 高位字节值表*/static u8 auchCRCHi=0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0
8、x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0
9、x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0
10、x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0
11、x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0
12、x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40 ;/*CRC 低位字节值表*/static u8 auchCRCLo=0 x00,0 xC0,0 xC1,0 x01,0 xC3,0 x03,0 x02,0 xC2,0 xC6,0 x06,0 x07,0 xC7,0 x05,0 xC5,0 xC4,0 x04,0 xCC,0 x0C,0 x0D,0 xCD,0 x0F,0 xCF,0 xCE,0 x0E,0 x
13、0A,0 xCA,0 xCB,0 x0B,0 xC9,0 x09,0 x08,0 xC8,0 xD8,0 x18,0 x19,0 xD9,0 x1B,0 xDB,0 xDA,0 x1A,0 x1E,0 xDE,0 xDF,0 x1F,0 xDD,0 x1D,0 x1C,0 xDC,0 x14,0 xD4,0 xD5,0 x15,0 xD7,0 x17,0 x16,0 xD6,0 xD2,0 x12,0 x13,0 xD3,0 x11,0 xD1,0 xD0,0 x10,0 xF0,0 x30,0 x31,0 xF1,0 x33,0 xF3,0 xF2,0 x32,0 x36,0 xF6,0 x
14、F7,0 x37,0 xF5,0 x35,0 x34,0 xF4,0 x3C,0 xFC,0 xFD,0 x3D,0 xFF,0 x3F,0 x3E,0 xFE,0 xFA,0 x3A,0 x3B,0 xFB,0 x39,0 xF9,0 xF8,0 x38,0 x28,0 xE8,0 xE9,0 x29,0 xEB,0 x2B,0 x2A,0 xEA,0 xEE,0 x2E,0 x2F,0 xEF,0 x2D,0 xED,0 xEC,0 x2C,0 xE4,0 x24,0 x25,0 xE5,0 x27,0 xE7,0 xE6,0 x26,0 x22,0 xE2,0 xE3,0 x23,0 x
15、E1,0 x21,0 x20,0 xE0,0 xA0,0 x60,0 x61,0 xA1,0 x63,0 xA3,0 xA2,0 x62,0 x66,0 xA6,0 xA7,0 x67,0 xA5,0 x65,0 x64,0 xA4,0 x6C,0 xAC,0 xAD,0 x6D,0 xAF,0 x6F,0 x6E,0 xAE,0 xAA,0 x6A,0 x6B,0 xAB,0 x69,0 xA9,0 xA8,0 x68,0 x78,0 xB8,0 xB9,0 x79,0 xBB,0 x7B,0 x7A,0 xBA,0 xBE,0 x7E,0 x7F,0 xBF,0 x7D,0 xBD,0 x
16、BC,0 x7C,0 xB4,0 x74,0 x75,0 xB5,0 x77,0 xB7,0 xB6,0 x76,0 x72,0 xB2,0 xB3,0 x73,0 xB1,0 x71,0 x70,0 xB0,0 x50,0 x90,0 x91,0 x51,0 x93,0 x53,0 x52,0 x92,0 x96,0 x56,0 x57,0 x97,0 x55,0 x95,0 x94,0 x54,0 x9C,0 x5C,0 x5D,0 x9D,0 x5F,0 x9F,0 x9E,0 x5E,0 x5A,0 x9A,0 x9B,0 x5B,0 x99,0 x59,0 x58,0 x98,0 x
17、88,0 x48,0 x49,0 x89,0 x4B,0 x8B,0 x8A,0 x4A,0 x4E,0 x8E,0 x8F,0 x4F,0 x8D,0 x4D,0 x4C,0 x8C,0 x44,0 x84,0 x85,0 x45,0 x87,0 x47,0 x46,0 x86,0 x82,0 x42,0 x43,0 x83,0 x41,0 x81,0 x80,0 x40 ;/声明 modbus 的函数 void ModInit(u8 Id);/用于 Modbus 初始化,参数 Id 为站号(1-255)void ModRcv(void);/用于 modbus 信息接收 void ModSe
18、nd(void);/用于 modbus 信息接收 void MessageHandle(u8*pointer_in,u8*pointer_out);/处理收到的信息帧 void ReadOutputBit(u8*pointer_1,u8*pointer_2);/读线圈 void ReadInputBit(u8*pointer_1,u8*pointer_2);/读输入位 void ReadHoldingReg(u8*pointer_1,u8*pointer_2);/读保持寄存器 void ReadInputReg(u8*pointer_1,u8*pointer_2);/读输入寄存器 void F
19、orceSingleCoil(u8*pointer_1,u8*pointer_2);/强制单个线圈 void PresetSingleReg(u8*pointer_1,u8*pointer_2);/预制单个寄存器 void ForceMulCoil(u8*pointer_1,u8*pointer_2);/强制多个线圈 void PresetMulReg(u8*pointer_1,u8*pointer_2);/预制多个寄存器 void ErrorHandle(u8 Mode,u8*Pointer);/错误信息帧处理 u16 CRC16(u8*puchMsgg,u8 usDataLen);/用于计
20、算 CRC 校验码 /*函数功能:用于 Modbus 初始化 函数输入:Id 为 Modbus 站号。函数输出:无。*/void ModInit(u8 Id)/modbus 参数初始化 PointToRcvBuf=Rcv_Buffer;PointToSendBuf=Send_Buffer;Send_Num=1;/发送的数据顺序(输出数组的第几个数)Mod_Id=Id;/站号设置 Rcv_Buffer1=Mod_Id;Send_Buffer1=Mod_Id;Comu_Busy=0;/*函数功能:用于 Modbus 信息接收 函数输入:无。函数输出:无。*/void ModRcv(void)Hav
21、eMes=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)/如
22、果处于接收一帧的正常过程中 if(Rcv_Num(*PointToSendBuf)/发送已经完成 Comu_Busy=0;*PointToSendBuf=0;Rcv_Num=0;Send_Num=1;/启动数据发送 USART_ITConfig(USART1,USART_IT_TC,DISABLE);/关闭数据发送中断 /*函数功能:Modbus 专用定时器(TIM2)函数输入:无。函数输出:无。*/void TIM2_IRQHandler(void)if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)/检查指定的 TIM 中断发生与否:TIM 中断源
23、TIM_ClearITPendingBit(TIM2,TIM_IT_Update );/清除 TIMx 的中断待处理位:TIM 中断源 Tim_Out=1;TIM_Cmd(TIM2,DISABLE);TIM_SetCounter(TIM2,0);Rcv_Complete=1;Rcv_Buffer0=Rcv_Num;if(HaveMes!=0&Rcv_Num4)Comu_Busy=1;MessageHandle(PointToRcvBuf,PointToSendBuf);/*函数功能:CRC 校验用函数 函数输入:puchMsgg 是要进行 CRC 校验的消息,usDataLen 是消息中字节数
24、 函数输出:计算出来的CRC校验码。*/u16 CRC16(u8*puchMsgg,u8 usDataLen)/puchMsgg 是要进行 CRC 校验的消息,usDataLen 是消息中字节数 u8 uchCRCHi=0 xFF;/*高 CRC 字节初始化*/u8 uchCRCLo=0 xFF;/*低 CRC 字节初始化*/u8 uIndex;/*CRC 循环中的索引*/while(usDataLen-)/*传输消息缓冲区*/uIndex=uchCRCHi *puchMsgg+;/*计算 CRC*/uchCRCHi=uchCRCLo auchCRCHiuIndex;uchCRCLo=auch
25、CRCLouIndex;return(uchCRCHi 8)|uchCRCLo);/*函数功能:对输入的信息帧进行处理,按照功能码不同,调用不同的函数处理 函数输入:两个指针,pointer_1 指向用来存放输入信息帧的数组,pointer_2 用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组织。函数输出:无。*/void MessageHandle(u8*pointer_in,u8*pointer_out)u16 CalKey;/计算出来的校验值 u16 RcvKey;/接收到的校验值 HaveMes=0;/清除信息位 /
26、获取接收到的校验值 RcvKey=(u16)*(pointer_in+(*pointer_in-1);RcvKey=RcvKey8;RcvKey=RcvKey|(u16)*(pointer_in+(*pointer_in);CalKey=CRC16(pointer_in+1,*pointer_in-2);if(CalKey=RcvKey)switch(*(pointer_in+2)/第二个字节为功能码 case 0 x01:ReadOutputBit(pointer_in,pointer_out);/读输出线圈 break;case 0 x02:ReadInputBit(pointer_in,
27、pointer_out);/读输入位 break;case 0 x03:ReadHoldingReg(pointer_in,pointer_out);/读保持寄存器 break;case 0 x04:ReadInputReg(pointer_in,pointer_out);/读输入寄存器 break;case 0 x05:ForceSingleCoil(pointer_in,pointer_out);/强制单个线圈状态 break;case 0 x06:PresetSingleReg(pointer_in,pointer_out);/预制单个寄存器 break;case 0 x0F:Force
28、MulCoil(pointer_in,pointer_out);/强制多个线圈 break;case 0 x10: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 用来指向存放输出信息帧的数组(两个数组的第一个元素都用
29、来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组织。函数输出:无。*/void ReadOutputBit(u8*pointer_1,u8*pointer_2)/pointer_1 用作输入,pointer_2用作输出 u16 Address=0;/待读取线圈起始地址(GPIO_X,X 为 A,B 两个端口,每个端口 16 位,对应地址 031)u16 Num=0;/要读取的线圈个数 u8 Byte=0;/要读取的线圈个数总共占用的字节数;u32 PortTemp;/用来存放从端口取过来的数据 u16 ReadData=0;/用来临时存放从端口读来的数据 u16 SendKey
30、;/要发送数据的校验值 Address=(u16)(*(pointer_1+3)*256+(*(pointer_1+4);/先得到线圈地址 Num=(u16)(*(pointer_1+5)*256+(*(pointer_1+6);/先得到要读取的线圈个数 *(pointer_2+2)=0 x01;/第三个字节为功能码 if(*(pointer_1)=8)/如果接收到的字节数不是 8 个,就是一个错误帧 if(Address32)/只要地址小于 32,就是合法地址 if(Address+Num0)/只要地址加数量大于 0 小于 80,就是合法数量 /用于 for 循环 u8 i;u8 j;Byt
31、e=Num/8;if(Num%8!=0)Byte+;/如果不整除的话,加一个字节,剩余的高位补零 *(pointer_2+3)=Byte;/第四个字节为要发送的个数 *(pointer_2)=1+1+1+Byte+2;/有效字节个数等于丛机地址+功能码+字节个数+线圈信息+CRC 校验 /将端口 C 和 D 的数据预先读入到临时的数据缓存中 ReadData=GPIO_ReadOutputData(GPIOD);PortTemp=(u32)(ReadData);PortTemp=PortTemp16;ReadData=GPIO_ReadOutputData(GPIOC);PortTemp=Po
32、rtTemp|(u32)(ReadData);/将 PortTemp 中的数据处理成需要的位数,再装入输出数据缓存中 PortTemp=PortTemp(31-(Address+Num-1)+Address);/将数据一个字节一个字节装到发送数据缓存数组中 for(i=4,j=Byte;j0;j-,i+)*(pointer_2+i)=(u8)(PortTemp&0 x00FF);/截取一个字节的长度,存放起来 PortTemp=PortTemp8;/再将数据向右移动 8 位 /写入校验码 SendKey=CRC16(pointer_2+1,*pointer_2-2);/将计算出来的校验码装入输
33、出数据缓存中 *(pointer_2+(*pointer_2-1)=(u8)(SendKey8);*(pointer_2+(*pointer_2)=(u8)(SendKey&0 x00FF);/启动数据发送 USART_ITConfig(USART1,USART_IT_TC,ENABLE);/开启数据发送中断 else ErrorHandle(3,pointer_2);/错误读取数量 else ErrorHandle(2,pointer_2);/错误起始地址 else Comu_Busy=0;/*函数功能:读取输入状态 函数输入:两个指针,pointer_1 指向用来存放输入信息帧的数组,po
34、inter_2 用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组织。函数输出:无。*/void ReadInputBit(u8*pointer_1,u8*pointer_2)/pointer_1 用作输入,pointer_2用作输出 u16 Address=0;/待读取输入位起始地址(GPIO_X,X 为 C,D 两个端口,每个端口 16 位,对应地址 0-31)u16 Num=0;/要读取的输入位个数 u8 Byte=0;/要读取的输入位个数总共占用的字节数;u32 PortTemp;/用来存放从端口取过来的数据,临时计算
35、用 u16 ReadData=0;/用来临时存放从端口读来的数据 u16 SendKey;/要发送数据的校验值 Address=(u16)(*(pointer_1+3)*256+(*(pointer_1+4);/先得到输入位地址 Num=(u16)(*(pointer_1+5)*256+(*(pointer_1+6);/先得到要读取的输入位个数 *(pointer_2+2)=0 x02;/第三个字节为功能码 if(*(pointer_1)=8)/如果接收到的字节数不是 8 个,就是一个错误帧 if(Address32)/只要地址小于 32,就是合法地址 if(Address+Num0)/只要地
36、址加数量大于 0 小于 80,就是合法数量 /用于 for 循环 u8 i;u8 j;Byte=Num/8;if(Num%8!=0)Byte+;/如果不整除的话,加一个字节,剩余的高位补零 *(pointer_2+3)=Byte;/第四个字节为要发送的个数 *(pointer_2)=1+1+1+Byte+2;/有效字节个数等于丛机地址+功能码+字节个数+输入位信息+CRC 校验 /将端口 A 和 B 的数据预先读入到临时的数据缓存中 ReadData=GPIO_ReadInputData(GPIOB);PortTemp=(u32)(ReadData);PortTemp=PortTemp16;R
37、eadData=GPIO_ReadInputData(GPIOA);PortTemp=PortTemp|(u32)(ReadData);/将 PortTemp 中的数据处理成需要的位数,再装入输出数据缓存中 PortTemp=PortTemp(31-(Address+Num-1)+Address);/将数据一个字节一个字节装到发送数据缓存数组中 for(i=4,j=Byte;j0;j-,i+)*(pointer_2+i)=(u8)(PortTemp&0 x00FF);/截取一个字节的长度,存放起来 PortTemp=PortTemp8;/再将数据向右移动 8 位 /写入校验码 SendKey=
38、CRC16(pointer_2+1,*pointer_2-2);/将计算出来的校验码装入输出数据缓存中 *(pointer_2+(*pointer_2-1)=(u8)(SendKey8);*(pointer_2+(*pointer_2)=(u8)(SendKey&0 x00FF);/启动数据发送 USART_ITConfig(USART1,USART_IT_TC,ENABLE);/开启数据发送中断 else ErrorHandle(3,pointer_2);/错误读取数量 else ErrorHandle(2,pointer_2);/错误起始地址 else Comu_Busy=0;/*函数功能
39、:读取保持寄存器 函数输入:两个指针,pointer_1 指向用来存放输入信息帧的数组,pointer_2 用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组织。函数输出:无。*/void ReadHoldingReg(u8*pointer_1,u8*pointer_2)/pointer_1 用作输入,pointer_2用作输出 u16 Address=0;/待读取寄存器起始地址(HoldRegi,i 为 0-99 对应地址从 0到 99)u16 Num=0;/要读取的寄存器个数 u16 SendKey;/要发送数据的校验值
40、Address=(u16)(*(pointer_1+3)*256+(*(pointer_1+4);/先得到寄存器起始地址 Num=(u16)(*(pointer_1+5)*256+(*(pointer_1+6);/先得到要读取的寄存器个数 *(pointer_2+2)=0 x03;/第三个字节为功能码 if(*(pointer_1)=8)/如果接收到的字节数不是 8 个,就是一个错误帧 if(Address100)/只要地址小于 100,就是合法地址 if(Address+Num0)/只要地址加数量大于 0 小于 100,就是合法数量 /用于 for 循环 u8 i;u8 j;*(pointe
41、r_2+3)=Num*2;/第四个字节为要发送的字节个数 *(pointer_2)=1+1+1+Num*2+2;/有效字节个数等于丛机地址+功能码+字节个数+寄存器信息+CRC 校验 for(i=Address,j=4;i8);/先放高位 *(pointer_2+j+1)=(u8)(HoldRegi&0 x00FF);/再放低位 /写入校验码 SendKey=CRC16(pointer_2+1,*pointer_2-2);/将计算出来的校验码装入输出数据缓存中 *(pointer_2+(*pointer_2-1)=(u8)(SendKey8);*(pointer_2+(*pointer_2)=
42、(u8)(SendKey&0 x00FF);/启动数据发送 USART_ITConfig(USART1,USART_IT_TC,ENABLE);/开启数据发送中断 else ErrorHandle(3,pointer_2);/错误读取数量 else ErrorHandle(2,pointer_2);/错误起始地址 else Comu_Busy=0;/*函数功能:读取输入寄存器(模拟量输入)函数输入:两个指针,pointer_1 指向用来存放输入信息帧的数组,pointer_2 用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组
43、织。函数输出:无。*/void ReadInputReg(u8*pointer_1,u8*pointer_2)/pointer_1 用作输入,pointer_2用作输出 u16 Address=0;/待读取寄存器起始地址(HoldRegi,i 为 0-99 对应地址从 0到 99)u16 Num=0;/要读取的寄存器个数 u16 SendKey;/要发送数据的校验值 u32 PortTemp;/用来存放从端口取过来的数据,临时计算用 u16 ReadData=0;/用来临时存放从端口读来的数据 u32 CalTemp=0;/用来临时计算 Address=(u16)(*(pointer_1+3)
44、*256+(*(pointer_1+4);/先得到寄存器起始地址 Num=(u16)(*(pointer_1+5)*256+(*(pointer_1+6);/先得到要读取的寄存器个数 *(pointer_2+2)=0 x04;/第三个字节为功能码 if(*(pointer_1)=8)/如果接收到的字节数不是 8 个,就是一个错误帧 if(Address2)/只要地址小于 2,就是合法地址 if(Address+Num0)/只要地址加数量大于 0 小于 2,就是合法数量 /用于 for 循环 u8 i;u8 j;*(pointer_2+3)=Num*2;/第四个字节为要发送的字节个数 *(poi
45、nter_2)=1+1+1+Num*2+2;/有效字节个数等于丛机地址+功能码+字节个数+寄存器信息+CRC 校验 /将端口 ADC1 和 ADC2 的数据预先读入到临时的数据缓存中 ReadData=ADC_GetConversionValue(ADC2);PortTemp=(u32)(ReadData);PortTemp=PortTemp16;ReadData=ADC_GetConversionValue(ADC1);PortTemp=PortTemp|(u32)(ReadData);/将 PortTemp 中的数据先进行预处理 PortTemp=PortTemp0;i+,j-=2)Cal
46、Temp=(u16)(PortTemp8);/先放高位 *(pointer_2+i+1)=(u8)(CalTemp&0 x00FF);/再放低位 /写入校验码 SendKey=CRC16(pointer_2+1,*pointer_2-2);/将计算出来的校验码装入输出数据缓存中 *(pointer_2+(*pointer_2-1)=(u8)(SendKey8);*(pointer_2+(*pointer_2)=(u8)(SendKey&0 x00FF);/启动数据发送 USART_ITConfig(USART1,USART_IT_TC,ENABLE);/开启数据发送中断 else ErrorH
47、andle(3,pointer_2);/错误读取数量 else ErrorHandle(2,pointer_2);/错误起始地址 else Comu_Busy=0;/*函数功能:强制单个线圈状态 函数输入:两个指针,pointer_1 指向用来存放输入信息帧的数组,pointer_2 用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照 Modbus 协议组织。函数输出:无。*/void ForceSingleCoil(u8*pointer_1,u8*pointer_2)/pointer_1 用作输入,pointer_2用作输出 u16 Addres
48、s=0;/待强制线圈起始地址(GPIO_X,X 为 A,B 两个端口,每个端口 16 位,对应地址 031)u16 Command=0;/命令为 0 xFF00 或者 0 x0000,0 xFF00 强制线圈 ON,0 x0000强制线圈 OFF;u32 PortTemp;/用来存放从端口取过来的数据 u32 CalTemp;/临时计算用 u16 ReadData=0;/用来临时存放从端口读来的数据 u16 SendKey;/要发送数据的校验值 Address=(u16)(*(pointer_1+3)*256+(*(pointer_1+4);/先得到线圈地址 Command=(u16)(*(p
49、ointer_1+5)*256+(*(pointer_1+6);/先得到命令值 *(pointer_2+2)=0 x05;/第三个字节为功能码 if(*(pointer_1)=8)/如果接收到的字节数不是 8 个,就是一个错误帧 if(Address32)/只要地址小于 32,就是合法地址 *(pointer_2)=1+1+2+2+2;/有效字节个数等于丛机地址+功能码+线圈地址+线圈命令+CRC 校验 *(pointer_2+3)=*(pointer_1+3);/将地址值写入输出的寄存器中 *(pointer_2+4)=*(pointer_1+4);/将端口 C 和 D 的数据预先读入到临时
50、的数据缓存中 ReadData=GPIO_ReadOutputData(GPIOD);PortTemp=(u32)(ReadData);PortTemp=PortTemp16;ReadData=GPIO_ReadOutputData(GPIOC);PortTemp=PortTemp|(u32)(ReadData);if(Command=0 xFF00|Command=0 x0000)/将 PortTemp 中的数据处理成需要的位数,再装入输出数据缓存中 switch(Command)case 0 xFF00:/将需要的一位置一 CalTemp=0 x00000001Address;PortTe