打印
[APM32F0]

APM32F030移植freemodbus

[复制链接]
1119|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Alden|  楼主 | 2023-3-16 10:39 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Alden 于 2023-3-16 10:39 编辑

#技术资源#
FreeModbus简介
FreeMODBUS是一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另外一层是网络层。
FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。
接下来基于其他的移植教程,将Freemodbus移植到APM32F030中,软件库基于极海SDK 《APM32F0xx_SDK_v1.7》
Freemodbus的源码可以直接在其官网:freemodbus官网 下载从机代码。
所需的文件只用modbus文件夹和demo里面BARE文件夹里的port文件。
实际使用中还需要对源文件做许多细节修改,可以直接使用修改好了的modbus进行接口移植。
参考来源:STM32 移植FreeModbus详细过程
将modbus的相关文件添加进APM32F030的例程中,即可进行下一步移植工作。
modbus通讯只需要使用一个串口和一个定时器,相关接口函数分别在portserial.c和porttimer.c文件中。
首先是portserial.c中串口的相关配置。
需要配置串口的初始化、中断处理、字节收发等。
串口初始化:
void USART1_Config(uint16_t buad)
{
   GPIO_Config_T gpioConfig;
    USART_Config_T usartConfigStruct;

    /* Enable GPIO clock */
    RCM_EnableAHBPeriphClock(MINI_COM1_TX_GPIO_CLK  | MINI_COM2_TX_GPIO_CLK);

    /* Enable COM1 or COM2 clock */
    RCM_EnableAPB2PeriphClock(MINI_COM1_CLK);
    RCM_EnableAPB2PeriphClock(MINI_COM2_CLK);

    /* Connect PXx to USARTx_Tx */
    GPIO_ConfigPinAF(MINI_COM1_TX_GPIO_PORT, MINI_COM1_TX_SOURCE, MINI_COM1_TX_AF);

    /* Connect PXx to USARRX_Rx */
    GPIO_ConfigPinAF(MINI_COM1_RX_GPIO_PORT, MINI_COM1_RX_SOURCE, MINI_COM1_RX_AF);

    /* Configure USART Tx as alternate function push-pull */
    gpioConfig.mode = GPIO_MODE_AF;
    gpioConfig.pin = MINI_COM1_TX_PIN;
    gpioConfig.speed = GPIO_SPEED_50MHz;
    gpioConfig.outtype = GPIO_OUT_TYPE_PP;
    gpioConfig.pupd = GPIO_PUPD_PU;
    GPIO_Config(MINI_COM1_TX_GPIO_PORT, &gpioConfig);

    /* Configure USART Rx as input floating */
    gpioConfig.pin  = MINI_COM1_RX_PIN;
    GPIO_Config(MINI_COM1_RX_GPIO_PORT, &gpioConfig);

    /* MINI_USARTs configured as follow: */
    /* BaudRate = 115200 baud */
    usartConfigStruct.baudRate = buad;
    /* Receive and transmit enabled */
    usartConfigStruct.mode     = USART_MODE_TX_RX;
    /* Hardware flow control disabled (RTS and CTS signals) */
    usartConfigStruct.hardwareFlowCtrl = USART_FLOW_CTRL_NONE;
    /* No parity */
    usartConfigStruct.parity   = USART_PARITY_NONE;
    /* One Stop Bit */
    usartConfigStruct.stopBits =  USART_STOP_BIT_1;
    /* Word Length = 8 Bits */
    usartConfigStruct.wordLength = USART_WORD_LEN_8B;
    /* USART_Config */
    USART_Config(MINI_COM1, &usartConfigStruct);

    /* Enable USART_Interrupt_RXBNEIE */
//  USART_EnableInterrupt(MINI_COM1, USART_INT_RXBNEIE);

    NVIC_EnableIRQRequest(MINI_COM1_IRQn, 2);

    /* Enable USART */
    USART_Enable(MINI_COM1);

}
中断处理:
void USART1_IRQHandler(void)
{
  //发生接收中断
  if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_RXBNE) == SET)
  {

    prvvUARTRxISR(); //串口接收中断调用函数
    //清除中断标志位   
    USART_ClearStatusFlag(USART1, USART_FLAG_RXBNE);   
  }
        
        if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_OVRE) == SET)
  {  
                                prvvUARTRxISR();         //串口发送中断调用函数
     USART_ClearStatusFlag(USART1, USART_FLAG_OVRE);

  }
  
  //发生完成中断
  if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_TXC)  == SET)
  {
    prvvUARTTxReadyISR();
    //清除中断标志
    USART_ClearStatusFlag(USART1, USART_FLAG_TXC);
  }
}
portserial.c文件的收发处理
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
        if(xRxEnable == TRUE)
        {
                //UART中断使能
                USART_EnableInterrupt(MINI_COM1, USART_INT_RXBNEIE);
        }
        else
        {
          //禁止接收和接收中断
                USART_DisableInterrupt(MINI_COM1, USART_INT_RXBNEIE);
        }
  //STM32串口发送中断使能
        if(xTxEnable == TRUE)
        {
          //使能发送中断
                USART_EnableInterrupt(MINI_COM1, USART_INT_TXCIE);
        }
        else
        {
    //禁止发送中断
                USART_DisableInterrupt(MINI_COM1, USART_INT_TXCIE);
        }
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
        
  USART1_Config((uint16_t)ulBaudRate);  
        
        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_TxData(USART1, 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.
     */
  *pucByte = USART_RxData(USART1);
        
        return TRUE;
}
porttimer.c文件中需要配置一个定时器,定时器时基为50us。
TMR14初始化:
void APM_MINI_TMR14_Init(uint16_t period)
{
    TMR_TimeBase_T  timeBaseConfig;

    /* Enable Clock */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR14);

    /* Set clockDivision = 1 */
    timeBaseConfig.clockDivision =  TMR_CKD_DIV1;
    /* Up-counter */
    timeBaseConfig.counterMode =  TMR_COUNTER_MODE_UP;
    /* Set divider = 2399.So TMR1 clock freq ~= 48/(2400) = 20kHZ */
    timeBaseConfig.div = (2400-1) ;
    /* Set counter = 0xffff */
    timeBaseConfig.period = period;
    /* Repetition counter = 0x0 */
   // timeBaseConfig.repetitionCounter =  0;

    TMR_ConfigTimeBase(TMR14, &timeBaseConfig);

    /* Enable update interrupt*/
  //  TMR_EnableInterrupt(TMR14, TMR_INT_UPDATE);
    NVIC_EnableIRQRequest(TMR14_IRQn, 2);

    /*  Enable TMR14  */
    TMR_Enable(TMR14);
}
TMR14中断函数:
void TMR14_IRQHandler(void)
{
            if (TMR_ReadIntFlag(TMR14, TMR_INT_FLAG_UPDATE)  != RESET)
    {
                                prvvTIMERExpiredISR();
             TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
                }
}
porttimer.c文件接口配置。
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
  APM_MINI_TMR14_Init(usTim1Timerout50us);
        
        return TRUE;
}


void vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */        
           TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
           TMR_EnableInterrupt(TMR14, TMR_INT_UPDATE);
     TMR_SetCounter(TMR14,0x0000);
           TMR_Enable(TMR14);
}

void vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */
        
                 TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
           TMR_DisableInterrupt(TMR14, TMR_INT_UPDATE);
     TMR_SetCounter(TMR14,0x0000);
           TMR_Disable(TMR14);
}
然后再定义各个模拟寄存器的地址和大小,补全补全输入寄存器操作函数、保持寄存器操作函数、线圈操作函数、离散寄存器函数
详细见prot.c文件。
然后完善main函数即可。
int main(void)
{


        eMBInit(MB_RTU, 0x01, 1, 9600, MB_PAR_NONE);  //初始化FreeModbus
        eMBEnable();  //启动FreeModbus
        while(1)
        {
                (void)eMBPoll();  //查询数据帧
        }
}
程序移植完成后即可使用modbus工具测试,配置ModbusRTU的校验方式。


然后就可以设置发送01 04 00 00 00 01,就会自动计算校验值发送出去,得到MCU回传的结果。

APM32F0xx_SDK_v1.7-Freemodbus.zip (1.45 MB) crc计算助手.zip (320.1 KB) freemodbus-master1.6.zip (4.24 MB)






使用特权

评论回复
沙发
tpgf| | 2023-4-10 14:30 | 只看该作者
FreeMODBUS 提供了RTU/ASCII 传输模式及TCP协议支持

使用特权

评论回复
板凳
nawu| | 2023-4-10 15:05 | 只看该作者
FreeModbus协议对硬件的需求非常少--基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。

使用特权

评论回复
地板
aoyi| | 2023-4-10 15:27 | 只看该作者
实际的存储器需求决定于所使用的Modbus模块的多少

使用特权

评论回复
5
zljiu| | 2023-4-10 15:54 | 只看该作者
FreeModbus是基于消息队列的协议。协议通过检测相应的消息来完成对应功能

使用特权

评论回复
6
gwsan| | 2023-4-10 16:21 | 只看该作者
对于FreeModbus的软件部分,仅仅需要一个简单的事件队列

使用特权

评论回复
7
tfqi| | 2023-4-10 16:31 | 只看该作者
硬件上需要一个异步串行接口,能够支持接收缓冲区满和发送缓存区空中断

使用特权

评论回复
8
chenjun89| | 2023-4-10 19:44 | 只看该作者
和标准modbus相比,有什么优势呢?

使用特权

评论回复
9
99288697| | 2024-10-28 07:52 | 只看该作者
航顺的F030能移植吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

37

主题

84

帖子

1

粉丝