当前位置: 首页 > news >正文

潍坊市作风建设年网站北京网站建设优化

潍坊市作风建设年网站,北京网站建设优化,如何利用淘宝建设网站挣钱,做网站那个搜索引擎好FreeModbus 简介 FreeModbus 是一个免费的软件协议栈,实现了 Modbus 从机功能: 纯 C 语言支持 Modbus RTU/ASCII支持 Modbus TCP 本文介绍 Modbus RTU 移植。 移植环境: 裸机Keil MDK 编译器Cortex-M3 内核芯片(LPC1778/88&…

FreeModbus 简介

FreeModbus 是一个免费的软件协议栈,实现了 Modbus 从机功能:

  • 纯 C 语言
  • 支持 Modbus RTU/ASCII
  • 支持 Modbus TCP

本文介绍 Modbus RTU 移植。

移植环境:

  • 裸机
  • Keil MDK 编译器
  • Cortex-M3 内核芯片(LPC1778/88)

移植概述

1.体系架构相关

项目描述
INLINE宏,编译器相关,内联指令或关键字
PR_BEGIN_EXTERN_C
PR_END_EXTERN_C
宏,按照 C 代码编译
ENTER_CRITICAL_SECTION( )
EXIT_CRITICAL_SECTION( )
宏,进入临界区和退出临界区
BOOL
UCHAR
CHAR
USHORT
SHORT
ULONG
LONG
数据类型
TRUE
FALSE
宏,BOOL 类型变量的值

2.定时器
需要移植的定时器函数

定时器函数描述
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )初始化,由协议栈回调, usTim1Timerout50us 的单位是 50us
void vMBPortTimersEnable( )使能定时器,协议栈回调
定时器计数器清零,然后开始计数
void vMBPortTimersDisable( )禁止定时器,由协议栈回调
定时器计数器清零,停止计数
void prvvTIMERExpiredISR( void )通知协议栈定时器中断发生,需手动安装到定时器中断服务函数中

3.串口
需要移植的函数

定时器函数描述
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )初始化串口硬件,由协议栈回调
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能/禁止串口发送和接收,由协议栈回调
BOOL xMBPortSerialPutByte( CHAR ucByte )通过串口发送一字节数据
BOOL xMBPortSerialGetByte( CHAR * pucByte )从串口接收一字节数据
void prvvUARTRxISR( void )通知协议栈串口接收中断发生,协议栈会进行数据接收处理。需手动安装到串口接收中断服务函数中
void prvvUARTTxReadyISR( void )通知协议栈串口发送中断发生,协议栈会进行数据发送。需手动安装到串口发送中断服务函数中

4.事件
事件相关回调函数需要移植:

事件回调函数描述
BOOL xMBPortEventInit( void )初始化
BOOL xMBPortEventPost( eMBEventType eEvent )事件投递
可以在这个函数中解析事件,并执行自己的事件函数。
BOOL xMBPortEventGet( eMBEventType * eEvent )获取事件

mb_config.h 文件属于协议栈的一部分,直接修改不合理
assert,直接调用 C 标准库函数, 但这个依赖硬件

移植细节

并不是所有函数都需要重头编写,协议栈 \freemodbus\demo\BARE\port\ 文件夹下给出了移植框架:

port
|---- port.h :体系架构相关
|---- porttimer.c :定时器相关
|---- portserial.c :串口相关
|---- portevent.c :事件相关

1.体系架构
port.h 文件:

#include <assert.h>
#include <stdint.h>
#include "cmsis_compiler.h"#define	INLINE                      __INLINE
#define PR_BEGIN_EXTERN_C           extern "C" {
#define	PR_END_EXTERN_C             }#ifndef assert
#define assert(ignore) ((void)0)
#endif#define ENTER_CRITICAL_SECTION( )   EnterCriticalSection()
#define EXIT_CRITICAL_SECTION( )    ExitCriticalSection()typedef uint8_t BOOL;typedef unsigned char UCHAR;
typedef char CHAR;typedef uint16_t USHORT;
typedef int16_t SHORT;typedef uint32_t ULONG;
typedef int32_t LONG;#ifndef TRUE
#define TRUE            1
#endif#ifndef FALSE
#define FALSE           0
#endifvoid EnterCriticalSection(void);
void ExitCriticalSection(void);

