6.1.4.Drv/PWM(Character Device Drv)

转载请注明出处: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_PULSECOUNT
static int pwm_start(FAR struct pwm_lowerhalf_s *dev,
  FAR const struct pwm_info_s *info,
  FAR void *handle);
#else
static int pwm_start(FAR struct pwm_lowerhalf_s *dev,
  FAR const struct pwm_info_s *info);
#endif

static 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驱动的一致性而设立的变量,目的是让驱动结构的一致性和可读性更强。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值