基於S3C2440的linux-3.6.6移植——LED驅動

來源:互聯網
上載者:User

 

目前的linux版本的許多驅動都是基於裝置模型,LED也不例外。

 

簡單地說,裝置模型就是系統認為所有的裝置都是掛接在匯流排上的,而要使裝置工作,就需要相應的驅動。裝置模型會產生一個虛擬檔案系統——sysfs,它給使用者提供了一個從使用者空間去訪問核心裝置的方法,它在linux裡的路徑是/sys。如果要寫程式訪問sysfs,可以像讀寫普通檔案一樣來操作/sys目錄下的檔案。

 

對於基於s3c2440的開發板來說,linux-3.6.6自動的LED驅動只需改變串連LED的IO連接埠,及高、低電平響應即可。我的開發板的四個LED串連在了B口的5到8引腳上,當輸出低電平時被點亮,與linux內建的LED驅動一致,因此無需做任何改動。

 

使用menuconfig來配置核心,這裡要加上對LED模組的內容,即:

Device Drivers--->

       [*]LED Support--->

              <*>LED Class Support

              <*>LED Support for Samsung S3C24xx GPIO LEDs

編譯核心,並把編譯好的核心下載到開發板上,運行:

[root@zhaocj /]#ls

bin      etc     lib      proc     sys      usr

dev      home    linuxrc  sbin     temp

[root@zhaocj /]#cd sys

[root@zhaocj /sys]#ls

block     class    devices   fs        module

bus       dev      firmware  kernel    power

進入sys目錄下,我們看到該目錄下有一些子目錄。

[root@zhaocj /sys]#cd class

[root@zhaocj class]#ls

backlight     hidraw       leds          rtc          vc

bdi          hwmon         mem           sound        video_output

block         i2c-adapter   misc         spi_master    vtconsole

firmware      i2c-dev       mmc_host     spidev        watchdog

gpio         input         mtd          tty

graphics      lcd          net           udc

進入class目錄,我們會看到在該目錄下有一些裝置,其中leds就是本次我們要操作的LED。

[root@zhaocj class]#cd leds

[root@zhaocj leds]#ls

backlight  led1      led2       led3       led4

在leds目錄下,會看到四個LED的目錄,這就是開發板上的四個LED。另外backlight目錄是關於LCD的背光,與LED無關。

[root@zhaocj leds]#cd led1

[root@zhaocj led1]#ls

brightness     max_brightness  subsystem

device         power           uevent

brightness檔案就是LED裝置,對其進行操作就可完成對LED的控制。

[root@zhaocj led1]#cat brightness

0

可以看出led1當前的狀態是關閉。(0表示關閉,1表示開啟)

[root@zhaocj led1]#cat >brightness<<eof

> 1

> eof

#[root@zhaocj led1]#

向brightness寫1,表示開啟LED。這時led1會被點亮。

 

當然,我們也可以編寫使用者程式來控制開發板上的四個LED

/**********************

****leds.c**************

**********************/

#include<stdint.h>

#include<string.h>#include<fcntl.h>#include<unistd.h>#include<stdio.h>#include<linux/input.h>#include<unistd.h>int main(int argc, char *argv[]){  int fd, no;/*判斷是要控制哪個LED,並開啟相應的檔案*/ no=(int)argv[1][3]-48; switch(no)   {   case 1:     fd = open("/sys/class/leds/led1/brightness", O_RDWR);  break;    case 2:      fd = open("/sys/class/leds/led2/brightness", O_RDWR);  break; case 3:   fd = open("/sys/class/leds/led3/brightness", O_RDWR);  break; case 4:   fd = open("/sys/class/leds/led4/brightness", O_RDWR);  break; default:     return -1;} if(fd<0)    {      printf("can not open file.\n");      return -1;    }/*完成開啟或關閉LED操作*/ if(!strcmp(argv[2],"on"))         write(fd, "1", 1); else if(!strcmp(argv[2],"off"))     write(fd, "0", 1);  close(fd);  return 0;}

 

上面的程式只做簡單測試之用。編譯該檔案:

