为了理解 TI-RTOS 的工作方式,我找了 SDK 的例子——rtos下面driver目录中的 uartecho 工程来分析。这个例子的作用很简单,就是从 UART 接收字符再发送出去,从开发板的虚拟串口可以测试(回显效果)。
SDK 中不包含 TI-RTOS 部分的预编译库代码,所以需要事先编译一下。如果是使用 CCS 开发环境,导入这个工程以后会自动处理。我用命令行直接编译工程,就需要到 SDK 的 kernel/tirtos/builds/CC1352P1_LAUNCHXL/release/gcc 目录下去手动 make 一下。除了GCC的路径,xdc_tools 的路径还需要在 imports.mak 中设置(小改一下,虽然它是随SDK安装的)。
这个例子不用到无线部分,于是文件也简单一些,一共是 uartecho.obj main_tirtos.obj CC1352P1_LAUNCHXL.obj CC1352P1_LAUNCHXL_fxns.obj ccfg.obj 这五个模块。我们只需要重点关注前两个。
主函数做的也就是初始化 RTOS 环境,比我分析过的 nortos 例子多些代码:
int main(void)
{
pthread_t thread;
pthread_attr_t attrs;
struct sched_param priParam;
int retc;
Board_initGeneral();
pthread_attr_init(&attrs);
priParam.sched_priority = 1;
retc = pthread_attr_setschedparam(&attrs, &priParam);
retc |= pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
retc |= pthread_attr_setstacksize(&attrs, THREADSTACKSIZE);
if (retc != 0) {
while (1) {}
}
retc = pthread_create(&thread, &attrs, mainThread, NULL);
if (retc != 0) {
while (1) {}
}
BIOS_start();
return (0);
}
照样先用 Board_initGeneral() 初始化板子,这个函数实现和我分析过的 rfEasyLink_nortos 例子当中的一样。然后,是设置线程的属性,使用了 pthread_attr_setschedparam(), pthread_attr_setdetachstate(), pthread_attr_setstacksize() 三个函数,接下来用 pthread_create() 创建线程。最后调用 BIOS_start() 开始运行。
mainThread() 函数看起来就是常规操作了,没有用到线程相关的 API.
void *mainThread(void *arg0)
{
char input;
const char echoPrompt[] = "Echoing characters:\r\n";
UART_Handle uart;
UART_Params uartParams;
GPIO_init();
UART_init();
GPIO_setConfig(Board_GPIO_LED0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_ON);
UART_Params_init(&uartParams);
uartParams.writeDataMode = UART_DATA_BINARY;
uartParams.readDataMode = UART_DATA_BINARY;
uartParams.readReturnMode = UART_RETURN_FULL;
uartParams.readEcho = UART_ECHO_OFF;
uartParams.baudRate = 115200;
uart = UART_open(Board_UART0, &uartParams);
if (uart == NULL) {
while (1);
}
UART_write(uart, echoPrompt, sizeof(echoPrompt));
while (1) {
UART_read(uart, &input, 1);
UART_write(uart, &input, 1);
}
}
那么,用 RTOS 的意义怎么体现?其实在 UART_read() 和 UART_write() 函数内部,它们并不是简单地对硬件设备寄存器进行读写操作。比如看 UART_read 是这样实现的:
int_fast32_t UART_read(UART_Handle handle, void *buffer, size_t size)
{
return (handle->fxnTablePtr->readFxn(handle, buffer, size));
}
UART_Handle 类型是指向 struct UART_Config_ 类型的指针,
typedef struct UART_Config_ {
/*! Pointer to a table of driver-specific implementations of UART APIs */
UART_FxnTable const *fxnTablePtr;
/*! Pointer to a driver specific data object */
void *object;
/*! Pointer to a driver specific hardware attributes structure */
void const *hwAttrs;
} UART_Config;
在主线程中事先对 UART 进行初始化,这里初始化了所有的 UART,是通过 SDK API UART_init().
void UART_init(void)
{
uint_least8_t i;
uint_fast32_t key;
key = HwiP_disable();
if (!isInitialized) {
isInitialized = (bool) true;
/* Call each driver's init function */
for (i = 0; i < UART_count; i++) {
UART_config[i].fxnTablePtr->initFxn((UART_Handle) &(UART_config[i]));
}
}
HwiP_restore(key);
}
值得注意的是,UART_init() 要用到的 UART_config[] 结构数组,以及 UART_config 变量都是在 CC1352P1_LAUNCHXL.c 当中定义的:
const UART_Config UART_config[CC1352P1_LAUNCHXL_UARTCOUNT] = {
{
.fxnTablePtr = &UARTCC26XX_fxnTable,
.object = &uartCC26XXObjects[CC1352P1_LAUNCHXL_UART0],
.hwAttrs = &uartCC26XXHWAttrs[CC1352P1_LAUNCHXL_UART0]
},
{
.fxnTablePtr = &UARTCC26XX_fxnTable,
.object = &uartCC26XXObjects[CC1352P1_LAUNCHXL_UART1],
.hwAttrs = &uartCC26XXHWAttrs[CC1352P1_LAUNCHXL_UART1]
},
};
因此 fxnTablePtr 指针是系统预设的常量,指向 UARTCC26XX_fxnTable ——它是driver代码中定义的常量:
const UART_FxnTable UARTCC26XX_fxnTable = {
UARTCC26XX_close,
UARTCC26XX_control,
UARTCC26XX_init,
UARTCC26XX_open,
UARTCC26XX_read,
UARTCC26XX_readPolling,
UARTCC26XX_readCancel,
UARTCC26XX_write,
UARTCC26XX_writePolling,
UARTCC26XX_writeCancel
};
所以在主线程调用的 UART_read(), 效果是调用了 UARTCC26XX_read() 函数,其源文件在 UARTCC26XX.c 中,写得还蛮长的,因此就不贴在这里了。其中用到了信号量(Semaphore)相关的函数,已经涉及操作系统方面的特性了。其实 SDK 的 UART 驱动是和 nortos 程序共用的,nortos 程序可以看成 TI-RTOS 的单线程简化版本。为什么还要把单线程程序做这么复杂?为了降低功耗,让 SDK 底层代码去管理省电模式。
作为 RTOS 的测试,不妨改写一个点灯的的程序吧。增加一个线程 testThread 如下:
#include <ti/sysbios/knl/Task.h>
void *testThread(void *arg0)
{
Task_sleep(100000);
GPIO_setConfig(Board_GPIO_LED1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
for(;;)
{
GPIO_write(Board_GPIO_LED1, Board_GPIO_LED_ON);
Task_sleep(100000);
GPIO_write(Board_GPIO_LED1, Board_GPIO_LED_OFF);
Task_sleep(100000);
}
}
在 main() 中也多加一行代码来创建它:
retc |= pthread_create(&thread1, &attrs, testThread, NULL);
重新编译以后下载,就实现了原来的 uartecho 附带 LED1 间歇亮灭的效果。
此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处