作业要求:利用GPIO子系统完成点灯实验
目录
实验过程:
1.GPIO子系统框架:
内核层又细分为三层分别为设备驱动层、核心层、厂商驱动层
设备驱动层:利用下层的接口实现对硬件的控制
核心层:为设备驱动层提供接口,隐藏下层细节
厂商驱动层:主要根据设备驱动层调用的接口来实现硬件寄存器的映射,初始化和相关配置
2.GPIO子系统API
1.解析对应设备的设备树节点
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
功能:根据设备树节点的名字解析设备树节点
参数:
from:当前节点父节点首地址(不知道就填NULL,默认从设备树根节点开始解析)
name:设备树节点名字 mynode
返回值:成功获取到解析的设备树节点信息结构体首地址,失败返回NULL
#include<linux/of_gpio.h>
2.根据解析得到的设备树节点结构体去解析得到对应的gpio编号
int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
功能:根据设备树节点信息结构体解析得到gpio编号
参数:
np:设备树节点结构体指针
proname:gpio编号对应的键名
index:选用的键值对中值的索引号(第一个值为0)
返回值:成功返回GPIO编号,失败返回错误码
3.向内核申请要使用的gpio编号
int gpio_request(unsigned gpio, const char *label)
参数:
gpio:要申请的gpio编号
label:填NULL
返回值:成功返回0,失败返回错误码
4.int gpio_direction_input(unsigned gpio)
设置gpio编号对应的gpio管脚为输入模式
5.int gpio_direction_output(unsigned gpio, int value)
设置gpio编号对应的gpio管脚为输出模式,并且输出电平(0:低电平,1:高电平)
6.void gpio_set_value(unsigned gpio, int value)
设置输出指定的数值(0:低电平,1:高电平)
7.int gpio_get_value(unsigned gpio)
根据返回值读取gpio编号对应的管脚状态(0:低电平,1:高电平)
8.void gpio_free(unsigned gpio);
将注册的gpio编号在内核中注销
3.gpio设备树的编写
3.1 led灯的硬件连接图
gpio控制器的设备树节点已经在内核中被编写完成,我们现在要做的事是在LED设备树节点中引用指定的gpio控制器对应的设备树节点即可,不必自己申请注册设备节点。
3.2 gpio控制器的设备树节点
gpio控制器设备树节点:
查看stm32mp157a-fsmp1a.dts文件头文件,找到了 stm32mp15xxac-pinctrl.dtsi
进入,得到:
&pinctrl {
gpioe: gpio@50006000 {
status = "okay";
ngpios = <16>;//表示这组gpio中管脚的数量
gpio-ranges = <&pinctrl 0 64 16>;//这组gpio的编号的七十至
};
};
查看stm32mp157a-fsmp1a.dts文件头文件,找到了 stm32mp157.dtsi->stm32mp153.dtsi
->stm32mp151.dtsi
pinctrl: pin-controller@50002000 {
#address-cells = <1>;//地址的个数
#size-cells = <1>;//地址长度的个数
compatible = "st,stm32mp157-pinctrl";//厂商信息
gpioe: gpio@50006000 {
gpio-controller;//空属性,起到声明作用
#gpio-cells = <2>;//在引用当前节点时,属性值u32的个数
reg = <0x4000 0x400>;//当前节点地址和地址长度
clocks = <&rcc GPIOE>;//使能GPIO控制器时钟
st,bank-name = "GPIOE";//名字时GPIOE
status = "disabled";//控制器工作状态
//disabled(不工作),okay(工作)
};
3.3编写led灯的设备树节点
在内核顶层目录下有一个Documentation文件夹,它是内核的帮助文档
在这个目录下/Documentation/devicetree/bindings/gpio目录中有gpio相关设备树的帮助信息
vi gpio.txt
gpioe: gpio@50006000 {
gpio-controller;//空属性,起到声明作用
#gpio-cells = <2>;
};
//编写自己的led设备树节点
myleds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};
4.利用GPIO子系统完成点灯的实例代码
#include <linux/init.h>
#include <linux/module.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/of.h>
#include<linux/io.h>
#include<linux/of_gpio.h>
struct cdev *cdev;
dev_t devno;
unsigned int minor=0;
#if 0
unsigned int major=0;
#else
unsigned int major=500;
#endif
struct timer_list timer;
struct device_node *dnode;
char kbuf[128]={0};
struct class *cls;
struct device *dev;
int gpioe10;
int gpiof10;
int gpioe8;
int gpioz5;
int gpioz6;
int gpioz7;
void timer_handler(struct timer_list *timer)
{
gpio_set_value(gpioe10,!gpio_get_value(gpioe10));
gpio_set_value(gpiof10,!gpio_get_value(gpiof10));
gpio_set_value(gpioe8,!gpio_get_value(gpioe8));
gpio_set_value(gpioz5,!gpio_get_value(gpioz5));
gpio_set_value(gpioz6,!gpio_get_value(gpioz6));
gpio_set_value(gpioz7,!gpio_get_value(gpioz7));
mod_timer(timer,jiffies+HZ);
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
if(size >sizeof(kbuf)) size=sizeof(kbuf);
ret = copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("copy to user is error\n");
return -EIO;
}
return size; //5.返回拷贝数据大小
}
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
if(size >sizeof(kbuf)) size=sizeof(kbuf);
ret = copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("copy from user is error\n");
return -EIO;
}
return size; //5.返回拷贝数据大小
}
int mycdev_close (struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
dnode=of_find_node_by_name(NULL,"myleds");
if(dnode==NULL)
{
printk("解析设备树节点失败\n");
return -EIO;
}
printk("解析设备树节点成功\n");\
gpioe10=of_get_named_gpio(dnode,"led1",0);
if(gpioe10<0)
{
printk("解析gpioe10编号失败\n");
return -EIO;
}
printk("解析gpioe10编号成功\n");
gpiof10=of_get_named_gpio(dnode,"led2",0);
if(gpiof10<0)
{
printk("解析gpiof10编号失败\n");
return -EIO;
}
printk("解析gpiof10编号成功\n");
gpioe8=of_get_named_gpio(dnode,"led3",0);
if(gpioe8<0)
{
printk("解析gpioe8编号失败\n");
return -EIO;
}
printk("解析gpioe8编号成功\n");
gpioz5=of_get_named_gpio(dnode,"led4",0);
if(gpioz5<0)
{
printk("解析gpioz5编号失败\n");
return -EIO;
}
printk("解析gpioz5编号成功\n");
gpioz6=of_get_named_gpio(dnode,"led5",0);
if(gpioz6<0)
{
printk("解析gpioz6编号失败\n");
return -EIO;
}
printk("解析gpioz6编号成功\n");
gpioz7=of_get_named_gpio(dnode,"led6",0);
if(gpioz7<0)
{
printk("解析gpioz7编号失败\n");
return -EIO;
}
printk("解析gpioez7编号成功\n");
gpio_request(gpioe10,NULL);
gpio_request(gpiof10,NULL);
gpio_request(gpioe8,NULL);
gpio_request(gpioz5,NULL);
gpio_request(gpioz6,NULL);
gpio_request(gpioz7,NULL);
gpio_direction_output(gpioe10,0);
gpio_direction_output(gpiof10,0);
gpio_direction_output(gpioe8,0);
gpio_direction_output(gpioz5,0);
gpio_direction_output(gpioz6,0);
gpio_direction_output(gpioz7,0);
timer.expires=jiffies+HZ;
timer_setup(&timer,timer_handler,0);
add_timer(&timer);
return 0;
}
static void __exit mycdev_exit(void)
{
/*
1.销毁设备节点
2.销毁目录
3.注册驱动对象
4.释放设备资源
5.释放对象空间
*/
gpio_free(gpioe10);
gpio_free(gpiof10);
gpio_free(gpioe8);
gpio_free(gpioz5);
gpio_free(gpioz6);
gpio_free(gpioz7);
del_timer(&timer);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");