找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4038|回复: 6
打印 上一主题 下一主题
收起左侧

我的ModBus主机-任务篇

  [复制链接]
跳转到指定楼层
楼主
ID:446156 发表于 2021-11-15 13:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
任务篇是建立在协议篇之上的。以我的项目为例,我有两个从机设备,一个是EPC(控制器控制电磁阀开度使电磁阀出口处的气压保持恒定)需要主机设定出口处气压和定时读取出口处实际气压;另一个是温湿压传感器变送器需要定时读取温湿度压数据。
我的理念是不管主机挂载几个从机设备,而是主机有几个任务。以上文为例,主机一共有三个任务,一是设定EPC出口处气压(写保持寄存器);二是读取EPC出口处气压(读输入寄存器);三是读取温湿压数据(读输入寄存器)。我们首先要建立这三个任务。我们根据从机名称来创建4个文件分别是"EPC.c"、"EPC.h"、"THP.c"和"THP.h"。按照惯例我们先来看"EPC.h"和"THP.h"。
  1. #ifndef __EPC_H
  2. #define __EPC_H

  3. #include "Header.h"
  4. #include "UartDebug.h"
  5. #include "Modbus_Master.h"
  6. #include "Delay.h"

  7. struct EpcPre
  8. {
  9.         struct ModbusMasterDevice *MMD;
  10.         uint8_t DeviceAddr;
  11.         int16_t P_Fb,P_Sta;
  12.         uint16_t Pset;
  13.         uint8_t S_P_En,R_En;
  14.         uint8_t DataUpdate;
  15.         uint8_t SetSuccess;
  16. };

  17. extern struct EpcPre EPC1;

  18. void EPCInit(void);
  19. uint8_t ReadEPC(void *Para);
  20. void ReadEPFEnable(struct EpcPre *EPF);
  21. uint8_t SetPreVal(void *Para);
  22. void SetPreValEnable(struct EpcPre *EPC,uint16_t Pre);

  23. #endif
复制代码
  1. #ifndef __THP_H
  2. #define __THP_H

  3. #include "Header.h"
  4. #include "UartDebug.h"
  5. #include "Modbus_Master.h"
  6. #include "Delay.h"

  7. struct TemHumPre
  8. {
  9.         struct ModbusMasterDevice *MMD;
  10.         uint8_t DeviceAddr;
  11.         float AirTemp;
  12.         float AirHun;
  13.         float AirPre;
  14.         uint8_t Enable;
  15.         uint8_t DataUpdate;
  16. };

  17. extern struct TemHumPre THP1;

  18. void THPInit(void);
  19. uint8_t ReadTHP(void *Para);
  20. void ReadTHPEnable(struct TemHumPre *THP);

  21. #endif
复制代码
这两个文件非常相似,下面我们来详细介绍一下。
略过头文件我们来看从机结构体。
“struct ModbusMasterDevice *MMD;” 从机挂载到的ModBus主机;
“uint8_t DeviceAddr;” 从机的设备地址;
“int16_t P_Fb,P_Sta;” “float AirTemp;”“float AirHun;”“float AirPre;”数据区,根据从机的需求而定;
“uint8_t S_P_En,R_En;”“uint8_t Enable; ”任务的使能标志,可以看到因为EPC有两个任务所以它有S_P_En和R_En两个使能标志,而THP只有一个任务所以它只有一个Enable;
“uint8_t DataUpdate;”读保持/输入寄存器成功标志;
“uint8_t SetSuccess;”写保持寄存器成功标志,THP没有相对应得任务所以也就没有该标志。
接下来我们来看具体得任务实现。
  1. #include "EPC.h"

  2. struct EpcPre EPC1;

  3. void EPCInit(void)
  4. {
  5.         EPC1.MMD = &MMDPort1;
  6.         EPC1.DeviceAddr = 0x01;
  7. }
  8. uint8_t ReadEPC(void *Para)
  9. {
  10.         struct EpcPre *EPC;
  11.        
  12.         EPC = (struct EpcPre *)Para;
  13.         if(ReadHoldInputReg(EPC->MMD,EPC->DeviceAddr,0x04,0,4) != 0)
  14.         {
  15.                 if(EPC->MMD->RIR_Update)
  16.                 {
  17.                         EPC->MMD->RIR_Update = 0;
  18.                         EPC->DataUpdate = 1;
  19.                         EPC->P_Fb = EPC->MMD->Input_Reg[0];
  20.                         EPC->P_Sta = EPC->MMD->Input_Reg[1];
  21.                 }
  22.                 EPC->R_En = 0;
  23.                 return 1;
  24.         }
  25.         return 0;
  26. }
  27. void ReadEPFEnable(struct EpcPre *EPC)
  28. {
  29.         EPC->R_En = 1;
  30. }
  31. uint8_t SetPreVal(void *Para)
  32. {
  33.         struct EpcPre *EPC;
  34.        
  35.         EPC = (struct EpcPre *)Para;
  36.         if(WriteHoldReg(EPC->MMD,EPC->DeviceAddr,0,1,&EPC->Pset) != 0)
  37.         {
  38.                 if(EPC->MMD->WHR_Success)
  39.                 {
  40.                         EPC->MMD->WHR_Success = 0;
  41.                         EPC->SetSuccess = 1;
  42.                 }
  43.                 EPC->S_P_En = 0;
  44.                 return 1;
  45.         }
  46.         return 0;
  47. }
  48. void SetPreValEnable(struct EpcPre *EPC,uint16_t Pre)
  49. {
  50.         EPC->S_P_En = 1;
  51.         EPC->Pset = Pre;
  52. }
