作者信息

作者: 彭东林

邮箱: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. 【UOJ#169】元旦老人与数列

    论文题. 考虑到这题的维护和区间操作是反向的,也就是说无法像V那题快速的合并标记. 我们知道,一个区间的最小值和其他值是可以分开来维护的,因为如果一个区间被整体覆盖,那么最小值始终是最小值. 对于被覆 ...

  2. 【HDU5306】Gorgeous Sequence

    这个题目是Segment-Tree-beats的论文的第一题. 首先我们考虑下这个问题的不同之处在于,有一个区间对x取max的操作. 那么如何维护这个操作呢? 就是对于线段树的区间,维护一个最大值标记 ...

  3. leetcode 之Rotate Image(8)

    这题需要搞清楚矩阵元素的位置关系,尤其是副对角线元素,沿着副对角线元素 void rotateImage(vector<vector<int>> &matrix) { ...

  4. POJ 3308

    http://poj.org/problem?id=3308 考虑答案不是乘积而是和的做法, 因为对于每一个伞兵我们要么在这行内安装大炮消灭它 要么在这列中安装大炮消灭它,所以容易看出这是一个最小边覆 ...

  5. swagger关闭生产访问

    通过profile注解来处理. Swagger的congif类上声明@Profile({"dev", "test"}),发布到生产上使用product的prof ...

  6. node自动调试

    supervisor 第一步:安装:npm -g install supervisor没有权限的时候可以sudo npm -g install supervisor 第二步:使用:supervisor ...

  7. Restful Framework (三)

    目录 一.版本 二.解析器 三.序列化 四.请求数据验证 一.版本 回到顶部 程序也来越大时,可能通过版本不同做不同的处理 没用rest_framework之前,我们可以通过以下这样的方式去获取. c ...

  8. hdu 5023

    A Corrupt Mayor's Performance Art Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 100000/100 ...

  9. Longest Palindromic Substring (最长回文字符串)——两种方法还没看,仍需认真看看

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  10. Majority Element——算法课上的一道题(经典)

    Given an array of size n, find the majority element. The majority element is the element that appear ...