linux PWM驱动屏幕亮度及pwm子系统框架(Linux驱动开发篇)

1、对象
imx6ull单片机,控制其下面的pwm3的外设。关于对象的详细介绍看裸机pwm控制屏幕亮度

  • 在dtsi中的位置 /soc/aips1/pwm3
  pwm3: pwm@02088000 { 
     compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm"; 
     reg = <0x02088000 0x4000>; 
     interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; 
     clocks = <&clks IMX6UL_CLK_PWM3>, 
           <&clks IMX6UL_CLK_PWM3>; 
     clock-names = "ipg", "per"; 
     #pwm-cells = <2>; 
  }; 

2、pwm子系统核心

  • Linux内核提供了个PWM子系统框架,编写 PWM驱动的时候一定要符合这个框架。

  • 其主要核心与其它框架一样:
    1、初始化 pwm_chip 结构体

    2、然后向内核注册初始化完成以后的pwm_chip

/*
@	PWM子系统的核心是pwm_chip 结构体
@	定义在文件 include/linux/pwm.h中
@	pwm_chip 结构体
*/
 struct pwm_chip { 
    struct device           *dev; 
    struct list_head        list; 
    const struct pwm_ops     *ops; /*PWM外设的各种操作函数集合==开发人员实现*/
    int               base; 
    unsigned int            npwm; 
    struct pwm_device       *pwms; 
    struct pwm_device     * (*of_xlate)(struct pwm_chip *pc, 
                             const struct of_phandle_args *args); 
    unsigned int            of_pwm_n_cells; 
    bool                can_sleep; 
 }; 
/*
@	pwm_ops结构体
*/
 struct pwm_ops { 
    int    (*request)(struct pwm_chip *chip,   //请求 PWM 
                      struct pwm_device *pwm);  
    void    (*free)(struct pwm_chip *chip,    //释放 PWM 
                       struct pwm_device *pwm); 
    int     (*config)(struct pwm_chip *chip,    //配置 PWM 周期和占空比 
                    struct pwm_device *pwm, 
                    int duty_ns, int period_ns); 
    int     (*set_polarity)(struct pwm_chip *chip,  //设置 PWM 极性 
                    struct pwm_device *pwm, 
                    enum pwm_polarity polarity);   
    int     (*enable)(struct pwm_chip *chip,    //使能 PWM 
                        struct pwm_device *pwm); 
    void     (*disable)(struct pwm_chip *chip,   //关闭 PWM 
                     struct pwm_device *pwm); 
    struct module       *owner; 
 };

/*
@	定义在 drivers/pwm/core.c 文件中
@	注册驱动函数
@	chip:要向内核注册的 pwm_chip
@	返回值:0 成功;负数 失败。 
*/ 
int pwmchip_add(struct pwm_chip    *chip) 

/*
@	卸载 PWM 驱动
@	chip:要移除的pwm_chip
@	返回值:0 成功;负数 失败
*/
int pwmchip_remove(struct pwm_chip *chip) 

3、Linux内核自带的I.MX6ULL PWM驱动简述

  • 打开pwm-imx.c这个文件,这是一个标准的平台设备驱动文件。
/*
@	用于设置PWM的频率和占空比
*/
 static int imx_pwm_config_v2(struct pwm_chip *chip, 
      struct pwm_device *pwm, int duty_ns, int period_ns) 
 { 
    struct imx_chip *imx = to_imx_chip(chip); 
    struct device *dev = chip->dev; 
    unsigned long long c; 
    unsigned long period_cycles, duty_cycles, prescale; 
    unsigned int period_ms; 
     bool enable = test_bit(PWMF_ENABLED, &pwm->flags); 
    int wait_count = 0, fifoav; 
    u32 cr, sr; 
.... 
    c = clk_get_rate(imx->clk_per); 
    c = c * period_ns; 
     do_div(c, 1000000000); 
    period_cycles = c; 
  
    prescale = period_cycles / 0x10000 + 1; 
  
    period_cycles /= prescale; 
    c = (unsigned long long)period_cycles * duty_ns; 
    do_div(c, period_ns); 
    duty_cycles = c; 
  
  /* 
   * according to imx pwm RM, the real period value should be 
   * PERIOD value in PWMPR plus 2. 
   */ 
    if (period_cycles > 2) 
        period_cycles -= 2; 
    else 
        period_cycles = 0; 
  
    writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); /*将计算得到的duty_cycles写入到 PWMSAR 寄存器中,设置PWM的占空比 */
    writel(period_cycles, imx->mmio_base + MX3_PWMPR); /*将计算得到的period_cycles写入到PWMPR 寄存器中,设置PWM的频率*/
  
    cr = MX3_PWMCR_PRESCALER(prescale) | 
         MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | 
        MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; 
  
    if (enable) 
        cr |= MX3_PWMCR_EN; 
  
    writel(cr, imx->mmio_base + MX3_PWMCR); 
  
    return 0; 
 } 
