pinctrl和gpio
相关资料
讯为视频链接: link
1.pinctrl子系统
1.1pinctrl子系统
(1)、获取设备树中pin信息管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
(2)、根据获得到的pin信息来设置pin的复用功能,对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成pin group,形成特定的功能。
(3)、根据获得到的pin信息来设置pin的电气特性,比如上下拉、速度、驱动能力。
1.2pinctrl帮助文档
不同soc厂家的pin controller的节点里面的属性表示什么意思可以通过linux源码目录/Documentation/devicetree/bindings下的txt文档查看。
2.gpio子系统
2.1gpio子系统提供的功能
(1)、主要实现引脚功能的配置,如设置为GPIO,特殊功能,GPIO的的方向,设置为中断等。
(2)、实现软硬件的分离
3代码
3.1 修改bcm2711-rpi-4-b.dts
在bcm2711-rpi-4-b.dts文件里面添加led_test,对于控制led灯,只需要控制引脚输出高低电平,不需要考虑引脚复用,所以没用pinctrl,(看bcm2711手册,感觉也不需要用pinctrl,然后试了下就用gpio子系统,能控制引脚高低电平)
3.2driver.c代码
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块
#include <linux/platform_device.h> //platform平台设备相关的头文件
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
int size;
int led_gpio = 0;
int ret = 0;
u32 out_values[2]={0}; //用于存储从设备树获取的reg数据
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = {0};
if ( copy_from_user( kbuf, ubuf, size) != 0)
{
printk( "copy_from_user error\n ");
return -1;
}
printk( "kbuf is %s\n ", kbuf);
//如果传递进内核的数据是1,则led亮
if(kbuf[0]==1){
gpio_set_value(led_gpio, 1);
}
else if(kbuf[0]==0){
gpio_set_value(led_gpio, 0);
}
return 0;
}
int misc_release( struct inode *inode, struct file *file)
{
printk( "hello misc_relaease bye bye \n ");
return 0;
}
int misc_open( struct inode *inode, struct file *file)
{
printk( "hello misc_open\n ");
return 0;
}
//文件操作集
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.write = misc_write,
};
//miscdevice 结构体
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops,
};
int led_probe( struct platform_device *pdev)
{
//5匹配成功后进入probe函数
int ret;
printk( "led_probe\n");
//6查找我们要查找的节点
test_device_node = of_find_node_by_path("/led_test@0xfe200000");
if(test_device_node == NULL){
printk("of_find_node_by_path error");
return -1;
}
printk("of_find_node_by_path success");
//从设备树获取要使用的GPIO
/* of_get_named_gpio 函数获取 GPIO 编号,
因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,
此函数会将设备树中类似<&gpio 4 0>的属性信息转换为对应的 GPIO 编号 */
led_gpio = of_get_named_gpio(test_device_node, "gpios", 0);
if (led_gpio < 0)
{
printk("of_get_named_gpio is error \n");
return -1;
}
printk("led_gpio is %d \n", led_gpio);
//申请一个 GPIO 管脚
ret = gpio_request(led_gpio, "led");
if (ret < 0)
{
printk("gpio_request is error \n");
return -1;
}
//设置某个 GPIO 为输出,并且设置默认输出值
gpio_direction_output(led_gpio, 1);
//注册杂项设备
ret = misc_register( &misc_dev);
if (ret < 0)
{
printk( "misc registe is error \n");
}
printk( "misc registe is succeed \n");
return 0;
}
int led_remove( struct platform_device *pdev)
{
gpio_free(led_gpio);
printk("led_remove\n");
return 0;
}
const struct of_device_id of_match_table_test[] = {
{
.compatible = "test_led"},
{},
};
struct platform_driver led_driver ={
//3.在led_driver结构体中完成了led_probe和led_remove
.probe = led_probe,
.remove = led_remove,
//4.在driver结构体里面填写匹配名字,让他匹配设备树里面的led_test节点
.driver = {
.owner = THIS_MODULE,
.name = "test_led", //匹配名字(匹配的是设备树),匹配成功后进入probe函数
.of_match_table = of_match_table_test//优先匹配of_match_table
},
};
static int led_driver_init( void)
{
//1.看驱动文件先从init函数看
int ret = 0;
//2.在init函数里面注册了platform_driver
ret = platform_driver_register( &led_driver);
if( ret<0)
{
printk( "platform_driver_register error \n");
}
printk( "platform_driver_register ok \n");
return 0;
}
static void led_driver_exit(void)
{
misc_deregister( &misc_dev); //卸载杂项设备
printk( "misc gooodbye! \n");
// platform 驱动卸载
platform_driver_unregister( &led_driver);
printk( "goodbye! \n");
}
module_init( led_driver_init);
module_exit( led_driver_exit);
MODULE_LICENSE( "GPL");
3.3 app.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] = "0";
fd = open("/dev/hello_misc",O_RDWR);//打开设备节点
if(fd < 0)
{
perror("open error \n");
return fd;
}
buf[0]=atoi(argv[1]);
//read(fd,buf,sizeof(buf));
write(fd,buf,sizeof(buf)); //向内核层写数据
//printf("buf is %s\n",buf);
close(fd);
return 0;
}
3.4 运行情况