作者信息

作者: 彭东林

邮箱:pengdonglin137@163.com

QQ:405728433

平台简介

开发板:tiny4412ADK + S700 + 4GB Flash

要移植的内核版本:Linux-4.4.0 (支持device tree)

u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动)

busybox版本:busybox 1.25

交叉编译工具链: arm-none-linux-gnueabi-gcc

(gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))

实验二、用蜂鸣器测试backlight

一般LCD的背光的亮度调节都是通过控制输入给背光控制芯片的占空比来实现的,由于目前还没有移植LCD驱动,我们先用蜂鸣器来模拟,实现的效果是:向/sys/class/backlight/backlight/brightness写入不同的亮度值,蜂鸣器会发出相应的响声。(注:这里的蜂鸣器的频率并不会改变,因为backlight实现的是控制PWM波的占空比,而不是频率,所以我们能听到的不同是蜂鸣器发出响声的维持时间在变化)。

修改设备树文件:arch/arm/boot/dts/exynos4412-tiny4412.dts

diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts

index 18ad4cd..5fb1fd0 100644

--- a/arch/arm/boot/dts/exynos4412-tiny4412.dts

+++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts

@@ -15,6 +15,7 @@

 #include "exynos4412.dtsi"

 #include <dt-bindings/gpio/gpio.h>

 #include <dt-bindings/usb4640/usb4640.h>

+#include <dt-bindings/pwm/pwm.h>

 

 / {

     model = "FriendlyARM TINY4412 board based on Exynos4412";

@@ -90,6 +91,18 @@

             regulator-max-microvolt = <2800000>;

         };

     };

+

+    bl: backlight {

+           compatible = "pwm-backlight";

+           pwms = <&pwm 0 1000000000 PWM_POLARITY_NORMAL>;

+           pwm-names = "backlight";

+           brightness-levels = <0 1 2 3 4 5 6 7 8 9 10>;

+           default-brightness-level = <3>;

+#if 0

+           pinctrl-0 = <&pwm0_out>;

+           pinctrl-names = "default";

+#endif

+    };

 };

 

 &rtc {

内核文档对这些属性有解释:

Documentation/devicetree/bindings/pwm/pwm.txt
Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt

这里有几点需要注意:

1、tiny4412上控制LCD背光的实际是PWM1,这里我们只是为了得到更直观的效果,采用了PWM0;

2、由于PWM0的GPIO复用功能在pwm里已经进行了设置,所以在backlight这个节点中不需要对pwm0_out设置,可以看到,我用#if 0 … #endif 注释掉了,否则,内核在启动时会出现错误,因为这个gpio资源在pwm里已经占了,所以在backlight中就不能再申请了;(在设备树里支持C/C++的代码注释规范

3、然后就是pwms参数的理解: 在arch/arm/boot/dts/exynos4.dtsi中有对pwm的定义,如下所示:

pwm: pwm@139D0000 {

    compatible = "samsung,exynos4210-pwm";

    reg = <0x139D0000 0x1000>;

    interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;

    clocks = <&clock CLK_PWM>;

    clock-names = "timers";

    #pwm-cells = <3>;

    status = "disabled";

};

注意其中的 #pwm-cells 的值是3,表示在其他地方可以引用pwm,并且可以传递3个参数,所以

pwms = <&pwm 0 1000000000 PWM_POLARITY_NORMAL>;

其中给pwm传递了三个参数,分别是:0、1000000000和PWM_POLARITY_NORMAL,具体如何解析这几个参数,要看驱动程序的实现,每个厂家可以不一样,对于我们的tiny4412使用了Linux系统提供的一个解析函数,这个函数在注册pwmchip的时候赋值的,在内核文件drivers/pwm/pwm-samsung.c中:

if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {

    ret = pwm_samsung_parse_dt(chip);

    if (ret)

        return ret;

 

    chip->chip.of_xlate = of_pwm_xlate_with_flags;

    chip->chip.of_pwm_n_cells = 3;

} else {

    if (!pdev->dev.platform_data) {

其中of_pwm_xlate_with_flags定义如下(drivers/pwm/core.c):

   1: struct pwm_device *

   2: of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)

   3: {

   4:     struct pwm_device *pwm;

   5:  

   6:     if (pc->of_pwm_n_cells < 3)

   7:         return ERR_PTR(-EINVAL);

   8:  

   9:     if (args->args[0] >= pc->npwm)

  10:         return ERR_PTR(-EINVAL);

  11:  

  12:     pwm = pwm_request_from_chip(pc, args->args[0], NULL);

  13:     if (IS_ERR(pwm))

  14:         return pwm;

  15:  

  16:     pwm_set_period(pwm, args->args[1]);

  17:  

  18:     if (args->args[2] & PWM_POLARITY_INVERTED)

  19:         pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);

  20:     else

  21:         pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);

  22:  

  23:     return pwm;

  24: }

  25: EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);

可以看到:

