基於tiny4412的Linux內核移植 -- PWM子系統學習(八)

来源:http://www.cnblogs.com/pengdonglin137/archive/2016/01/30/5172155.html
-Advertisement-
Play Games


作者信息

作者: 彭東林

郵箱:[email protected]

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

可以發現,蜂鳴器不響了。

 

未完待續。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • javascript計算字元串的長度區分中英文:計算字元串的長度是常有的操作,代碼如下: var str="I love 螞蟻部落"; console.log(str.length); 使用length計算字元串的長度,得出的結果是字元串中字元的個數,而在實際應用中可能需要計算字元串所占有的位元組長度
  • 使用<link>標簽引入css文件簡單代碼實例:本章節介紹一下如何利用<link>標簽引入外部css文件。雖然這個非常的簡單,但是很多初學者比較容易忘記它的基本使用格式。下麵就演示一下如何使用此標簽引入外部樣式表:代碼如下: <link rel="stylesheet" type="text/css
  • 獲取table表格指定列的所有單元格的內容:本章節介紹一下如何獲取表格指定列的所有單元格的內容,這可能在一些應用或者說擴展應用中會需要。代碼實例如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author"
  • 在SQL SERVER 2005/2008中,如果將有序的記錄插入臨時表,則從臨時表查詢出來的記錄是有序的(不依賴ORDER BY也是有序狀態),但是從SQL SERVER 2012開始,即使插入的記錄集有序,查詢出來的結果變成無序了。需要依賴ORDER BY來或得到一個有序結果。例如下麵例子: S...
  • OLEDB等待事件介紹 OLEDB等待類型是SQL SERVER 資料庫中最常見的幾種等待類型之一。它意味著某個會話(SPID)通過SQL Server Native Client OLEDB Provider發生了調用請求並等待資料庫返回所需的數據。它出現在遠程系統(remote system )...
  • runstats工具是《 oracle database 9i/10g/11g編程藝術 深入資料庫體繫結構》作者寫的一個統計性能工具,能對做同一件事的兩個方法進行比較,得到孰優孰劣的結果。 (看到runstats想到了db2 里有runstats命令收集統計信息) runststs工具主要測量三個要
  • 前言 - 會寫這篇除了是要記錄一下使用的過程之外,也是發現到網路上找來的教學幾乎都是跟其它環境做結合 比較沒有單純利用command進行的流程。也沒有整體觀念的介紹,所以將我所理解的整理分享給大家。 因我對於Git版本控制比較熟悉,這當中會借用一點Git的觀念來做解釋 :) liquibase -
  • 雖然西西不建議大家去用命令刪除資料庫表中的東西,但是這些刪除命令總有用的著的地方。 說到刪除表數據的關鍵字,大家記得最多的可能就是delete了 然而我們做資料庫開發,讀取資料庫數據.對另外的兩兄弟用得就比較少了 現在來介紹另外兩個兄弟,都是刪除表數據的,其實也是很容易理解的 老大------dro
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...