基于WINCE6.0+S3C6410的背光驱动
********************************LoongEmbedded********************************
作者:LoongEmbedded(kandi)
時間:2011.7.31
類別:WINCE 驅動開發
********************************LoongEmbedded********************************
1.??? 硬件電路設計
基于PWM來調整背光亮度的硬件設計電路如下圖所示:
圖1
2.??? 基于PWM的控制原理
我們使用S3C6410的定時器1來輸出PWM信號來調整背光亮度,見PWM定時器部分的描述
圖2
?
3.??? 軟件實現
3.1? 定時器1的時鐘值的確定
1)??? PCLK
本設計中采用ARM主頻為533MHZ,HCLK=133MHZ,PCLK=66MHZ,至于這個值的確定見時鐘控制器部分。
?
2)??? PCLK時鐘的第一級分頻值
見圖2的描述,也就是8位Prescaler 0的值的確定,見相關寄存器TCFG0的描述
圖3
代碼中的實現如下:
圖4
圖4中,我們選擇定時器1的Prescaler 0的值為0x3,根據給出的公式:
圖5
這樣算出PCLK始終經過第一級分頻之后的時鐘頻率為66MHZ/(3+1)=16.5MHZ。
?
3)??? PCLK時鐘的第二級分頻值
見圖2的描述,可知每個定制器都有自己的時鐘分割器,分頻值為1/1、1/2、1/4、1/8、1/16或者是TCLK0作為定時器1的時鐘源,圖4中,我們選擇的是1/8的分頻系數,見相關寄存器TCFG1的描述
圖6
結合圖5,這樣我們就可以算出定時器的時鐘頻率為16.5 MHZ/8=2.0625MHZ,同時可以算出定時器時鐘周期為=0.4848us。
?
?
3.2? 定時器控制寄存器TCON的自動重新裝載位和手動更新位
下圖是TCON寄存器中的相關描述
圖7
在代碼中的內容見圖4,下面大概描述這兩位的作用:
1)??? 自動重新裝載位
當定時器1的下降寄存器的值下降到0的時候,只有使能了自動重新裝載位,也即置1,TCNTB1寄存器的值才能自動重新裝載到下降寄存器中,從而開始下個周期,才能輸出周期新的PWM信號。
?
2)??? 手動更新位
只有手動更新位置1的時候,TCNTB1和TCMPB1的值才會裝載到TCNT1和TCMP1,也即下降寄存器和比較寄存器中。但是在我們開啟定時器的時候,需要對手動更新位清零,否則不會有PWM周期信號輸出。
?
3.3? 定時器1計數寄存器TCNTB1、比較寄存器TCMPB1的值和定時器控制寄存器的輸出反轉位
代碼實現如下:
圖8
我們知道
1)??? TCNTB1寄存器
我們知道下降計數器最初被加載的值來之于TCNTB1,而下降寄存器的值遞減到0,這里是從5000遞減到0的時候(每遞減一次,就對應一個定時器1的一個時鐘周期,也即0.4848us),會產生INTF_PWM通知CPU定時器操作完成,這時TCNTB1的值就會自動被重新加載到下降定時器中開始輸出下個PWM周期時鐘。所以我們可以算出此時定時器1輸出的一個時鐘周期時間為5000*0.4848us=2.4242ms,我們用示波器測出的PWM波形如下:
圖9
從圖9可知定時器1輸出的PWM時鐘的周期時間為2.4ms這和2.4242ms很接近,從而驗證了理論值是正確的。我們同時也知道是有TCNTB1的值和定時器的時鐘一起決定了輸出的一個PWM時鐘的周期時間的長短。
?
2)??? TCMPB1寄存器
脈沖寬度調制功能(PWM)使用TCMPB1寄存器的值,當下降計數器的值等于比較寄存器的值的時候,定時器控制邏輯會改變輸出電平,所以可知比較寄存器決定了PWM輸出的turn on或turn off時間,結合圖8,我們通過背光調節的應用程序來調節背光,比如背光的第5格對應傳遞進來的dwValue=50,可以算出TCMPB1=1000+35*50=2750,這個值具體的實際意義是什么呢?在定時器1的Start/Stop位置位,并且對手動更新位清零后,就開始輸出PWM時鐘信后,此時輸出低電平(或者高電平,由反轉位來決定),從這時開始下降計數器的值就從5000開始遞減,當遞減到和比較寄存器的值2750相等的時候,定時器控制邏輯就會輸出高電平(或是低電平),那么可以算出PWM一個周期時間內輸出高電平的時間為(5000-2750)*0.4848us=1.0908ms,我們用示波器測出的波形如下圖:
圖10
從圖10可知定時器1輸出的PWM時鐘的一個周期時間內輸出低電平時間1.080ms,這和1.0908ms,從而驗證了理論值是正確的。從而也知道了此時一個PWM周期時間2.4ms內輸出低電平,也即點亮背光LED燈的時間為1.0908ms。
?
3) 定時器控制寄存器的輸出反轉位
CPU默認的情況下沒有使用輸出反轉位,但推薦使用該位,該位的描述見圖7的第10位
那么使用或者不使用反轉位時,輸出的PWM信號有什么差別呢?先看CPU中相關圖:
圖11
那么對于定時器1,如果使用反轉位,那么在“2) TCMPB1寄存器”中描述的情況下,結合圖11,輸出的PWM波形就和圖11是相反的,見下圖:
圖12
從圖11和圖12可知,從一個周期時鐘來看,圖11低電平的時間和圖12高電平的時間都為1.080ms,相信大家可以意會了。
?
3.4? 背光驅動等待的觸發事件
背光驅動是基于事件觸發的方式來出來,也就是說背光驅動的IST根據等待的事件被觸發來做相應的處理,要等待的事件有下面幾種
1)??? 注冊表值被改變的事件
這里指注冊表鍵HKEY_CURRENT_USER\ControlPanel\Backlight下面鍵值的改變,內容如下:
圖13
2)??? 系統電源狀態改變
指從電池供電的狀態切換到用AC供電的狀態,或者從AC供電切換到電池供電的狀態。
?
3)??? 顯示設備的通知
背光驅動通過調用RequestDeviceNotifications函數來告訴設備管理器:如果顯示驅動加載或者卸載的時候,設備管理要發消息告訴我(也就是背光驅動。)
4)??? 退出背光驅動
在背光驅動的初始化數BKL_Init被調用的過程中,如果在分配虛擬內存,或者是創建線程,又或者是其他動作失敗的時候,會釋放分配的資源,這時候就會觸發退出背光驅動的事件;在卸載驅動的時候,不僅要會釋放分配的資源,也要觸發退出背光驅動的事件
?
3.5? 具體的軟件實現
?
3.5.1??? 初始化函數BKL_Init()
下面分幾部分學習這個函數體
第一部分:
圖14
第二部分:
圖15
3.5.2??? 背光驅動的線程fnBackLightThread
第一部分:
圖16
接著學習這部分用到的結構體和系統的API函數
1)??? 消息隊列結構體
typedef struct MSGQUEUEOPTIONS_OS {
??? DWORD dwSize;?????????????????????????? // size of the structure
??? DWORD dwFlags;????????????????????????? // behavior of message queue
??? DWORD dwMaxMessages;??????????????????? // max # of msgs in queue
??? DWORD cbMaxMessage;???????????????????? // max size of msg
??? BOOL? bReadAccess;????????????????????? // read access requested
} MSGQUEUEOPTIONS, FAR *LPMSGQUEUEOPTIONS, *PMSGQUEUEOPTIONS;
?
DwSize:此結構體所有的成員所占用的字節數。
dwFlags:描述定義的消息隊列的行為,設置為MSGQUEUE_NOPRECOMMIT,表示允許根據需要(也即動態)分配隊列緩沖區并且在讀取消息后允許釋放分配的隊列緩沖區;設置為MSGQUEUE_ALLOW_BROKEN,表示允許直接讀或者寫操作而不管之前是否有過度或者寫操作。
dwMaxMessages:在任何時候隊列允許的最大消息數,如果沒有指定在任何時候隊列允許的最大消息數,則對此成員賦值為0.
cbMaxMessage:每個消息最大的字節數。
bReadAccess:指明隊列的屬性,如果為TRUE,表示可以從隊列中讀消息;如果為FALSE,表示可以往隊列中寫消息。
?
?
2)??? 創建一個消息隊列的函數
CreateMsgQueue(LPCWSTR lpName, LPMSGQUEUEOPTIONS lpOptions);創建一個消息隊列
lpName:此函數的第一個參數表示消息隊列的名字.
lpOptions:指向MSGQUEUEOPTIONS結構體,主要是設置消息隊列的屬性。
返回值:返回指定隊列的只讀或只寫句柄。
?
3)??? 請求電源管理器通知電源狀態改變的函數
HANDLE RequestPowerNotifications(HANDLE? hMsgQ,DWORD?? Flags);
此函數允許應用程序和驅動向電源管理器注冊電源通知的事件。
hMsgQ:這是個指向消息隊列的句柄,由CreateMsgQueue函數返回。
Flags:此參數的描述見下圖:
圖17
4)??? 請求設備管理器通知指定設備驅動加載或者卸載的時候的通知的函數
HANDLE RequestDeviceNotifications (const GUID *devclass, HANDLE hMsgQ, BOOL fAll);
Devclass:指向一個設備接口GUID,設置為NULL表示請求所有的設備接口的通知,但不推薦設置為NULL,因為這樣會引起設備管理器額外的工作,并且在大多數情況下是沒必要的,代表性的情況是一個驅動只需要預先知道它需要知道的接口就可以了。
hMsgQ:這是個指向消息隊列的句柄,由CreateMsgQueue函數返回。
fAll:設置為TRUE,表示設備管理器會將本驅動關注的所有當前存在的設備的通知發送給本驅動;設置為FALSE,表示設備管理器將隨后的本驅動關注的設備的通知發送給本驅動。
返回值:返回一個通知句柄。
?
5)??? GUID的結構體
typedef struct {
??? unsigned long? Data1;
??? unsigned short Data2;
??? unsigned short Data3;
??? byte?????????? Data4[ 8 ];
} GUID;
ConvertStringToGuid函數根據{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}格式把PMCLASS_DISPLAY的值TEXT("{EB91C7C9-8BF6-4a2d-9AB8-69724EED97D1}")分離出來保存在GUID結構體類型的變量中,以便告訴設備管理器背光驅動需要在什么驅動加載或卸載的時候通知背光驅動。
?
第二部分:
圖18
在此學習CeFindFirstRegChange函數:
HANDLE CeFindFirstRegChange (
? HKEY hKey,
? BOOL bWatchSubtree,
? DWORD dwNotifyFilter
)
此函數用于創建一個改變通知的句柄并且設置初始化的改變通知的過濾條件(由第二個參數和第三個參數決定),當指定注冊表鍵或子鍵的改變吻合過濾條件時,等待此改變通知的句柄的線程會得到執行,參數詳述見下圖:
圖19
第三部分:
圖20
BOOL ReadMsgQueue(
? HANDLE hMsgQ,
? LPVOID lpBuffer,
? DWORD cbBufferSize,
? LPDWORD lpNumberOfBytesRead,
? DWORD dwTimeout,
? DWORD* pdwFlags
);
hMsgQ:一個打開的消息隊列的句柄。
lpBuffer:指向保存所讀消息的buffer,此值不能為NULL。
cbBufferSize:保存讀取的消息的buffer的大小,以字節為單位,此參數不能為0。
cbBufferSize:實際保存在lpBuffer中的字節數,才參數不能為NULL。
dwTimeout:在讀取操作之前的超時時間,如果設置為0,表示如果沒有數據讀取,讀操作不會阻塞;如果設置為INFINITE,讀操作會阻塞,直到數據有效或者隊列狀態的改變。
pdwFlags:指向DWORD變量,此變量指示消息屬性的,如果值為MSGQUEUE_MSGALERT指定這是個警告的消息。
返回值:TRUE,表示讀取成功,FALSE表示失敗。
?
第四部分:
圖21
?
背光驅動還有一個較為重要的函數BKL_IOControl來控制背光亮度,應該可以看懂,在此就不介紹了,另外就是BKL_PowerUp和BKL_PowerDown在睡眠和喚醒的時候會用到,如果系統支持睡眠和喚醒功能,那么就應該需要在這兩個函數中來關閉和打開背光的顯示。
總結
以上是生活随笔為你收集整理的基于WINCE6.0+S3C6410的背光驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于WINCE6.0+S3C6410通过
- 下一篇: 解决WINCE6.0新建工程编译出错的问