楼主在这边给大家介绍下如何使用PopMetal的GPIO。先讲过程,再讲原理吧,
该驱动需要涉及到的知识点:1,DTS设备树的作用,2,platform虚拟总线驱动的编写。
第一步,添加DTS节点
在/kernel/arch/arm/boot/dts/rockchip.dts下添加如下内容。
下图rockchip-leds-gpio这部分的内容,修改保存,
第二步,在kernel/drivers下创建个LED文件夹,然后加入如下几个文件驱动文件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并烧写进板子里,
然后DTS中定义的引脚就会按照驱动的内容,进行高低电平的变化。
需要源码可下载: led.zip
好了,现在我们来介绍下原理,首先是DTS,和另一块开发板PX2不一样,PopMetal并没有写相应的mach-*****文件,而是通过设备树的方式获取引脚的编号,设备树的引入可减少了内核为支持新硬件而需要的改变,提高代码重用,加速了Linux支持包的开发,使得单个内核镜像能支持多个系统。简单来说,它就是给内核的一个参数,这些参数会启动相应的驱动,这样参数不一样,内核源码支持的系统就不一样。
而在这里我们要清楚的是我们要用相应的引脚就必须创建相应的节点,并且赋上一些参数。
而驱动又是怎么识别到这些参数的呢?这里我们先讲讲platform虚拟总线驱动,这个总线跟IIC,SPI等总线不一样,是由内核虚拟出来的,我们就以上面的代码为例,给大家讲讲他是怎么工作的,
首先这个驱动的核心是:
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,//移除函数,硬件移除时,执行
};
在这里我们只给部分内容赋予了值,本身这个结构体还有别的子项,需要了解的同学可以在这编博客看看:http://blog.sina.com.cn/s/blog_53c733350100zdav.html
在这个结构体中,我们就是通过.of_match_table = of_match_ptr(leds_of_match),来匹配设备树上的参数,而即系统会根据设备树种定义的compatible参数比较驱动中的leds_of_match中定义的 .compatible 参数,
来为参数找到相应的驱动,而定义的probe和remove函数则是对探测到设备做出反应,及移除设备时做出反应,而module_platform_driver(leds_driver)是将驱动挂到总线上去,
现在我们看看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节点上的led-gpios的值取下,而我们之前在rockchip.dts中队led-gpios的定义如下:
led-gpios=&GPIO6 GPIO_A6 GPIO_ACTIVE_LOW,意思就是选择引脚gpio6_a6,且该引脚低电平有效。
上面这句赋值便已经将引脚的编号赋给了led-gpios,故接下来我们就可以用GPIO_requset_one GPIO_set_value,等函数去操作这个GPIO了,像gpio_set_value(led-gpios,1)将该引脚设置为高电平。
当然这些操作只是相对于引脚没复用的GPIO口,引脚如果有复用功能,我们还得进行一些别的操作把引脚的功能选好。