基于空闲中断的MODBUS通讯协议
基于空闲中断的MODBUS通讯协议
⼀.简介
MODBUS协议,最为⼀款⼯业通讯协议,由于其可靠性和免费性,得到了⼴泛的应⽤,本⽂是基于STM8L051F3单⽚机,利⽤IAR for STM8,通过UASRT接收中断中的空闲中断,来实现MODBUS的通讯。 (友情提⽰:在需要⽤到通讯协议的条件,尽量在预算⾜够的情况下,选择⼤FLASH的单⽚机,以免内存溢出,⽆法编译通过 )⼆.⼲货
1.配置USART_DMA
1.1 USART.h
#ifndef _UART_H
#define _UART_H
/
/头⽂件⾃⾏添加
extern unsigned char RxBuffer[];//数据接受的缓存处 void UART_Init(u32 bound);//串⼝初始化
#endif
1.2 USART.c
#include "UART.h"
#define USART_DMA_CHANNEL_RX DMA1_Channel2
#define USART_DMA_CHANNEL_TX DMA1_Channel1
#define USART_DMA_FLAG_TCRX(uint16_t)DMA1_FLAG_TC2
#define USART_DMA_FLAG_TCTX(uint16_t)DMA1_FLAG_TC1
#define USART_DR_ADDRESS(uint16_t)0x5231//USART_RX的地址
/
* USART1 Data register Address */
#define DATA_TO_RECEIVE(uint8_t)0x0A//接收数组⼤⼩为10
unsigned char RxBuffer[DATA_TO_RECEIVE];
void UART_DMA_Config(void)
{
/* Deinitialize DMA channels */
//DMA_GlobalDeInit();
DMA_DeInit(DMA1_Channel1);
//DMA_DeInit(DMA1_Channel2);
CLK_PeripheralClockConfig(CLK_Peripheral_DMA1,ENABLE);
/* DMA channel Rx of USART Configuration */
DMA_Init(USART_DMA_CHANNEL_RX,
(uint16_t)RxBuffer,
(uint16_t)USART_DR_ADDRESS,
DATA_TO_RECEIVE,
DMA_DIR_PeripheralToMemory,
DMA_Mode_Normal,
DMA_MemoryIncMode_Inc,
DMA_Priority_Low,
DMA_MemoryDataSize_Byte);
/* DMA channel Tx of USART Configuration */
// DMA_Init(USART_DMA_CHANNEL_TX,
/苏州蓝缨学校
/ (uint16_t)TxBuffer,
// DMA_DIR_MemoryToPeripheral,
// DMA_Mode_Normal,
// DMA_MemoryIncMode_Inc,
// DMA_Priority_High,
人造神// DMA_MemoryDataSize_Byte);
/* Enable the USART Tx/Rx DMA requests */
// USART_DMACmd(USART1, USART_DMAReq_TX, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_RX,ENABLE);
/* Global DMA Enable */
DMA_GlobalCmd(ENABLE);
/
lw6-220
* Enable the USART Tx DMA channel */
// DMA_Cmd(USART_DMA_CHANNEL_TX,DISABLE);
/* Enable the USART Rx DMA channel */
DMA_Cmd(USART_DMA_CHANNEL_RX,ENABLE);
}
void UART_Init(u32 bound)
{
/* USART configured as follow:
- BaudRate = USART_BAUDRATE baud
- Word Length = 8 Bits
- One Stop Bit
-
No parity
- Receive and transmit enabled
- USART Clock disabled
*/
CLK_PeripheralClockConfig(CLK_Peripheral_USART1,ENABLE);
SYSCFG_REMAPPinConfig(REMAP_Pin_USART1TxRxPortA,ENABLE);//引脚映射
GPIO_Init(GPIOA, GPIO_Pin_2, GPIO_Mode_Out_PP_High_Fast);//TXD
GPIO_Init(GPIOA, GPIO_Pin_3, GPIO_Mode_In_PU_No_IT);//RXD
GPIO_ExternalPullUpConfig(GPIOA, GPIO_Pin_2,ENABLE);
GPIO_ExternalPullUpConfig(GPIOA, GPIO_Pin_3,ENABLE);
USART_DeInit(USART1);//复位UART1
USART_Init(USART1,(uint32_t)bound, USART_WordLength_8b, USART_StopBits_1,
猴耳环
USART_Parity_No,(USART_Mode_TypeDef)(USART_Mode_Tx | USART_Mode_Rx));
/* USART DMA */
UART_DMA_Config();
// while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCTX) == RESET); //while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCRX) == RESET);
USART_ClearFlag(USART1,USART_FLAG_TC);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
/* USART Disable */
USART_Cmd(USART1,ENABLE);
}
2.编写MODBUS通讯⽂件
2.1 MODBUS.h
#ifndef _rs485_H
#define _rs485_H
//头⽂件⾃⾏添加
extern unsigned int regGroup[4];//Modbus16位寄存器组 extern unsigned char flagFrame;//MODBUS处理标志位 void RS485_Init(unsigned long int bound);//RS485通讯初始化
void UartDriver(void);//串⼝驱动函数
unsigned char rs485_UartWrite(unsigned char *buf2 ,unsigned char len2);//串⼝发送数据
static unsigned int GetCRC16(unsigned char *ptr, unsigned char len);//CRC校验
#endif
2.2 MODBUS.c
#include "MY_rs485.h"
u8 flagFrame=0;//帧接收完成标志,即接收到⼀帧新数据
#define MAX_REG4
unsigned int regGroup[MAX_REG]={1,0,0,0};//Modbus16位寄存器组,地址为0x00~REGMAX
//RS485初始化
void RS485_Init(u32 bound)
{
UART_Init(bound);
}
//计算发送的数据长度,并且将数据放到*buf数组中
u8 UartRead(u8 *buf, u8 len)
{
u8 i;
for(i=len;i>0;i--)//检测实际接收到的数据长度
{
if(RxBuffer[i]!=0x00)
break;
}
len=i+1;//读取长度设置为实际接收到的数据长度
for(i=0;i<len;i++)//拷贝接收到的数据到接收指针中
{
*buf=RxBuffer[i];//将数据复制到buf中
buf++;
}
return len;//返回实际读取长度
}
// 发送数据
u8 rs485_UartWrite(u8 *buf ,u8 len)
{
u8 i=0;
Delay(3);//3MS延时
for(i=0;i<len;i++)
{
USART_SendData8(USART1,buf[i]);//通过USARTx外设发送单个数据
while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE));//检查指定的USART标志位设置与否,发送数据空位标志}
return1;
}
//放在循环中处理的函数
void UartDriver()
{
unsigned char i=0,cnt;
unsigned int crc;
unsigned char crch,crcl;
static u8 len;
static u8 buf[10];
char bound_flag=0;
if(flagFrame)//帧接收完成标志
{
flagFrame=0;//帧接收完成标志清零
len =UartRead(buf,sizeof(buf)-1);//将接收到的命令读到缓冲区中
if(buf[0]==(unsigned char)(regGroup[0]))//判断地址是不是0x01
{
crc=GetCRC16(buf,len-2);//计算CRC校验值
crch=crc>>8;//crc⾼位
crcl=crc&0xFF;//crc低位
if((buf[len-2]==crch)&&(buf[len-1]==crcl))//判断CRC校验是否正确
{
switch(buf[1])
{
case0x03://读多个寄存器功能码起始地址 2字节寄存器数量 2字节
if((buf[2]<<8+buf[3])>=0&&(buf[2]<<8+buf[3])<=MAX_REG)//寄存器地址⽀持0x0000~REGMAX 寄存器地址 : (buf[2]*4+buf[3]) {
i=buf[2]<<8;
i=i+buf[3];//提取寄存器地址
cnt=buf[5];//提取待读取的寄存器数量
buf[2]=cnt*2;//读取数据的字节数,为寄存器*2
len=3;
while(cnt--)
{
buf[len++]=regGroup[i]>>8;//寄存器⾼字节补0
buf[len++]=regGroup[i++];//低字节
}
break;
}
else//寄存器地址不被⽀持时,返回错误码
{
buf[1]=0x83;//功能码最⾼位置1
buf[2]=0x02;//设置异常码为02-⽆效地址
len=3;
break;
}
case0x06://写⼊单个寄存器
if((buf[2]==0x00)&&(buf[3]<=MAX_REG))//寄存器地址⽀持0x0000-REGMAX
{
if(buf[3]==1)
{
bound_flag=1;
}
if(buf[3]<=MAX_REG-1)
{
regGroup[buf[3]]=buf[4]*256+buf[5];//保存寄存器数据
}
len -=2;//长度-2以重新计算CRC并返回原帧
break;
}
else
{//寄存器地址不被⽀持,返回错误码
buf[1]=0x86;//功能码最⾼位置1
buf[2]=0x02;//设置异常码为02-⽆效地址
len=3;
break;
}
default://其他不⽀持的功能码
buf[1]=0x80;//功能码最⾼位置1
buf[2]=0x01;//设置异常码为01—⽆效功能
len=3;
break;
多肽类药物}
crc=GetCRC16(buf,len);//计算CRC校验值
buf[len++]=crc>>8;//CRC⾼字节
buf[len++]=crc&0xff;//CRC低字节
rs485_UartWrite(buf,len);//发送响应帧
if(bound_flag)
{
bound_flag=0;
UART_Init(regGroup[1]);
}
}
}
}
}
static unsigned int GetCRC16(unsigned char *ptr, unsigned char len) {
u16 index;
u8 crch =0xFF;//⾼CRC字节
u8 crcl =0xFF;//低CRC字节
u8 TabH[]={//CRC⾼位字节值表
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,
0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,
0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,
0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,
正比例应用题0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,
0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,
0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
0x80,0x41,0x00,0xC1,0x81,0x40
};
u8 TabL[]={//CRC低位字节值表
0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,
0x07,0xC7,0x05,0xC5,0xC4,0x04,0xCC,0x0C,0x0D,0xCD,
0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,
0x08,0xC8,0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,
0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,0x14,0xD4, 0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,
0x11,0xD1,0xD0,0x10,0xF0,0x30,0x31,0xF1,0x33,0xF3,
0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A, 0x3B,0xFB,0x39,0xF9,0xF8,0x38,0x28,0xE8,0xE9,0x29,
0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,
0xEC,0x2C,0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,
0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,0xA0,0x60,
0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,
0xA5,0x65,0x64,0xA4,0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,