日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux 查看led设备,Linux下LedButton设备驱动——详细设计

發(fā)布時(shí)間:2025/3/15 linux 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 查看led设备,Linux下LedButton设备驱动——详细设计 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

數(shù)據(jù)結(jié)構(gòu)

點(diǎn)擊(此處)折疊或打開

struct pca9555_led {

u8 id;

struct i2c_client *client;

char *name;

struct led_classdev ldev;

struct work_struct work;

enum pca9555_state state;

};

struct pca9555_btn {

int irq;

char *name;

u8 id;

int keycode;

struct input_dev idev;

struct i2c_client *client;

};

struct pca9555_platform_data {

struct pca9555_led leds[5];

struct pca9555_btn btns[8];

};

上面的結(jié)構(gòu)體定義在pca9555.h中,PCA9555有16個(gè)I/O,5個(gè)接led,8個(gè)接按鍵,結(jié)構(gòu)體pca9555_platform_data描述了9555的使用情況。結(jié)構(gòu)體類型pca9555_led和pca9555_btn分別用于描述led和Button,他們都屬于i2c設(shè)備,因此都包括結(jié)構(gòu)體指針變量struct i2c_client *client,led需要向led-class中注冊(cè),其注冊(cè)的設(shè)備結(jié)構(gòu)類型為led_classdev,Button為輸入設(shè)備,在設(shè)備結(jié)構(gòu)體中包含向input子系統(tǒng)注冊(cè)的類型input_dev,并且包含中斷號(hào),按鍵碼等信息。

點(diǎn)擊(此處)折疊或打開

static struct i2c_driver pca9555_driver = {

.driver = {

.name = "pca9555",

},

.probe = pca9555_probe, //當(dāng)有i2c_client與i2c_driver匹配時(shí)調(diào)用

.remove = pca9555_remove, //注銷時(shí)調(diào)用

.id_table = pca9555_id, //根據(jù)id進(jìn)行匹配

}

struct pca9555_data{

struct i2c_client *client;

struct pca9555_led leds[5];

struct pca9555_btn btns[8];

struct mutex update_lock;

};

這兩個(gè)結(jié)構(gòu)體定義在驅(qū)動(dòng)文件pca9555.c中,pca9555_driver在i2c驅(qū)動(dòng)注冊(cè)時(shí)作為參數(shù)被調(diào)用。pca9555_data中除了定義leds、btns之外定義了互斥變量update_lock,在通過i2c總線讀寫設(shè)備時(shí)用到。

I2C設(shè)備的注冊(cè)

在Linux2.6內(nèi)核中支持兩種編寫i2c驅(qū)動(dòng)程序的方式(這里所有內(nèi)核版本為linux2.6.28):Adapter方式(LEGACY)和Probe方式(new style)。對(duì)于LEGACY方式的驅(qū)動(dòng)設(shè)備部分在驅(qū)動(dòng)運(yùn)行的時(shí)候動(dòng)態(tài)創(chuàng)建,新式的驅(qū)動(dòng)(probe方式)傾向于向傳統(tǒng)的Linux下設(shè)備驅(qū)動(dòng)看齊,采用靜態(tài)定義的方式來(lái)注冊(cè)設(shè)備。使用接口為:

int __init? i2c_register_board_info(int busnum,

struct i2c_board_info const *info, unsigned len)

該函數(shù)定義在linux2.6.28/driver/i2c/i2c-boardinfo.c中。在平臺(tái)代碼中將會(huì)調(diào)用該函數(shù)完成i2c_board_info的注冊(cè)。注冊(cè)過程會(huì)根據(jù)info參數(shù)提供的設(shè)備信息封裝一個(gè)devinfo的結(jié)構(gòu)體,并添加到全局鏈表_i2c_board_list中。

對(duì)于i.MX233,在linux2.6.28\arch\arm\mach-stmp3xxx\stmp378x_devb.c中,在完成設(shè)備結(jié)構(gòu)體的部分初始化后將會(huì)調(diào)用該接口完成注冊(cè)。相關(guān)代碼如下:

