前言:因为在网上没有找到TI-RTOS的学习视频,虽然很多地方与free-RTOS相通,但是本人仍不想为了学习TI-RTOS先去学习freeRTOS,所幸关于TI-RTOS,ti公司给了许多相关的文档资料,虽然本人英文不太好,但没有办法,只能抱着试一试的心态,努力啃一啃,故在此做学习笔记。
TI-RTOS驱动程序有一个用于静态配置和所有驱动程序实现的一组api的公共框架。
我们应该首先理解通用框架,在了解了框架之后在去理解各个外设驱动实现的详细信息。
驱动对象的声明
所有TI-RTOS驱动程序都要求应用程序分配数据存储,并定义一组具有特定硬件属性的数据结构。驱动程序设计为两层层次结构(Interface和Implementation),以促进可伸缩的驱动程序添加和增强,同时提供一致的应用程序编程接口(fxnTable)
我们先来看一张官方手册给的驱动框架图:
其中:
- 红色箭头A->B代表A中调用了B中的APIs或者A中声明了B中的回调函数
- 蓝色箭头A->B代表在A中配置了B中定义的结构体
- 黑色箭头代表基本的硬件寄存器的调用
如上图所示,应用程序通过上层的驱动接口来与TI-RTOS交互。这种驱动接口通过配置一套存储着指向一或多个具体的底层驱动实现数据的指针的数据结构体来驱动具体的外设。驱动接口定义的数据结构体包含在Drivers目录中,而驱动的底层实现定义的结构体包含在想要的Drivers目录的子目录中,以对应【驱动名称+芯片系列名】作为文件名称。
这里先提一句,在TI-RTOS的例程中,main()函数中首先会进行开发板初始化(Board_init)操作,这里的初始化函数是在LAUNCHXL.c文件中实现的,开发板初始化函数做了三件事,1是上电时序的初始化,2是初始化一些所有线程都会用到的外设驱动(比如PIN),3是钩子函数的初始化(这里上电初始化和钩子初始化的原理都还不太懂)。
其中LAUNCHXL.c文件便是上图中的Board.c文件
-Board Initlization(sdk的例程中该文件为LAUNCHPADXL.c)
从上图中可以看到Board.c文件主要是被main()函数调用了开发板初始化函数,并且调用了Drive Interface中的驱动初始化函数,在少数情况下还会调用Driverlib中的某些初始化函数。事实便是如此,如果我们打开LAUNCHXL.c文件查看的话,我们可以看到在这个文件中除了开发板初始化函数外,还有各种外设驱动的config结构体,object和HWAttrs结构体的实例化。但这几个结构体是做什么的呢?别着急,我们接下来慢慢来学习。
注意:PIN的驱动配置与其余大部分外设相比都不太相同,且不同外设关于驱动的配置都稍有差异
-Driver Interface
Config结构体在Driver.h文件中被定义,在上图中该头文件被称为Driver Interface(下文成为驱动接口文件)。
Config结构体定义如下:
typedef struct Driver_Config{
Driver_FxnTable const *fxnTablePtr;
Void *object;
Void const *hwAttrs;
}Drive_Config;
若我们打开某一外设的Driver.h文件,可以看到这个文件除了一些驱动的控制函数的声明和FxnTablePtr结构体外,就是Config结构体,其中FxnTablePtr结构体主要存储了该驱动的控制函数的函数地址,而Config结构体主要是用来嵌套FxnTablePtr、Object和HWAttrs结构体,其中存储的是这几个结构体的地址。
Config结构体在应用程序中(其实就是LAUNCHPADXL.c文件中)需要声明一个Drive_Config数组,数组的元素是相关的外设驱动配置,其代码如下:
#include LAUNCHPADXL.h
const Driver_Config Driver_Config[DriverCount] = {
{
.fxnTablePtr = &Drvier[产品系列名]_fxnTable;
.object = &Drvier[产品系列名]_Object[num0];
.HWAttrs = &Drvier[产品系列名]_HWAttrs[num0];
},
{
.fxnTablePtr = &Drvier[产品系列名]_fxnTable;
.object = &Drvier[产品系列名]_Object[num1];
.HWAttrs = &Drvier[产品系列名]_HWAttrs[num1];
},
...
}
可以看到,其中除了函数查找表(fxnTable)已经被驱动实现文件定义外,Object和HWAttrs都需要对应具体的外设实例,并且需要在应用程序中进行具体的幅值配置。通过这样一个外设驱动配置接口,我们可以通过区分序列号配置任意数量个外设驱动。
-Driver Implementation
Object和HWAttrs结构体都在Driver[芯片系列名].h文件中被定义,Driver[芯片系列名].h文件在上图中被称为Driver Implementation(下面称起为驱动实现),
例如如UartCC26xx.h是CC26xx系列产品中的关于UART外设的驱动实现文件名,其关于UART的Object和HWAttrs的定义代码如下:
//UART Drvier Object
typedef struct UARTCC26XX_Object {
[UART抽象的各种软件属性](?:可以这样理解吗)
} UARTCC26XX_Object, *UARTCC26XX_Handle;
//UART Driver HWAttrs
typedef struct UARTCC26XX_HWAttrs {
【UART具有的各种硬件属性】
} UARTCC26XX_HWAttrs
我们打开某一驱动实现头文件可以看到该文件中最重要的就是定义了关于驱动的Object和HWAttrs这两个结构体。其中HWAttrs定义了该驱动相关的硬件属性,如外设基地址、中断序号,软硬件中断优先级等,Object定义了外设的各种抽象属性,不同的外设有不同的抽象,比如串口的object抽象有串口常用的读写模式、数据模式、回调和收发控制变量等抽象属性。
注意Object结构体中的成员不可以被应用程序访问
此外Object和HWAttrs结构体在应用程序中(其实就是LAUNCHPADXL.c文件中)也需要单独进行具体的配置,其代码如下:
static DriverA_Object driverAObject[];
const DriverA_HWAttrs driverAHWAttrs[] = {
type field0;
type field1;
...
type fieldn;
};
总结:
上图中Board.c通过配置在驱动实现文件中的config结构体来配置并使用某一驱动,而config结构体通过访问外设的Object和HWAttrs结构体与外设的驱动接口文件建立连接,而驱动实现文件通过调用对应外设的driverlib中的Driver Library API来定义该驱动抽象的各种属性,而Driverlib会从对应的寄存器读出或写入具体的数据。