樓主在這邊給大家介紹下如何使用PopMetal的GPIO。先講過(guò)程,再講原理吧,
該驅動(dòng)需要涉及到的知識點(diǎn):1,DTS設備樹(shù)的作用,2,platform虛擬總線(xiàn)驅動(dòng)的編寫(xiě)。
第一步,添加DTS節點(diǎn)
在/kernel/arch/arm/boot/dts/rockchip.dts下添加如下內容。
下圖rockchip-leds-gpio這部分的內容,修改保存,
第二步,在kernel/drivers下創(chuàng )建個(gè)LED文件夾,然后加入如下幾個(gè)文件驅動(dòng)文件leds.c,Makefile和Kconfig.如下圖
源碼:
/***********************************************************************************
* driver for led0
*
**********************************************************************************/
#include <linux/miscdevice.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
static int leds_probe(struct platform_device *pdev)
{ int ret =-1
int i
int led
enum of_gpio_flags flag
struct device_node *led_node = pdev->dev.of_node
led = of_get_named_gpio_flags(led_node,"led-gpios",0,&flag)
printk("get gpio id successful\n")
if(!gpio_is_valid(led)){
printk("invalid led-gpios: %d\n",led)
return -1
}
if(gpio_request(led,"led_gpio")){
printk("led gpio request failed!\n")
return ret
}
gpio_direction_output(led,1)
for(i=0 i < 10 i++)
{
gpio_set_value(led,1)
mdelay(500)
gpio_set_value(led,0)
mdelay(500)
printk("it's %d\n",i)
}
return 0
}
static int leds_remove(struct platform_device *pdev)
{
return 0
}
static struct of_device_id leds_of_match[] = {
{ .compatible = "rockchip-leds-gpio" },
{ }
}
MODULE_DEVICE_TABLE(of, leds_of_match)
static struct platform_driver leds_driver = {
.driver = {
.name = "leds-drivers",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(leds_of_match),
},
.probe = leds_probe,
.remove = leds_remove,
};
/*static int __init leds_init(void)
{
printk(KERN_INFO "Enter %s\n", __FUNCTION__)
return platform_driver_register(&leds_driver)
return 0
}
static void __exit leds_exit(void)
{
platform_driver_unregister(&leds_driver)
printk("close leds\n")
}*/module_platform_driver(leds_driver)
module_platform_driver(leds_driver)
MODULE_DESCRIPTION("leds Driver")
MODULE_LICENSE("GPL")
MODULE_ALIAS("platform:leds-drivers")
/***********************************************************************************
* driver for led0
*
**********************************************************************************/
Kconfig:
Makefile:
第三步,修改drivers下的Kconfig和Makefile,修改內容如下
在Kconfig末尾添加:source “drivers/led/Kconfig”
在Makefile末尾添加: obj-$(CONFIG_LED0_TEST) +=led/
第四步,編譯新的kernel與resource并燒寫(xiě)進(jìn)板子里,
然后DTS中定義的引腳就會(huì )按照驅動(dòng)的內容,進(jìn)行高低電平的變化。
好了,現在我們來(lái)介紹下原理,首先是DTS,和另一塊開(kāi)發(fā)板PX2不一樣,PopMetal并沒(méi)有寫(xiě)相應的mach-*****文件,而是通過(guò)設備樹(shù)的方式獲取引腳的編號,設備樹(shù)的引入可減少了內核為支持新硬件而需要的改變,提高代碼重用,加速了Linux支持包的開(kāi)發(fā),使得單個(gè)內核鏡像能支持多個(gè)系統。簡(jiǎn)單來(lái)說(shuō),它就是給內核的一個(gè)參數,這些參數會(huì )啟動(dòng)相應的驅動(dòng),這樣參數不一樣,內核源碼支持的系統就不一樣。
而在這里我們要清楚的是我們要用相應的引腳就必須創(chuàng )建相應的節點(diǎn),并且賦上一些參數。
而驅動(dòng)又是怎么識別到這些參數的呢?這里我們先講講platform虛擬總線(xiàn)驅動(dòng),這個(gè)總線(xiàn)跟IIC,SPI等總線(xiàn)不一樣,是由內核虛擬出來(lái)的,我們就以上面的代碼為例,給大家講講他是怎么工作的,
首先這個(gè)驅動(dòng)的核心是:
static struct platform_driver leds_driver = {
.driver = {
.name = "leds-drivers",//驅動(dòng)名
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(leds_of_match),//匹配設備樹(shù)
},
.probe = leds_probe,//探測函數,檢測硬件上是否存在設備,檢測到便執行
.remove = leds_remove,//移除函數,硬件移除時(shí),執行
};
在這里我們只給部分內容賦予了值,本身這個(gè)結構體還有別的子項,需要了解的同學(xué)可以在這編博客看看:http://blog.sina.com.cn/s/blog_53c733350100zdav.html
在這個(gè)結構體中,我們就是通過(guò).of_match_table = of_match_ptr(leds_of_match),來(lái)匹配設備樹(shù)上的參數,而即系統會(huì )根據設備樹(shù)種定義的compatible參數比較驅動(dòng)中的leds_of_match中定義的 .compatible 參數,
來(lái)為參數找到相應的驅動(dòng),而定義的probe和remove函數則是對探測到設備做出反應,及移除設備時(shí)做出反應,而module_platform_driver(leds_driver)是將驅動(dòng)掛到總線(xiàn)上去,
現在我們看看probe是怎么獲取到GPIO的值的,其中它的主要函數如下:
struct device_node *led_node = pdev->dev.of_node
led = of_get_named_gpio_flags(led_node,"led-gpios",0,&flag)
功能就是將led_node節點(diǎn)上的led-gpios的值取下,而我們之前在rockchip.dts中隊led-gpios的定義如下:
led-gpios=&GPIO6 GPIO_A6 GPIO_ACTIVE_LOW,意思就是選擇引腳gpio6_a6,且該引腳低電平有效。
上面這句賦值便已經(jīng)將引腳的編號賦給了led-gpios,故接下來(lái)我們就可以用GPIO_requset_one GPIO_set_value,等函數去操作這個(gè)GPIO了,像gpio_set_value(led-gpios,1)將該引腳設置為高電平。
當然這些操作只是相對于引腳沒(méi)復用的GPIO口,引腳如果有復用功能,我們還得進(jìn)行一些別的操作把引腳的功能選好。