點(diǎn)擊(此處)折疊或打開

static struct pca9555_platform_data imx233_lbtn = {

.leds = {

{

.id = 0;

.name = "led0";

.state = PCA9555_OFF;

},

……

{

id = 11;

.name = "led4"

.state = PCA9555_OFF;

},

}

.btns = {

{

.irq = 17;

.name = "btn1";

.id = 2;

.keycode = KEY_1;

}, ……

{

.irq = 17;

.name = "btn8";

.id = 15;

.keycode = KEY_8;

},

}

};

static struct i2c_board_info __initdata stmp3xxx_i2c_devices[] = {

{ I2C_BOARD_INFO("stfm1000", 0xc0), .flags = I2C_M_TEN },

{ I2C_BOARD_INFO("pca9555",0x20),

.platform_data = &imx233_lbtn,

},

};

static void __init stmp378x_devb_init(void)

{

struct fsl_usb2_platform_data *udata;

stmp3xxx_init();

i2c_register_board_info(0, stmp3xxx_i2c_devices, ARRAY_SIZE(stmp3xxx_i2c_devices));

stmp3xxx_set_mmc_data(&stmp3xxx_mmc.dev);

..

}?????? 其中紅色部分為注冊(cè)pca9555所添加的,0×20為i2c中從設(shè)備pca9555的地址。leds和btns中的id值根據(jù)引腳編號(hào)得到,在驅(qū)動(dòng)中l(wèi)ed和button訪問對(duì)應(yīng)引腳值時(shí)用到。另外btns中的irq為中斷號(hào)。9555中的8個(gè)按鍵共享一個(gè)GPIO中斷,其中斷號(hào)為17。btns中的keycode定義了按鍵碼,將會(huì)上報(bào)到輸入子系統(tǒng)中,當(dāng)中斷產(chǎn)生時(shí)產(chǎn)生相應(yīng)的輸出。

完成i2c_board_info注冊(cè)后,在I2C核心中會(huì)根據(jù)板級(jí)i2c設(shè)備配置信息,創(chuàng)建i2c客戶端設(shè)備(i2c_client)添加到i2c子系統(tǒng)中。i2c驅(qū)動(dòng)

接下來(lái)的部分全部在pca9555.c中實(shí)現(xiàn)。Probe

在數(shù)據(jù)結(jié)構(gòu)中提到static struct i2c_driver pca9555_driver ,在加載驅(qū)動(dòng)模塊時(shí)將會(huì)將其注冊(cè)到i2c子系統(tǒng)中。接口如下:

i2c_add_driver(&pca9555_driver);

在pca9555_driver中定義了一個(gè)probe和remove兩個(gè)回調(diào)函數(shù)。當(dāng)加載驅(qū)動(dòng)模塊后i2c_client和i2c_driver匹配時(shí)將會(huì)調(diào)用pca9555_probe()。

函數(shù)pca9555_probe()所做的工作很簡(jiǎn)單。主要是取得板級(jí)的設(shè)備信息,這里將其保存到變量pca9555_pdata中,然后申請(qǐng)pca9555_data類型變量data空間并完成相關(guān)指針傳遞。接著初始化互斥量update->lock,最后跳轉(zhuǎn)到pca9555_configure()函數(shù)執(zhí)行。

pca9555_configure(client, data, pca9555_pdata);Configure

在pca9555_configure()中所做的工作主要分三個(gè)部分:配置PCA9555引腳功能

點(diǎn)擊(此處)折疊或打開

u8 con[2] = { 0x54,0xf7};

for(i=0;i<2;i++) {

i2c_smbus_write_byte_data(client,PCA9555_REG_PINVERSION(i),0X0);

i2c_smbus_write_byte_data(client,PCA9555_REG_CONF(i),con[i]);

}