arm-linux-gcc  -o  leds  leds.c

把leds檔案下載到temp目錄下,運行:

[root@zhaocj /temp]# ./leds  led2  on

則點亮led2。

[root@zhaocj /temp]# ./leds  led2  off

則關閉led2。

 

下面我就來簡單分析一下linux內建的LED子系統。

在mach-zhaocj2440.c檔案,建立了LED裝置,如下:

/* LEDS */

 

static struct s3c24xx_led_platdata zhaocj2440_led1_pdata = {

       .name             = "led1",

       .gpio              = S3C2410_GPB(5),

       .flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

       .def_trigger    = "heartbeat",

};

 

static struct s3c24xx_led_platdata zhaocj2440_led2_pdata = {

       .name             = "led2",

       .gpio              = S3C2410_GPB(6),

       .flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

       .def_trigger    = "nand-disk",

};

 

static struct s3c24xx_led_platdata zhaocj2440_led3_pdata = {

       .name             = "led3",

       .gpio              = S3C2410_GPB(7),

       .flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

       .def_trigger    = "mmc0",

};

 

static struct s3c24xx_led_platdata zhaocj2440_led4_pdata = {

       .name             = "led4",

       .gpio              = S3C2410_GPB(8),

       .flags             = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

       .def_trigger    = "",

};

定義了四個LED資料,名字分別為led1~led4,這就是我們在leds目錄下看到這四個子目錄。它們所串連的引腳分別為B口的5~8,這是由S3C2410_GPB()宏定義完成的。標識S3C24XX_LEDF_ACTLOW表示的是低電平有效,S3C24XX_LEDF_TRISTATE表示的三態無效。另外def_trigger表示的是觸發控制,如我們對nand進行讀寫操作時,led2會不停的閃,在這裡我們沒有用到這個功能,暫時不用理會。

 

static struct platform_device zhaocj2440_led1= {

       .name             = "s3c24xx_led",

       .id          = 1,

       .dev        = {

              .platform_data       = &zhaocj2440_led1_pdata,

       },

};

 

static struct platform_device zhaocj2440_led2= {

       .name             = "s3c24xx_led",

       .id          = 2,

       .dev        = {

              .platform_data       = &zhaocj2440_led2_pdata,

       },

};

 

static struct platform_device zhaocj2440_led3= {

       .name             = "s3c24xx_led",

       .id          = 3,

       .dev        = {

              .platform_data       = &zhaocj2440_led3_pdata,

       },

};

 

static struct platform_device zhaocj2440_led4= {

       .name             = "s3c24xx_led",

       .id          = 4,

       .dev        = {

              .platform_data       = &zhaocj2440_led4_pdata,

       },

};

上面則建立了匯流排平台裝置,四個LED的裝置名稱都是s3c24xx_led,子裝置id分別從1到4,裝置資料則是上面定義的四個LED資料。然後把這四個LED裝置再添加到開發板的裝置數組中,即:

static struct platform_device *zhaocj2440_devices[]__initdata = {

……

       &zhaocj2440_led1,

       &zhaocj2440_led2,

       &zhaocj2440_led3,

       &zhaocj2440_led4,

……

};

最後,在開發板系統初始化過程中,再把裝置數組中的裝置逐一註冊到系統匯流排上,即:

static void __init zhaocj2440_init(void)

{

……

platform_add_devices(zhaocj2440_devices,ARRAY_SIZE(zhaocj2440_devices));

……

}

這樣就完成了LED裝置的建立。

 

光有裝置還不能工作,任何一個裝置的運行還需要與之相對應的驅動。對於基於s3c24xx的LED來說,它的驅動是在drivers/leds目錄下Leds-s3c24xx.c檔案內建立的,即:

static struct platform_driver s3c24xx_led_driver = {

       .probe            = s3c24xx_led_probe,

       .remove          = s3c24xx_led_remove,

       .driver            = {

              .name             = "s3c24xx_led",

              .owner           = THIS_MODULE,

       },

};