进入和退出临界区函数,实际上是开关中断,这部分点击这里可以获取详细的信息。我们新建一个 port.c 文件,在这个文件中实现一个可以嵌套使用的进入和退出临界区代码:

#include "cmsis_compiler.h"static uint32_t nesting_count = 0;
static uint32_t old_state;void EnterCriticalSection(void)
{uint32_t cur_state;cur_state = __get_PRIMASK();__disable_irq();if(nesting_count == 0)old_state = cur_state;nesting_count ++;
}void ExitCriticalSection(void)
{nesting_count --;if(0 == nesting_count)__set_PRIMASK(old_state);
}

2.定时器
Modbus RTU 使用超时机制判断数据帧结束:串口超过 3.5 个字符传输时间没有收到数据,则认为一帧结束。
这需要一个硬件定时器。
协议栈会根据传入的波特率自动计算 3.5 个字符传输时间是多少,单位是 50us,简化后的代码如下所示:

/* If baudrate > 19200 then we should use the fixed timer values t35 = 1750us. * Otherwise t35 must be 3.5 times the character time.*/
if( ulBaudRate > 19200 )
{usTimerT35_50us = 35;       /* 1800us. */
}
else
{/* The timer reload value for a character is given by:** ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )*             = 11 * Ticks_per_1s / Baudrate*             = 220000 / Baudrate* The reload for t3.5 is 1.5 times this value and similary* for t3.5.*/usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
}
xMBPortTimersInit( ( USHORT ) usTimerT35_50us )

所以就可以根据传入的 3.5 个字符传输时间 usTimerT35_50us 来初始化硬件定时器。我的系统刚好有个 50us 中断一次的定时器,所以我直接使用这个定时器来移植,移植代码在 porttime.c 文件中:

#include <stdbool.h>
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"static bool IsTimerEnable = false;
static USHORT Timerout50usCount = 0;
static USHORT Timerout50usCountCur = 0;/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{Timerout50usCount = usTim1Timerout50us;IsTimerEnable = false;return TRUE;
}inline void
vMBPortTimersEnable(  )
{/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */IsTimerEnable = true;Timerout50usCountCur = 0;
}inline void
vMBPortTimersDisable(  )
{/* Disable any pending timers. */IsTimerEnable = false;Timerout50usCountCur = 0;
}/*需手动安装到定时器中断服务函数*/
void
vMBPortTimersISR(  )
{if(IsTimerEnable){Timerout50usCountCur ++;if(Timerout50usCountCur >= Timerout50usCount)prvvTIMERExpiredISR();}
}/* 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(  );
}

有一点我很好奇, 3.5 个字符传输时间 usTimerT35_50us 为什么要格式化成 50us 的倍数?
我注意到代码 xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) 在传递参数时进行了一次数据强制转换,也就是协议栈使用的 USHORT 数据类型,一般这个数据类型最大值是 65536,如果不转换成 50us 的倍数,低波特率(比如 1200bps )必然会出现数据溢出现象。
那协议栈为什么又非要使用 USHORT 数据类型呢?
不清楚,大概是当时主流 MCU 还不是 32 位的,USHORT 数据类型可以更快更节省 RAM 。

何时使能定时器?

  1. 启动协议栈(eMBRTUStart
  2. 接收到 1 字节数据(xMBRTUReceiveFSM):复位计数器,重新开始计时

何时关闭定时器?

  1. 停止协议栈(eMBRTUStop
  2. 超时发生(3.5 个字符传输时间):收到新的数据帧,停止计时

定时器与接收关系密切,参与接收状态机的状态迁移:
在这里插入图片描述
3.串口
串口用于收发数据。移植代码在 portserial.c 中:

#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );void down3_set_to_recv(void);
void down3_set_to_send(void);
void down3_put_byte( CHAR data);
void down3_get_byte(CHAR *pucByte);
void init_down3_uart2(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity);/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/if(xRxEnable){down3_set_to_recv();}if(xTxEnable){down3_set_to_send();prvvUARTTxReadyISR();}
}BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{init_down3_uart2(ucPORT, ulBaudRate, ucDataBits, eParity);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. */down3_put_byte(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.*/down3_get_byte(pucByte);return TRUE;
}/*需手动安装到串口接收中断服务函数*/
void
vMBPortSerialRecvISR(void)
{prvvUARTRxISR();
}/*需手动安装到串口发送中断服务函数*/
void
vMBProtSerialSendISR(void)
{prvvUARTTxReadyISR();
}/* 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.*/
static void prvvUARTRxISR( void )
{pxMBFrameCBByteReceived(  );
}