將極性反轉(zhuǎn)寄存器配置為全0,然后通過9555的兩個(gè)8位控制寄存器配置I/O引腳的輸入輸出功能。保證接led的引腳為輸出引腳,連有按鍵的為輸入引腳。PCA9555_REG_PINVERSION(i)、PCA9555_REG_CONF(i)定義了操作pca9555特定寄存器的命令字節(jié)的值,在后面出現(xiàn)時(shí)將不解釋。led設(shè)備注冊(cè)

點(diǎn)擊(此處)折疊或打開

for(i=0;i<5;i++) {

struct pca9555_led *led = &data->leds[i];

struct pca9555_led *pled = &pdata->leds[i];

led->client = client;

led->id = pled->id;

led->state = pled->state;

led->name = pled->name;

led->ldev.name = led->name;

led->ldev.brightness_set = pca9555_set_brightness;

err = led_classdev_register(&client->dev,&led->ldev);

if(err<0) {

dev_err(&client->dev,

"couldn't register LED %s \n",led->name);

return -1;

}

}

分別初始化5個(gè)led設(shè)備,并定義了回調(diào)函數(shù)pca9555_set_brightness(),然后將led設(shè)備注冊(cè)到led-class中, 在前面led-class中已經(jīng)介紹。剩下的工作就是實(shí)現(xiàn)回調(diào)函數(shù)pca9555_set_brightness()來(lái)控制led的亮滅。將在后面具體分析。btn設(shè)備注冊(cè)

點(diǎn)擊(此處)折疊或打開

for( i =0;i < 8; i++) {

struct pca9555_btn *pbtn = &pdata->btns[i];

btns[i] = &data->btns[i];

btns[i]->client = client;

btns[i]->name = pbtn->name;

btns[i]->id = pbtn->id;

btns[i]->irq = pbtn->irq;

btns[i]->keycode = pbtn->keycode;;

btns[i]->idev.name = btns[i]->name;

btns[i]->idev.evbit[0] = BIT_MASK(EV_KEY);

set_bit(btns[i]->keycode,btns[i]->idev.keybit);

if(request_irq(btns[i]->irq,button_key_event, IRQF_SHARED,btns[i]->name,&btns[i])) {

printk("button can not be allocate irq");

return -EBUSY;

}

printk(KERN_INFO"%s successfully loaded\n",btns[i]->name);

ret = input_register_device(&btns[i]->idev);

if(ret) {

input_free_device(&btns[i]->idev);

return ret;

}

}

分別初始化8個(gè)按鍵所對(duì)應(yīng)的變量,并設(shè)置idev域,使其支持按鍵事件并設(shè)置對(duì)應(yīng)的按鍵碼。在前面的Linux輸入子系統(tǒng)部分有介紹。接著申請(qǐng)中斷,由于這里的8個(gè)按鍵共享一個(gè)GPIO中斷,中斷類型設(shè)置為IRQF_SHARED,且中斷處理函數(shù)為button_key_event,當(dāng)中斷觸發(fā)時(shí)將會(huì)回調(diào)執(zhí)行該函數(shù)。將會(huì)在后面詳細(xì)分析該函數(shù)。最后注冊(cè)輸入設(shè)備到Linux輸入子系統(tǒng)。回調(diào)函數(shù)

點(diǎn)擊(此處)折疊或打開

pca9555_set_brightness

static void pca9555_set_brightness(struct led_classdev *led_cdev,

enum led_brightness value)

{

struct pca9555_led *led = ldev_to_led(led_cdev);

if(value)

led->state = PCA9555_ON;

else

led->state = PCA9555_OFF;

pca9555_setled(led);}

在/sys/class/leds/中相應(yīng)的led設(shè)備目錄下,通過echo寫入brightness文件的值(0~255)將會(huì)傳遞到value 中,根據(jù)寫入的值是非為0設(shè)置state域,然后調(diào)用pca9555_setled(led)來(lái)點(diǎn)亮或熄滅led設(shè)備。

點(diǎn)擊(此處)折疊或打開

static void pca9555_setled(struct pca9555_led *led)

