目录
GPIO1节点
内核设备树新增rgb_led节点(使用gpio子系统)
常用的对外接口
头文件
of_find_node_by_path()函数
of_get_named_gpio()函数
gpio_request()函数
gpio_free()函数
gpio_direction_output()函数
gpio_direction_input()函数
gpio_get_value()函数
gpio_set_value()函数
GPIO子系统实验:IO引脚高低电平控制
修改设备树
dts_led.c文件
执行过程
GPIO1节点
gpio1: gpio@209c000 {
// 用于和gpio子系统平台驱动做匹配,对应的驱动文件为 drivers/gpio/gpio-mxc.c
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
//对照数据手册,gpio1寄存器组的起始地址即为0x209c000,第二个值表示范围
reg = <0x209c000 0x4000>;
//描述中断相关的内容
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
//描述初始化外设时钟
clocks = <&clks IMX6UL_CLK_GPIO1>;
//表明gpio1节点是一个gpio控制器
gpio-controller;
//表示要用2个cell描述一个 GPIO引脚
#gpio-cells = <2>;
//表示 gpio1 节点是个中断控制器
interrupt-controller;
//表示要用2个cell描述一个中断
#interrupt-cells = <2>;
//gpio编号转换成pin编号,如gpio子系统的0~9对应pinctrl的23~32号pin
gpio-ranges = <&iomuxc 0 23 10>, <&iomuxc 10 17 6>,
<&iomuxc 16 33 16>;
};
内核设备树新增rgb_led节点(使用gpio子系统)
rgb_led{
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default";
compatible = "fire,rgb_led";
pinctrl-0 = <&pinctrl_rgb_led>;
// 以下每个属性都有一个属性值
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
rgb_led_red:自定义属性
&gpio1 4表示GPIO1_IO04
GPIO_ACTIVE_LOW表示低电平有效
常用的对外接口
头文件
#define <linux/gpio.h>
#define <linux/of_gpio.h>
of_find_node_by_path()函数
// path:设备树节点的绝对路径
// 获取指定路径节点的device_node结构体
inline struct device_node *of_find_node_by_path(const char *path)
/*
* 返回值:
* 成功:目标节点
* 失败:NULL
*/
of_get_named_gpio()函数
/*
* np:指定的设备树节点
* propname:GPIO属性名,比如rgb_led_red、rgb_led_green、rgb_led_blue
* index:引脚索引值。若一个属性有多个属性值,index表示选第几个属性值,0表示第一个属性值
*/
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index);
/*
* 返回值:
* 成功:GPIO编号
* 失败:负数
*/
gpio_request()函数
/*
* 把指定的gpio引脚添加到pin_ctrl子系统管理,避免不同的外设使用同一个引脚
* gpio:要申请的GPIO编号,就是在gpio子系统里的编号
* label:给gpio设置个名字
*/
static inline int gpio_request(unsigned gpio, const char *label);
/* 返回值:
* 成功:0
* 失败:负数
*/
gpio_free()函数
/*
* 释放在pinctrl子系统中注册的gpio编号
* gpio:要释放的GPIO编号(pinctrl子系统的编号)
*/
static inline void gpio_free(unsigned gpio);
// 返回值:无
gpio_direction_output()函数
/*
* gpio:要操作的GPIO编号
* value:设置默认输出值
*/
static inline int gpio_direction_output(unsigned gpio , int value);
/*
* 成功:0
* 失败:负数
*/
gpio_direction_input()函数
//gpio:要操作的GPIO编号
int gpio_direction_input(unsigned gpio);
/*
* 返回值:
* 成功:0
* 失败:负数
*/
gpio_get_value()函数
//gpio:要操作的GPIO编号
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio);
/*
* 返回值:
* 成功:GPIO的电平值(0、1)
* 失败:负数
*/
gpio_set_value()函数
/*
* gpio:要操作的GPIO编号
* value:要设置的输出值
*/
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value);
// 返回值:无
GPIO子系统实验:IO引脚高低电平控制
修改设备树
打开内核/arch/arm/boot/dts/imx6ull-mmc-npi.dts,在/内修改用户加入内容。
rgb_led{
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default";
compatible = "fire,rgb_led";
pinctrl-0 = <&pinctrl_rgb_led>;
// 以下每个属性都有一个属性值
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
虚拟机:
重新编译设备树:make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
然后拷贝到共享文件夹:sudo cp 内核/arch/arm/boot/dts/imx6ull-mmc-npi.dtb /home/couvrir/桌面/sharedir
开发板:
替代旧的二进制设备树:sudo cp /mnt/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb
同步缓冲区:sync
重启设备:sudo reboot
dts_led.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define DEV_NAME "rgb_led"
#define DEV_CNT (1)
int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;
/* 定义字符设备的设备号 */
static dev_t led_devno;
/* 定义字符设备结构体 */
static struct cdev led_chrdev;
/* 保存创建的类 */
struct class *class_led;
/* 保存创建的设备 */
struct device *device;
/* rgb_led的设备树节点结构体 */
struct device_node *rgb_led_device_node;
/* 定义led资源结构体,保存获取的节点信息以及转换后的虚拟寄存器地址 */
struct led_resource{
struct device_node *device_node; //rgb_led_red的设备树节点
void __iomem *virtual_CCM_CCGR;
void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
void __iomem *virtual_DR;
void __iomem *virtual_GDIR;
};
/* 定义RGB三个灯的led_resource结构体,保存获取的节点信息 */
struct led_resource led_red;
struct led_resource led_green;
struct led_resource led_blue;
static int led_chrdev_open(struct inode *inode, struct file *filp)
{
printk("open form driver\n");
return 0;
}
static ssize_t led_chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret, error;
unsigned int register_data = 0; //暂存读取的寄存器数据
unsigned char receive_data[10]; //用于保存接收到的数据
unsigned int write_data = 0;
if(cnt > 10) cnt = 10;
error = copy_from_user(receive_data, buf, cnt);
if(error < 0) return -1;
ret = kstrtoint(receive_data, 16, &write_data);
if(ret) return -1;
/* 设置GPIO1_04输出电平 */
if(write_data & 0x04){
gpio_set_value(rgb_led_red, 0);
}else{
gpio_set_value(rgb_led_red, 1);
}
/* 设置GPIO4_20输出电平 */
if(write_data & 0x02){
gpio_set_value(rgb_led_green, 0);
}else{
gpio_set_value(rgb_led_green, 1);
}
/* 设置GPIO4_19输出电平 */
if(write_data & 0x01){
gpio_set_value(rgb_led_blue, 0);
}else{
gpio_set_value(rgb_led_blue, 1);
}
return cnt;
}
static struct file_operations led_chrdev_fops = {
.owner = THIS_MODULE,
.open = led_chrdev_open,
.write = led_chrdev_write,
};
static int led_probe(struct platform_device *pdv)
{
int ret = -1; //保存错误状态码
unsigned int register_data = 0;
printk(KERN_ALERT "match successed!\n");
/* 获取rgb_led的设备树节点 */
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if(rgb_led_device_node == NULL){
printk(KERN_ERR "get rgb_led failed!\n");
return -1;
}
/* 获取red led GPIO 引脚号 */
rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
if(rgb_led_red < 0){
printk(KERN_ERR "rgb_led_red failed!\n");
return -1;
}
/* 获取green led GPIO 引脚号 */
rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
if(rgb_led_green < 0){
printk(KERN_ERR "rgb_led_green failed!\n");
return -1;
}
/* 获取blue led GPIO 引脚号 */
rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
if(rgb_led_blue < 0){
printk(KERN_ERR "rgb_led_blue failed!\n");
return -1;
}
/* 设置GPIO为输出模式,并默认高电平 */
gpio_direction_output(rgb_led_red, 1);
gpio_direction_output(rgb_led_green, 1);
gpio_direction_output(rgb_led_blue, 1);
/* 第一步
* 采用动态分配的方式获取设备编号,次设备号为0
* 设备名称为rgb-leds,可通过命令cat /proc/devices查看
* DEV_CNT为1,当前只申请一个设备编号
*/
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
/* 第二步
* 关联字符设备结构体cdev与文件操作结构体file_operations
*/
led_chrdev.owner = THIS_MODULE;
cdev_init(&led_chrdev, &led_chrdev_fops);
/* 第三步
* 添加设备到cdev_map哈希表中
*/
ret = cdev_add(&led_chrdev, led_devno, DEV_CNT);
if(ret < 0){
printk("fail to add cdev\n");
goto add_err;
}
/* 第四步:创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/* 第五步:创建设备 */
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
alloc_err:
return -1;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("error!\n");
}
static const struct of_device_id rgb_led[] = {
{.compatible = "fire,rgb_led"},
{/* sentinel */}
};
/* 定义平台设备结构体 */
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}
};
static int __init led_platform_driver_init(void)
{
int DriverState;
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_ALERT "DriverState is %d\n", DriverState);
return 0;
}
static void __exit led_platform_driver_exit(void){
/* 取消物理地址映射到虚拟地址 */
iounmap(led_red.virtual_CCM_CCGR);
iounmap(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_red.virtual_DR);
iounmap(led_red.virtual_GDIR);
iounmap(led_green.virtual_CCM_CCGR);
iounmap(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_green.virtual_DR);
iounmap(led_green.virtual_GDIR);
iounmap(led_blue.virtual_CCM_CCGR);
iounmap(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_blue.virtual_DR);
iounmap(led_blue.virtual_GDIR);
/* 销毁设备 */
device_destroy(class_led, led_devno);
/* 删除设备号 */
cdev_del(&led_chrdev);
/* 取消注册字符设备 */
unregister_chrdev_region(led_devno, DEV_CNT);
/* 销毁类 */
class_destroy(class_led);
platform_driver_unregister(&led_platform_driver);
printk(KERN_ALERT "led_platform_driver exit\n");
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");
执行过程
虚拟机:执行make和make copy。生成.ko文件。
开发板(在挂载目录下执行):
sudo insmod dts_led.ko
ls /dev/rgb_led
sudo sh -c "echo 1 > /dev/rgb_led"
sudo sh -c "echo 2 > /dev/rgb_led"
sudo sh -c "echo 4 > /dev/rgb_led"
sudo sh -c "echo 7 > /dev/rgb_led"
sudo sh -c "echo 0 > /dev/rgb_led"
sudo rmmod dts_led.ko文章来源:https://www.toymoban.com/news/detail-681712.html
文章来源地址https://www.toymoban.com/news/detail-681712.html
到了这里,关于9.GPIO子系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!