/*
@	此函数用于打开或关闭对应的PWM
*/
 static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable
 { 
    struct imx_chip *imx = to_imx_chip(chip); 
    u32 val; 
  
    val = readl(imx->mmio_base + MX3_PWMCR); /*读取PWMCR寄存器的值*/
  
    if (enable) /*如果 enable 为真,表示使能 PWM*/
          val |= MX3_PWMCR_EN; 
    else /*如果enable不为真,表示关闭PWM*/
        val &= ~MX3_PWMCR_EN; 
  
    writel(val, imx->mmio_base + MX3_PWMCR); /*将新的 val值写入到PWMCR寄存器*/
 } 
 static struct imx_pwm_data imx_pwm_data_v2 = { 
     .config = imx_pwm_config_v2, /*最终操作 I.MX6ULL 的 PWM 外设寄存器,进行实际配置的函数*/
     .set_enable = imx_pwm_set_enable_v2, /*具体使能PWM的函数*/
 };
  
static const struct of_device_id imx_pwm_dt_ids[] = { 
   { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, 
   { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, 
   { /* sentinel */ } 
}; 
 
...... 
 
 /*当设备树节点和驱动匹配以后 imx_pwm_probe 函数就会执行*/
static struct platform_driver imx_pwm_driver = { 
    .driver     = { 
        .name   = "imx-pwm", 
        .of_match_table = imx_pwm_dt_ids, 
    }, 
    .probe      = imx_pwm_probe, 
      .remove     = imx_pwm_remove, 
 }; 
 
 static int imx_pwm_probe(struct platform_device *pdev) 
 { 
    const struct of_device_id *of_id = 
            of_match_device(imx_pwm_dt_ids, &pdev->dev); 
    const struct imx_pwm_data *data; 
    struct imx_chip *imx; /*引出核心PWM 子系统核心部件pwm_chip*/
    struct resource *r; 
    int ret = 0; 
  
    if (!of_id) 
        return -ENODEV; 
  
	
	/*=============================开始初始化pwm_chip变量*/

  	/*为imx_chip 类型的结构体指针变量,申请内存*/
    imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL
        if (imx == NULL) 
        return -ENOMEM; 
.... 
    imx->chip.ops = &imx_pwm_ops; /*自己实现*/
    imx->chip.dev = &pdev->dev; 
    imx->chip.base = -1; 
    imx->chip.npwm = 1; 
    imx->chip.can_sleep = true; 
  
    r = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*得到控制器的物理基地址*/
    imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); /*改成虚拟基地址*/
    if (IS_ERR(imx->mmio_base)) 
        return PTR_ERR(imx->mmio_base); 
  
    data = of_id->data; 
    imx->config = data->config; 
    imx->set_enable = data->set_enable; 
  
    ret = pwmchip_add(&imx->chip); 
    if (ret < 0) 
        return ret; 
  
    platform_set_drvdata(pdev, imx); 
    return 0; 
 } 

/*调用的操作集合==使能、关闭和配置PWM的函数*/
 static struct pwm_ops imx_pwm_ops = { 
     .enable = imx_pwm_enable, 
     .disable = imx_pwm_disable, 
     .config = imx_pwm_config, 
     .owner = THIS_MODULE, 
 }; 

4、实战

  • 总线驱动nxp官方已经帮我们写好了,关于设备驱动我们这也没有设备。

  • 所以我们的目的就只是配置总线驱动(menuconfig),

  • 以及让两者进行匹配的设置(设备树)

  • 1、修改IO
    添加GPIO1_IO04引脚信息==pinctrl系统
    在这里插入图片描述

  • 2、在dts里面追加节点信息
    在这里插入图片描述

  • 3、防止冲突,屏蔽其它IO
    检查一下设备树中有没有其他外设用到GPIO1_IO04,如果有的话需要屏蔽掉!注意,不能只屏蔽掉GPIO1_IO04 的pinctrl配置信息,也要搜索一下“gpio1 4”,看看有没有哪里用到,用到的话也要屏蔽掉

    设备树修改完成以后重新编译设备树,然后使用新的设备树启动系统

  • 4、配置NXP官方的Linux 内核,使能PWM驱动
    在这里插入图片描述
    在这里插入图片描述
    如何在其他外设上添加 PWM 功能
    还是修改设备树

 backlight { 
     compatible = "pwm-backlight"; /*匹配到内核自带的 PWM背光驱动,驱动文件为drivers/video/backlight/pwm_bl.c,*/
     pwms = <&pwm1 0 5000000>; /*指定背光使用哪一路 PWM,以及 PWM相关的属性*/
     brightness-levels = <0 4 8 16 32 64 128 255>; /*:背光等级数组,范围0~255,对应占空比为0%~100%*/
     default-brightness-level = <7>; /*:默认的背光等级,*/
     status = "okay"; 
 }; 
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值