第8行和第12行,将args[0]与npwm比较,以及根据args[0]获得相应的pwm_device,可以知道args[0]表示的是使用的那个PWM,这里我们传递的是0,表示获得PWM0对应的pwm_device;

第16行,根据args[1]设置PWM0的周期,可以知道,args[1]表示的就是周期,这里我们传递的是1000000000;

第19和第21行,可以知道args[2]表示的是否invert;

那么是怎么把这几个参数放入args中的呢?这里以backlight为例列出了函数调用过程:

用pwm控制的背光灯的代码路径:drivers/video/backlight/pwm_bl.c

pwm_backlight_probe

--> devm_pwm_get

--> pwm_get

--> of_pwm_get

--> of_property_match_string

--> of_parse_phandle_with_args [从设备树中解析传给pwm的参数,放到args中]

--> of_node_to_pwmchip

--> pc->of_xlate(pc, &args) [这里就调用到了of_pwm_xlate_with_flags]

4、接下来是对brightness-levels的理解

   1: +           brightness-levels = <0 1 2 3 4 5 6 7 8 9 10>;

   2: +           default-brightness-level = <3>;

上面我们把PWM0的周期设置为了1000000000纳秒,也就是1秒。

这里的brightness-levels将占空比设置为了10个级别(0表示的是关闭背光),每个级别都对应一个占空比,其中default-brightness-level的是默认的级别,这只是一个索引号,跟brightness-levels中的3不是一个概念,将brightness-levels看成一个数组,而default-brightness-level是数组的下标索引号,将来我们向brightness中写入的也是索引号。那么,系统是如何将brightness-levels中的级别设置为具体的占空比的呢?下面是代码(drivers/video/backlight/pwm_bl.c):

   1: static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)

   2: {

   3:     int err;

   4:  

   5:     if (pb->enabled)

   6:         return;

   7:  

   8:     err = regulator_enable(pb->power_supply);

   9:     if (err < 0)

  10:         dev_err(pb->dev, "failed to enable power supply\n");

  11:  

  12:     if (pb->enable_gpio)

  13:         gpiod_set_value(pb->enable_gpio, 1);

  14:  

  15:     pwm_enable(pb->pwm);

  16:     pb->enabled = true;

  17: }

  18:  

  19: static void pwm_backlight_power_off(struct pwm_bl_data *pb)

  20: {

  21:     if (!pb->enabled)

  22:         return;

  23:  

  24:     pwm_config(pb->pwm, 0, pb->period);

  25:     pwm_disable(pb->pwm);

  26:  

  27:     if (pb->enable_gpio)

  28:         gpiod_set_value(pb->enable_gpio, 0);

  29:  

  30:     regulator_disable(pb->power_supply);

  31:     pb->enabled = false;

  32: }

  33:  

  34: static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)

  35: {

  36:     unsigned int lth = pb->lth_brightness;

  37:     int duty_cycle;

  38:  

  39:     if (pb->levels)

  40:         duty_cycle = pb->levels[brightness];

  41:     else

  42:         duty_cycle = brightness;

  43:  

  44:     return (duty_cycle * (pb->period - lth) / pb->scale) + lth;

  45: }

  46:  

  47: static int pwm_backlight_update_status(struct backlight_device *bl)

  48: {

  49:     struct pwm_bl_data *pb = bl_get_data(bl);

  50:     int brightness = bl->props.brightness;

  51:     int duty_cycle;

  52:  

  53:     if (bl->props.power != FB_BLANK_UNBLANK ||

  54:         bl->props.fb_blank != FB_BLANK_UNBLANK ||

  55:         bl->props.state & BL_CORE_FBBLANK)

  56:         brightness = 0;

  57:  

  58:     if (pb->notify)

  59:         brightness = pb->notify(pb->dev, brightness);

  60:  

  61:     if (brightness > 0) {

  62:         duty_cycle = compute_duty_cycle(pb, brightness);

  63:         pwm_config(pb->pwm, duty_cycle, pb->period);

  64:         pwm_backlight_power_on(pb, brightness);

  65:     } else

  66:         pwm_backlight_power_off(pb);

  67:  

  68:     if (pb->notify_after)

  69:         pb->notify_after(pb->dev, brightness);

  70:  

  71:     return 0;

  72: }

我们重点看pwm_backlight_update_status和compute_duty_cycle。

当向brightness中写入合法的亮度索引后,就会调用到pwm_backlight_update_status,其中的变量brightness就是写入的所用值,如果不为0的话(为0的话,会调用pwm_backlight_power_off关闭背光),就会调用compute_duty_cycle,这个函数将索引转换为duty_cycle(在normal模式下表示的是高电平的持续时间,用来控制占空比),然后调用pwm_config配置tcmpb寄存器,实现占空比的改变。

   1: static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)

   2: {

   3:     unsigned int lth = pb->lth_brightness;

   4:     int duty_cycle;

   5:  

   6:     if (pb->levels)

   7:         duty_cycle = pb->levels[brightness];

   8:     else

   9:         duty_cycle = brightness;

  10:  

  11:     return (duty_cycle * (pb->period - lth) / pb->scale) + lth;

  12: }

