標(biāo)題:
我的ModBus主機(jī)-任務(wù)篇
[打印本頁(yè)]
作者:
qinlu123
時(shí)間:
2021-11-15 13:27
標(biāo)題:
我的ModBus主機(jī)-任務(wù)篇
任務(wù)篇是建立在協(xié)議篇之上的。以我的項(xiàng)目為例,我有兩個(gè)從機(jī)設(shè)備,一個(gè)是EPC(控制器控制電磁閥開(kāi)度使電磁閥出口處的氣壓保持恒定)需要主機(jī)設(shè)定出口處氣壓和定時(shí)讀取出口處實(shí)際氣壓;另一個(gè)是溫濕壓傳感器變送器需要定時(shí)讀取溫濕度壓數(shù)據(jù)。
我的理念是不管主機(jī)掛載幾個(gè)從機(jī)設(shè)備,而是主機(jī)有幾個(gè)任務(wù)。以上文為例,主機(jī)一共有三個(gè)任務(wù),一是設(shè)定EPC出口處氣壓(寫(xiě)保持寄存器);二是讀取EPC出口處氣壓(讀輸入寄存器);三是讀取溫濕壓數(shù)據(jù)(讀輸入寄存器)。我們首先要建立這三個(gè)任務(wù)。我們根據(jù)從機(jī)名稱(chēng)來(lái)創(chuàng)建4個(gè)文件分別是"EPC.c"、"EPC.h"、"THP.c"和"THP.h"。按照慣例我們先來(lái)看"EPC.h"和"THP.h"。
#ifndef __EPC_H
#define __EPC_H
#include "Header.h"
#include "UartDebug.h"
#include "Modbus_Master.h"
#include "Delay.h"
struct EpcPre
{
struct ModbusMasterDevice *MMD;
uint8_t DeviceAddr;
int16_t P_Fb,P_Sta;
uint16_t Pset;
uint8_t S_P_En,R_En;
uint8_t DataUpdate;
uint8_t SetSuccess;
};
extern struct EpcPre EPC1;
void EPCInit(void);
uint8_t ReadEPC(void *Para);
void ReadEPFEnable(struct EpcPre *EPF);
uint8_t SetPreVal(void *Para);
void SetPreValEnable(struct EpcPre *EPC,uint16_t Pre);
#endif
復(fù)制代碼
#ifndef __THP_H
#define __THP_H
#include "Header.h"
#include "UartDebug.h"
#include "Modbus_Master.h"
#include "Delay.h"
struct TemHumPre
{
struct ModbusMasterDevice *MMD;
uint8_t DeviceAddr;
float AirTemp;
float AirHun;
float AirPre;
uint8_t Enable;
uint8_t DataUpdate;
};
extern struct TemHumPre THP1;
void THPInit(void);
uint8_t ReadTHP(void *Para);
void ReadTHPEnable(struct TemHumPre *THP);
#endif
復(fù)制代碼
這兩個(gè)文件非常相似,下面我們來(lái)詳細(xì)介紹一下。
略過(guò)頭文件我們來(lái)看從機(jī)結(jié)構(gòu)體。
“struct ModbusMasterDevice *MMD;” 從機(jī)掛載到的ModBus主機(jī);
“uint8_t DeviceAddr;” 從機(jī)的設(shè)備地址;
“int16_t P_Fb,P_Sta;” “float AirTemp;”“float AirHun;”“float AirPre;”數(shù)據(jù)區(qū),根據(jù)從機(jī)的需求而定;
“uint8_t S_P_En,R_En;”“uint8_t Enable; ”任務(wù)的使能標(biāo)志,可以看到因?yàn)镋PC有兩個(gè)任務(wù)所以它有S_P_En和R_En兩個(gè)使能標(biāo)志,而THP只有一個(gè)任務(wù)所以它只有一個(gè)Enable;
“uint8_t DataUpdate;”讀保持/輸入寄存器成功標(biāo)志;
“uint8_t SetSuccess;”寫(xiě)保持寄存器成功標(biāo)志,THP沒(méi)有相對(duì)應(yīng)得任務(wù)所以也就沒(méi)有該標(biāo)志。
接下來(lái)我們來(lái)看具體得任務(wù)實(shí)現(xiàn)。
#include "EPC.h"
struct EpcPre EPC1;
void EPCInit(void)
{
EPC1.MMD = &MMDPort1;
EPC1.DeviceAddr = 0x01;
}
uint8_t ReadEPC(void *Para)
{
struct EpcPre *EPC;
EPC = (struct EpcPre *)Para;
if(ReadHoldInputReg(EPC->MMD,EPC->DeviceAddr,0x04,0,4) != 0)
{
if(EPC->MMD->RIR_Update)
{
EPC->MMD->RIR_Update = 0;
EPC->DataUpdate = 1;
EPC->P_Fb = EPC->MMD->Input_Reg[0];
EPC->P_Sta = EPC->MMD->Input_Reg[1];
}
EPC->R_En = 0;
return 1;
}
return 0;
}
void ReadEPFEnable(struct EpcPre *EPC)
{
EPC->R_En = 1;
}
uint8_t SetPreVal(void *Para)
{
struct EpcPre *EPC;
EPC = (struct EpcPre *)Para;
if(WriteHoldReg(EPC->MMD,EPC->DeviceAddr,0,1,&EPC->Pset) != 0)
{
if(EPC->MMD->WHR_Success)
{
EPC->MMD->WHR_Success = 0;
EPC->SetSuccess = 1;
}
EPC->S_P_En = 0;
return 1;
}
return 0;
}
void SetPreValEnable(struct EpcPre *EPC,uint16_t Pre)
{
EPC->S_P_En = 1;
EPC->Pset = Pre;
}
復(fù)制代碼
#include "THP.h"
struct TemHumPre THP1;
void THPInit(void)
{
THP1.MMD = &MMDPort1;
THP1.DeviceAddr = 0x03;
}
uint8_t ReadTHP(void *Para)
{
struct TemHumPre *THP;
THP = (struct TemHumPre*)Para;
if(ReadHoldInputReg(THP->MMD,THP->DeviceAddr,0x04,0,3) != 0)
{
if(THP->MMD->RIR_Update)
{
THP->MMD->RIR_Update = 0;
if(THP->MMD->Input_Reg[0] != 0xffff)
{
THP->AirTemp = (float)(THP->MMD->Input_Reg[0]-10000)/10;
}
if(THP->MMD->Input_Reg[1] != 0xffff)
{
THP->AirHun = (float)THP->MMD->Input_Reg[1]/10;
}
if(THP->MMD->Input_Reg[2] != 0xffff)
{
THP->AirPre = (float)THP->MMD->Input_Reg[2]/10;
}
}
THP->Enable = 0;
return 1;
}
return 0;
}
void ReadTHPEnable(struct TemHumPre *THP)
{
THP->Enable = 1;
}
復(fù)制代碼
整體來(lái)看,每個(gè)任務(wù)都分為任務(wù)主體和任務(wù)使能兩個(gè)部分,任務(wù)主體由下一章要介紹的調(diào)度篇來(lái)調(diào)用,使能由用戶(hù)來(lái)調(diào)用,比如THP需要每隔1S讀取一次那么就使函數(shù)“
void ReadTHPEnable(struct TemHumPre *THP)
”每隔1S執(zhí)行一次。
static void Task_1000ms(void)
{
ReadTHPEnable(&THP1);
}
復(fù)制代碼
而EPC需要500ms讀取一次氣壓,則使函數(shù)“
void ReadEPFEnable(struct EpcPre *EPC)
”每隔500ms執(zhí)行一次。
static void Task_500ms(void)
{
ReadEPFEnable(&EPC1);
}
復(fù)制代碼
對(duì)于寫(xiě)保持寄存器相關(guān)的任務(wù)我們可以順道把要寫(xiě)的數(shù)據(jù)放在使能函數(shù)里,這樣可以使寫(xiě)保持寄存器的任務(wù)主體函數(shù)更加簡(jiǎn)潔。
下面我們需要著重講解一下任務(wù)主體的實(shí)現(xiàn)。
以讀取EPC氣壓為例,任務(wù)會(huì)首先執(zhí)行函數(shù)“
ReadHoldInputReg
()”并判斷返回值,根據(jù)協(xié)議篇可以知道在等待從機(jī)應(yīng)答的那段時(shí)間“
ReadHoldInputReg
()”會(huì)返回0,于是“
ReadEPC
()”也返回0告知上層函數(shù)目前正在等待從機(jī)應(yīng)答;若“
ReadHoldInputReg
()”返回1則說(shuō)明從機(jī)成功應(yīng)答或者應(yīng)答超時(shí)了,我們?cè)趺粗赖降资菓?yīng)答了還是超時(shí)了呢?這就需要讀取變量
RIR_Update
,若為1則說(shuō)明應(yīng)答成功我們就需要把讀到的數(shù)據(jù)進(jìn)行保存和處理,
RIR_Update
也完成了它的使命所以將其置0,并將
DataUpdate
置1告知上層函數(shù)數(shù)據(jù)讀取成功。從機(jī)無(wú)論是應(yīng)答還是超時(shí)都需要將R_En置0并返回1這點(diǎn)至關(guān)重要。
所有的任務(wù)必須嚴(yán)格按照該格式來(lái)實(shí)現(xiàn),否則會(huì)影響上層的調(diào)度工作。
至此任務(wù)篇就介紹完畢了。
作者:
LH4492
時(shí)間:
2022-4-18 11:03
教程很好,學(xué)習(xí)學(xué)習(xí),感謝!
作者:
qhp777
時(shí)間:
2023-5-12 16:43
crc沒(méi)看到,
作者:
linlin1
時(shí)間:
2024-7-10 15:32
這是哪個(gè)系類(lèi)單片機(jī)的呢
作者:
linlin1
時(shí)間:
2024-7-10 15:32
只是什么系列的單片機(jī)呢
作者:
qinlu123
時(shí)間:
2024-10-30 16:58
qhp777 發(fā)表于 2023-5-12 16:43
crc沒(méi)看到,
CRC在協(xié)議篇里
作者:
qinlu123
時(shí)間:
2024-10-30 16:59
linlin1 發(fā)表于 2024-7-10 15:32
只是什么系列的單片機(jī)呢
這些代碼不分單片機(jī),底層應(yīng)用層分離的只需要修改一點(diǎn)點(diǎn)接口代碼就行
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1