{

struct i2c_client *client = led->client;

struct pca9555_data *data = i2c_get_clientdata(client);

char reg;

mutex_lock(&data->update_lock);

reg = i2c_smbus_read_byte_data(client,LED_REG(led->id));

if(led->state)

reg = reg & ~(0x1 << ((led->id) % 8));

else

reg = reg | (0x1 <id) %8));

i2c_smbus_write_byte_data(client,LED_REG(led->id),reg);

mutex_unlock(&data->update_lock);

}

在前面提到的led的id值在這里被用到,通過宏LED_REG(led->id)確定控制該led所在引腳的寄存器字節(jié),讀取該寄存器中的值到reg中,然后根據(jù)先前設(shè)置的state域,設(shè)置led引腳所對(duì)應(yīng)的位的值,最后將處理過的reg重新寫入到相應(yīng)的寄存器中。這樣就達(dá)到改變led所在引腳的電位的目的,從而控制led的亮度。另外指出的是在讀寫的時(shí)候需要用到互斥機(jī)制。button_key_event

在configure中申請(qǐng)中斷時(shí),將8個(gè)按鍵共用了一個(gè)GPIO中斷,當(dāng)這些按鍵中的任意一個(gè)產(chǎn)生中斷時(shí)都會(huì)跳轉(zhuǎn)到中斷處理函數(shù)button_key_event中執(zhí)行,這是需要判斷到底是哪個(gè)按鍵被按下,然后向輸入子系統(tǒng)上報(bào)相應(yīng)按鍵對(duì)應(yīng)的事件。

為了確定被按下的按鍵,定義如下兩個(gè)字符型的全局變量inreg1,inreg2,用于保存中斷發(fā)生前反映pca9555輸入引腳電位的兩個(gè)輸入寄存器的值。在發(fā)生第一次中斷之前,在模塊加載中先讀取這連個(gè)寄存器的值。這里放在pca9555_configure()函數(shù)的最后,代碼如下:inreg1 = i2c_smbus_read_byte_data(client,PCA9555_REG_INPUT(0)); inreg2 = i2c_smbus_read_byte_data(client,PCA9555_REG_INPUT(1));

中斷處理程序button_key_event()函數(shù)代碼如下:

點(diǎn)擊(此處)折疊或打開

static irqreturn_t button_key_event(int irq,void *dev_id)

{

//struct pca9555_btn *button = (struct pca9555_btn *)dev_id;

struct i2c_client *client = btns[0]->client;

char linreg1,linreg2;

linreg1 = i2c_smbus_read_byte_data(client,PCA9555_REG_INPUT(0));

linreg2 = i2c_smbus_read_byte_data(client,PCA9555_REG_INPUT(1));

switch((int)inreg1^linreg1) {

case 2:

input_report_key(&btns[0]->idev,btns[0]->keycode,linreg1);

input_sync(&btns[0]->idev);

break;

......

}

switch((int)inreg2^linreg2) {

case 2:

input_report_key(&btns[3]->idev,btns[3]->keycode,linreg2);

input_sync(&btns[3]->idev);

break;

......

}

inreg1 = linreg1;

inreg2 = linreg2;

return IRQ_HANDLED;

}

當(dāng)中斷發(fā)生后,在中斷處理程序中首先讀取按鍵被按下后pca9555中兩個(gè)輸入寄存器中的值,然后與被按下前寄存器中的值inreg1、inreg2比較,確定電位發(fā)生變化的引腳所對(duì)應(yīng)的按鍵,然后通過input_report_key向輸入子系統(tǒng)上報(bào)對(duì)應(yīng)的按鍵事件。接著將這次按鍵被按下后寄存器的值保存到inreg1、inreg2中作為下次按鍵被按下之前的值. 最后退出中斷處理程序。

對(duì)于驅(qū)動(dòng)的設(shè)計(jì)先寫到這,在后面調(diào)試過程中發(fā)現(xiàn)問題再補(bǔ)充和完善。

總結(jié)

以上是生活随笔為你收集整理的linux 查看led设备,Linux下LedButton设备驱动——详细设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。