一 、开发思路
我这里开发的平台是新唐M031,它是Cortex-M0的内核、32位单片机。因为要和上位机进行RS485通讯,所以选用了Modbus-RTU来作为通讯协议。我这是用串口接收中断+定时器中断来接收一帧数据,然后modbus从机程序自己手撸。
二、 Modbus介绍
modbus没什么好介绍的,熟悉下功能码就ok了。直接上链接 https://www.cnblogs.com/endv/p/8650491.html
三、 串口初始化及中断处理函数
各模块时钟初始化统一放到sys_init里面
void SYS_Init(void)
{
/* Unlock protected registers */
SYS_UnlockReg();
/* Enable HIRC */
CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);
/* Waiting for HIRC clock ready */
CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
/* Switch HCLK clock source to HIRC */
CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));
/* Set both PCLK0 and PCLK1 as HCLK/2 */
CLK->PCLKDIV = (CLK_PCLKDIV_APB0DIV_DIV2 | CLK_PCLKDIV_APB1DIV_DIV2);
CLK_EnableModuleClock(TMR0_MODULE);
CLK_EnableModuleClock(TMR1_MODULE);
/* Select IP clock source */
CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HIRC, 0);
CLK_SetModuleClock(TMR1_MODULE, CLK_CLKSEL1_TMR1SEL_HIRC, 0);
SystemCoreClockUpdate();
SYS_LockReg();
}
rs485.h
nRxBuff用来存串口1接收到的modbus请求帧,nRxLenth用来存放收到的字节长度。
#ifndef __RS485_H__
#define __RS485_H__
#include "NuMicro.h"
#include "stdio.h"
#include "string.h"
#include "modbus_slave.h"
#define nBunfSize 128
#define BaudRate 115200
typedef struct{
uint16_t nRxLenth;
uint8_t nRxBuff[nBunfSize];
}UART0DATA;
void Rs485_Init(void);
void Time0_Init(void);
void delay_us(uint16_t nDelay);
void delay_ms(uint16_t nDelay);
void UART13_IRQHandler(void);
void TMR0_IRQHandler(void);
#endif
rs485.c
思路:我这里在串口接收中断里面,接收到第一个字节,然后定时器0才开始计时,然后在定时器计时12.5ms,然后关闭定时器。有人会问为什么是12.5ms,首先我这里波特率是很高的115200,我认为12.5ms内一帧modbus请求帧已经发完。你们这个时间可以自己根据自己的情况去调。
3.1 串口初始化
UART0DATA Uart0Data;
volatile uint16_t nRevNum = 0;
uint8_t RevBuff[32] = {0};
void Rs485_Init(void)
{
/* Switch UART1 clock source to HIRC */
CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART1SEL_HIRC, CLK_CLKDIV0_UART1(1));
/* Enable UART peripheral clock */
CLK_EnableModuleClock(UART1_MODULE);
/* Update System Core Clock */
/* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
SystemCoreClockUpdate();
SYS->GPA_MFPL = (SYS->GPA_MFPL & ~(SYS_GPA_MFPL_PA2MFP_Msk | SYS_GPA_MFPL_PA3MFP_Msk)) | \
( SYS_GPA_MFPL_PA2MFP_UART1_RXD | SYS_GPA_MFPL_PA3MFP_UART1_TXD);
/* Reset UART1 */
SYS_ResetModule(UART1_RST);
/* Configure UART0 and set UART0 baud rate */
UART_Open(UART1, BaudRate);
UART_SetLine_Config(UART1,BaudRate,UART_WORD_LEN_8,UART_PARITY_NONE,UART_STOP_BIT_1);
//接收超时中断
UART_ENABLE_INT(UART1,UART_INTEN_RDAIEN_Msk|UART_INTEN_RXTOIEN_Msk|UART_INTEN_RLSIEN_Msk);
NVIC_EnableIRQ(UART13_IRQn);
NVIC_SetPriority(UART13_IRQn,0);
}
3.2 串口接收中断
void UART13_IRQHandler(void)
{
if(UART_GET_INT_FLAG(UART1,UART_INTSTS_RDAIF_Msk | UART_INTSTS_RXTOIF_Msk | UART_INTSTS_RXTOINT_Msk))
{
UART_ClearIntFlag(UART1, (UART_INTSTS_RLSINT_Msk| UART_INTSTS_BUFERRINT_Msk));
while(!UART_GET_RX_EMPTY(UART1))
{
//sTemp = UART0->DAT;
Uart0Data.nRxBuff[nRevNum++] = UART1->DAT;
TIMER_Start(TIMER0); //打开定时器
if(nRevNum > nBunfSize){
nRevNum = 0;
memset(Uart0Data.nRxBuff,0,sizeof(Uart0Data.nRxBuff));
}
}//end while
}
}
四、 定时器初始化及中断处理函数
4.1 定时器初始化
void Time0_Init(void)
{
/* Open Timer0 in periodic mode, enable interrupt and 1 interrupt tick per second */
TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 80); //10ms进一次中断
TIMER_EnableInt(TIMER0);
//TIMER_SET_CMP_VALUE(TIMER0, 0xFFFFFF);//修改比较寄存器的值
//TIMER_SET_PRESCALE_VALUE(TIMER0, 0x0);//修改预分频的值
/* Enable Timer0NVIC */
NVIC_EnableIRQ(TMR0_IRQn);
/* Start Timer0 counting */
//TIMER_Start(TIMER0);
NVIC_SetPriority(TMR0_IRQn,1);
}
4.2 定时器中断函数
Modbus_Data_Parse()是modbus从机入口函数,也可以放到main函数的while(1)里面
void TMR0_IRQHandler(void)
{
// uint16_t i = 0;
if(TIMER_GetIntFlag(TIMER0) == 1)
{
// Clear Timer0 time-out interrupt flag
TIMER_ClearIntFlag(TIMER0);
TIMER_Stop(TIMER0); //关闭定时器
Uart0Data.nRxLenth = nRevNum;
nRevNum = 0;
//modbus从机入口函数
Modbus_Data_Parse();
}
}
五、 Modbus从机程序
这里才是关键,先简单介绍这里的开发思路。Modbus_Data_Parse是modbus从机入口函数,进来首先是读下从机地址(Modbus地址由3位的拨码开关决定),然后对串口1接收的数据进行判断并进行解析,校验成功的数据丢到Md_Recv_t结构体里面。最后根据功能码执行不同的函数对请求帧返回对应的响应帧,注意我这里把01/02,03/04看成一样的,执行同一个函数。
modbus_slave.h
#ifndef __MODBUS_SLAVE_H__
#define __MODBUS_SLAVE_H__
#include "NuMicro.h"
#include "stdio.h"
#include "string.h"
#include "stk8321.h"
#include "gpio_coil.h"
#include "filter.h"
#include "rs485.h"
#define SENDLENTH 64
#define READLENTH 64
#define ReadCoilSta 0x01 //读线圈量
#define ReadInputDis 0x02 //读输入离散量
#define ReadHoldReg 0x03 //读保持寄存器
#define ReadInputReg 0x04 //读输入寄存器
#define ForceSingleCoil 0x05 //写位
#define WriteMulCoils 0x0F //写多位
#define WriteSingleReg 0x06 //写单个寄存器
#define WireMulReg 0x10 //写多个寄存器
//线圈0-15
#define Coil0_0 0x1000 //LED1
#define Coil0_1 0x1001 //LED2
#define Coil0_2 0x1002 //LED3
#define Coil0_3 0x1003 //LED4
#define Coil0_4 0x1004 //LED5
#define Coil0_5 0x1005 //LED6
#define Coil0_6 0x1006 //LED7
#define Coil0_7 0x1007 //LED8
#define Coil1_0 0x1008 //LED9
#define Coil1_1 0x1009 //LED10
#define Coil1_2 0x100A //LED11
#define Coil1_3 0x100B //LED12
#define Coil1_4 0x100C
#define Coil1_5 0x100D
#define Coil1_6 0x100E
#define Coil1_7 0x100F
//可读可写寄存器
#define Adjust_Level_Reg 0x2000 //Led调光寄存器
//保持寄存器(版本号寄存器)
#define VersionReg_1 0x0001
#define VersionReg_2 0x0002
#define VersionReg_3 0x0003
#define VersionReg_4 0x0004
#define VersionReg_5 0x0005
#define VersionReg_6 0x0006
#define VersionReg_7 0x0007
#define VersionReg_8 0x0008
#define VersionReg_9 0x0009
#define VersionReg_10 0x000A
//保持寄存器(加速度值寄存器)
#define AxReg 0x3000 //请求寄存器数量1-3
#define AyReg 0x3001 //请求寄存器数量1-2
#define AzReg 0x3002 //请求寄存器数量1
//modbus错误码
typedef enum{
ERR1 = 1, //非法功能码
ERR2 = 2, //非法数据地址
ERR3 = 3, //非法数据值
RRR4 = 4, //从机设备故障
RRR5 = 8, //校验错误
}Error_Code;
typedef struct{
uint16_t nAddr;
uint8_t nCmd;
uint16_t nRegStartAddr;
uint8_t nRegLen;
uint16_t Md_Data[20];
}Md_Recv_t;
//void Read_Coil_Data(void);
void Modbus_Data_Parse(void);
uint8_t Modbus_Data_Check(uint8_t *RecvBuf, uint32_t nLen, Md_Recv_t *pMd_Recv);
void ParseRecieve(void);
void Modbus_Send_Error(uint8_t Cmd, uint8_t Err);
#endif //end __MODBUS_SLAVE_H__
modbus_slave.c
#include "modbus_slave.h"
uint8_t SlaveAddr = 0x01; //从机设备地址
extern int16_t sHoldRegValue[4];
extern UART0DATA Uart0Data;
extern int Light_Grade;
Error_Code Md_Err;
//uint8_t Uart0Data.nRxBuff[8]; // 接收缓冲区
uint8_t nSendBuff[SENDLENTH] = {0};
uint8_t nReadBuff[READLENTH] = {0};
uint32_t sCoilvalue[20] = {0};
//协议版本号 M31_LPD_T01_1001
//int8_t *version = "M31_LPD_T01_1001";
int8_t version[32] = "M31_LPD_T01_1001";
Modbus_Data_Parse()
/**************************************
//函数功能:解析上位机发过来的modbus数据帧
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Data_Parse(void)
Md_Recv_t Md_Recv;
//delay_ms(10);
SlaveAddr = Get_ModbusAddr();
if(Modbus_Data_Check(Uart0Data.nRxBuff,Uart0Data.nRxLenth,&Md_Recv) == 0)
{
switch(Md_Recv.nCmd)
{
case ReadCoilSta:
case ReadInputDis:
Modbus_Read_Coil(&Md_Recv);
break;
case ReadHoldReg:
case ReadInputReg:
Modbus_Read_Reg(&Md_Recv);
break;
case ForceSingleCoil:
Modbus_Write_SingleCoil(&Md_Recv);
break;
case WriteSingleReg:
Modbus_Write_Reg(&Md_Recv);
break;
case WriteMulCoils:
Modbus_Write_MuilCoil(&Md_Recv);
break;
case WireMulReg:
Modbus_Write_MuilReg(&Md_Recv);
break;
default:
break;
}
memset(&Md_Recv, 0, sizeof(Md_Recv));
memset(Uart0Data.nRxBuff, 0, sizeof(Uart0Data.nRxBuff));
Uart0Data.nRxLenth = 0;
}
}
Modbus_Data_Check
/**************************************
//函数功能:对上位机发来的帧进行判断
//函数返回值:无
//函数形参:RecvBuf:串口实际接收到的数据,nLen:接收到的长度,pMd_Recv:modbus结构指针
//时间:2020/06/22
//备注:无
*************************************/
uint8_t Modbus_Data_Check(uint8_t *RecvBuf, uint32_t nLen, Md_Recv_t *pMd_Recv)
//printf("FILE:%s\tLINE:%d\r\n", __FILE__, __LINE__);
uint16_t nCRC = 0;
uint16_t i = 0;
uint16_t nRetByte = 0;
//先进行长度判断,Modbus请求帧和控制帧长度应大于等于8
if(nLen >= 8)
{
nCRC = CRC16(RecvBuf, nLen-2);
//判断CRC校验,校验成功说明是modbus标准帧
if(nCRC == ((uint16_t)(RecvBuf[nLen - 2] << 8 | RecvBuf[nLen - 1])))
{
pMd_Recv->nAddr = RecvBuf[0];
pMd_Recv->nCmd = RecvBuf[1];
if(pMd_Recv->nAddr == SlaveAddr)
{
switch(RecvBuf[1])
{
case ReadCoilSta:
case ReadInputDis:
case ReadHoldReg:
case ReadInputReg:
pMd_Recv->nRegStartAddr = (uint16_t)(RecvBuf[2] << 8 | RecvBuf[3]);
pMd_Recv->nRegLen = (uint16_t)(RecvBuf[4] << 8 | RecvBuf[5]);
break;
case ForceSingleCoil:
case WriteSingleReg:
//01 05 00 00 ff 00 crc1 crc2 8byte
//01 06 00 01 aa bb crc1 crc2 8byte
pMd_Recv->nRegLen = 2;
pMd_Recv->nRegStartAddr = (uint16_t)(RecvBuf[2] << 8 | RecvBuf[3]);
pMd_Recv->Md_Data[0] = (uint16_t)(RecvBuf[4] << 8 | RecvBuf[5]);
break;
case WireMulReg:
//01 10 00 13 00 02 04 aa aa bb bb crc1 crc ---- 7+len+2字节
pMd_Recv->nRegStartAddr = (uint16_t)(RecvBuf[2] << 8 | RecvBuf[3]);
pMd_Recv->nRegLen = (uint16_t)(RecvBuf[4] << 8 | RecvBuf[5]);
for(i = 0; i < pMd_Recv->nRegLen; i++)
{
pMd_Recv->Md_Data[i] = (uint16_t)(RecvBuf[7+2*i] << 8 | RecvBuf[8+2*i]);
}
break;
case WriteMulCoils:
pMd_Recv->nRegStartAddr = (uint16_t)(RecvBuf[2] << 8 | RecvBuf[3]);
pMd_Recv->nRegLen = (uint16_t)(RecvBuf[4] << 8 | RecvBuf[5]);
nRetByte = (pMd_Recv->nRegLen / 8);
if((pMd_Recv->nRegLen % 8) != 0){
nRetByte++;
}
for(i = 0; i < nRetByte; i++)
{
pMd_Recv->Md_Data[i] = (uint16_t)RecvBuf[7+i];
}
break;
default:
break;
}
return 0;//success
}
else
{
return 3;
}
}
else
{
return 2;
}
}
else
{
return 1;
}
}
CRC校验函数
/**************************************
//函数功能:标准CRC校验函数
//函数返回值:CRC(2字节)
//函数形参:Msg-->校验帧,len-->校验长度
//时间:2020/06/13
//备注:多项式0xA001
*************************************/
uint16_t CRC16(uint8_t *Msg, uint16_t len)
{
uint16_t nCRC = 0xffff;
uint16_t i,temp;
while(len--)
{
nCRC = nCRC^(*Msg++);
for(i=0;i++<8;)
{
if(nCRC&0x0001)
nCRC = (nCRC>>1)^0xa001;
else
nCRC>>=1;
}
}
temp = nCRC&0xff;
nCRC = ((nCRC>>8)&0xff)+(temp<<8);
return(nCRC);
}
发送错误码函数
/**************************************
//函数功能:Modbus错误码发送
//函数返回值:无
//函数形参:Cmd:命令码,Err:错误类型
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Send_Error(uint8_t Cmd, uint8_t Err)
{
uint16_t nCRC = 0;
nSendBuff[0] = SlaveAddr;
nSendBuff[1] = 0x80 + Cmd;
nSendBuff[2] = Err;
nCRC = CRC16(nSendBuff,3);
nSendBuff[3] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[4] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,5);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:Modbus读版本号
//函数返回值:无
//函数形参:pMd_Recv
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Read_Version(Md_Recv_t *pMd_Recv)
{
uint16_t nCRC = 0;
uint16_t SendIndex = 0;
nSendBuff[SendIndex++] = pMd_Recv->nAddr;
nSendBuff[SendIndex++] = pMd_Recv->nCmd;
nSendBuff[SendIndex++] = pMd_Recv->nRegLen*2;
memcpy((char *)nSendBuff+SendIndex,version,pMd_Recv->nRegLen*2);
SendIndex += pMd_Recv->nRegLen*2;
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,SendIndex);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:对上位机01/02命令处理
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Read_Coil(Md_Recv_t *pMd_Recv)
uint16_t nCRC = 0;
uint16_t i = 0;
uint16_t j = 0;
//uint16_t nOffset = 0;
//uint16_t uStatus[16] = {0};
uint16_t nSendByte = 0;
uint32_t nCoilValue = 0;
uint32_t nCoilAddr = 0;
uint16_t nExitFlag = 0;
memset(nSendBuff,0,sizeof(nSendBuff));
memset(nReadBuff,0,sizeof(nSendBuff));
if((pMd_Recv->nRegStartAddr < Coil0_0) || (pMd_Recv->nRegStartAddr > Coil1_7) || pMd_Recv->nRegLen > 0x0010 || (pMd_Recv->nRegStartAddr + pMd_Recv->nRegLen) > 0x1010) //超出范围
{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
//nOffset = pMd_Recv->nRegStartAddr - Coil0_0; //先计算器寄存器偏移量
nCoilAddr = pMd_Recv->nRegStartAddr;
nSendBuff[0] = pMd_Recv->nAddr;
nSendBuff[1] = pMd_Recv->nCmd;
nSendByte = pMd_Recv->nRegLen / 8; //字节数
if(pMd_Recv->nRegLen % 8){ //有余数字节数加1
nSendByte++;
}
nSendBuff[2] = (uint8_t)nSendByte;
//小端模式,低位存放在低地址
for(i = 0; i < nSendByte; i++)
{
nSendBuff[3 + i] = 0x00;
for(j = 0; j < 8; j++)
{
//读线圈状态
Read_Coil_Data(nCoilAddr,&nCoilValue);
nSendBuff[3 + i] |= nCoilValue << j;
nCoilAddr++;
if(nCoilAddr >= pMd_Recv->nRegLen + pMd_Recv->nRegStartAddr){
nExitFlag = 1;
break;
}
}
if(nExitFlag == 1){
break;
}
}
nCRC = CRC16(nSendBuff,nSendByte+3);
nSendBuff[nSendByte+3] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[nSendByte+4] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,nSendByte+5);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:对上位机03/04命令处理
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Read_Reg(Md_Recv_t *pMd_Recv)
{
//printf("FILE:%s\tLINE:%d\r\n", __FILE__, __LINE__);
uint16_t nCRC = 0;
uint16_t i = 0;
//uint16_t uStatus[4] = {0};
int16_t iStatus[4] = {0};
uint16_t SendIndex = 0;
memset(nSendBuff,0,sizeof(nSendBuff));
memset(nReadBuff,0,sizeof(nSendBuff));
if(pMd_Recv->nRegStartAddr == 0x0001 && pMd_Recv->nRegLen != 0){
Modbus_Read_Version(pMd_Recv);
return;
}
else if((pMd_Recv->nRegStartAddr > AzReg) || (pMd_Recv->nRegStartAddr < Adjust_Level_Reg) || pMd_Recv->nRegLen > 0x0003 || (pMd_Recv->nRegStartAddr + pMd_Recv->nRegLen) > 0x3003) //超出范围
{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
for(i = 0; i < pMd_Recv->nRegLen; i++)
{
//printf("FILE:%s\tLINE:%d\r\n", __FILE__, __LINE__);
switch(pMd_Recv->nRegStartAddr++)
{
case AxReg:
iStatus[i] = sHoldRegValue[0];
//STK8321_Get_AxData(&iStatus[i]);
break;
case AyReg:
iStatus[i] = sHoldRegValue[1];
//STK8321_Get_AyData(&iStatus[i]);
break;
case AzReg:
iStatus[i] = sHoldRegValue[2];
//STK8321_Get_AzData(&iStatus[i]);
break;
case Adjust_Level_Reg:
iStatus[i] = Light_Grade;
break;
}
nReadBuff[2*i] = iStatus[i] >> 8 & 0xFF;
nReadBuff[2*i+1] = iStatus[i] & 0xFF;
}
pMd_Recv->nRegStartAddr -= i;
nSendBuff[SendIndex++] = pMd_Recv->nAddr;
nSendBuff[SendIndex++] = pMd_Recv->nCmd;
nSendBuff[SendIndex++] = pMd_Recv->nRegLen*2;
memcpy(&nSendBuff[SendIndex],nReadBuff,(2*i));
SendIndex += 2*i;
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,SendIndex);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:对上位机0x05写单个线圈处理
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Write_SingleCoil(Md_Recv_t *pMd_Recv)
{
uint16_t nCRC = 0;
uint16_t SendIndex = 0;
uint32_t nCoilValue = 0;
uint32_t nCoilAddr = 0;
uint32_t nONOFF= 0;
nCoilAddr = pMd_Recv->nRegStartAddr;
if(nCoilAddr < Coil0_0 || nCoilAddr > Coil1_7)
{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
nONOFF = pMd_Recv->Md_Data[0];
if(nONOFF == 0xFF00){
nCoilValue = 1;
}
else if(nONOFF == 0x0000){
nCoilValue = 0;
}
Set_Coil_Data(nCoilAddr,nCoilValue);
nSendBuff[SendIndex++] = pMd_Recv->nAddr;
nSendBuff[SendIndex++] = pMd_Recv->nCmd;
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->nRegStartAddr >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->nRegStartAddr & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->Md_Data[0] >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->Md_Data[0] & 0x00ff);
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,SendIndex);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:对上位机0x0F写多个线圈处理
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:01 0F 10 03 00 0A 02 CD 01 61 9A
//100A 1009 1008 1007 1006 1005 1004 1003
// 1 1 0 0 1 1 0 1
// 其余位补0 100C 100B
// 0 1
*************************************/
void Modbus_Write_MuilCoil(Md_Recv_t *pMd_Recv)
{
uint16_t nCRC = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t SendIndex = 0;
uint16_t nSendByte = 0;
uint16_t nCoilAddr = 0;
uint16_t nCoilValue = 0;
uint16_t nExitFlag = 0;
nCoilAddr = pMd_Recv->nRegStartAddr;
nSendByte = pMd_Recv->nRegLen / 8;
if(pMd_Recv->nRegLen % 8){
nSendByte++;
}
if(nCoilAddr < Coil0_0 || nCoilAddr > Coil1_7 || pMd_Recv->nRegLen > 0x10 || (nCoilAddr + pMd_Recv->nRegLen) > 0x1010)
{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
for(i = 0; i < nSendByte; i++)
{
for(j = 0; j < 8; j++)
{
nCoilValue = (pMd_Recv->Md_Data[i] >> j) & 0x01;
Set_Coil_Data(nCoilAddr,nCoilValue);
nCoilAddr++;
if(nCoilAddr >= pMd_Recv->nRegLen+pMd_Recv->nRegStartAddr){
nExitFlag = 1;
break;
}
}
if(nExitFlag == 1){
break;
}
}
nSendBuff[SendIndex++] = pMd_Recv->nAddr;
nSendBuff[SendIndex++] = pMd_Recv->nCmd;
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->nRegStartAddr >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->nRegStartAddr & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->nRegLen >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->nRegLen & 0x00ff);
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,SendIndex);
memset(nSendBuff,0,SENDLENTH);
}
/**************************************
//函数功能:对上位机06命令写单个寄存器处理
//函数返回值:无
//函数形参:无
//时间:2020/06/22
//备注:无
*************************************/
void Modbus_Write_Reg(Md_Recv_t *pMd_Recv)
{
uint16_t nCRC = 0;
uint16_t SendIndex = 0;
// uint32_t nRegValue = 0;
uint32_t nRegAddr = 0;
nRegAddr = pMd_Recv->nRegStartAddr;
if(nRegAddr < Adjust_Level_Reg || nRegAddr > AzReg)
{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
switch(nRegAddr)
{
case Adjust_Level_Reg:
Light_Grade = pMd_Recv->Md_Data[0];
break;
default:
break;
}
if(Light_Grade == pMd_Recv->Md_Data[0]){
nSendBuff[SendIndex++] = pMd_Recv->nAddr;
nSendBuff[SendIndex++] = pMd_Recv->nCmd;
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->nRegStartAddr >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->nRegStartAddr & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)((pMd_Recv->Md_Data[0] >> 8) & 0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(pMd_Recv->Md_Data[0] & 0x00ff);
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(UART1,nSendBuff,SendIndex);
memset(nSendBuff,0,SENDLENTH);
}else{
Modbus_Send_Error(pMd_Recv->nCmd,ERR2);
return;
}
}
六、 总结
我这里用接收中断+定时器并不是最优的处理方式,当我定时器还在计时发了2条请求帧,我的程序只能处理第一条。所以这里我感觉可以搞个队列、或者搞一个链表,将一帧一帧的数据存到队列或者链表,然后从里面取数据去解析。原谅我是个菜鸡,写不出这样的代码,芜湖。祝大家快乐的撸码,写的代码都没bug。