本帖最后由 aple0807 于 2021-1-25 20:23 编辑
今天测试下工作中最常用的通信协议-modbus。
管脚配置:
static gpio_init_cfg_type gpio_cfg[] =
{
//UART1
{GPIOA, GPIO_Mode_AF_PP, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_9}, //u1-TX
{GPIOA, GPIO_Mode_IN_PU, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_10}, //u1-RX
//ADC
{GPIOA, GPIO_Mode_IN_ANALOG, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_0}, //ADC
//LED
{GPIOD, GPIO_Mode_OUT_PP, GPIO_MaxSpeed_50MHz, 1, GPIO_Pins_13 | GPIO_Pins_14 | GPIO_Pins_15}, //LED
{GPIOD, GPIO_Mode_AF_PP, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_15}, //LED-PWM
//UART8
{GPIOE, GPIO_Mode_AF_PP, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_1}, //u8-TX
{GPIOE, GPIO_Mode_IN_PU, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_0}, //u8-RX
//UART6
{GPIOC, GPIO_Mode_AF_PP, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_6}, //u6-TX
{GPIOC, GPIO_Mode_IN_PU, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_7}, //u6-RX
//SPI1
{GPIOA, GPIO_Mode_AF_PP, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_5 | GPIO_Pins_7}, //SPI1-SCK MOSI
{GPIOA, GPIO_Mode_IN_PU, GPIO_MaxSpeed_50MHz, 0, GPIO_Pins_6}, //SPI1-MISO
{GPIOA, GPIO_Mode_OUT_PP, GPIO_MaxSpeed_50MHz, 1, GPIO_Pins_15}, //SPI1-SS
};
for(index=0; index < sizeof(gpio_cfg)/sizeof(gpio_init_cfg_type);index++)
{
GPIO_InitType GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = gpio_cfg[index].mode;
GPIO_InitStructure.GPIO_MaxSpeed = gpio_cfg[index].speed;
GPIO_InitStructure.GPIO_Pins = gpio_cfg[index].pin;
GPIO_Init(gpio_cfg[index].port, &GPIO_InitStructure);
if((gpio_cfg[index].mode == GPIO_Mode_OUT_OD) || (gpio_cfg[index].mode == GPIO_Mode_OUT_PP))
{
if(gpio_cfg[index].val_init)
{
GPIO_SetBits(gpio_cfg[index].port, gpio_cfg[index].pin);
}
else
{
GPIO_ResetBits(gpio_cfg[index].port, gpio_cfg[index].pin);
}
}
}
测试开通两个通道,一路主机-UART6,一路从机-UART8。
协议解析用现成的库,只需要编写底层数据收发文件。
#include "sys_ext.h"
#include "os_obj.h"
#include "mbapp.h"
#define mbPORT USART6
#define mbURT_IRQn USART6_IRQn
#define mbISR USART6_IRQHandler
#define RCU_USART RCC_APB2PERIPH_USART6
#define RCU_USARTRST RCC_APB2PERIPH_USART6
#define mbObj mb.obj06
#define mbCmdBuff mb.CmdBuff06
static volatile uint8_t alDir;
//RS485 Dir-Pin
#define mbRxEnable()
#define mbTxEnable()
static void mbswTimerEnable(void);
static void mbEnable(uint8_t xRxEnable, uint8_t xTxEnable);
static void mbDataSend(void);
#include "mb_fun.h"
/*****************************************************************************//*!
*
* [url=home.php?mod=space&uid=247401]@brief[/url] port.
* [url=home.php?mod=space&uid=72445]@[/url] Pass/ Fail criteria: none
*****************************************************************************/
static void mb_port_pin_cfg(void)
{
intx_disable();
RCC_APB2PeriphClockCmd(RCU_USART, ENABLE);
RCC_APB2PeriphResetCmd(RCU_USARTRST, ENABLE);
RCC_APB2PeriphResetCmd(RCU_USARTRST, DISABLE);
intx_enable();
}
/*****************************************************************************//*!
*
* [url=home.php?mod=space&uid=247401]@brief[/url] RTU timer enable.
* [url=home.php?mod=space&uid=72445]@[/url] Pass/ Fail criteria: none
*****************************************************************************/
__STATIC_INLINE void mbswTimerEnable(void)
{
mbObj.bRtuTimerOn = 1;
mbObj.RtuTimerCnt = 0;
}
/*****************************************************************************//*!
* @brief UART0 RX interrupt routine.
* @brief Uart interrupt.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void mbISR()
{
volatile uint32_t IntSt;
volatile uint8_t Data;
mbObj.TimeOutCnt = 0;
IntSt = mbPORT->STS;
if (IntSt & USART_STS_RDNE)
{
Data = mbPORT->DT;
mbObj.RunSt.bits.PortSt = 1;
if (MB_RX_RCVEND == mbObj.RcvSt)
return;
if (mbObj.SndSt != MB_TX_IDLE)
return;
mbObj.RcvSt = MB_RX_RCV; //指示正在接收数据
mbswTimerEnable();
if (mbObj.RcvCnt >= MB_BUFF_SIZE)
return; //指针越界检查
if ((IntSt & (USART_STS_PERR | USART_STS_FERR | USART_STS_ORERR)) != 0) //帧错误 //只读,由硬件管理
{
if (mbObj.RcvCnt >= 1)
{
mbObj.ErrSt.bits.ErrHal = 1;
}
}
mbObj.AduBuff[mbObj.RcvCnt++] = Data;
}
else if ((mbObj.SndSize <= mbObj.SndCnt) && (IntSt & USART_STS_TRAC))
{
mbObj.SndSt = MB_TX_IDLE; //发送结束
if (mbObj.RunSt.bits.MasterMode) //主机进入接收等待状态
{
mbObj.RcvSt = MB_RX_WAIT;
}
mbEnable(ENABLE, DISABLE);
}
else if (IntSt & USART_STS_TDE)
{
if (mbObj.SndSize > mbObj.SndCnt)
{
mbPORT->DT = mbObj.AduBuff[mbObj.SndCnt++];
}
else //写缓冲区结束
{
mbPORT->CTRL1 &= ~((uint32_t)(USART_CTRL1_TDEIEN));
mbPORT->CTRL1 |= (uint32_t)(USART_CTRL1_TRACIEN);
}
}
else
{
}
cpu_data_sync();
}
/*****************************************************************************//*!
*
* @brief mb object init.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void mbObjInit(mb_mode_type mbMode)
{
mb_obj_init(&mbObj);
mbObj.AduSend = &mbDataSend;
mbObj.RunSt.bits.RTUMode = 1;
mbObj.Fun.Slave.RegCoilsCB = &eMBRegCoilsCB;
mbObj.Fun.Slave.RegDiscreteCB = &eMBRegDiscreteCB;
mbObj.Fun.Slave.RegHoldingCB = &eMBRegHoldingCB;
mbObj.Fun.Slave.RegInputCB = &eMBRegInputCB;
mmb_cmd_buff_init(&mbObj, mbCmdBuff, 32, 16);
mbObj.Fun.Master.ErrCnt = 0;
mbObj.api = &mb_fun;
//SLAVE FUNCTION INIT
if (MB_RTU_SLAVE == mbMode)
{
}
else
{
//MASER FUNCTION INIT
mbObj.RunSt.bits.MasterMode = 1;
}
}
/*****************************************************************************//*!
* @brief com modle init .
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void mb06_Init(mb_mode_type mbMode, uint32_t ulBaudRate, mb_parity_type eParity)
{
uint32_t usTimerT35_50us;
USART_InitType cfg;
mb_port_pin_cfg();
mbObjInit(mbMode);
cfg.USART_BaudRate = ulBaudRate;
cfg.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
cfg.USART_HardwareFlowControl = 0;
if (MB_PAR_NONE == eParity)
{
cfg.USART_Parity = USART_Parity_No;
cfg.USART_StopBits = USART_StopBits_2;
cfg.USART_WordLength = USART_WordLength_8b;
}
else if (MB_PAR_NONE_1S == eParity)
{
cfg.USART_Parity = USART_Parity_No;
cfg.USART_StopBits = USART_StopBits_1;
cfg.USART_WordLength = USART_WordLength_8b;
}
else if (MB_PAR_ODD == eParity)
{
cfg.USART_Parity = USART_Parity_Odd;
cfg.USART_StopBits = USART_StopBits_1;
cfg.USART_WordLength = USART_WordLength_9b;
}
else
{
cfg.USART_Parity = USART_Parity_Even;
cfg.USART_StopBits = USART_StopBits_1;
cfg.USART_WordLength = USART_WordLength_9b;
}
if (ulBaudRate > 19200)
{
usTimerT35_50us = 35; /* 1800us. */
mbObj.RtuTimerSv = 3;
}
else
{
usTimerT35_50us = (7UL * 220000UL) / (2UL * ulBaudRate);
mbObj.RtuTimerSv = usTimerT35_50us / 20 + 2;
}
USART_Init(mbPORT, &cfg);
USART_Cmd(mbPORT, ENABLE);
nvic_irq_set(mbURT_IRQn, 0x0F, 1);
mbEnable(ENABLE, DISABLE);
}
/*****************************************************************************//*!
*
* @brief Uart En or Dis.
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void mbEnable(uint8_t xRxEnable, uint8_t xTxEnable)
{
volatile uint8_t u8Temp;
if (xRxEnable)
{
mbRxEnable();
mbPORT->CTRL1 &= ~((uint32_t)USART_CTRL1_TDEIEN | USART_CTRL1_TRACIEN);
mbPORT->CTRL1 |= (uint32_t)USART_CTRL1_REN | USART_CTRL1_UEN;
u8Temp = mbPORT->DT;
while ((mbPORT->STS & (USART_STS_RDNE | USART_STS_ORERR)) != 0)
{
u8Temp = mbPORT->DT;
__DSB();
}
mbPORT->CTRL1 |= USART_CTRL1_RDNEIEN;
}
else if (xTxEnable)
{
mbPORT->CTRL1 &= ~((uint32_t)USART_CTRL1_RDNEIEN | USART_CTRL1_REN);
mbPORT->CTRL1 |= (uint32_t)USART_CTRL1_TEN | USART_CTRL1_UEN;
mbTxEnable();
mbPORT->DT = mbObj.AduBuff[mbObj.SndCnt++];
mbPORT->CTRL1 |= (uint32_t)(USART_CTRL1_TDEIEN);
}
else
{
}
}
/*****************************************************************************//*!
*
* @brief Send data.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void mbDataSend(void)
{
mbEnable(DISABLE, ENABLE);
}
RTU定时使用软件定时器,这样可以避免硬件资源不足,方便添加多通道。
/**
* @brief app timer isr
* @param None
* @retval None
*/
void AdcInsertedHandle(void);
void app_timer_isr(void)
{
APP_TIM->STS = 0;
sys_st.u_tick++;
AdcInsertedHandle();
if(com.obj01.fun)
com.obj01.fun->timer_handle();
if(mb.obj08.api)
mb.obj08.api->rtu_timer_run();
if(mb.obj06.api)
mb.obj06.api->rtu_timer_run();
cpu_data_sync();
}
modbus任务配置两条静态处理命令,分别为读取8字和写8字。
根据测试平均每秒执行指令30+次。
void modbus_task (void const *argument)
{
mb_obj_init(&mb.obj08);
//从机配置
mb08_Init(MB_RTU_SLAVE, mb_baud_tab[MB_BAUD_19200], MB_PAR_NONE);
mb.obj08.SlaveId = 1;
mb.obj08.os_event_send = mb_os_send;
mb.obj08.os_rtu_end_event = 1;
mb.obj08.os_timeout_event = 2;
mb_obj_init(&mb.obj06);
//主机配置
mb06_Init(MB_RTU_MASTER, mb_baud_tab[MB_BAUD_19200], MB_PAR_NONE);
mb.obj06.SlaveId = 1;
mb.obj06.RtuRcvendDelaySet = 3;
mb.obj06.rcv_end_handle_comp = rcv_end_handle_comp;
mb.obj06.os_event_send = mb_os_send;
mb.obj06.os_rtu_end_event = 1;
mb.obj06.os_timeout_event = 2;
mb.obj06.api->stc_cmd_req(0, 1, FUN_CODE_WRITE_MULTIPLE_REG, mb_tst.pv_w, 0, 8, 0);
mb.obj06.api->stc_cmd_req(0, 1, FUN_CODE_READ_REG, mb_tst.pv_r, 0, 8, 0);
//任务处理
for(;;)
{
osSignalWait(0, ms_ticks(100));
mb_poll(&mb.obj08);
mb_poll(&mb.obj06);
}
}
附上测试工程,IAR8.40版以上可用
software.rar
(9.11 MB)
|
|