Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181970
  • 博文数量: 20
  • 博客积分: 125
  • 博客等级: 入伍新兵
  • 技术积分: 985
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-08 13:48
个人简介

热爱开源,喜欢分析操作系统架构

文章分类

全部博文(20)

文章存档

2013年(17)

2012年(3)

分类: C/C++

2012-11-02 00:00:00

   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_device

  
  调用rt_hw_serial_register之后,rt_device以rt_object的形式被rt_thread组织起来,在后面我们可以很方便的通过rt_device_find利用之前注册的名字来找到相应的设备,事实是之后看见的finsh_set_device函数就是这么做的。而flag和通用接口函数可以连接上层和底层,完成相应的设备操作。rx_indiacate和tx_complete同样也是联系上下层的接口,但与前者是完成上层到底层操作不同的是,它们是通过中断调用向上层传递特定的信息。最后private是存放设备私有变量的地方,之前的部分还是rt_thread通用的架构,而private则是对于底层特定操作或是存放特定数据的地方,熟悉linux内核的同学一定对这种结构的形式非常熟悉,因为在内核特别是在驱动中这是经常使用的手段。

 

   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采用的是典型的环形缓存区。


Untitled document (1)   

  在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关键难点之一。

阅读(2916) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~