复制代码
  1. #include "THP.h"

  2. struct TemHumPre THP1;

  3. void THPInit(void)
  4. {
  5.         THP1.MMD = &MMDPort1;
  6.         THP1.DeviceAddr = 0x03;
  7. }
  8. uint8_t ReadTHP(void *Para)
  9. {
  10.         struct TemHumPre *THP;
  11.        
  12.         THP = (struct TemHumPre*)Para;
  13.         if(ReadHoldInputReg(THP->MMD,THP->DeviceAddr,0x04,0,3) != 0)
  14.         {
  15.                 if(THP->MMD->RIR_Update)
  16.                 {
  17.                         THP->MMD->RIR_Update = 0;
  18.                         if(THP->MMD->Input_Reg[0] != 0xffff)
  19.                         {
  20.                                 THP->AirTemp = (float)(THP->MMD->Input_Reg[0]-10000)/10;
  21.                         }
  22.                         if(THP->MMD->Input_Reg[1] != 0xffff)
  23.                         {
  24.                                 THP->AirHun = (float)THP->MMD->Input_Reg[1]/10;
  25.                         }
  26.                         if(THP->MMD->Input_Reg[2] != 0xffff)
  27.                         {
  28.                                 THP->AirPre = (float)THP->MMD->Input_Reg[2]/10;
  29.                         }
  30.                 }
  31.                 THP->Enable = 0;
  32.                 return 1;
  33.         }
  34.         return 0;
  35. }
  36. void ReadTHPEnable(struct TemHumPre *THP)
  37. {
  38.         THP->Enable = 1;
  39. }
复制代码
整体来看,每个任务都分为任务主体和任务使能两个部分,任务主体由下一章要介绍的调度篇来调用,使能由用户来调用,比如THP需要每隔1S读取一次那么就使函数“void ReadTHPEnable(struct TemHumPre *THP)”每隔1S执行一次。
  1. static void Task_1000ms(void)
  2. {
  3.         ReadTHPEnable(&THP1);
  4. }
复制代码
而EPC需要500ms读取一次气压,则使函数“void ReadEPFEnable(struct EpcPre *EPC)”每隔500ms执行一次。
  1. static void Task_500ms(void)
  2. {
  3.         ReadEPFEnable(&EPC1);
  4. }
复制代码
对于写保持寄存器相关的任务我们可以顺道把要写的数据放在使能函数里,这样可以使写保持寄存器的任务主体函数更加简洁。
下面我们需要着重讲解一下任务主体的实现。
以读取EPC气压为例,任务会首先执行函数“ReadHoldInputReg()”并判断返回值,根据协议篇可以知道在等待从机应答的那段时间“ReadHoldInputReg()”会返回0,于是“ReadEPC()”也返回0告知上层函数目前正在等待从机应答;若“ReadHoldInputReg()”返回1则说明从机成功应答或者应答超时了,我们怎么知道到底是应答了还是超时了呢?这就需要读取变量RIR_Update,若为1则说明应答成功我们就需要把读到的数据进行保存和处理,RIR_Update也完成了它的使命所以将其置0,并将DataUpdate置1告知上层函数数据读取成功。从机无论是应答还是超时都需要将R_En置0并返回1这点至关重要。
所有的任务必须严格按照该格式来实现,否则会影响上层的调度工作。
至此任务篇就介绍完毕了。





评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:654585 发表于 2022-4-18 11:03 | 只看该作者
教程很好,学习学习,感谢!
回复

使用道具 举报

板凳
ID:27536 发表于 2023-5-12 16:43 | 只看该作者
crc没看到,
回复

使用道具 举报

地板
ID:50577 发表于 2024-7-10 15:32 | 只看该作者
这是哪个系类单片机的呢
回复

使用道具 举报

5#
ID:50577 发表于 2024-7-10 15:32 | 只看该作者
只是什么系列的单片机呢
回复

使用道具 举报

6#
ID:446156 发表于 2024-10-30 16:58 | 只看该作者

CRC在协议篇里
回复

使用道具 举报

7#
ID:446156 发表于 2024-10-30 16:59 | 只看该作者
linlin1 发表于 2024-7-10 15:32
只是什么系列的单片机呢

这些代码不分单片机,底层应用层分离的只需要修改一点点接口代码就行
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表