485 Modbus协议程序实例(串口中断接收/发送)

编程入门 行业动态 更新时间:2024-10-14 00:26:57

485 Modbus协议程序实例(<a href=https://www.elefans.com/category/jswz/34/1769224.html style=串口中断接收/发送)"/>

485 Modbus协议程序实例(串口中断接收/发送)

基于串口接收中断与发送中断完成的485通讯实例

优点:逻辑简单,响应快速。

缺点:容易受到单片机本身资源占用以及运行环境干扰的影响出现丢包情况。

对应程序初始化后在主循环运行Uart1_Communicate即可。

使用联合union来将字分解成组成这个对象的各个字节或位。

    typedef union{struct {u8	b0:1;//定义位域长为1u8	b1:1;u8	b2:1;u8	b3:1;u8	b4:1;u8	b5:1;u8	b6:1;u8	b7:1;u8	b8:1;u8	b9:1;u8	b10:1;u8	b11:1;u8	b12:1;u8	b13:1;u8	b14:1;u8	b15:1;} bit;struct{u8	low;	/* low	8 bit */u8	high;	/* high 8 bit */}byte;u16 	word;}word_def;#define TX_MODE			(GPIOB->ODR |= GPIO_Pin_8)#define RX_MODE			(GPIOB->ODR &= ~GPIO_Pin_8)word_def UartStateAdr;#define ReceiveVerifyFlag						UartStateAdr.bit.b3//接收数据需求校验标志位#define NeedReplyFlag						UartStateAdr.bit.b2//接收处理完毕需求回复标志#define ReceiveCompletedFlag				UartStateAdr.bit.b1//接收完毕标志#define ReceiveHeaderFlag					UartStateAdr.bit.b0//数据帧头接收标志typedef struct     //对应存放寄存器数据的数组坐标的结构体{u8 A;u8 B;} ModbusRegs_Coordinate;  ModbusRegs_Coordinate  G_Coordinate;//数组坐标结构体变量(全局)u8 RXmodeFlag;//大循环中持续置位GPIO,避免在其他实践中遇到在中断直接操作GOIO运行后却未置位的情况u8 IDcode=0x02;//Modbus协议从机IDu8 Functioncode;//接收的功能码u8 G_SendByteTable_8u[256];//发送区数组u8 G_BufferReceive_8u[256];//接收缓冲区u8 G_ReceiveByteTable[256];//校验后实际接收数据u8 ReceiveDataTemp;//接收传递变量u16 G_ModbusRegs_16u[6][128];  //定义700个寄存器u16 Read_RegAddress;//读取寄存器起始地址u16 Read_ProcessRegAddress;//读取寄存器过程中地址u16 Read_RegNum;//读取寄存器的个数u16 Write_RegAddress;//写寄存器地址 或 写多个寄存器起始地址u16 Write_ProcessRegAddress;//写寄存器地址 或 写多个寄存器末尾地址u16 Write_RegNum;//写寄存器的个数u16 ReceivePacketState;//接收数据位(无效数据、接收校验完毕清零,接收中按数据长度自增)u16 ReceiveLength=8;//接收数据长度初始化为8u16 SendPacketState;//发送数据位u16 SendLength;//发送数据长度u16 CRC_Check;//根据前面数据计算获得的校验值(与接收到的校验比较验证)u16 SendCRC_Check;//发送数据CRC校验码
/*v将寄存器地址至数组行列坐标转化v*/ModbusRegs_Coordinate RegCoordinateCalcuate(u16 Address){ModbusRegs_Coordinate  Coordinate;Coordinate.A=(Address>>7);//第A+1行Coordinate.B=(Address%128);//第B+1列return (Coordinate);}
/*^将寄存器地址至数组行列坐标转化^*//*vCRC校验计算v*/u16 Crc_cal_value(u8 *data_value,u8 data_length){u16  crc_value=0xFFFF;u8	i,j;for (j=0; j<data_length; j++){crc_value = crc_value ^ *data_value;data_value++;for (i=0; i<8; i++){if(crc_value & 0x0001){crc_value = crc_value >> 1;crc_value = crc_value ^ 0xa001;}else{crc_value = crc_value >> 1;}}}return(crc_value);}	
/*^CRC校验计算^*/void Uart1_IO_Init(void){GPIO_InitTypeDef GPIO_InitStruct;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_8;					//485使能端口GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_13;				//LEDGPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOC,&GPIO_InitStruct);//设置复用功能的类型GPIO_PinAFConfig(GPIOB,GPIO_Pin_6,GPIO_AF_0);GPIO_PinAFConfig(GPIOB,GPIO_Pin_7,GPIO_AF_0);//复用开漏输出GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;GPIO_Init(GPIOB,&GPIO_InitStruct);//复用输入上拉GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOB,&GPIO_InitStruct);RX_MODE;}void Uart1_Init(void){NVIC_InitTypeDef NVIC_InitStructure;//启动外围时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//8倍过采样模式USART1->CR1 = (1<<15);//关闭溢出检测USART1->CR3 = (1<<12);//波特率的计算// BORD = Fpclk/(8*(2 - OVER8)*DIV)//目标波特率 9600 DIV = 625//2400 DIV = 2500USART1->BRR = (52<<4);//使能串口,清空TC标志USART1->CR1 |=(1<<0);						// 使能串口USART1->CR1 |=(1<<3)+(1<<2);				// 允许接收和发送USART1->ICR |=(1<<6);						// 清除发送完成标志/* Enable the USARTx Interrupt */NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPriority = 0x03;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);USART_ITConfig(USART1, USART_IT_TC, ENABLE);}void Uart1_Communicate(void){u8 Cycle;//缓冲区传送确认区的过程变量u8 a,b;//a用于写寄存器的过程变量,b用于读寄存器的过程变量u8 timecnt;//RX_Mode转TX_Mode的延时(必须有延时,否则当485芯片高阻态建立较慢时通讯数据会紊乱)/*v485状态再置位v*/if(RXmodeFlag)RX_MODE;//避免在中断中运行GPIOB->ODR &= ~GPIO_Pin_8后不置位的情况/*v485状态再置位v*/	/*v数据校验v*/if(ReceiveVerifyFlag){CRC_Check=Crc_cal_value(G_BufferReceive_8u,ReceiveLength-2);if( CRC_Check == (G_BufferReceive_8u[ReceiveLength-2]+G_BufferReceive_8u[ReceiveLength-1]*256) )//校验正确{for(Cycle = 0; Cycle < ReceiveLength; Cycle++){G_ReceiveByteTable[Cycle] = G_BufferReceive_8u[Cycle];}ReceiveCompletedFlag=1;}ReceivePacketState=0;ReceiveVerifyFlag=0;}/*^数据校验^*//*v接收数据处理v*/if(ReceiveCompletedFlag){switch(Functioncode){case 0x03://读{Read_RegAddress=G_BufferReceive_8u[2]*256+G_BufferReceive_8u[3];Read_RegNum=G_BufferReceive_8u[4]*256+G_BufferReceive_8u[5];//读数据赋值发送缓存数组在发送处理中break;}case 0x04://读{Read_RegAddress=G_BufferReceive_8u[2]*256+G_BufferReceive_8u[3];Read_RegNum=G_BufferReceive_8u[4]*256+G_BufferReceive_8u[5];//读数据赋值发送缓存数组在发送处理中break;}case 0x06://写单个{Write_RegAddress=G_BufferReceive_8u[2]*256+G_BufferReceive_8u[3];Write_RegNum=1;G_Coordinate = RegCoordinateCalcuate(Write_RegAddress);G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]=G_BufferReceive_8u[4]*256+G_BufferReceive_8u[5] ;break;}case 0x10://写n个(起始地址与寄存器数量在中断中已经计算){Write_ProcessRegAddress=Write_RegAddress;for(a=0;a<Write_RegNum;a++){G_Coordinate = RegCoordinateCalcuate(Write_ProcessRegAddress);G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]=G_BufferReceive_8u[a*2+7]*256+G_BufferReceive_8u[a*2+8] ;Write_ProcessRegAddress++;}				break;}default :{break;}}NeedReplyFlag=1;ReceiveCompletedFlag=0;}/*^接收数据处理^*//*v发送处理v*/if(NeedReplyFlag){RXmodeFlag=0;switch(Functioncode){case 0x03://读的回复{SendLength=Read_RegNum*2+5;SendPacketState=1;Read_ProcessRegAddress = Read_RegAddress;G_SendByteTable_8u[0]=IDcode;G_SendByteTable_8u[1]=0x03;G_SendByteTable_8u[2]=(u8)(Read_RegNum*2);for(b=0;b<Read_RegNum;b++){G_Coordinate = RegCoordinateCalcuate(Read_ProcessRegAddress);G_SendByteTable_8u[b*2+3]=(u8)(G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]>>8);G_SendByteTable_8u[b*2+4]=(u8)G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B];Read_ProcessRegAddress++;}	SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,SendLength-2);G_SendByteTable_8u[SendLength-2]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位G_SendByteTable_8u[SendLength-1]=(u8)(SendCRC_Check>>8);/*-------------------------------------------------------------------------------*/TX_MODE;for(timecnt=0;timecnt<60;timecnt++);//模式转换延时/*-------------------------------------------------------------------------------*/USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);break;}case 0x04://读的回复{SendLength=Read_RegNum*2+5;SendPacketState=1;Read_ProcessRegAddress = Read_RegAddress;G_SendByteTable_8u[0]=IDcode;G_SendByteTable_8u[1]=0x04;G_SendByteTable_8u[2]=(u8)(Read_RegNum*2);for(b=0;b<Read_RegNum;b++){G_Coordinate = RegCoordinateCalcuate(Read_ProcessRegAddress);G_SendByteTable_8u[b*2+3]=(u8)(G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]>>8);G_SendByteTable_8u[b*2+4]=(u8)G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B];Read_ProcessRegAddress++;}	SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,SendLength-2);G_SendByteTable_8u[SendLength-2]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位G_SendByteTable_8u[SendLength-1]=(u8)(SendCRC_Check>>8);/*-------------------------------------------------------------------------------*/TX_MODE;for(timecnt=0;timecnt<60;timecnt++);//模式转换延时/*-------------------------------------------------------------------------------*/USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);break;}case 0x06://写单个的回复{SendLength=8;SendPacketState=1;for(b=0;b<SendLength;b++)G_SendByteTable_8u[b] = G_ReceiveByteTable[b];//06的回复数据与接收数据相同/*-------------------------------------------------------------------------------*/TX_MODE;for(timecnt=0;timecnt<60;timecnt++);//模式转换延时/*-------------------------------------------------------------------------------*/USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);break;}case 0x10://写n个(起始地址与寄存器数量在中断中已经计算)的回复{SendLength=8;SendPacketState=1;for(b=0;b<6;b++)G_SendByteTable_8u[b] = G_ReceiveByteTable[b];//06的回复数据与接收数据相同SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,6);G_SendByteTable_8u[6]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位G_SendByteTable_8u[7]=(u8)(SendCRC_Check>>8);/*-------------------------------------------------------------------------------*/TX_MODE;for(timecnt=0;timecnt<60;timecnt++);//模式转换延时/*-------------------------------------------------------------------------------*/USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);break;}default :{break;}}NeedReplyFlag=0;}/*^发送处理^*/}void USART1_IRQHandler(void){/*v发送v*/if((USART1->ISR & USART_ISR_TC) == USART_ISR_TC){USART1->ICR = USART_ISR_TC;if(SendPacketState<SendLength){USART1->TDR = (G_SendByteTable_8u[SendPacketState] & (uint16_t)0x00FF);	SendPacketState ++;}else {RX_MODE;RXmodeFlag=1;}}		/*^发送^*//*v接收处理v*/if((USART1->ISR & USART_ISR_RXNE) == USART_ISR_RXNE){USART1->ICR = USART_ISR_RXNE;ReceiveDataTemp = (USART1->RDR&0x00FF);//取接收的数据if(ReceiveDataTemp == IDcode&&ReceiveVerifyFlag==0&&ReceivePacketState==0)// 找到帧头且无需求校验的数据条且接收位为头位{		ReceiveHeaderFlag = 1;G_BufferReceive_8u[ReceivePacketState] = ReceiveDataTemp;ReceivePacketState++;}else if(ReceiveHeaderFlag){if(ReceivePacketState==1)//接收第二位功能码识别{if(ReceiveDataTemp==0x03||ReceiveDataTemp==0x04||ReceiveDataTemp==0x06||ReceiveDataTemp==0x10)//若为四个有效功能码之一{Functioncode=ReceiveDataTemp;G_BufferReceive_8u[ReceivePacketState] = Functioncode;ReceivePacketState++;}	else//为无效功能码{ReceivePacketState=0;ReceiveHeaderFlag=0;//本帧头无效,等待下一个帧头}	}else if(ReceivePacketState<ReceiveLength)//接收位其他位{G_BufferReceive_8u[ReceivePacketState] = ReceiveDataTemp;ReceivePacketState++;if(Functioncode==0x03||Functioncode==0x04||Functioncode==0x06)//计算接收字节长度ReceiveLength=8;if(Functioncode==0x10&&ReceivePacketState==7){Write_RegAddress=G_BufferReceive_8u[2]*256+G_BufferReceive_8u[3];Write_RegNum=G_BufferReceive_8u[4]*256+G_BufferReceive_8u[5];if(Write_RegNum*2!=G_BufferReceive_8u[6]) {ReceivePacketState=0;ReceiveHeaderFlag=0;//若字节数与寄存器数不对应则判定本次不合法}	else ReceiveLength=Write_RegNum*2+9;}	if(ReceivePacketState==ReceiveLength)ReceiveVerifyFlag=1;//暂时判定接收合法进入主循环函数判断校验合法性(由于校验消耗时间不放在中断中进行)}}}		/*^接收处理^*/		}

更多推荐

485 Modbus协议程序实例(串口中断接收/发送)

本文发布于:2024-03-23 22:43:10,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1743701.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:串口   实例   协议   程序   Modbus

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!