FreeMODBUS庫 實現串列埠的選擇切換
FreeMODBUS庫 實現串列埠的選擇切換
日期 | 作者 | 版本 | 說明 |
---|---|---|---|
2020.12.16 | Hxj | V1.0 | 完成主體 |
目錄
介紹
在FreeMODBUS 庫中是不支援串列埠在執行的時候進行切換,同時也不支援多串列埠同時執行,必須得從新的配置才能進行串列埠的輸出的選擇。這樣就使得程式有他的侷限性。下文將會著重介紹如何進行選擇性配置串列埠的輸出。
一、FreeMODEBUS的串列埠配置
原始碼
標頭檔案
/*
* mb_user.h
*
* Created on: May 24, 2020
* Author: 81001
*/
#ifndef FREEMODBUS_USER_MB_USER_H_
#define FREEMODBUS_USER_MB_USER_H_
#include "mb_user.h"
#include "mb.h"
#include "mbplatform.h"
#include "mb_coilsreg.h"
#include "mb_discretereg.h"
#include "mb_inputreg.h"
#include "mb_holdingreg.h"
#include "gpio_ext.h"
//是否啟用FreeModbus模組
#define FREEMODBUS_ENABLE 1
//選擇PORT型別是否為RS485模組
#define FREEMODBUS_PORT_INTERFACE_RS485
//#define FREEMODBUS_PORT_INTERFACE_RS232
//設定FreeModbus從機的地址
#define FREEMODBUS_DEV_ADDR 0x01
#define RS485_T_CTRL GPIOE_OUT(15)
#define FREEMODBUS_TIMER 2
extern uint8_t FreeModbus_PortNum;
extern USART_TypeDef *USART_Modbus;
extern uint32_t FreeModbus_PortBaudrate;
extern eMBParity Freemodbus_PortParity;
extern uint8_t DipValue;
extern uint8_t PORT_Switch;
void User_MB_InitPortParam(uint8_t portNum, uint32_t baudRate, eMBParity parity);
void User_MB_Config();
void User_MB_ConfigNVIC();
void User_MB_InitRegs();
void User_MB_RefreshInputRegister();
void User_MB_RefreshCoilsRegister();
void User_MB_RefreshDiscreteRegister();
void User_MB_RefreshHoldingRegister();
#endif /* FREEMODBUS_USER_MB_USER_H_ */
原始檔
/*
* mb_user.c
*
* Created on: May 24, 2020
* Author: 81001
*/
#include "mb_user.h"
#include "sensor.h"
#include "user.h"
#include "tc.h"
#include "globalVariate.h"
#include "usart_ext.h"
#include "bldcmotor.h"
uint8_t FreeModbus_PortNum = 3;
USART_TypeDef *USART_Modbus = USART3;
uint32_t FreeModbus_PortBaudrate = 115200;
eMBParity Freemodbus_PortParity = MB_PAR_NONE;
uint8_t PORT_Switch = 0;
/**
* @brief 當freemodbus中出錯時,呼叫此函式進行錯誤處理
* @param file: 一般情況下,填入出錯所在的檔案 __FILE__
* @param line: 一般情況下,填入出錯所在的行 __LINE__
* @param function: 一般情況下,填入__ASSERT_FUNC
* @param error: freemodbus庫中傳入出錯引數(直接轉化為字串)
*/
void __attribute__((weak)) User_AssertFunc(const char *file, int line,
const char *function, const char *error)
{
/*not realize yet*/
}
/**
* @brief 初始化modbus的通訊串列埠引數
* 此函式並沒有真正的配置通訊埠,而是初始化modbus通訊埠引數的幾個全域性變數。
* 設計這些全域性變數的主要原因是,在整個專案工程中還涉及其他地方需要用到這些引數,
* 例如串列埠的中斷服務函式中需要根據本串列埠有無被佔用為modbus通訊埠而切換相應的服務內容,
* NVIC與外設配置也依賴這幾個變數的值實現通訊埠外設的自動配置。
* @param portNum: 串列埠號,1~5 對應 USART1~USART5外設
* @param baudRate: 波特率
* @param parity: 校驗,列舉變數
* @param MB_PAR_NONE: 無校驗
* @param MB_PAR_ODD: 奇校驗
* @param MB_PAR_EVEN: 偶校驗
*/
void __attribute__((weak)) User_MB_InitPortParam(uint8_t portNum,
uint32_t baudRate, eMBParity parity)
{
FreeModbus_PortNum = portNum;
FreeModbus_PortBaudrate = baudRate;
Freemodbus_PortParity = parity;
switch (portNum)
{
case 1:
USART_Modbus = USART1;
break;
case 2:
USART_Modbus = USART2;
break;
case 3:
USART_Modbus = USART3;
break;
#if defined(STM32F10X_HD) || defined(STM32F10X_HD_VL) || defined(STM32F10X_XL) || defined(STM32F10X_CL)
case 4:
USART_Modbus = UART4;
break;
case 5:
USART_Modbus = UART5;
break;
#endif
default:
break;
}
}
/**
* @brief 提供了一個快速配置並啟動Modbus功能的模板(採用預設配置)
* 需要注意的是為了使此函式儘可能的簡潔易用,並未設定函式引數。
* 使用者需要根據自己專案的實際情況修改函式中的各個配置引數。
*/
void User_MB_Config()
{
switch (BitData_PortCtrl.ByteData)
{
//Bit0 = 0, configure DB-9 port
case 0:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_DB9_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_DB9_TXD_PORT, USART_DB9_TXD_PIN, USART_DB9_RXD_PORT, USART_DB9_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
//Bit0 = 1, configure LCD port
case 1:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_LCD_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_LCD_TXD_PORT, USART_LCD_TXD_PIN, USART_LCD_RXD_PORT, USART_LCD_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
//Bit0 = 0 & Bit1 = 1, configure RS485 port
case 2:
//Bit0 = 1 & Bit1 = 1, configure RS485 port
case 3:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_485_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_485_TXD_PORT, USART_485_TXD_PIN, USART_485_RXD_PORT, USART_485_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
default:
break;
}
//初始化Modbus四種暫存器
User_MB_InitRegs();
//初始化Modbus
eMBInit(MB_RTU, FREEMODBUS_DEV_ADDR, FreeModbus_PortNum,
FreeModbus_PortBaudrate, Freemodbus_PortParity);
//使能Modbus功能
eMBEnable();
}
/**
* @brief 配置FreeModbus相關的中斷
* 主要是配置時基定時器的中斷與串列埠收發中斷
* 預設選擇NVIC_PriorityGroup_2中斷優先順序分組
*/
void __attribute__((weak)) User_MB_ConfigNVIC()
{
NVIC_InitTypeDef NVIC_InitStructure;
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
switch (FreeModbus_PortNum)
{
case 1:
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
break;
case 2:
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
break;
case 3:
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
break;
#if defined(STM32F10X_HD) || defined(STM32F10X_HD_VL) || defined(STM32F10X_XL) || defined(STM32F10X_CL)
case 4:
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
break;
case 5:
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
break;
#endif
default:
break;
}
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
switch (FREEMODBUS_TIMER)
{
case 1:
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
break;
case 2:
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
break;
case 3:
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
break;
case 4:
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
break;
default:
break;
}
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 完成各個暫存器的初始化
*/
void User_MB_InitRegs()
{
RegHoldingBuf_Init();
RegDiscreteBuf_Init();
RegCoilsBuf_Init();
RegInputBuf_Init();
}
/**
* @brief 重新整理輸入暫存器,將感測器資料賦值到輸入暫存器
* @param none
* @retval none
*/
void User_MB_RefreshInputRegister()
{
}
/**
* @brief 重新整理線圈暫存器,根據線圈暫存器的值執行對應動作
* @param none
* @retval none
*/
void User_MB_RefreshCoilsRegister()
{
//設定緩衝陣列,提高輪詢效率,只有緩衝陣列中的資料發生變化的時候,才會實際暫存器的讀寫操作
static uint8_t coilsReg_buffer[32] =
{ 2 };
(void) coilsReg_buffer;
//開啟串列埠切換
if (coilsReg_buffer[24] != CoilsReg_GetBit(24))
{
coilsReg_buffer[24] = CoilsReg_GetBit(24);
BitData_PortCtrl.BitData.bit1 = coilsReg_buffer[24];
}
}
/**
* @brief Refresh discrete registers
*/
void User_MB_RefreshDiscreteRegister()
{
//設定緩衝陣列,提高輪詢效率,只有緩衝陣列中的資料發生變化的時候,才會實際暫存器的讀寫操作
static uint8_t discreteReg_buffer[10] =
{ 99 };
(void) discreteReg_buffer;
}
/**
* @brief Refresh holding registers
*/
void User_MB_RefreshHoldingRegister()
{
}
1.串列埠的選擇配置函式 void User_MB_Config()
解析
BitData_PortCtrl.ByteData
與BitData_PortCtrl.BitData
共用一塊記憶體 所以可以直接用BitData_PortCtrl.ByteData
進行判斷。
/**
* @brief 提供了一個快速配置並啟動Modbus功能的模板(採用預設配置)
* 需要注意的是為了使此函式儘可能的簡潔易用,並未設定函式引數。
* 使用者需要根據自己專案的實際情況修改函式中的各個配置引數。
*/
void User_MB_Config()
{
switch (BitData_PortCtrl.ByteData)
{
//Bit0 = 0, configure DB-9 port
case 0:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_DB9_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_DB9_TXD_PORT, USART_DB9_TXD_PIN, USART_DB9_RXD_PORT, USART_DB9_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
//Bit0 = 1, configure LCD port
case 1:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_LCD_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_LCD_TXD_PORT, USART_LCD_TXD_PIN, USART_LCD_RXD_PORT, USART_LCD_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
//Bit0 = 0 & Bit1 = 1, configure RS485 port
case 2:
//Bit0 = 1 & Bit1 = 1, configure RS485 port
case 3:
//配置Modbus通訊串列埠的全域性引數
User_MB_InitPortParam(USART_485_NUM, 115200, MB_PAR_NONE);
//配置GPIO
USART_ConfigGPIO(USART_485_TXD_PORT, USART_485_TXD_PIN, USART_485_RXD_PORT, USART_485_RXD_PIN);
//配置NVIC
User_MB_ConfigNVIC();
break;
default:
break;
}
//初始化Modbus四種暫存器
User_MB_InitRegs();
//初始化Modbus
eMBInit(MB_RTU, FREEMODBUS_DEV_ADDR, FreeModbus_PortNum,
FreeModbus_PortBaudrate, Freemodbus_PortParity);
//使能Modbus功能
eMBEnable();
}
二.使用指南
思路說明:
本文實現的是通過使用硬體(撥碼開關)來實現主串列埠的選擇(DB9 或 LCD),主次串列埠的切換是通過一位線圈暫存器控制通過 寫入0 1進行主次串列埠的切換。
串列埠的配置通過呼叫 void User_MB_Config()
;
使用步驟
1.在程式初始化階段 通過一個位域操作 建立一個類似位操作的共用體,通過最小的空間來進行最多的操作。
typedef struct
{
uint8_t bit0:1;
uint8_t bit1:1;
uint8_t bit2:1;
uint8_t bit3:1;
uint8_t bit4:1;
uint8_t bit5:1;
uint8_t bit6:1;
uint8_t bit7:1;
} BitDataStruct;
typedef union
{
BitDataStruct BitData;
uint8_t ByteData;
} BitDataUnion;
2.在進入任務前 獲取撥碼開關的值將其儲存在BitData_PortCtrl.BitData.bit0
DipValue = User_GetDipSwitchValue();//讀取撥碼開關的值
BitData_PortCtrl.BitData.bit0 = (DipValue & 0x01);//將其儲存
//OFF==0 ON ==1
uint8_t User_GetDipSwitchValue()
{
uint8_t value = 0x00;
uint8_t dipSwitchBit1 = (~(*DIP_SWITCH_BIT_1)) & 0x01;
uint8_t dipSwitchBit2 = (~(*DIP_SWITCH_BIT_2)) & 0x01;
uint8_t dipSwitchBit3 = (~(*DIP_SWITCH_BIT_3)) & 0x01;
uint8_t dipSwitchBit4 = (~(*DIP_SWITCH_BIT_4)) & 0x01;
value = ((dipSwitchBit1 << 0) | (dipSwitchBit2 << 1) | (dipSwitchBit3 << 2) | (dipSwitchBit4 << 3)) & 0x0F;
return value;
}
3.在FreeRTOS系統中建立一個任務進行串列埠的選擇配置並進行輪詢Modbus。
void ModbusPoll_task(void *pvParameters)
{
User_MB_Config();//串列埠的初始化配置
TickType_t ticks = xTaskGetTickCount();
while (1)
{
eMBPoll();// modebus 的輪詢
//1ms 一個處理週期
vTaskDelayUntil( &ticks, 1 );
}
}
4.在重新整理線圈暫存器中 輪詢串列埠切換控制位是否發生改變。改變後將值賦值給BitData_PortCtrl.BitData.bit1
/**
* @brief 重新整理線圈暫存器,根據線圈暫存器的值執行對應動作
* @param none
* @retval none
*/
void User_MB_RefreshCoilsRegister()
{
//設定緩衝陣列,提高輪詢效率,只有緩衝陣列中的資料發生變化的時候,才會實際暫存器的讀寫操作
static uint8_t coilsReg_buffer[32] =
{ 2 };
(void) coilsReg_buffer;
//開啟串列埠切換
if (coilsReg_buffer[24] != CoilsReg_GetBit(24))
{
coilsReg_buffer[24] = CoilsReg_GetBit(24);
BitData_PortCtrl.BitData.bit1 = coilsReg_buffer[24];
}
}
5.在FreeRTOS系統中建立一個串列埠切換任務。輪詢BitData_PortCtrl.BitData.bit1
是否發生變化。改變後先將void ModbusPoll_task(void *pvParameters)
任務刪除 之後再進行建立新的void ModbusPoll_task(void *pvParameters)
任務這樣配置函式 void User_MB_Config()
將進行重新的選擇。
void PortSwitch_task(void *pvParameters)
{
static uint8_t Port=0;
TickType_t ticks = xTaskGetTickCount();
while(1)
{
if(Port != BitData_PortCtrl.BitData.bit1)
{
Port = BitData_PortCtrl.BitData.bit1;
taskENTER_CRITICAL(); //進入臨界區
eMBDisable();
vTaskDelete(ModbusPollTask_Handler); //刪除ModbusPollTask_Handler任務
xTaskCreate((TaskFunction_t )ModbusPoll_task,
(const char* )"ModbusPoll_task",
(uint16_t )MODBUS_POLL_STK_SIZE,
(void* )NULL,
(UBaseType_t )MODBUS_POLL_TASK_PRIO,
(TaskHandle_t* )&ModbusPollTask_Handler);
taskEXIT_CRITICAL(); //退出臨界區
}
vTaskDelayUntil( &ticks, 500);
}
}