最近一直工作需要在学习modbus协议,小白一名吧,找了很多资料,但是依旧无从下手,最终借鉴着前辈的足迹,写下如下个人学习经验,仅供参考,欢迎指正!
首先,说一点modbus,这个看起来就头大,洋洋洒洒好多页,其实我想说一点,把它当作一个规定就好,按照它的要求去发送自己的功能代码。
一般模式:{0x00,0x03,0x00,0x05,0x00,0x03,0x00,0x00}; //从机地址,功能码,寄存器高,寄存器低,读寄存器个数高,读寄存器个数低,CRC低,CRC高--------具体这句代码是要做什么,这个就是modbus的内容,必须要知道(不知道就要去看噢!)。
直奔主题:思路和代码对应
第一步:拟定指令协议--其实就是我们要利用modbus来做什么。就是上面的一般模式那行(建议自己做一个文档来记录说明)。
第二步:找合适的数据结构来装我们要操作的数据(接收缓冲区)。
一般是结构体,根据自己需要来安排,如我的就是以下:
typedef struct
{
bool MesgRecvState;
bool MesgExecState;
uint16_t MesgLength;
uint8_t MesgData[MB_ADU_MAX_LEN];
}MBMESG_Typedef;
第三步:轮训发送指令。
while(1)
{
if(TIM2StateType.T2State==true)//0.5S
{
static uint16_t send_count=0;
if(send_count%3==0)
{
Modbus_Require_Communication(); //串口4与传感器--485(与本帖相关的是这个,另外两个是其他的)
}
else if(send_count%3==1)
{
IOBoard1_SendDataToIOBoard2(); //串口2与IO板2
}
else if(send_count%3==2)
{
IOBoard1_SendDataToIOBoard3(); //串口3与IO板3
}
send_count++;
TIM2StateType.T2State=false;
}
Modbus_Response_Communication();
ADU2_Response_Communication();
ADU3_Response_Communication();
if(TIM2StateType.T3State==true) //1.0S
{
IOBoard1_SendDatatoCKBoard(); //串口1与程控板
TIM2StateType.T3State=false;
}
//IWDG_Feed();
}
这个是一个主要的框架吧,“ Modbus_Require_Communication(); //串口4与传感器--485”,与本帖相关的是这个,另外两个是其他的通讯。
第四步:数据接收。
在上一步,我们把指令协议发出去了,就是说我们通过modbus协议向其他外设要东西了,那么其他设备遵循modbus协议就要返回相应的数据,于是,我们就要接收数据。(这是我们的重点)
我这里是通过串口中断来接收的:
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
{
recv_data = USART_ReceiveData(UART4);
Modbus_Message_Input_Data(&ModbusMessage, TIM7, recv_data); //具体在下面
USART_ClearITPendingBit(UART4, USART_IT_RXNE);
}
void Modbus_Message_Input_Data(MBMESG_Typedef *MBMESGx,TIM_TypeDef* TIMx, uint16_t Data)
{
if(MBMESGx->MesgRecvState==false)//接收缓冲区能接收吗?
{
if(MBMESGx->MesgLength<MB_ADU_MAX_LEN)//接收的数据长度,超过最大接收数据长度了吗?
{
if(MBMESGx->MesgLength==0)//第一个数据,要开启定时器
{
TIM_Cmd(TIMx, ENABLE);
}
MBMESGx->MesgData[MBMESGx->MesgLength]=Data;
MBMESGx->MesgLength++;
TIM_SetCounter(TIMx,0);//清定时器
}
else
{
MBMESGx->MesgRecvState=true;
}
}
通过定时器中断来判断(3.5t),接收一帧数据是否完成,如下:
void TIM7_IRQHandler(void)
{
if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
{
TIM_Cmd(TIM7,DISABLE); //关闭定时器7
TIM_SetCounter(TIM7,0);//定时器7清零
Modebus_Receive_Set(&ModbusMessage);
Modbus_ADU_Copy_Message(&ModbusMessage);
Modbus_Message_Rest(&ModbusMessage);
TIM_ClearITPendingBit(TIM7, TIM_IT_Update); //清除TIM7更新中断标志
}
}
中断中的函数:
void Modbus_ADU_Receive_Set(MBADU_Typedef *MBADUx)//置接收完成标志
{
MBADUx->ADURecvState=true;
}
void Modbus_ADU_Rest(MBADU_Typedef *MBADUx)//清接收缓冲
{
MBADUx->ADURecvState=false;
MBADUx->ADUExecState=false;
MBADUx->ADULength=0;
memset(MBADUx->ADUData,0,MB_ADU_MAX_LEN);
}
MBADU_Typedef *Modbus_Receive_Index(void)//找寻可接收结构体
{
uint8_t index=0;
uint8_t flag_sum=0;
for(index=0; index<MB_MAX_ADU_QUEUE; index++)
{
flag_sum = MBADUQueue[index].ADURecvState;//????????---flag_sum += MBADUQueue[index].ADURecvState;
}
if(flag_sum==MB_MAX_ADU_QUEUE)
{
return NULL;
}
for(index=0; index<MB_MAX_ADU_QUEUE; index++)
{
if(MBADUQueue[index].ADURecvState==false)
{
return &MBADUQueue[index];
}
}
return NULL;
}
void Modbus_ADU_Copy_Message(MBMESG_Typedef *MBMESGx)//数据赋值
{
MBADU_Typedef *ADUx=NULL;
ADUx=Modbus_Receive_Index();
if(ADUx==NULL) return;
ADUx->ADURecvState=MBMESGx->MesgRecvState;
ADUx->ADULength=MBMESGx->MesgLength;
ADUx->ADURecvState=true;
memcpy(ADUx->ADUData,MBMESGx->MesgData,MBMESGx->MesgLength);
}
其实以这步做的就是:通过数据帧之间的间隔来判断数据是否接收完成,接收完成之后,从接收缓冲区中把数据存到另外一个待处理缓冲区。
第五步:数据发送。
void Modbus_Response_Communication(void)
{
MBADU_Typedef *ExeADUx=NULL;
ExeADUx=ModBus_Execte_Index();
if(ExeADUx==NULL) return;
Modbus_ADU_Analysis(ExeADUx);
Modbus_ADU_Rest(ExeADUx);
}
MBADU_Typedef *ModBus_Execte_Index(void)//查询待处理缓冲区
{
uint8_t index=0;
for(index=0; index<MB_MAX_ADU_QUEUE; index++)
{
if(MBADUQueue[index].ADURecvState==true)
{
return &MBADUQueue[index];
}
}
return NULL;
}
void Modbus_ADU_Analysis(MBADU_Typedef *MBADUx)//对所接收的数据判断
{
if(Modbus_ReceiveIsOk(MBADUx)==true)
{
if(Modbus_LengthIsOk(MBADUx)==true)
{
if(Modbus_SlaveIsOk(MBADUx)==true)
{
if(Modbus_CRCIsOk(MBADUx)==true)
{
uint8_t control_code=0;
control_code=Modbus_ControlCode(MBADUx);
switch(control_code)
{
case MB_READ_MULTI_HOLDRESG:
Modbbus_Read_Multi_HoldRegs(&ModbusRegs, MBADUx);
break;
case MB_READ_MULTI_INPUTRESG:
Modbus_Read_Multi_InputRegs(&ModbusRegs, MBADUx);
break;
default:
break;
}
}
}
}
}
}
void Modbus_ADU_Rest(MBADU_Typedef *MBADUx)//清待处理缓冲区
{
MBADUx->ADURecvState=false;
MBADUx->ADUExecState=false;
MBADUx->ADULength=0;
memset(MBADUx->ADUData,0,MB_ADU_MAX_LEN);
}
至此,就结束一轮处理。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
这个是我自己写的,基于modbus-rtu轮训4个传感器的工程。我手里只有一个传感器,所以测试的时候也只能测一个。你可以看看。
一周热门 更多>