[N32L43X评测] 6.USART实现ModbusRTU从站
[复制链接]
MODBUS协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络、总线可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为MODBUS Master,从设备方使用的协议称为MODBUS Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。MODBUS通讯物理接口可以选用串口(包括RS232、RS485和RS422),也可以选择以太网口
通信遵循以下的过程:
主设备向从设备发送请求
从设备分析并处理主设备的请求,然后向主设备发送结果
如果出现任何差错,从设备将返回一个异常功能码
MODBUS的工作方式是请求/应答,每次通讯都是主站先发送指令,可以是广播,或是向特定从站的单播;从站响应指令,并按要求应答,或者报告异常。当主站不发送请求时,从站不会自己发出数据,从站和从站之间不能直接通讯
MODBUS有三种通信方式:
以太网:对应的通信模式是MODBUS TCP/IP
异步串行传输(各种介质如有线RS-232-/422/485/;光纤、无线等):对应的通信模式是MODBUS RTU或MODBUS ASCII
高速令牌传递网络:对应的通信模式是MODBUS PLUS
MODBUS协议的报文(或帧)的基本格式是:表头 + 功能码 + 数据区 + 校验码
功能码和数据区在不同类型的网络都是固定不变的,表头和校验码则因网络底层的实现方式不同而有所区别。表头包含了从站的地址,功能码告诉从站要执行何种功能,数据区是具体的信息
对于不同类型的网络,MODBUS的协议层实现是一样的,区别在于下层的实现方式,常见的有TCP/IP和串行通讯两种
MODBUS TCP基于以太网和TCP/IP协议,MODBUS RTU和MODBUS ASCII则是使用异步串行传输(通常是RS-232/422/485)
在工业控制领域,工业仪表间的通信比较常用的是基于MODBUS RTU的485口通信,MODBUS RTU协议需要用时间间隔来判断一帧报文的开始和结束,协议规定的时间为3.5个字符周期,就是说一帧报文开始前,必须有大于3.5个字符周期的空闲时间,一帧报文结束后,也必须要有3.5个字符周期的空闲时间;同时一帧报文中,字符间空闲时间大于1.5字符周期
针对3.5个字符周期,其实是一个具体时间,但是这个时间跟波特率相关。在串口通信中,1个字符包括1位起始位、8位数据位(一般情况)、1位校验位(或者没有)、1位停止位(一般情况下),因此1个字符包括11个位,那么3.5个字符就是38.5个位,波特率表示的含义是每秒传输的二进制位的个位,在波特率为9600的情况下,3.5个字符周期=1000ms/9600bit*38.5bit=4.0104167ms
MODBUS RTU详解可参考:
MODBUS APPLICATION PROTOCOL SPECIFICATION V1.1b3.pdf
(1.67 MB, 下载次数: 4)
Modbus 协议.pdf
(3.27 MB, 下载次数: 5)
此篇主要介绍USART1实现MODBUS RTU从站功能
硬件连接
GND —— GND
TXD —— PA10(RX)
RXD —— PA9(TX)
软件代码
USART代码:
- void USART_Initial(void)
- {
- NVIC_InitType NVIC_InitStructure;
- GPIO_InitType GPIO_InitStructure;
- USART_InitType USART_InitStructure;
- USART_ClockInitType USART_ClockStructure;
-
-
- RCC_EnableAPB2PeriphClk(USART_GPIO_CLK, ENABLE);
-
- RCC_EnableAPB2PeriphClk(USART_CLK, ENABLE);
-
-
-
- GPIO_InitStruct(&GPIO_InitStructure);
-
- GPIO_InitStructure.Pin = USART_TxPin;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Alternate = USART_Tx_GPIO_AF;
- GPIO_InitPeripheral(USART_GPIO, &GPIO_InitStructure);
-
- GPIO_InitStructure.Pin = USART_RxPin;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
- GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
- GPIO_InitStructure.GPIO_Alternate = USART_Rx_GPIO_AF;
- GPIO_InitPeripheral(USART_GPIO, &GPIO_InitStructure);
-
-
- USART_StructInit(&USART_InitStructure);
- USART_InitStructure.BaudRate = System.Com_Baud.UWD;
- USART_InitStructure.WordLength = USART_WL_8B;
- USART_InitStructure.StopBits = USART_STPB_1;
- USART_InitStructure.Parity = USART_PE_NO;
- USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
- USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
-
- USART_Init(USART, &USART_InitStructure);
-
- USART_ClockStructure.Clock = USART_CLK_DISABLE;
- USART_ClockStructure.Polarity = USART_CLKPOL_LOW;
- USART_ClockStructure.Phase = USART_CLKPHA_2EDGE;
- USART_ClockStructure.LastBit = USART_CLKLB_DISABLE;
- USART_ClockInit(USART, &USART_ClockStructure);
-
- USART_ClrFlag(USART, USART_FLAG_TXDE);
- USART_ClrFlag(USART, USART_FLAG_RXDNE);
-
- USART_ConfigInt(USART, USART_INT_TXDE, DISABLE);
- USART_ConfigInt(USART, USART_INT_RXDNE, ENABLE);
-
-
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
-
- NVIC_InitStructure.NVIC_IRQChannel = USART_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- UART_STR.Send_Flag = 0;
- UART_STR.Read_Flag = 0;
- UART_STR.Send_Dly = 0;
- UART_STR.In_Num = 0;
- UART_STR.Out_Num = 0;
- UART_STR.Start = 0;
- UART_STR.Read_Dly = 0;
- UART_STR.Read_All = 0;
-
-
- USART_Enable(USART, ENABLE);
- }
MODBUS RTU从站代码:
- void Serial_Set_Time(void)
- {
- switch(System.Com_Baud.UWD)
- {
- case 2400:
- System.Delay_Time = 6;
- System.Stop_Time = 7;
- break;
-
- case 4800:
- System.Delay_Time = 3;
- System.Stop_Time = 7;
- break;
-
- case 9600:
- System.Delay_Time = 2;
- System.Stop_Time = 4;
- break;
-
- case 19200:
- case 38400:
- System.Delay_Time = 1;
- System.Stop_Time = 2;
- break;
-
- default:
- System.Delay_Time = 1;
- System.Stop_Time = 2;
- break;
- }
- }
-
-
-
- void USART_SendRec_Dly(void)
- {
- if(UART_STR.Send_Flag)
- {
- if(UART_STR.Send_Dly == 0)
- {
- UART_STR.Send_Flag = 0;
- USART_ConfigInt(USART, USART_INT_TXDE, ENABLE);
- USART_ConfigInt(USART, USART_INT_RXDNE, DISABLE);
- USART1->STS |= USART_INT_TXDE;
- }
- else
- {
- UART_STR.Send_Dly--;
- }
- }
-
- if(UART_STR.Read_Dly >= System.Delay_Time)
- {
- UART_STR.Read_Flag = 1;
-
- if(UART_STR.Start > System.Stop_Time)
- {
- UART_STR.Read_All = 1;
- }
- }
- else
- {
- UART_STR.Read_Dly++;
- UART_STR.Read_Flag = 0;
- }
- }
-
-
- void USART_Send_Ready(void)
- {
- UART_STR.Send_Flag = 1;
- UART_STR.Send_Dly = 1;
- UART_STR.In_Num = 0;
- UART_STR.Start = 0;
- UART_STR.Read_All = 0;
- UART_STR.SendBuf = UART_STR.OutBuf;
- }
-
-
-
-
-
-
-
-
-
-
- unsigned short int GetCRC16(unsigned char *puchMsg, unsigned short int usDataLen)
- {
- unsigned char uchCRCHi = 0xFF;
- unsigned char uchCRCLo = 0xFF;
- unsigned int uIndex = 0;
-
- while (usDataLen--)
- {
- uIndex = uchCRCHi ^ *puchMsg++;
- uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
- uchCRCLo = auchCRCLo[uIndex];
- }
-
- return (unsigned short int)((unsigned short int)uchCRCHi << 8 | uchCRCLo);
- }
-
-
-
- void Function_Write_Address(unsigned short int Addr, unsigned short int Temp)
- {
- UNION_WD_2BY Temp_Data;
- Temp_Data.HUWD = Temp;
-
- switch(Addr)
- {
- case 0x00:
- if(Temp_Data.HUWD)
- {
- GPIO_SetBits(LED1_PORT, LED1_PIN);
- }
- else
- {
- GPIO_ResetBits(LED1_PORT, LED1_PIN);
- }
-
- break;
-
- case 0x01:
- if(Temp_Data.HUWD)
- {
- GPIO_SetBits(LED2_PORT, LED2_PIN);
- }
- else
- {
- GPIO_ResetBits(LED2_PORT, LED2_PIN);
- }
-
- break;
-
- case 0x02:
- if(Temp_Data.HUWD)
- {
- GPIO_SetBits(LED3_PORT, LED3_PIN);
- }
- else
- {
- GPIO_ResetBits(LED3_PORT, LED3_PIN);
- }
-
- break;
-
- default:
- break;
- }
- }
-
-
-
-
-
-
-
-
-
-
- unsigned int USART_Deal(unsigned char *inbuff, unsigned short int Incount, unsigned char *outbuff)
- {
- static union
- {
- short int IWD;
- unsigned short int UWD;
- unsigned char UBY[2];
- } SSADR, SSDAT, SSEND;
- static unsigned char CommFun;
- static unsigned short int Temp_OutNum;
- static unsigned char *sbuff;
- static unsigned short int Cal_CRC16, Tem_CRC16;
- static unsigned short int Temp_Cnt;
- UNION_WD_2BY DO_Temp, Temp_Data;
- unsigned char i;
-
- CommFun = *(inbuff + 1);
- SSADR.UBY[INTER_B_H] = *(inbuff + 2);
- SSADR.UBY[INTER_B_L] = *(inbuff + 3);
- SSDAT.UBY[INTER_B_H] = *(inbuff + 4);
- SSDAT.UBY[INTER_B_L] = *(inbuff + 5);
- SSEND.UWD = SSADR.UWD + SSDAT.UWD;
- Temp_OutNum = 0;
-
- if(Incount > 2)
- {
- Cal_CRC16 = GetCRC16(UART_STR.InBuf, Incount - 2);
- }
-
- Tem_CRC16 = *(inbuff + Incount - 2);
- Tem_CRC16 <<= 8;
- Tem_CRC16 += *(inbuff + Incount - 1);
-
- if(Cal_CRC16 == Tem_CRC16)
- {
- switch(CommFun)
- {
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 8:
- if( Incount == 8 ) break;
- else return 0;
-
- case 7:
- case 11:
- case 12:
- case 17:
- if( Incount == 12 ) break;
- else return 0;
-
- case 15:
- case 16:
- if( Incount == (*(inbuff + 6) + 9) ) break;
- else return 0;
-
- case 20:
- case 21:
- if( (Incount == (*(inbuff + 2) + 5)) && (*(inbuff + 3) == 6) ) break;
- else return 0;
-
- case 22:
- if( Incount == 10 ) break;
- else return 0;
-
- case 23:
- if( Incount == (*(inbuff + 10) + 13) ) break;
- else return 0;
-
- case 24:
- if( Incount == 6 ) break;
- else return 0;
-
- case 43:
- if( Incount == 7 ) break;
- else return 0;
-
- default:
- if( CommFun < 0x80 ) break;
- else return 0;
- }
- }
- else return 0;
-
- sbuff = outbuff;
- *sbuff++ = System.Com_Addr.UBY[0];
- *sbuff = CommFun;
-
- if(CommFun == 3 || CommFun == 4)
- {
- if(SSDAT.UWD == 0 || SSDAT.UWD > 0X007D)
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x03;
- Temp_OutNum = 3;
- }
- else if( SSADR.UWD >= 250 || (SSEND.UWD >= 251) )
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x02;
- Temp_OutNum = 3;
- }
- else
- {
- *(++sbuff) = SSDAT.UWD * 2;
- sbuff = outbuff + 3;
-
- for(Temp_Cnt = SSADR.UWD; Temp_Cnt < SSEND.UWD; Temp_Cnt++)
- {
- if(Temp_Cnt >= 0x00 && Temp_Cnt < 0x31)
- {
- switch(Temp_Cnt)
- {
-
- case 0x00:
- LED1_State = GPIO_ReadOutputDataBit(LED1_PORT, LED1_PIN);
- *sbuff++ = 0x00;
- *sbuff++ = LED1_State;
- break;
-
-
- case 0x01:
- LED2_State = GPIO_ReadOutputDataBit(LED2_PORT, LED2_PIN);
- *sbuff++ = 0x00;
- *sbuff++ = LED2_State;
- break;
-
-
- case 0x02:
- LED3_State = GPIO_ReadOutputDataBit(LED3_PORT, LED3_PIN);
- *sbuff++ = 0x00;
- *sbuff++ = LED3_State;
- break;
-
-
- case 0x03:
- KEY1_State = GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_4);
- *sbuff++ = 0x00;
- *sbuff++ = KEY1_State;
- break;
-
-
- case 0X04:
- KEY2_State = GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_5);
- *sbuff++ = 0x00;
- *sbuff++ = KEY2_State;
- break;
-
-
- case 0X05:
- KEY3_State = GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_6);
- *sbuff++ = 0x00;
- *sbuff++ = KEY3_State;
- break;
-
-
-
- default:
- *sbuff++ = 0;
- *sbuff++ = 0;
- break;
- }
- }
- else
- {
- *sbuff++ = 0;
- *sbuff++ = 0;
- }
- }
-
- Temp_OutNum = *(outbuff + 2) + 3;
- }
- }
-
-
- else if(CommFun == 6)
- {
-
-
-
-
-
-
-
- {
- if((SSADR.UWD >= 0x03 && SSADR.UWD <= 0X05) || SSADR.UWD >= 250 )
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x02;
- Temp_OutNum = 3;
- }
- else
- {
- Function_Write_Address(SSADR.UWD, SSDAT.UWD);
-
- sbuff = outbuff + 2;
- *sbuff++ = SSADR.UBY[INTER_B_H];
- *sbuff++ = SSADR.UBY[INTER_B_L];
- *sbuff++ = SSDAT.UBY[INTER_B_H];
- *sbuff++ = SSDAT.UBY[INTER_B_L];
- Temp_OutNum = 6;
- }
- }
- }
-
- else if(CommFun == 16)
- {
- if(SSDAT.UWD == 0 || SSDAT.UWD > 0x7b || SSDAT.UWD * 2 != *(inbuff + 6) )
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x03;
- Temp_OutNum = 3;
- }
- else if( SSADR.UWD >= 250 || (SSEND.UWD >= 251))
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x02;
- Temp_OutNum = 3;
- }
- else
- {
- if(*(inbuff + 6) > 0x07)
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x03;
- Temp_OutNum = 3;
- }
- else
- {
- ModBus_CodeAddress.UBY[1] = *(inbuff + 2);
- ModBus_CodeAddress.UBY[0] = *(inbuff + 3);
- ModBus_DataLength.UBY[1] = *(inbuff + 4);
- ModBus_DataLength.UBY[0] = *(inbuff + 5);
- ModBus_DataEnd = ModBus_CodeAddress.UWD + ModBus_DataLength.UWD;
-
- for(i = 7, ModBus_Temp = ModBus_CodeAddress.UWD; ModBus_Temp < ModBus_DataEnd; ModBus_Temp++, i = i + 2)
- {
- Temp_Data.UBY[1] = *(inbuff + i);
- Temp_Data.UBY[0] = *(inbuff + i + 1);
- Function_Write_Address(ModBus_Temp, Temp_Data.HUWD);
- }
-
- sbuff = outbuff + 2;
- *sbuff++ = SSADR.UBY[INTER_B_H];
- *sbuff++ = SSADR.UBY[INTER_B_L];
- *sbuff++ = SSDAT.UBY[INTER_B_H];
- *sbuff++ = SSDAT.UBY[INTER_B_L];
- Temp_OutNum = 6;
- }
- }
- }
-
- else
- {
- (*sbuff) += 0x80;
- *(++sbuff) = 0x01;
- Temp_OutNum = 3;
- }
-
- sbuff = outbuff;
-
- if(Temp_OutNum > 2)
- Cal_CRC16 = GetCRC16(sbuff, Temp_OutNum);
-
- sbuff = outbuff + Temp_OutNum;
- *(sbuff++) = (Cal_CRC16 >> 8);
- *(sbuff++) = Cal_CRC16;
- Temp_OutNum += 2;
- return Temp_OutNum;
-
- }
-
-
-
- void USART1_IRQHandler(void)
- {
- unsigned char TempData;
-
- if (USART_GetIntStatus(USART, USART_INT_RXDNE) != RESET)
- {
-
- TempData = USART_ReceiveData(USART);
- USART_ClrIntPendingBit(USART, USART_INT_RXDNE);
-
- if(UART_STR.In_Num == 0)
- {
- if(TempData == System.Com_Addr.UBY[0] && UART_STR.Read_All == 0 && UART_STR.Read_Flag == 1)
- {
- UART_STR.InBuf[UART_STR.In_Num++] = TempData;
- UART_STR.Start = 1;
- }
- }
- else if(UART_STR.Read_Flag == 0)
- {
- if(UART_STR.In_Num >= 260)
- {
- UART_STR.In_Num = 0;
- UART_STR.Start = 0;
- }
- else
- {
- UART_STR.Start++;
- UART_STR.InBuf[UART_STR.In_Num++] = TempData;
- }
- }
- else
- {
- UART_STR.In_Num = 0;
- }
-
- UART_STR.Read_Dly = 0;
- UART_STR.Read_Flag = 0;
- }
-
- else if (USART_GetIntStatus(USART, USART_INT_TXDE) != RESET)
- {
- if(UART_STR.Out_Num != 0)
- {
- UART_STR.Out_Num--;
- USART_SendData(USART, *UART_STR.SendBuf);
- UART_STR.SendBuf++;
- }
- else
- {
- USART_ConfigInt(USART, USART_INT_TXDE, DISABLE);
- }
-
- USART_ClrIntPendingBit(USART, USART_INT_TXDE);
-
- if(UART_STR.Out_Num <= 1)
- USART_ConfigInt(USART, USART_INT_RXDNE, ENABLE);
- }
- else
- {
- USART_ClrFlag(USART, USART_FLAG_OREF | USART_FLAG_NEF | USART_FLAG_FEF | USART_FLAG_PEF);
- }
-
- }
运行测试
从站地址设为0x01,波特率设为115200,初始设置板载LED1、LED3亮,LED2灭,KEY1、KEY2、KEY3按键松开(GPIO设置为上拉输入,按下时为0低电平,松开时为1高电平)
0x03读寄存器命令
使用modscan32测试,设置从站地址、0x03读命令
点击连接设置-->连接,选择调试串口,设置串口参数,点击确认
连接成功后,显示LED1-3,KEY1-3当前状态
0x06写寄存器命令
双击更新设置LED1、LED2、LED3寄存器值,LED1、LED3灭,LED2亮
更新后,显示
测试代码
N32L43x_USART_MODBUS-RTU_Slave.rar
(483.49 KB, 下载次数: 10)
modscan32软件
Modscan32.rar
(1.72 MB, 下载次数: 8)
|