一 、开发思路

我这里开发的平台是新唐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 115200typedef 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请求帧和控制帧长度应大于等于8if(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  8bytepMd_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;elsenCRC>>=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){          //有余数字节数加1nSendByte++;}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。

基于单片机的modbus从机程序相关推荐

  1. 【单片机毕业设计】【mcuclub-jj-020】基于单片机的洗碗机的设计

    最近设计了一个项目基于单片机的洗碗机系统,与大家分享一下: 一.基本介绍 项目名:洗碗机 项目编号:mcuclub-jj-020 单片机类型:STC89C52 具体功能: 1.清洗过程:加水(5s)- ...

  2. 基于单片机的密码锁c语言设计程序,基于单片机的电子密码锁及程序

    <基于单片机的电子密码锁及程序>由会员分享,可在线阅读,更多相关<基于单片机的电子密码锁及程序(17页珍藏版)>请在人人文库网上搜索. 1.基于单片机的电子密码锁设计摘要随着科 ...

  3. 基于单片机的洗碗机控制器

    设计简介: 本设计是基于单片机的洗碗机控制器,主要实现以下功能: LCD1602显示温度.温度最大值以及状态 按键设置温度最大值 洗碗过程:进水-->加热-->排水-->冲洗--&g ...

  4. 基于单片机的c语言倒计时程序,30秒倒计时c语言51单片机实现.doc

    30秒倒计时c语言51单片机实现 原理图: 程序: #include #define uchar unsigned char #define uint unsigned int sbit dula=P ...

  5. 电子琴仿真c语言程序,基于单片机简易电子琴仿真和源程序

    #include #define KeyPort P1 unsigned char High,Low; //定时器预装值的高8位和低8位 sbit SPK=P3^7;          //定义蜂鸣器 ...

  6. 51单片机温度控制C语言程序详解,基于单片机的温度控制系统设计(附程序清单及原理图).doc...

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp电子工程/通信技术&nbsp>&nbsp电子设计 基于单片机的温度控制系统设计(附程序清单及 ...

  7. 51单片机的电子钟c语言程序,基于51单片机的电子钟C语言程序

    基于单片机C语言的电子钟程序代码与仿真 基于51单片机的电子钟C语言程序: 程序代码 #include #include #define uchar unsigned char #define uin ...

  8. 利用4位led显示本机秒表时间的单片机c语言程序,基于单片机的一个2位的led数码显示作为_秒表_设计.doc...

    基于单片机的一个2位的led数码显示作为_秒表_设计 PAGE 1 PAGE 29 工程技术学院 课程设计 题 目:用单片机AT89C51设计一个2位的LED数码显示作为"秒表" ...

  9. 单片机4人抢答器程序C语言,基于单片机的4人抢答器.pdf

    基于单片机的4人抢答器 本文由wosulewen贡献 doc文档可能在WAP端浏览体验不佳.建议您优先选择TXT,或下载源文件到本机 查看. 福建工程学院国脉信息学院 目录 摘要----------- ...

最新文章

  1. 吴恩达朋友圈宣布“喜讯”:AI专家王冬岩加入Landing AI...
  2. 前端定时器 setInterval 和 setTimeout
  3. leetcode算法题--队列的最大值
  4. 全球及中国智能隐形眼镜行业供需现状与营销策略建议报告2022-2028年
  5. [转]轻松掌握Ajax.net系列教程十五:使用AutoCompleteExtender
  6. (原創) 如何使用Operator Overloading? (C/C++)
  7. Java编码与乱码问题
  8. sdl2 opengl d3d9的mipmap和各项异性过滤渲染
  9. 关于python中字典描述正确的是_python总结七
  10. mysql 内存表 限制_MySQL内存表的弊端
  11. 不知为不知--信息论和最大熵原则
  12. Java Ajax技术详解:(一)Ajax 简介
  13. 三星6818基于uboot的流水灯程序
  14. 用心筛选新股 追求利润最大化
  15. Linux开发环境部署
  16. python-web开发[12]之css案例、bootstrap探索
  17. bm17bm6bm18
  18. css wangeditor 修改_内容复制到wangEditor富文本编辑器样式排版错误重置方法
  19. 在迷茫时阅读。。。工作感悟
  20. A Cross-Modal Image and Text Retrieval Method Based on Efficient Feature Extraction and Interactive

热门文章

  1. 网易云直播SDK使用总结
  2. 企业信息化投入中咨询服务_咨询服务企业如何实施项目核算信息化建设
  3. 智能手机功能设计实现
  4. 苹果A系列芯片的三年AI进化:为何要大规模升级AI算力?
  5. 用.bat文件多开微信及打开固定浏览器浏览固定页面 开机自启
  6. oracle 2019 ocp,2019 OCP简介
  7. 最近三年收藏网站,做一次云备份
  8. 手机令牌 dKey M6介绍
  9. libcurl linux 静态链接库_Linux ubuntu OpenSSL + curl 静态库编译及使用
  10. vue 2个方法先后执行_《拖延心理学》:2个对策,3个法宝,6个方法,教你战胜拖延...