其中:

第7行,将索引号brightness转化为具体的level值(即1 2 3 4 5 6 7 8 9 10 中的一个);

第11行就是转换函数了,其中lth是0,period是1000000000,scale是数组的最大值10,剩下的就是一个简单的线性比例关系了,很好理解。

测试

重新编译设备树

   1: make dtbs

编译内核

make uImage LOADADDR=0x40008000 -j2

启动内核,系统起来后,进入 /sys/class/backlight:

   1: [root@tiny4412 root]# cd /sys/class/backlight/

   2: [root@tiny4412 backlight]# ls

   3: backlight

   4: [root@tiny4412 backlight]# cd backlight/

   5: [root@tiny4412 backlight]# ls

   6: actual_brightness  device             subsystem

   7: bl_power           max_brightness     type

   8: brightness         power              uevent

向brightness中写入亮度索引:

   1: [root@tiny4412 backlight]# cat max_brightness 

   2: 10

   3: [root@tiny4412 backlight]# cat actual_brightness 

   4: 3

   5: [root@tiny4412 backlight]# cat brightness 

   6: 3

   7: [root@tiny4412 backlight]# echo 9 > brightness

此时可以听到蜂鸣器的声音变了。

关闭背光

   1: [root@tiny4412 backlight]# echo 0 > brightness

可以发现,蜂鸣器不响了。

未完待续。

基于tiny4412的Linux内核移植 -- PWM子系统学习(八)的更多相关文章

  1. 基于tiny4412的Linux内核移植 -- PWM子系统学习(七)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  2. 基于tiny4412的Linux内核移植 -- 设备树的展开

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  3. 基于tiny4412的Linux内核移植 -- 设备树的展开【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/5248114.html#_lab2_3_1 阅读目录(Content) 作者信息 平台简介 摘要 正文 一.根 ...

  4. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  5. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九-2)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  6. 基于tiny4412的Linux内核移植(支持device tree)(三)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  7. 基于tiny4412的Linux内核移植(支持device tree)(一)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  8. 基于tiny4412的Linux内核移植 --- aliases节点解析

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  9. 基于tiny4412的Linux内核移植 --- aliases节点解析【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/5252348.html 阅读目录(Content) 作者信息 平台简介 正文 回到顶部(go to top) ...

随机推荐

  1. 从LFS官方文档构建完整Linux系统

    从LFS官方文档构建完整Linux系统 http://www.cnblogs.com/sonofdark/p/4962609.html 这不是新手教程!!! Parallels Desktop (为防 ...

  2. 【hihocoder】sam-2

    原意是把sam那一堆做完…… 这题还是很sb的,$\sum{maxlen(s)-minlen(s)+1}$就是本质不同的子串数量 然后因为suffix link的性质,maxlen[fa[s]]=mi ...

  3. redis使用教程

    一.redis 的安装 官方就是个坑:只说make一下即可用,确实可以用,我以为装好了,结果好多问题: 安装步骤:make =>  make test  => make install 1 ...

  4. maven添加jar包到本地仓库

    mvn install:install-file -Dfile=desutill.jar -DgroupId=com.bfd -DartifactId=des -Dversion=1.0 -Dpack ...

  5. DOM方法index()相关问题(为何$(this).index(this)是错误的 )

    写jQuery的时候遇到一个关于index()的问题,查找相关资料后,解决了,把自己的想法写在下面. index() 方法返回指定元素相对于其他指定元素的 index 位置. 完全语法为:$(sele ...

  6. bzoj 4327 AC自动机

    思路:将能跑到的状态标记一下,在bfs搜一下就好啦. #include<bits/stdc++.h> #define LL long long #define ll long long # ...

  7. 【剑指offer】面试题 64. 求 1+2+3+...+n

    面试题 64. 求 1+2+3+...+n 题目:求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 1.采 ...

  8. 安装 gcc 编译器

    1.安装编译工具 gcc.gcc-c++.make 注意解决依赖关系,推荐使用 yum 安装,若不能联网可使用安装光 盘做为 yum 源 1)编辑 yum 配置文件: Mount /dev/cdrom ...

  9. (7) go 函数

    1.格式 调用 2.包 (1)包 本质 文件夹.每一个文件都必须属于一个包 (2)给包取别名 (3)函数的首字母大小,决定是否能被外包访问 (3) 3.多返回值 4.递归 5.基本数据类型和数组都是拷 ...

  10. 连接LilyPad之Windows平台的驱动

    连接LilyPad之Windows平台的驱动 LilyPad和其他的Arduino控制板的不同之处是它是为电子织物和可穿戴设计的.那么,它的大小就必须要紧凑.所以,它并没有板载其他大多数板子都具有的U ...