4.事件
协议栈使用前后台架构,中断产生 事件 ,主循环处理 事件

事件生产者消费者描述
EV_READY定时器中断 (porttimer.c)
prvvTIMERExpiredISR
主循环 (mb.c)
eMBPoll
协议栈初始化完毕
EV_FRAME_RECEIVED定时器中断 (porttimer.c)
prvvTIMERExpiredISR
主循环 (mb.c)
eMBPoll
接收到一帧数据
如果数据帧校验正确,则产生 EV_EXECUTE 事件
EV_EXECUTE主循环 (mb.c)
eMBPoll
主循环 (mb.c)
eMBPoll
解析命令,生成应答数据,添加 CRC ,启动数据发送,数据将由串口发送中断发送
EV_FRAME_SENT串口发送中断 (portserial.c)
prvvUARTTxReadyISR
主循环 (mb.c)
eMBPoll
应答数据全部发送完成

事件一般用队列实现,以便消费者来不及处理事件时,暂时保存事件。对于简单应用,如果满足消费者消费事件的速度 大于等于 生产者生产事件的速度,则可以使用协议栈 \freemodbus\demo\BARE\port\portevent.c 文件中的源码,直接使用,不用修改:

#include "mb.h"
#include "mbport.h"/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{xEventInQueue = FALSE;return TRUE;
}BOOL
xMBPortEventPost( eMBEventType eEvent )
{xEventInQueue = TRUE;eQueuedEvent = eEvent;return TRUE;
}BOOL
xMBPortEventGet( eMBEventType * eEvent )
{BOOL            xEventHappened = FALSE;if( xEventInQueue ){*eEvent = eQueuedEvent;xEventInQueue = FALSE;xEventHappened = TRUE;}return xEventHappened;
}

在发送事件处就可以完成的功能,为什么要绕一圈非得用事件来完成呢?
方便解耦。
对于裸机环境,使用事件将处理过程从中断转移到主循环,从而使中断服务函数简单。
对于有操作系统的应用,事件可以方便的实现操作系统移植层,实现协议栈进程与中断之间的通讯。协议栈进程会因为等待事件而进入阻塞状态。

http://www.hkea.cn/news/451890/

相关文章:

  • 中国菲律宾历史战绩网站关键词优化工具
  • 西宁网站建设最好的公司哪家好优秀网站设计案例
  • 沧州做网站费用搜索引擎优化是做什么的
  • 社区网站推广方案线上运营的5个步骤
  • 湘潭学校网站建设 z磐石网络网站关键词优化教程
  • wordpress多程序用户同步汕头seo排名
  • 旅游网站 建设平台分析百度seo一本通
  • 怎么用dw做网站app开发网站
  • 昆山做网站的公司有哪些seo整站优化推广
  • 网站建设谈单情景对话青岛seo百科
  • 网站做自适应好不好网页分析报告案例
  • 大连手机自适应网站建设公司seo诊断站长
  • 有哪些好的网站十大电商代运营公司
  • 个人网页设计欣赏网站整站优化快速排名
  • 多少钱立案seo 公司
  • 医学类的网站做Google百度怎么优化排名
  • 手机网站怎样做枸橼酸西地那非片的功效与作用
  • 邯郸做wap网站的公司六六seo基础运营第三讲
  • 六安市建设银行网站seo编辑的工作内容
  • seo外包平台福州百度快照优化
  • 橙子建站广告怎么投放竞价网络推广
  • 中国公司查询网站网络公司起名
  • wordpress邮箱内容更改一键关键词优化
  • 楼市最新消息2022年房价走势seo网络推广经理
  • wordpress免费中文企业主题seo权重优化软件
  • 周口网站建设哪家好济南专业seo推广公司
  • 济南网站忧化怎么把抖音关键词做上去
  • 网站建设与维护的题目网站点击软件排名
  • 网站收录服务企业网络的组网方案
  • nba排名灰色词seo排名