文档章节

移植Modbus到STM32F103(2):移植FreeModbus到usart3并运行示例代码

K
 Konstantine
发布于 02/22 03:17
字数 1187
阅读 23
收藏 0

FreeModbus是Modbus的一个被广泛移植的实现。其源码在github,最新版是1.6。

FreeModbus支持Modbus功能码里的0x01~0x06,0x0F~0x11和0x17,对其他功能码比如异常诊断和事件计数等并没有提供支持,但并不影响Modbus的使用。

另外,FreeModbus仅提供了服务器(从机)的实现,客户端(主机)的实现可以在github上找到一些。

FreeModbus的文件主要在两个文件夹里,一个在/modbus/,一个在/demo/BARE/。前一个文件夹是协议的上层功能,包括协议的几个应用函数,基本不用修改。第二个文件夹有一个demo.c,是一个运行示例,也不需要修改。需要修改的是/demo/BARE/port/里的内容,这几个文件的功能完成了硬件的配置,包括串口和定时器的初始化,以及中断函数等。

对于STM32,FreeModbus的源码并没有给出示例,不过网上的相关移植已经有很多了,很容易就能找到,所以只需要复制/demo/BARE/port里的内容,然后稍微修改一下就好了。

一个个文件来说。

第一个是port.h,只需要包含库文件"stm32f10x.h",并增加临界区的进入和离开指令即可。我这里使用的是__set_PRIMASK函数,关闭除了NMI和硬fault以外的中断。

#define ENTER_CRITICAL_SECTION( )   __set_PRIMASK(1); //disable interrupts
#define EXIT_CRITICAL_SECTION( )    __set_PRIMASK(0); //enable interrupts

第二个是portserial.c,需要填满几个串口的相关函数。

使能或失能串口发送完成中断和串口接收中断:

void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    if (xRxEnable == TRUE)
    {
        USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    }
    else if (xRxEnable == FALSE)
    {
        USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
    }
    if (xTxEnable == TRUE)
    {
        USART_ITConfig(USART3, USART_IT_TC, ENABLE);
    }
    else if (xTxEnable == FALSE)
    {
        USART_ITConfig(USART3, USART_IT_TC, DISABLE);
    }
}

初始化串口3和相应引脚,初始化NVIC。为了方便,这里就不管串口号、数据位、校验位了,只有波特率可以起作用:

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    
    USART_InitTypeDef USART_InitStructure;
    
    USART_InitStructure.USART_BaudRate = ulBaudRate;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART3, &USART_InitStructure);
    
    USART_Cmd(USART3, ENABLE);
    
    vMBPortSerialEnable(FALSE, FALSE);
    
    NVIC_InitTypeDef  NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel          = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd       = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority  = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority  = 1;
    NVIC_Init(&NVIC_InitStructure);

    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_SendData(USART3, 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_ReceiveData(USART3);
    return TRUE;
}

串口中断函数,只需要判断中断类型,并调用FreeModbus已经实现好的prvvUARTTxReadyISR和prvvUARTRxISR函数即可。注意到发送状态机在发送完毕后会关闭串口中断,所以不需要清零TC位;STM32F103的manual中讲到,TC不需要软清零,只需要读一下TC位,下次写TDR寄存器时就会自动清零;如果手动清零了TC位,那么下次要发送时,即使使能了TCIE,会因为TC=0无法进入中断。所以这里只读了一下TC寄存器,并不清零。另外因为STM32F103使能RXNE后,ORE也会触发中断,所以需要做相应的清除操作,否则就会卡在中断里:

void  USART3_IRQHandler( void )
{
    if(USART_GetITStatus(USART3, USART_IT_TC) == SET)
    {
        prvvUARTTxReadyISR();
    }

    if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
    {
        prvvUARTRxISR();
    }
    else
    {
        USART_ClearITPendingBit(USART3, USART_IT_ORE);
    }
}

第三个是porttimer.c,也有几个函数需要填空。

初始化定时器和NVIC。这里用的是定时器2,挂在APB1上,我的APB1总线时钟是36MHz,所以预分频器设为36MHz/20kHz=1800:

BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    
    TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = 1800 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    vMBPortTimersDisable();
    
    NVIC_InitTypeDef NVIC_InitStructure;
    
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_Init(&NVIC_InitStructure);
    
    return TRUE;
    
}

使能定时器(注意到这两个函数定义成了内嵌函数,可以让定时器启动和关闭更快):

inline void
vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
    vMBPortTimersDisable();
    TIM_Cmd(TIM2, DISABLE);
    TIM_SetCounter(TIM2, 0);
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM2, ENABLE);
}

失能定时器:

inline void
vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */
    TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
    TIM_Cmd(TIM2, DISABLE);
}

定时器中断函数:

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        TIM_ClearFlag(TIM2, TIM_FLAG_Update);
        prvvTIMERExpiredISR();
    }
}

使用Modbus Poll来测试demo.c的运行情况,配置如下:

结果应该能看到地址为999的寄存器数据在变化,其他两个寄存器值都是0,说明协议已经移植成功了。

© 著作权归作者所有

共有 人打赏支持
K
粉丝 1
博文 13
码字总数 8909
作品 0
朝阳
私信 提问
移植Modbus到STM32F103(4):串口数据长度和校验的支持

在移植Modbus到STM32F103(2):移植FreeModbus到usart3并运行示例代码里,为了方便,没有移植数据位和校验位。这两个其实是很容易移植的。 先说数据位,官方宣称STM32F103支持8位和9位数据位...

Konstantine
03/03
0
0
armink/FreeModbus_Slave-Master-RTT-STM32

FreeModbus V1.6 主机使用说明 一、简述 FreeModbus是一款开源的Modbus协议栈,但是只有从机开源,主机源码是需要收费的。同时网上也没有发现比较好的开源的Modbus主机协议栈,所以才开发这款...

armink
2015/11/15
0
0
基于stm32的modbus协议移植

在移植过程中得益于这两篇blog,大家可以参考下。 http://ntn314.blog.163.com/blog/static/161743584201233084434579/ http://bbs.eeworld.com.cn/thread-362508-1-1.html 这两篇博客上对m......

xiaocui911
2015/11/10
0
0
NuttX 6.21 发布,实时嵌入式系统

NuttX 6.21 扩展移植到 NXP LPC43xx, the STMicro STM32 family, and the Microchip PIC32 families. 支持 FreeModBus,修复了很多 bug。 Nuttx 是一个实时嵌入式操作系统(RTOS),它有一个...

oschina
2012/08/27
1K
5
RT-Thread 1.2.0 正式版本发布

RT-Thread 1.2.0正式版本发布,实现roadmap中提到的大部分内容 1,文档方面已完成《RT-Thread编程手册》,同时还有论坛上jiezhi童鞋的《一起来学RT-Thread系列连载教程》 2,BSP分支方面新增...

lgnq
2014/01/06
2.9K
2

没有更多内容

加载失败,请刷新页面

加载更多

winscp中使用sudo的方法

用截图了解如何在 WinSCP 中使用 sudo。 首先你需要检查你尝试使用 WinSCP 连接的 sftp 服务器的二进制文件的位置。 你可以使用以下命令检查 SFTP 服务器二进制文件位置: [root@kerneltalk...

Linux就该这么学
20分钟前
1
0
四、MyBatis中查询执行流程

一、查询执行大致流程 在MyBatis中,查询执行的大致流程如下:

yangjianzhou
27分钟前
1
0
系统幂等设计

前言 幂等简单的定义: 系统中的多次操作,不管多少次,都应该产生一样的效果,或返回一样的效果。 比如实际的业务请求为创建一个活动,理论上需要根据业务形态开发幂等创建活动的接口,这样...

春哥大魔王的博客
51分钟前
2
0
DevSecOps 运维模式中的安全性

导读 本文想从技术的角度谈谈我对云计算数据中心 DevSecOps 运维模式中的安全性的理解,和过去几年我在云服务业务连续性管理方面的探索。 现在公有云服务商都不约而同地转向 DevSecOps 模式。...

问题终结者
54分钟前
0
0
java 基础脑图 转载来的

NotFound403
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部