分类: C/C++
2014-12-11 10:57:34
原文地址:finish shell分析之底层usart 作者:shaomengchao
rt_thread的finsh shell系统不愧是调试的一项利器,它可以除了完成一般shell的功能外,甚至还可以自定义命令。这个对功能单一的嵌入式系统来说是十分可贵的。在此我并不想对finsh shell具体有哪些功能再多做赘述,这些都可以在rt_thread的官方文档上找到,我主要想说明的是finsh shell完成功能的这个大致的过程。
finsih shell的底层部分是usart,具体是又那个usart口完成就靠finsh_set_device指定。本人的硬件平台是stm32f103ze,这里rt_thread使用的是usart1,可能是考虑到调试时候的方便,usart1正好也是控制台的输出口。虽然有两套系统使用usart1,但基于rt_thread的驱动机制,这两者是不会冲突的。现在我来说明uasrt驱动是如何在rt_thread里完成注册、初始化以及顺利使用的。
不同于其他驱动,usart的驱动开始的相当早,在中断、时钟、FSMC的之后就轮到它初始化了。毕竟usart承担着系统控制台的输出的任务,只有在它完成后,系统之后的信息才能被打印在终端上,之后所遇到的问题才能显现出来,因此能够越早启动uasrt自然是越好的。
在stm32的平台上rt_thread使用的是usart1,关于其硬件配置和stm32一般配置相同于,不再赘述,我们关注的是usart1是如何与rt_thread联系起来的。在此之前先介绍rt_thread的驱动的核心rt_device结构体。
rt_hw_serial_register之后,usart1在硬件上已经配置好了,设置为正常的发送和中断的接受,而且已经把其注册塞入rt_thread的核心object的管理架构中了。但是我们此时还是没有完成usart的设备的初始化,毕竟private部分,我们还没有配置呢。
之后系统会将所有注册的设备都初始化一边,调用的是rt_device->init函数,对于usart1,就是rt_serial_init,它来完成我们之前没有完成的private部分的设置了。由于usart1没有使用dma发送,唯一需要配置的就是int_rx部分。uasrt1是采用中断接受的方式,像大多数操作系统一样,需要在底层设立专门的数据缓存区,方便上层以同步或是异步的方式进行读写。这里int_rx采用的是典型的环形缓存区。
在usart接受中断调rt_hw_serial_isr,获取一个数据save_index加1,而上层调rt_serial_read读取串口数据,读取一个read_index加1,两者一旦到达边界就回到数据区头。一旦read_index=save_index就认为环形缓存区数据已经取空,而save_index=read_index-1的时候则认为缓存区已满。针对缓存已满的情况下,save_index和read_index同时加1,相当于丢弃了最老的数据。usart中断是随时可能发生的,上层是如何得知环形缓存区数据更新的呢?之前在rt_device的rx_indicata就起了关键作用,一旦上层某个部分要求串口接收数据,它就会调rt_device_set_rx_indicate,加自己的回调函数赋给rx_indicate,一旦中断发生,接收新数据后,就会调用上层赋给的rx_inidate函数来执行上层的接受操作。在finish shell里我们通过finsh_set_device注册了finsh_rx_ind函数,这个函数完成的功能很简单,就是将全局变量finsih_shell这个结构体里的rx_sem给释放了。具体rt_thread的信号量同步问题,如果要说的就扯的远了,这里就不多说了。只是说下这个操作的结果是唤醒了我们下一篇要讲的finish shell最重要的进程tshell,让它来处理接受的数据。
至于usart1的发送部分,由于没有采用dma模式,rt_serial_write 就非常简单了,也就是最简单的发一个等一个不停的发直到发完为止,实在没什么好说的。而下一篇我将涉及finish非常重要的一个部分“语法解释”,它才是finish shell关键难点之一。