6.1.4.Drv/PWM(Character Device Drv)

来源:互联网 发布:java两年经验工资多少 编辑:程序博客网 时间:2024/06/14 10:43
转载请注明出处:http://blog.csdn.net/alvin_jack/article/details/70665462
文章均出自个人理解,错误之处敬请指出;

前言
     上篇1.1Drv/Character Device Drv(字符型驱动设备)的叙述方式感觉没有逻辑性,只是从框架的角度进行了阐述,这篇换个思路,从调用顺序上来描述PWM驱动设备的实现原理;

基础知识
     在ARM单片机中,PWM实际实现机制就是通过TIMER定时器来实现的,不管是调频还是脉宽,核心的思想就是通过一个计数器和两个寄存器(计算频率/调节脉宽),我之前遇到过有把PWM单独拿出来作为外设模块的MCU(例如飞思卡尔的),也有归类到定时器下的(例如意法半导体的),不管怎么算,核心的思想是不会变的,所以这次就从意法的mcu出发来叙述Nuttx下的PWM驱动的实现原理。

启动流程
     Nuttx的启动就不再详细叙述,有空再写篇关于Nuttx启动详细讲解文章,这里从PWM的入口驱动开始讲解,也就是在config/stm32f103-minimum/src/stm32_appinit.c文件中board_app_initialize()函数;
board_app_initialize--                   #板载应用初始化        |          v     stm32_bringup()------                  #逐个启动板载应用                       |                         v                 stm32_pwm_setup()----    #执行pwm初始化                              |                              v                    stm32_pwminitialize(STM32F103MINIMUM_PWMTIMER)---   #初始化指定的TIMx                    pwm_register("/dev/pwm0", pwm)---                   #注册PWM驱动到内核文件按系统                     .....                    #初始化其他板载应用                     .....

     其实咋一看流程非常清晰,几乎和写单片机程序没什么两样,但是越是看起来简单的东西实际实现起来就越不简单;

stm32_pwminitialize(STM32F103MINIMUM_PWMTIMER)
         Nuttx中的驱动是类linux的,和上一篇讲到的串口一样也是需要一些特定的文件结构(我们尝试着以字符型驱动去理解分析试试);
      在开始之前首先分析下层级关系,在PWM的驱动中也存在和串口(uart_dev_s、uart_ops_s)类似的数据结构(stm32_pwmtimer_s、pwm_ops_s),说明Nuttx的套路是一样的,我们的估计没有错,对上的接口都是*_dev_s和*_ops_s两种数据接口;现在先分析流程,后面来分析数据结构;
stm32_pwminitialize(int timer)--           #stm32pwm初始化,形参为指定定时器通道(硬件层面)        |          v        stm32_pwmtimer_s *lower = &g_pwm3dev; #此处的g_pwm3dev,就是定义的*_dev_s(硬件有多少就定义多少)                       |                       v      pwm_register("/dev/pwm0", pwm);       #将获得的*_dev_s注册到系统(pwm就是g_pwm3dev)

     好像和上面的有点重复 --!,这里主要是要分析stm32_pwmtimer_s、pwm_ops_s、pwm_lowerhalf_s三个数据结构,好像和uart的驱动有点区别了;

stm32_pwmtimer_s
     这个结构体主要是描述驱动的主要模块,板卡的所有PWM驱动模块都是被封装成这样的结构体变量的;所处的路径是:
nuttx/arch/arm/src/stm32/stm32_pwm.c中,这个文件貌是是所有stm32公用的(不然怎么这么多定时器);
static struct stm32_pwmtimer_s g_pwm1dev;static struct stm32_pwmtimer_s g_pwm2dev;...static struct stm32_pwmtimer_s g_pwm16dev;static struct stm32_pwmtimer_s g_pwm17dev;static struct stm32_pwmtimer_s g_pwm1dev ={  .ops = &g_pwmops,                    #ops结构,老熟人了  .timid = 1,                          #用的硬件Timer1就填1  .channels =                          #通道数据结构  {#ifdef CONFIG_STM32_TIM1_CHANNEL1  {  .channel = 1,                        #用的通道1就填1  .pincfg = PWM_TIM1_CH1CFG,           #映射实际的GPIO  .mode = CONFIG_STM32_TIM1_CH1MODE,   #PWM通道的工作模式(频率/脉宽)  },#endif...#这里还有很多个通道,分析结构就先省略掉  .timtype = TIMTYPE_TIM1,             #定时器的工作模式(向上计数/向下计数...)  .mode = CONFIG_STM32_TIM1_MODE,#ifdef CONFIG_PWM_PULSECOUNT  .irq = STM32_IRQ_TIM1UP,             #计数中断#endif  .base = STM32_TIM1_BASE,             #stm32定时器的基地址,也是老熟人了  .pclk = TIMCLK_TIM1,                 #stm32定时器的时钟分频系数};

其实这么一看,和uart的dev比起来,pwm简单太多了;

pwm_ops_s
     这个数据结构应该也是用来操作timer寄存器组的结构体了,
所处的路径是:
nuttx/arch/arm/src/stm32/stm32_pwm.c中,这个结构体也是多个dev公用的,全文也只是定义了一个;
static const struct pwm_ops_s g_pwmops ={  .setup    = pwm_setup,               #pwm初始设置  .shutdown = pwm_shutdown,            #  .start    = pwm_start,               #  .stop     = pwm_stop,                #  .ioctl    = pwm_ioctl,               #};static int pwm_setup(FAR struct pwm_lowerhalf_s *dev);static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev);#ifdef CONFIG_PWM_PULSECOUNTstatic int pwm_start(FAR struct pwm_lowerhalf_s *dev,  FAR const struct pwm_info_s *info,  FAR void *handle);#elsestatic int pwm_start(FAR struct pwm_lowerhalf_s *dev,  FAR const struct pwm_info_s *info);#endifstatic int pwm_stop(FAR struct pwm_lowerhalf_s *dev);static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev,                     int cmd, unsigned long arg);

     毫无疑问,ops都会使用到dev的数据结构,但是pwm这里面偏偏要搞一个pwm_lowerhalf_s这个数据结构,不知道干啥的,但会儿收拾ta;简单来说ops就是之前用来写裸机配置单片机外设那一套,只是把所有配置参数都封装在了中,配置方法封装在了ops中;所以具体的方法就不分析了,具体问题具体解决吧;

pwm_lowerhalf_s
所处的路径:nuttx/include/nuttx/drivers/pwm.h
/* This structure is a set a callback functions used to call from the upper- * half, generic PWM driver into lower-half, platform-specific logic that * supports the low-level timer outputs. */struct pwm_lowerhalf_s;

     说出来你可能不信,这货是个空的结构体,大概表达的意思是说,上半部分调用的回调函数可以通过这个结构进入PWM驱动器的下半部分,平台专用逻辑,支持低电平定时器输出。按这么说,就是个傀儡呗...为了验证这一说法,我们截取一段ops的代码来佐证:
static int pwm_start(FAR struct pwm_lowerhalf_s *dev,  FAR const struct pwm_info_s *info){  int ret = OK;  FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev;.........  return ret;}

     明显可以看出来,在ops实际使用的情况,是强行转换为了stm32_pwmtimer_s,也就是说pwm_lowerhalf_s是为整个os驱动的一致性而设立的变量,目的是让驱动结构的一致性和可读性更强。
0 0
原创粉丝点击