~~~~~~ 有两点需要注意的:1、modbus rtu标准协议规定:每帧数据间隔至少为3.5个字符时间。 2、接收发送数据最好改为中断模式,不然通信速度不够。
/**
1. 功能:根据ModBus规则计算CRC16
2. 参数:
3. _pBuf:待计算数据缓冲区,计算得到的结果存入_pBuf的最后两字节
4. _usLen:待计算数据长度(字节数)
5. 返回值:16位校验值
*/
static unsigned short int getModbusCRC16(unsigned char *_pBuf, unsigned short int _usLen)
{
unsigned short int CRCValue = 0xFFFF; //初始化CRC变量各位为1
unsigned char i,j;
for(i=0;i<_usLen;++i)
{
CRCValue ^= *(_pBuf+i); //当前数据异或CRC低字节
for(j=0;j<8;++j) //一个字节重复右移8次
{
if((CRCValue & 0x01) == 0x01) //判断右移前最低位是否为1
{
CRCValue = (CRCValue >> 1)^0xA001; //如果为1则右移并异或表达式
}else
{
CRCValue >>= 1; //否则直接右移一位
}
}
}
return CRCValue;
}
用定时器3判断接收空闲时间,当空闲时间大于指定时间,认为一帧结束
~~~~~~ 在主函数调用这个定时器初始化函数并传进参数,使用定时器为TIM3,进行7200分频(72Mhz),即100us计数溢出一次,选用更新中断,使能跟新中断。
/**
* 功能:初始化定时器
* 参数:
* TIMx:指定待设置的定时器,TIM1-TIM4
* prescaler:设置预分频值 0-65535
* period:设置中断周期,即设置重装载寄存器的值 0-65535
* IT_Source:中断源,比如更新中断,四个通道的输入比较中断,取值查看:TIM_interrupt_sources
* NewState:是否使能IT_Source参数指定的中断,ENABLE,DISENABLE
* 返回值:None
*/
void initTIMx(TIM_TypeDef* TIMx,u16 prescaler,u16 period,u16 IT_Source,FunctionalState NewState)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
switch((u32)TIMx)
{
case (u32)TIM1: RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);break; //开启定时器1时钟
case (u32)TIM2: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);break; //开启定时器2时钟
case (u32)TIM3: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);break; //开启定时器3时钟
case (u32)TIM4: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);break; //开启定时器4时钟
/*其他密度的单片机对case进行删减即可兼容,本程序针对中密度单片机*/
default : break;
}
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频因子,该分频不是预分频值,要区分
TIM_TimeBaseStructure.TIM_Period = period; //设置定时器周期 重装值
TIM_TimeBaseStructure.TIM_Prescaler =prescaler; //设置预分频值
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); //生效更改
TIM_Cmd(TIMx, ENABLE); //开启计数器
TIM_ITConfig(TIMx,IT_Source,NewState); //使能定时器更新中断
}
/****************************************定时器中断服务函数************************************************/
//用定时器3判断接收空闲时间,当空闲时间大于指定时间,认为一帧结束
u8 RS485_FrameFlag=0;
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //由于定时器中断源很多,因此要判断是哪个中断源触发的中断
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //软件清除中断挂起位,否则会一直卡死在中断服务函数
TIM_Cmd(TIM3,DISABLE);//停止定时器
//RS485_TX_EN=1;//停止接收,切换为发送状态
//GPIO_SetBits(GPIOA,GPIO_Pin_9)
RS485_FrameFlag=1;//置位帧结束标记
}
}
/**
1. 功能:初始化UART
2. 参数:None
3. 返回值:None
*/
void initUART(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1时钟
/*********************GPIO Config***************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //发送管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //接收管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//485使能脚
/*
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//PA1 通用推挽输出
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOG,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_9);//默认接收状态
*/
//RS485_TX_EN=0;//默认为接收模式
//USART_DeInit(USART1);//复位串口1
//读FLASH第64页内容
u32 BaudRate=9600;
FLASH_ReadMoreData(0x800FC00,FLASH_data,2);
/********************UART Config*****************************/
switch (FLASH_data[0]) //波特率选择
{
case 1:BaudRate=4800;break;
case 2:BaudRate=9600;break;
case 3:BaudRate=14400;break;
case 4:BaudRate=19200;break;
case 5:BaudRate=38400;break;
case 6:BaudRate=57600;break;
case 7:BaudRate=115200;break;
default:BaudRate=9600;break;
}
if(FLASH_data[0]>0xff) FLASH_data[1]=0x0f; //设备地址初始化
USART_InitStructure.USART_BaudRate = BaudRate; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8bits数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1bit停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //发送接收均使能
USART_Init(USART1, &USART_InitStructure); //设置生效
USART_Cmd(USART1, ENABLE); //使能串口外设
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能串口接收中断
}
/****************************中断服务函数****************************************/
/**
* 功能:串口1中断服务函数
* 参数:None
* 返回值:None
*/
u8 RS485_RX_BUFF[2048]; //接收缓冲区2048字节
u16 RS485_RX_CNT=0; //接收计数器
void USART1_IRQHandler(void)
{
u8 res;
u8 err;
if(USART_GetITStatus(USART1, USART_IT_RXNE)) //判断接收数据寄存器是否有数据
{
if(USART_GetFlagStatus(USART1,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE))err=1;//帧错误或校验错误检测到噪音
else err=0;
res=USART_ReceiveData(USART1); //读接收到的字节,同时相关标志自动清除
if((RS485_RX_CNT<2047)&&(err==0))
{
RS485_RX_BUFF[RS485_RX_CNT]=res;
RS485_RX_CNT++;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除定时器溢出中断
TIM_SetCounter(TIM3,0);//当接收到一个新的字节,将定时器3复位为0,重新计时(相当于喂狗)
TIM_Cmd(TIM3,ENABLE);//开始计时
}
}
}
/******************************************************************
* 文件:NVIC.c
* 功能:实现NVIC相关函数
* 日期:2018-02-26
* 作者:zx
* 版本:Ver.1.0 | 最初版本
*
* Copyright (C) 2018 zx. All rights reserved.
*******************************************************************/
#include "NVIC/NVIC.h"
/**
* 功能:初始化NVIC,初始化内容包括:分配优先级分组号,分配对应中断优先级
* 参数:NVIC_PriorityGroup : 设置优先级分组号
* 参数值:NVIC_PriorityGroup_x
* 返回值:None
*/
void initNVIC(u32 NVIC_PriorityGroup)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup); //中断分组
setNVIC(USART1_IRQn,0,1,ENABLE); //使能串口1总中断
setNVIC(TIM3_IRQn,0,2,ENABLE); //使能定时器3总中断
}
/**
* 功能:设置对应外设中断
* 参数:InterruptNum: 对应中断的中断编号
* PreemptionPriority:指定抢占优先级
* SubPriority:指定子优先级
* NVIC_Sta:使能或者失能中断
* 返回值:None
*/
void setNVIC(u8 InterruptNum, u8 PreemptionPriority, u8 SubPriority, FunctionalState NVIC_Sta)
{
NVIC_InitTypeDef NVIC_InitStructure; //定义NVIC初始化结构体
NVIC_InitStructure.NVIC_IRQChannel = InterruptNum; //指定配置的中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PreemptionPriority; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = SubPriority; //设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = NVIC_Sta; //使/失能中断
NVIC_Init(&NVIC_InitStructure); //设置生效
}
#include "MCU_FLASH/MCU_FLASH.h"
//从指定地址开始读取多个数据
void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
{
uint16_t dataIndex;
for(dataIndex=0;dataIndex<countToRead;dataIndex++)
{
readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
}
}
//读取指定地址的半字(16位数据)
uint16_t FLASH_ReadHalfWord(uint32_t address)
{
return *(__IO uint16_t*)address;
}
//读取指定地址的全字(32位数据)
uint32_t FLASH_ReadWord(uint32_t address)
{
uint32_t temp1,temp2;
temp1=*(__IO uint16_t*)address;
temp2=*(__IO uint16_t*)(address+2);
return (temp2<<16)+temp1;
}
//从指定地址开始写入多个数据
void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite)
{
if(startAddress<FLASH_BASE||((startAddress+countToWrite*2)>=(FLASH_BASE+1024*FLASH_SIZE)))
{
return;//非法地址
}
FLASH_Unlock(); //解锁写保护
uint32_t offsetAddress=startAddress-FLASH_BASE; //计算去掉0X08000000后的实际偏移地址
uint32_t sectorPosition=offsetAddress/SECTOR_SIZE; //计算扇区地址,对于STM32f103C8T6为0~64
uint32_t sectorStartAddress=sectorPosition*SECTOR_SIZE+FLASH_BASE; //对应扇区的首地址
FLASH_ErasePage(sectorStartAddress);//擦除这个扇区
uint16_t dataIndex;
for(dataIndex=0;dataIndex<countToWrite;dataIndex++)
{
FLASH_ProgramHalfWord(startAddress+dataIndex*2,writeData[dataIndex]);
}
FLASH_Lock();//上锁写保护
}
/******************************************************************
* 文件:modbus.h
* 功能:声明modbus相关函数
* 日期:2019-6-25
* 作者:haoge
* 版本:Ver.1.0 | 最初版本
*
* Copyright (C) 2019 haoge. All rights reserved.
*******************************************************************/
#ifndef __MODBUS_H
#define __MODBUS_H
#include "stm32f10x.h"
//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //+12 的原因是ORD地址=基地址+偏移量12
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)
#define GPIOH_ODR_Addr (GPIOH_BASE+12)
#define GPIOI_ODR_Addr (GPIOI_BASE+12)
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //+8 的原因是IRD地址=基地址+偏移量8
#define GPIOB_IDR_Addr (GPIOB_BASE+8)
#define GPIOC_IDR_Addr (GPIOC_BASE+8)
#define GPIOD_IDR_Addr (GPIOD_BASE+8)
#define GPIOE_IDR_Addr (GPIOE_BASE+8)
#define GPIOF_IDR_Addr (GPIOF_BASE+8)
#define GPIOG_IDR_Addr (GPIOG_BASE+8)
#define GPIOH_IDR_Addr (GPIOH_BASE+8)
#define GPIOI_IDR_Addr (GPIOI_BASE+8)
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
#define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入
#define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
#define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
void Modbus_RegMap(void);
void RS485_Service(void);
void Modbus_01_Solve(void);
void Modbus_02_Solve(void);
void Modbus_03_Solve(void);
void Modbus_04_Solve(void);
void Modbus_05_Solve(void);
void Modbus_06_Solve(void);
void Modbus_15_Solve(void);
void Modbus_16_Solve(void);
extern void RS485_SendData(u8 *buff,u8 len);
#endif
2.Modbus寄存器和STM32单片机寄存器的映射关系函数
extern u16 FLASH_data[2];
//
vu32 *Modbus_InputIO[100]; //输入开关量寄存器指针(这里使用的是"位带"操作)
vu32 *Modbus_OutputIO[100]; //输出开关量寄存器指针(这里使用的是"位带"操作)
u16 *Modbus_HoldReg[1000]; //保持寄存器指针
u16 *Modbus_InputReg[1000]; //输入寄存器指针
u32 testData1=100,testData2=200,testData3=300,testData4=400;
u32 testData5=500,testData6=600,testData7=700,testData8=800;
/**
1. 功能:Modbus寄存器和STM32单片机寄存器的映射关系
2. 参数:无
3. 返回值:无
*/
void Modbus_RegMap(void)
{
//输入开关量寄存器指针指向
//&PEin(4):取PE4的地址,(vu32*)&PEin(4)将PE4地址强制转换为uw32类型的地址,Modbus_InputIO[0]=(vu32*)&PEin(4);
//将转换好的地址送给地址指针Modbus_InputIO[0];
Modbus_InputIO[0]=(vu32*)&PAin(8); //KEY0
Modbus_InputIO[1]=(vu32*)&PBin(10); //KEY1
//Modbus_InputIO[2]=(vu32*)&PEin(2); //KEY2
//Modbus_InputIO[3]=(vu32*)&PAin(0); //KEY3
//输出开关量寄存器指针指向
Modbus_OutputIO[0]=(vu32*)&PBout(1); //LED0
Modbus_OutputIO[1]=(vu32*)&PBout(11); //LED1
Modbus_OutputIO[2]=(vu32*)&PBout(14); //LED2
Modbus_OutputIO[3]=(vu32*)&PBout(15); //LED3
//保持寄存器指针指向
Modbus_HoldReg[0]=(u16*)&testData1; //测试数据1
Modbus_HoldReg[1]=(u16*)&testData2; //测试数据2
Modbus_HoldReg[2]=(u16*)&testData3; //测试数据3
Modbus_HoldReg[3]=(u16*)&testData4; //测试数据4
Modbus_HoldReg[4]=(u16*)&testData5; //测试数据5
Modbus_HoldReg[5]=(u16*)&testData6; //测试数据6
Modbus_HoldReg[6]=(u16*)&testData7; //测试数据7
Modbus_HoldReg[7]=(u16*)&testData8; //测试数据8
Modbus_HoldReg[8]=(u16*)&FLASH_data[0];//串口波特率
Modbus_HoldReg[9]=(u16*)&FLASH_data[1];//设备地址
//输入寄存器指针指向
Modbus_InputReg[0]=(u16*)&testData1; //测试数据1
Modbus_InputReg[1]=(u16*)&testData2; //测试数据2
Modbus_InputReg[2]=(u16*)&testData3; //测试数据3
Modbus_InputReg[3]=(u16*)&testData4; //测试数据4
Modbus_InputReg[4]=(u16*)&testData5; //测试数据5
Modbus_InputReg[5]=(u16*)&testData6; //测试数据6
Modbus_InputReg[6]=(u16*)&testData7; //测试数据7
Modbus_InputReg[7]=(u16*)&testData8; //测试数据8
}
extern u8 RS485_FrameFlag; //帧结束标记
u8 RS485_Addr=3; //从机地址
//u16 RS485_Frame_Distance=4; //数据帧最小间隔(ms),超过此时间则认为是下一帧
extern u8 RS485_RX_BUFF[2048]; //接收缓冲区2048字节
extern u16 RS485_RX_CNT; //接收计数器
u8 RS485_TX_BUFF[2048]; //发送缓冲区
u16 RS485_TX_CNT=0; //发送计数器
//RS485服务程序,用于处理接收到的数据(请在主函数中循环调用)
u16 startRegAddr;
u16 RegNum;
u16 calCRC;
/**
1. 功能:ModBus服务函数
2. 参数:无
3. 返回值:无
*/
void RS485_Service(void)
{
RS485_Addr=(u8)FLASH_data[1];//读取FLASH里设备地址
u16 recCRC;
if(RS485_FrameFlag==1)
{
if(RS485_RX_BUFF[0]==RS485_Addr)//地址正确
{
if((RS485_RX_BUFF[1]==01)||(RS485_RX_BUFF[1]==02)||(RS485_RX_BUFF[1]==03)||(RS485_RX_BUFF[1]==04)||(RS485_RX_BUFF[1]==05)||(RS485_RX_BUFF[1]==06)||(RS485_RX_BUFF[1]==15)||(RS485_RX_BUFF[1]==16))//功能码正确
{
startRegAddr=(((u16)RS485_RX_BUFF[2])<<8)|RS485_RX_BUFF[3];//获取寄存器起始地址
if(startRegAddr<1000)//寄存器地址在范围内
{
calCRC=getModbusCRC16(RS485_RX_BUFF,RS485_RX_CNT-2);//计算所接收数据的CRC
recCRC=RS485_RX_BUFF[RS485_RX_CNT-2]|(((u16)RS485_RX_BUFF[RS485_RX_CNT-1])<<8);//接收到的CRC(低字节在前,高字节在后)
if(calCRC==recCRC)//CRC校验正确
{
//toggleLED();
/
switch(RS485_RX_BUFF[1])//根据不同的功能码进行处理
{
case 01://读输出开关量
{
Modbus_01_Solve();
break;
}
case 02://读输入开关量
{
Modbus_02_Solve();
break;
}
case 03: //读多个保持寄存器
{
Modbus_03_Solve();
break;
}
case 04: //读多个输入寄存器
{
Modbus_04_Solve();
break;
}
case 05://写单个输出开关量
{
Modbus_05_Solve();
break;
}
case 06: //写单个保持寄存器
{
Modbus_06_Solve();
break;
}
case 15://写多个输出开关量
{
Modbus_15_Solve();
break;
}
case 16: //写多个保持寄存器
{
Modbus_16_Solve();
break;
}
}
//
}
else//CRC校验错误
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x04; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
else//寄存器地址超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
else//功能码错误
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x01; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
RS485_FrameFlag=0;//复位帧结束标志
RS485_RX_CNT=0;//接收计数器清零
//RS485_TX_EN=0;//开启接收模式
//GPIO_ResetBits(GPIOA,GPIO_Pin_9);//默认接收状态
}
}
//Modbus功能码01处理程序
//读输出开关量
void Modbus_01_Solve(void)//-------------------------程序已验证可用
{
u16 ByteNum;
u16 i;
RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
ByteNum=RegNum/8;//字节数
if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
for(i=0;i<RegNum;i++)
{
if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
RS485_TX_BUFF[3+i/8]|=((*Modbus_OutputIO[startRegAddr+i])<<7)&0x80;
if(i==RegNum-1)//发送到最后一个位了
{
if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
}
}
calCRC=getModbusCRC16(RS485_TX_BUFF,ByteNum+3);
RS485_TX_BUFF[ByteNum+3]=calCRC&0xFF;
RS485_TX_BUFF[ByteNum+4]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,ByteNum+5);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码02处理程序
//Modbus功能码02处理程序 -----必须先配置初始化按键才可以OK KEY_Init();
//读输入开关量
void Modbus_02_Solve(void)//-------------------------程序已验证可用
{
u16 ByteNum;
u16 i;
RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
ByteNum=RegNum/8;//字节数
if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
for(i=0;i<RegNum;i++)
{
if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
RS485_TX_BUFF[3+i/8]|=((*Modbus_InputIO[startRegAddr+i])<<7)&0x80;
if(i==RegNum-1)//发送到最后一个位了
{
if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
}
}
calCRC=getModbusCRC16(RS485_TX_BUFF,ByteNum+3);
RS485_TX_BUFF[ByteNum+3]=calCRC&0xFF;
RS485_TX_BUFF[ByteNum+4]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,ByteNum+5);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码03处理程序
//Modbus功能码03处理程序
//读保持寄存器
void Modbus_03_Solve(void) //-------------------------程序已验证可用
{
u8 i;
RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RegNum*2;
for(i=0;i<RegNum;i++)
{
RS485_TX_BUFF[3+i*2]=(*Modbus_HoldReg[startRegAddr+i]>>8)&0xFF; //先发送高字节
RS485_TX_BUFF[4+i*2]=*Modbus_HoldReg[startRegAddr+i]&0xFF; //后发送低字节
}
calCRC=getModbusCRC16(RS485_TX_BUFF,RegNum*2+3);
RS485_TX_BUFF[RegNum*2+3]=calCRC&0xFF;
RS485_TX_BUFF[RegNum*2+4]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,RegNum*2+5);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码04处理程序
//Modbus功能码04处理程序
//读输入寄存器
void Modbus_04_Solve(void) //-------------------------程序已验证可用
{
u8 i;
RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RegNum*2;
for(i=0;i<RegNum;i++)
{
RS485_TX_BUFF[3+i*2]=(*Modbus_HoldReg[startRegAddr+i]>>8)&0xFF; //先发送高字节
RS485_TX_BUFF[4+i*2]=*Modbus_HoldReg[startRegAddr+i]&0xFF; //后发送低字节
}
calCRC=getModbusCRC16(RS485_TX_BUFF,RegNum*2+3);
RS485_TX_BUFF[RegNum*2+3]=calCRC&0xFF;
RS485_TX_BUFF[RegNum*2+4]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,RegNum*2+5);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码05处理程序
//Modbus功能码05处理程序
//写单个输出开关量
void Modbus_05_Solve(void)//-------------------------程序已验证可用
{
if(startRegAddr<100)//寄存器地址在范围内
{
if((RS485_RX_BUFF[4]==0xFF)||(RS485_RX_BUFF[5]==0xFF)) *Modbus_OutputIO[startRegAddr]=0x01;
else *Modbus_OutputIO[startRegAddr]=0x00;
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
calCRC=getModbusCRC16(RS485_TX_BUFF,6);
RS485_TX_BUFF[6]=calCRC&0xFF;
RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,8);
}
else//寄存器地址超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码06处理程序
//Modbus功能码06处理程序
//写单个保持寄存器
void Modbus_06_Solve(void)//-------------------------程序已验证可用
{
*Modbus_HoldReg[startRegAddr]=RS485_RX_BUFF[4]<<8;//高字节在前 修改为高字节在前,低字节在后
*Modbus_HoldReg[startRegAddr]|=((u16)RS485_RX_BUFF[5]);//低字节在后
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
calCRC=getModbusCRC16(RS485_TX_BUFF,6);
RS485_TX_BUFF[6]=calCRC&0xFF;
RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,8);
if(startRegAddr==8||startRegAddr==9)
FLASH_WriteMoreData(0x800FC00,FLASH_data,2);
}
Modbus功能码15处理程序
//Modbus功能码15处理程序
//写多个输出开关量
void Modbus_15_Solve(void)//-------------------------程序已验证可用
{
u16 i;
RegNum=(((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
{
for(i=0;i<RegNum;i++)
{
if(RS485_RX_BUFF[7+i/8]&0x01) *Modbus_OutputIO[startRegAddr+i]=0x01;
else *Modbus_OutputIO[startRegAddr+i]=0x00;
RS485_RX_BUFF[7+i/8]>>=1;//从低位开始
}
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
calCRC=getModbusCRC16(RS485_TX_BUFF,6);
RS485_TX_BUFF[6]=calCRC&0xFF;
RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,8);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}
Modbus功能码16处理程序
//Modbus功能码16处理程序
//写多个保持寄存器
void Modbus_16_Solve(void)//-------------------------程序已验证可用
{
u8 i;
RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
{
for(i=0;i<RegNum;i++)
{
*Modbus_HoldReg[startRegAddr+i]=RS485_RX_BUFF[7+i*2]<<8;//高字节在前 修改为高字节在前,低字节在后
*Modbus_HoldReg[startRegAddr+i]|=((u16)RS485_RX_BUFF[8+i*2]);//低字节在后
}
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
calCRC=getModbusCRC16(RS485_TX_BUFF,6);
RS485_TX_BUFF[6]=calCRC&0xFF;
RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
RS485_SendData(RS485_TX_BUFF,8);
}
else//寄存器地址+数量超出范围
{
RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
RS485_TX_BUFF[2]=0x02; //异常码
RS485_SendData(RS485_TX_BUFF,3);
}
}