zigbee Zstack 中串口操作的深度解析、一

最近在学习 TI 的 Z-stack 协议栈的过程中(1.4.3-1.2.1 版本,CC2430 处理器),对于协议栈串口操作的相
关问题一直是一知半解,同时在论坛上和 QQ 群里也总是有人问这个问题,也经常有人在串口应用中出问
题。所以最近抽点儿时间仔细研究了一下协议栈的串口操作部分函数,做一个随手笔记,以供大家参考。
为了说清楚协议栈的串口问题,我要先交代一下 CC2430 的 DMA 和中断问题
一、 DMA 控制器相关事宜
为了搞清楚 UART 的 DMA 操作,在弄 UART 之前,有必要把 DMA 的问题搞清楚。这里我不想把
CC2430 的 user guide 拿出来仔细说,有需要的就去看看吧。我这里从协议栈的 hal_dma.h 和 hal_dma.c 开
始,把 DMA 的一些基本事情交代了,好为后面的 UART 解释铺垫道路。
用 source insight 打开 hal_dma.h 文件,里面基本上是大量的宏定义、常数定义,在有就是一个结构体
typedef struct {
uint8 srcAddrH;
uint8 srcAddrL;
uint8 dstAddrH;
uint8 dstAddrL;
uint8 xferLenV;
uint8 xferLenL;
uint8 ctrlA;
uint8 ctrlB;
} halDMADesc_t;
还有两个变量:
extern halDMADesc_t dmaCh0;
extern halDMADesc_t dmaCh1234[4];
之外就是 void HalDmaInit( void )这个初始化 DMA 的函数。
这里所以要提一下变量 dmaCh0 和 dmaCh1234[4]以及 halDMADesc_t 这个结构体,是因为在 UART 部
分的驱动里面大量地用到了它们三个。我们先看结构体 halDMADesc_t,这里面主要定义的是 DMA 操作
过程中的源地址变量、目的地址变量(都是 16 位地址,用高低字节的两个 uint8 变量来组成)、要传输的数
据长度以及几个寄存器的控制变量。
为什么要这么做一个结构体呢?这个事情还是要从 CC2430 处理器 user guide 中 DMA 控制器操作说明
说起。我们先节选手册上关于 DMA 的几段描述:
[第一段: from TI CC2430 Data Sheet(rev.2.1) SWRS036F, Page 88 of 211]
在这里插入图片描述
[第二段: from TI CC2430 Data Sheet(rev.2.1) SWRS036F, Page 92 of 211]
在这里插入图片描述
仔细阅读这两段文档,大致意思是 DMA 通道被使用之前 must first be configured, 然后就要 must be
armed before any transfers are allowed to be initiated.再后,就是 trigger event occurs. 也就是说 DAM 通道要先配置,然后被武装起来(arm,我不知道该怎么翻译更恰当),之后就等待触发事件的到来并开始进行数据传输。对我们来说,如何配置 DMA 很重要,从上面的这段文档还可以看出:配置参数需要先放在一个 structure里面(也就是前面的 halDMADesc_t),并且每一个 DMA 通道需要一个这样的结构体。更重要的是: DMA通道 0 可以有一个单独的配置结构体, DMA 通道 1~4 的配置结构体必须位于连续的地址空间。这也就是为什么定义了 dmaCh0 结构体和 dmaCh1234[4]结构体数组。而这个 hal_dma.h 文件中定义的大量宏操作基本都是用来操作 dmaCh0 结构体及 dmaCh1234[4]这个结构体数组的。
再看一下 hal_dma.c 文件,发现很简单,只有那两个结构体的定义,再有就是 DMA 初始化函数的一
个简单实现。不过从这个简单实现来看,真正的结构体填充、硬寄存器操作统统都没有实现,这样的 DMA初始化大家肯定都不会满意的。那么????怎么回事儿?其实换一个角度想, DMA 是为外设和数据空间服务的,在这里, DMA 怎么知道自己该如何初始化啊?真正的初始化过程,包括如何确定源地址和目的地址,如何确定需要 DMA的数据长度这些,都应该在真正用到 DMA 的地方去初始化,去 arm,去 trigger
二、串口中断相关事宜
在 Z-STACK 中,可以通过预编译和#define 来选择是用 DMA 还是中断进行数据传输,前面我交代了
一下 DMA 的基本事情,接下来我们先对协议栈的中断问题简单啰嗦一点儿。
我们先看协议栈中 hal_uart.c 文件中的串口操作中断服务程序:
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR ) //uart0 接收
HAL_ISR_FUNCTION( halUart1RxIsr, URX1_VECTOR ) //uart1 接收
HAL_ISR_FUNCTION( halUart0TxIsr, UTX0_VECTOR ) //uart0 发送
HAL_ISR_FUNCTION( halUart1TxIsr, UTX1_VECTOR ) //uart1 发送
这四个是 uart0 和 uart1 的串口接收和发送中断服务程序,顺带提一下协议栈定义中断服务程序的方法
吧。这个要看一下 hal_mcu.h 文件:
#define HAL_ISR_FUNC_PROTOTYPE(f,v) _PRAGMA(vector=v) __near_func __interrupt void f(void)
#define HAL_ISR_FUNCTION(f,v) HAL_ISR_FUNC_PROTOTYPE(f,v);
从这两个#define 的定义可以了解到:
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )按照定义展开来就是:
_PRAGMA(URX0_VECTOR) __near_func __interrupt void halUart0RxIsr(void);
其中_PRAGMA 是编译器预定义的声明中断服务程序的一个方法, URX0_VECTOR 是中断编号(在
ioCC2430.h 文件中定义)。
对于有过 51 单片机开发经验的同学来说,了解这些就可以按照 ioCC2430.h 中给出的中断号为自己实
现 ISR 了,当然了,为了让中断服务程序能够工作,针对不同的片内外设还需要进行允许中断啊、中断方式设置啊这些操作,具体同学们看 datasheet 吧。
言归正传,回到我们这个笔记的初衷,串口耶!好,下面我们开始串口,里面会涉及到上面提到的
DMA 和 ISR 相关事宜,并且还会比较多地说。接下来,我分别按照 DMA 方式和 ISR 方式来讨论串口数据发送和接收过程,以此让各位同学对串口工作有更深的认识,至于串口数据收发过程中 OS 的消息传递过程我不想说了,论坛上已经有很多文档可以参看,具体如何操作接收数据的 buffer 和发送数据的 buffer 我也不想说的太细,否则这个文档要写几十页了,等有时间专门再针对那个问题讨论一下。

原创文章 81 获赞 48 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_15181569/article/details/93216032
今日推荐