裝置和驅動是如何匹配的呢?即裝置如何找到它所對應的驅動的呢?靠的就是name。我們會發現platform_device和platform_driver都有元素name,它們的內容如果一致,裝置和驅動就會配對成功。對於LED來說,它們的name都是s3c24xx_led。當裝置和驅動匹配上以後,就要運行probe所指定的函數,簡單地說,它就是完成一些初始化工作。當需要移除裝置時,就需要運行remove所指定的函數,它完成的任務是登出裝置。對於支援熱插拔的裝置來說,尤為重要。

 

現在就來說一下s3c24xx_led_probe函數:

static int s3c24xx_led_probe(struct platform_device *dev)

{

       structs3c24xx_led_platdata*pdata = dev->dev.platform_data;

       structs3c24xx_gpio_led *led;

       intret;

 

       /*用於給LED分配記憶體空間*/

led =devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),

                        GFP_KERNEL);

       if(led == NULL) {

              dev_err(&dev->dev,"No memory for device\n");

              return-ENOMEM;

       }

 

       /*儲存LED裝置結構*/

       platform_set_drvdata(dev,led);

 

       /*給LED結構體賦值,其中s3c24xx_led_set就是具體負責操作LED的函數*/

       led->cdev.brightness_set= s3c24xx_led_set;

       led->cdev.default_trigger= pdata->def_trigger;

       led->cdev.name= pdata->name;

       led->cdev.flags|= LED_CORE_SUSPENDRESUME;

 

       led->pdata = pdata;

 

       /*為LED分配io引腳*/

       ret =devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED");

       if(ret < 0)

              returnret;

 

       /*no point in having a pull-up if we are always driving */

 

       /*無需上拉*/

       s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE);

 

       /*設定io引腳為輸入*/

       if(pdata->flags & S3C24XX_LEDF_TRISTATE)

              gpio_direction_input(pdata->gpio);

       else

              gpio_direction_output(pdata->gpio,

                     pdata->flags& S3C24XX_LEDF_ACTLOW ? 1 :0);

 

       /*register our new led device */

 

       /*註冊一個新的LED裝置類對象

該函數是在drivers/leds目錄下的Led-class.c檔案內定義的*/

       ret= led_classdev_register(&dev->dev, &led->cdev);

       if(ret < 0)

              dev_err(&dev->dev,"led_classdev_register failed\n");

 

       return ret;

}

 

從以上分析可以看出,s3c24xx_led_probe函數主要就是完成LED裝置的一些初始化工作。而負責開、關LED任務的是s3c24xx_led_set函數,在該函數內,gpio_set_value(pd->gpio, state);是具體完成為相應引腳置1或清零的任務。

 

drivers/leds目錄下的Led-class.c檔案是LED子系統的底層核心檔案,它主要負責建立LED類,以及建立裝置節點,上面提到的led_classdev_register函數就是在這個檔案中定義的。為了更好的理解LED子系統,我們再簡單分析一下該檔案。

 

在子系統初始化時,會調用leds_init函數,它的第一段代碼:

leds_class = class_create(THIS_MODULE,"leds");

就是建立leds類,也就是我們在sys/class目錄下看到的leds目錄。另外

leds_class->dev_attrs = led_class_attrs;

是賦予該類的屬性。那麼我們再來看看led_class_attrs結構的第一句代碼:

__ATTR(brightness, 0644, led_brightness_show,led_brightness_store)

其中brightness就是我們對LED具體操作的裝置檔案名稱,0644是該檔案的許可權,led_brightness_show是讀檔案所調用的函數,led_brightness_store是寫檔案所調用的函數。讀檔案也就是讀取LED的狀態(是關還是開),寫檔案也就是完成開啟LED或關閉LED操作。

 

最後再分析一下前面提到的led_classdev_register函數。在該函數內首先利用device_create函數建立裝置節點,也就是在leds目錄下,產生led1~led4這四個目錄。另一項重要的任務就是把裝置節點添加到leds的鏈表中。

 

對linux內建的LED子系統的分析就到這裡。我想只要理解了該子系統,那麼自己完全可以寫出關於GPIO讀寫操作的任何驅動程式來。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.