#申请原创#@21小跑堂
AT32L021这款芯片的flash和sram其实已经足够跑一个RTOS了,笔者本来计划移植下Nuttx RTOS的,但 移植后测试结果发现Nuttx的flash和sram占用对于资源比较少的MCU系统还是比较高的,即它的功能集/flash or sram占用这个指标对于flash和sram较少的MCU来说很低,这和Nuttx的设计实现有关系,它做了很多抽象又号称实现了posix,所以代码中有很多中间分层部分。对于flash/sram资源比较充足的MCU,Nuttx挺合适的。等有空笔者写一篇RTOS比较文章,此为题外话略过不再提。接下来比较合适的RTOS 选择是ThreadX、FreeRTOS以及RT-Thread Nano,笔者这次选了FreeRTOS,原因无他,官方的Firmware Library已经做好了FreeRTOS适配,在middlewares/freertos/目录下还有现成的FreeRTOS代码,在utilities/at32l021_freertos_demo有现成的demo例程,咱就用它了省点力气嘿嘿。
1. shell选择
当下shell有不少选择,比如Letter shell, microshell(就是面包板os中用的那个shell), nr_micro_shell等等,笔者觉得它们其实差不太多,你会用其中一个,其它的也会用了。实际使用时挑一个顺眼的即可,笔者这次选中的是nr_mircro_shell。
2. nr_micro_shell的移植
nr_micro_shell的移植很简单的,主要完成一件事:循环获取串口终端的输入字符,然后作为参数传给shell()函数即可。获取输入字符蛮简单的,串口接收中断中读寄存器就能获取输入字符啦。但这里还 是有一些技巧的,因为我们是RTOS环境,为了效率我们不太可能在中断中就调用shell()函数,我们更 期望的是在一个任务中执行shell()函数,这样就涉及到中断中获得的串口输入怎么传递给shell任务,而且还不能丢掉输入字符。本论坛网友的解决方案是自己实现了一个ringbuf,然后通过信号量唤醒shell任务。shell任务醒来后读取ringbuf,不断读取数据后以参数传递给shell(),此过程持续到ringbuf为空,然后再次阻塞在take信号量上。但是笔者认为FreeRTOS的队列在这里非常适合,如果FreeRTOS自带的原语能满足需求那么就一定不再重新发明轮子。
3. 主要逻辑
shell任务很简单,代码片段如下所示:
static void task_shell(void const *argument)
{
unsigned char ch;
while (1) {
if (xQueueReceive(rx_queue, &ch, portMAX_DELAY))
shell(ch);
}
}
它主要就是不断尝试获取队列中的数据,如果成功就以参数传给shell(),如果不能,那按FreeRTOS队 列的设计,shell任务就会阻塞
接下来看串口的中断处理,代码如下所示:
void USART1_IRQHandler(void)
{
uint8_t rxdata;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if(usart_interrupt_flag_get(USART1, USART_RDBF_FLAG) != RESET)
{
/* read one byte from the receive data register */
rxdata = usart_data_receive(USART1);
xQueueSendFromISR(rx_queue, &rxdata, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken == pdTRUE)
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
中断处理逻辑为:读取串口中断标志,如果是RDBF表明串口有输入数据,那么就调用usart_data_receive()函数读取串口输入数据,然后发送给队列。为什么调用portYIELD_FROM_ISR()网上有很多文章,这里不再赘述,简而言之一句话:如果有高优先级任务Ready就主动触发调度。这其实是FreeRTOS环境中 中断处理的一个基本套路。
4. nr_micro_shell命令的添加
关于添加命令这一块,nr_micro_shell自身的文档、本论坛网友文章都有提及,笔者不再赘述了。笔者利用FreeRTOS的一些API实现了一些类linux命令,比如uptime、msleep、free、ps、top等。其中uptime就是xTaskGetTickCount(), msleep()就是利用vTaskDelay()函数,free就是vPortGetHeapStats()然后解析HeapStats_t结构体的一些字段,ps就是利用vTaskList(),top就是利用vTaskGetRunTimeStats()函数。
附上AT32L021上的运行视频给大家欣赏:https://www.bilibili.com/video/BV1X96zY6Ec7/
|