本次移植主要参考了如下内容:
https://blog.csdn.net/qq153471503/article/details/104840279
https://blog.csdn.net/childbor/article/details/124690719?spm=1001.2014.3001.5502
https://blog.csdn.net/childbor/article/details/123848831
由于工作原因接触了这款芯片,为了通信方便查阅了几种modbus开源的资料,freemodbus资料多一些,于是开始动手分别在 HC32F460KETA STM32F030C8T6 做移植。
工具及资料下载
这里使用华大的工具生成工程项目,选择对号的芯片,这里我顺便生成了串口2及一路GPIO。
值得注意的是:TIMEOUT中断这个功能,我没有测试成功,所以项目中仅用了发送数据寄存器空中中断使能,及接收数据寄存器满中断使能;分频选择的DIV64,这里默认是DIV1但是串口不工作,选择了DIV16与DIV64都正常。
初始化定时器:对于不是十分了解freemodbus的伙伴们,最好看下上面链接中的文章,以便了解定时器的作用,避免走弯路。
这里使用的时钟是32.768,不分频;还开启了硬件清零事件使能,这个功能理解是匹配中断后,将计数器清零,测试时发现不开启该功能,同时也不手动进行清零,定时器容易重复,所以打开更省事。
时钟配置
这里我什么也没改,其中串口2使用的是PCLK1时钟,timer0使用的是XTAL32
项目生成
这里这个驱动库(DDL)位置是前文中官网下载,配置路径即可
生成代码
2.FreeModbus加入到项目中
freemodbus下载地址:GitHub - cwalter-at/freemodbus: BSD licensed MODBUS RTU/ASCII and TCP slave
为了方便快速,将以上文件合并到一个modbus文件夹内了
KEIL5导入
其中pack文件在前文提供的官网链接中也是有的,下载安装即可
打开生成好的项目
配置JLINK
芯片是M4系列的,这里找不到HC开头的就选M4即可,其他细节就不粘贴图了
将之前准备好的modbus文件夹放在本项目MDK目录下,并将目录加入项目
配置modbus文件夹的头文件
此时编译会出错,因为freemodbus中的断言引用问题,这里暂时去掉断言,在HC生成代码中,看到DDL_ASSERT();,还没有深入研究,网上粗略查了下,说建议用DDL_ASSERT();,暂未深究
修改定时器porttimer.c
/* ----------------------- Platform includes --------------------------------*/
#include "main.h" //引入main.h
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* INT_SRC_TMR0_1_CMP_B Callback. */
static void INT_SRC_TMR0_1_CMP_B_IrqCallback(void);
/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
stc_irq_signin_config_t stcIrq;
stcIrq.enIntSrc = INT_SRC_TMR0_1_CMP_B;
stcIrq.enIRQn = INT044_IRQn;
stcIrq.pfnCallback = &INT_SRC_TMR0_1_CMP_B_IrqCallback;
(void)INTC_IrqSignIn(&stcIrq);
NVIC_ClearPendingIRQ(INT044_IRQn);
NVIC_SetPriority(INT044_IRQn, DDL_IRQ_PRIO_15);
NVIC_EnableIRQ(INT044_IRQn);
stc_tmr0_init_t stcTmr0Init;
/* Enable AOS clock */
FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_AOS, ENABLE);
/* Timer0 trigger event set */
AOS_SetTriggerEventSrc(AOS_TMR0, EVT_SRC_PORT_EIRQ0);
/* Enable timer0_1 clock */
FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR0_1, ENABLE);
/************************* Configure TMR0_1_B***************************/
(void)TMR0_StructInit(&stcTmr0Init);
stcTmr0Init.u32ClockSrc = TMR0_CLK_SRC_XTAL32;
stcTmr0Init.u32ClockDiv = TMR0_CLK_DIV1;
stcTmr0Init.u32Func = TMR0_FUNC_CMP;
stcTmr0Init.u16CompareValue = 0x0100U;
(void)TMR0_Init(CM_TMR0_1, TMR0_CH_B, &stcTmr0Init);
DDL_DelayMS(1U);
TMR0_HWClearCondCmd(CM_TMR0_1, TMR0_CH_B, ENABLE);
DDL_DelayMS(1U);
TMR0_IntCmd(CM_TMR0_1, TMR0_INT_CMP_B, ENABLE);
DDL_DelayMS(1U);
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
/* TMR0 start counting */
TMR0_IntCmd(CM_TMR0_1, TMR0_INT_CMP_B, ENABLE);
TMR0_SetCountValue(CM_TMR0_1, TMR0_CH_B,0);
TMR0_Start(CM_TMR0_1, TMR0_CH_B);
}
inline void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
TMR0_IntCmd(CM_TMR0_1, TMR0_INT_CMP_B, DISABLE);
TMR0_Stop(CM_TMR0_1, TMR0_CH_B);
}
/* Create an ISR which is called whenever the timer has expired. This function
* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
* the timer has expired.
*/
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
/* INT_SRC_TMR0_1_CMP_B Callback. */
static void INT_SRC_TMR0_1_CMP_B_IrqCallback(void)
{
//add your codes here
prvvTIMERExpiredISR( );
}
修改串口portserial.c
#include "main.h" //引入main.h
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );
static void INT_SRC_USART2_RI_IrqCallback(void);
static void INT_SRC_USART2_TI_IrqCallback(void);
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if(xRxEnable){
USART_FuncCmd(CM_USART2, (USART_RX | USART_INT_RX), ENABLE);
}else{
while(SET == USART_GetStatus(CM_USART2,USART_FLAG_RX_FULL)){};
USART_FuncCmd(CM_USART2, (USART_RX | USART_INT_RX), DISABLE);
}
if(xTxEnable){
USART_FuncCmd(CM_USART2, (USART_TX | USART_INT_TX_EMPTY), ENABLE);
}else{
while(RESET == USART_GetStatus(CM_USART2,USART_FLAG_TX_CPLT)){};
USART_FuncCmd(CM_USART2, (USART_TX | USART_INT_TX_EMPTY), DISABLE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
stc_irq_signin_config_t stcIrq;
stcIrq.enIntSrc = INT_SRC_USART2_RI;
stcIrq.enIRQn = INT081_IRQn;
stcIrq.pfnCallback = &INT_SRC_USART2_RI_IrqCallback;
(void)INTC_IrqSignIn(&stcIrq);
NVIC_ClearPendingIRQ(INT081_IRQn);
NVIC_SetPriority(INT081_IRQn, DDL_IRQ_PRIO_15);
NVIC_EnableIRQ(INT081_IRQn);
stcIrq.enIntSrc = INT_SRC_USART2_TI;
stcIrq.enIRQn = INT080_IRQn;
stcIrq.pfnCallback = &INT_SRC_USART2_TI_IrqCallback;
(void)INTC_IrqSignIn(&stcIrq);
NVIC_ClearPendingIRQ(INT080_IRQn);
NVIC_SetPriority(INT080_IRQn, DDL_IRQ_PRIO_15);
NVIC_EnableIRQ(INT080_IRQn);
stc_usart_uart_init_t stcUartInit;
/* Enable USART2 clock */
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_USART2, ENABLE);
/************************* Configure USART2***************************/
USART_DeInit(CM_USART2);
(void)USART_UART_StructInit(&stcUartInit);
stcUartInit.u32ClockSrc = USART_CLK_SRC_INTERNCLK;
stcUartInit.u32ClockDiv = USART_CLK_DIV64;
stcUartInit.u32CKOutput = USART_CK_OUTPUT_DISABLE;
stcUartInit.u32Baudrate = 9600UL;
stcUartInit.u32DataWidth = USART_DATA_WIDTH_8BIT;
stcUartInit.u32StopBit = USART_STOPBIT_1BIT;
stcUartInit.u32Parity = USART_PARITY_NONE;
stcUartInit.u32OverSampleBit = USART_OVER_SAMPLE_16BIT;
stcUartInit.u32FirstBit = USART_FIRST_BIT_LSB;
stcUartInit.u32StartBitPolarity = USART_START_BIT_FALLING;
stcUartInit.u32HWFlowControl = USART_HW_FLOWCTRL_RTS;
USART_UART_Init(CM_USART2, &stcUartInit, NULL);
return TRUE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
/* Put a byte in the UARTs transmit buffer. This function is called
* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
* called. */
USART_WriteData(CM_USART2,ucByte);
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
返回UARTs接收缓冲区中的字节。这个函数在pxmbframecbbyterreceived()被调用后由协议栈调用。
*/
*pucByte = USART_ReadData(CM_USART2);
return TRUE;
}
/* Create an interrupt handler for the transmit buffer empty interrupt
* (or an equivalent) for your target processor. This function should then
* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
* a new character can be sent. The protocol stack will then call
* xMBPortSerialPutByte( ) to send the character.
*/
static void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
/* Create an interrupt handler for the receive interrupt for your target
* processor. This function should then call pxMBFrameCBByteReceived( ). The
* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
* character.
为目标处理器的接收中断创建一个中断处理程序。然后这个函数应该调用pxmbframecbbyterreceived()
的协议栈将调用xMBPortSerialGetByte()来检索的性格。
*/
static void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
/* INT_SRC_USART2_RI Callback. */
static void INT_SRC_USART2_RI_IrqCallback(void)
{
//add your codes here
prvvUARTRxISR();
}
static void INT_SRC_USART2_TI_IrqCallback(void)
{
//add your codes here
prvvUARTTxReadyISR();
}
还需要创建一个port.c文件,这里面主要放的是modbus标准协议返回的内容,是从本文开头第一个博客中抄写的。
#include "mb.h"
#include "mbport.h"
// 十路输入寄存器
#define REG_INPUT_SIZE 10
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
// 十路保持寄存器
#define REG_HOLD_SIZE 10
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
// 十路线圈
#define REG_COILS_SIZE 10
uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1};
// 十路离散量
#define REG_DISC_SIZE 10
uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1};
/// CMD4命令处理回调函数
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
USHORT usRegIndex = usAddress - 1;
// 非法检测
if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
{
return MB_ENOREG;
}
// 循环读取
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
usRegIndex++;
usNRegs--;
}
// 模拟输入寄存器被改变
for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
{
REG_INPUT_BUF[usRegIndex]++;
}
return MB_ENOERR;
}
/// CMD6、3、16命令处理回调函数
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
USHORT usRegIndex = usAddress - 1;
// 非法检测
if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
{
return MB_ENOREG;
}
// 写寄存器
if(eMode == MB_REG_WRITE)
{
while( usNRegs > 0 )
{
REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
pucRegBuffer += 2;
usRegIndex++;
usNRegs--;
}
}
// 读寄存器
else
{
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
usRegIndex++;
usNRegs--;
}
}
return MB_ENOERR;
}
/// CMD1、5、15命令处理回调函数
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
USHORT usRegIndex = usAddress - 1;
UCHAR ucBits = 0;
UCHAR ucState = 0;
UCHAR ucLoops = 0;
// 非法检测
if((usRegIndex + usNCoils) > REG_COILS_SIZE)
{
return MB_ENOREG;
}
if(eMode == MB_REG_WRITE)
{
ucLoops = (usNCoils - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = *pucRegBuffer++;
ucBits = 0;
while(usNCoils != 0 && ucBits < 8)
{
REG_COILS_BUF[usRegIndex++] = (ucState >> ucBits) & 0X01;
usNCoils--;
ucBits++;
}
ucLoops--;
}
}
else
{
ucLoops = (usNCoils - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = 0;
ucBits = 0;
while(usNCoils != 0 && ucBits < 8)
{
if(REG_COILS_BUF[usRegIndex])
{
ucState |= (1 << ucBits);
}
usNCoils--;
usRegIndex++;
ucBits++;
}
*pucRegBuffer++ = ucState;
ucLoops--;
}
}
return MB_ENOERR;
}
/// CMD2命令处理回调函数
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
USHORT usRegIndex = usAddress - 1;
UCHAR ucBits = 0;
UCHAR ucState = 0;
UCHAR ucLoops = 0;
// 非法检测
if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
{
return MB_ENOREG;
}
ucLoops = (usNDiscrete - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = 0;
ucBits = 0;
while(usNDiscrete != 0 && ucBits < 8)
{
if(REG_DISC_BUF[usRegIndex])
{
ucState |= (1 << ucBits);
}
usNDiscrete--;
usRegIndex++;
ucBits++;
}
*pucRegBuffer++ = ucState;
ucLoops--;
}
// 模拟离散量输入被改变
for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
{
REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
}
return MB_ENOERR;
}
最后是main.c的内容,由于工具生成的初始化,都在main.c中,上面两段代码中的初始化就是在main.c中迁移过去的。
#include "main.h"
#include "mb.h"
#include "mbport.h"
/* ----------------------- Defines ------------------------------------------*/
/* ----------------------- Static variables ---------------------------------*/
/*******************************************************************************
* Global variable definitions (declared in header file with 'extern')
******************************************************************************/
/*******************************************************************************
* Local function prototypes ('static')
******************************************************************************/
/*******************************************************************************
* Local variable definitions ('static')
******************************************************************************/
/*******************************************************************************
* Function implementation - global ('extern') and local ('static')
******************************************************************************/
//Clock Config
static void App_ClkCfg(void)
{
/* Set bus clock div. */
CLK_SetClockDiv(CLK_BUS_CLK_ALL, (CLK_HCLK_DIV1 | CLK_EXCLK_DIV2 | CLK_PCLK0_DIV1 | CLK_PCLK1_DIV2 | \
CLK_PCLK2_DIV4 | CLK_PCLK3_DIV4 | CLK_PCLK4_DIV2));
/* sram init include read/write wait cycle setting */
SRAM_SetWaitCycle(SRAM_SRAM_ALL, SRAM_WAIT_CYCLE1, SRAM_WAIT_CYCLE1);
SRAM_SetWaitCycle(SRAM_SRAMH, SRAM_WAIT_CYCLE0, SRAM_WAIT_CYCLE0);
/* flash read wait cycle setting */
EFM_SetWaitCycle(EFM_WAIT_CYCLE5);
/* XTAL config */
stc_clock_xtal_init_t stcXtalInit;
(void)CLK_XtalStructInit(&stcXtalInit);
stcXtalInit.u8State = CLK_XTAL_ON;
stcXtalInit.u8Drv = CLK_XTAL_DRV_HIGH;
stcXtalInit.u8Mode = CLK_XTAL_MD_OSC;
stcXtalInit.u8StableTime = CLK_XTAL_STB_2MS;
(void)CLK_XtalInit(&stcXtalInit);
/* MPLL config */
stc_clock_pll_init_t stcMPLLInit;
(void)CLK_PLLStructInit(&stcMPLLInit);
stcMPLLInit.PLLCFGR = 0UL;
stcMPLLInit.PLLCFGR_f.PLLM = (1UL - 1UL);
stcMPLLInit.PLLCFGR_f.PLLN = (50UL - 1UL);
stcMPLLInit.PLLCFGR_f.PLLP = (2UL - 1UL);
stcMPLLInit.PLLCFGR_f.PLLQ = (2UL - 1UL);
stcMPLLInit.PLLCFGR_f.PLLR = (2UL - 1UL);
stcMPLLInit.u8PLLState = CLK_PLL_ON;
stcMPLLInit.PLLCFGR_f.PLLSRC = CLK_PLL_SRC_XTAL;
(void)CLK_PLLInit(&stcMPLLInit);
/* UPLL config */
stc_clock_pllx_init_t stcUPLLInit;
(void)CLK_PLLxStructInit(&stcUPLLInit);
stcUPLLInit.PLLCFGR = 0UL;
stcUPLLInit.PLLCFGR_f.PLLM = (2UL - 1UL);
stcUPLLInit.PLLCFGR_f.PLLN = (60UL - 1UL);
stcUPLLInit.PLLCFGR_f.PLLP = (2UL - 1UL);
stcUPLLInit.PLLCFGR_f.PLLQ = (2UL - 1UL);
stcUPLLInit.PLLCFGR_f.PLLR = (5UL - 1UL);
stcUPLLInit.u8PLLState = CLK_PLLX_ON;
(void)CLK_PLLxInit(&stcUPLLInit);
/* 3 cycles for 126MHz ~ 200MHz */
GPIO_SetReadWaitCycle(GPIO_RD_WAIT3);
/* Switch driver ability */
PWC_HighSpeedToHighPerformance();
/* Set the system clock source */
CLK_SetSysClockSrc(CLK_SYSCLK_SRC_PLL);
}
//Port Config
static void App_PortCfg(void)
{
/* GPIO initialize */
stc_gpio_init_t stcGpioInit;
/* PB0 set to GPIO-Output */
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinDir = PIN_DIR_OUT;
stcGpioInit.u16PinAttr = PIN_ATTR_DIGITAL;
(void)GPIO_Init(GPIO_PORT_B, GPIO_PIN_00, &stcGpioInit);
GPIO_SetFunc(GPIO_PORT_C,GPIO_PIN_03,GPIO_FUNC_36);//USART2-TX
GPIO_SetFunc(GPIO_PORT_A,GPIO_PIN_00,GPIO_FUNC_37);//USART2-RX
}
/**
* @brief Main function of the project
* @param None
* @retval int32_t return value, if needed
*/
int32_t main(void)
{
/* Register write unprotected for some required peripherals. */
LL_PERIPH_WE(LL_PERIPH_ALL);
//Clock Config
App_ClkCfg();
//Port Config
App_PortCfg();
eMBErrorCode eStatus;
eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
/* Enable the Modbus Protocol Stack. */
eStatus = eMBEnable( );
GPIO_SetPins(GPIO_PORT_B, GPIO_PIN_00);
/* Register write protected for some required peripherals. */
LL_PERIPH_WP(LL_PERIPH_ALL);
for (;;) {
eMBPoll( );
}
}
main.c中main函数加入的freemodbus内容
eMBErrorCode eStatus;
eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
/* Enable the Modbus Protocol Stack. */
eStatus = eMBEnable( );
for (;;) {
eMBPoll( );
}
最后测试成功
以上移植过程,皆是摸索中前进的结果,如有错误,请伙伴们纠正。