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