i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED相关的知识,希望对你有一定的参考价值。
前置知识
一、编写基本设备驱动模块
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
dev_t led_dts_dev;
struct cdev *led_dts_cdev;
struct class *led_dts_class;
struct device *led_dts_device0;
static int led_dts_open(struct inode *inode, struct file *fp)
return 0;
static int led_dts_read(struct file *fp, char __user *buf, size_t size, loff_t *off)
return 0;
static int led_dts_write(struct file *fp, const char __user *buf, size_t size, loff_t *off)
return 0;
static int led_dts_release(struct inode *inode, struct file *fp)
return 0;
static struct file_operations led_dts_fops =
.owner = THIS_MODULE,
.open = led_dts_open,
.read = led_dts_read,
.write = led_dts_write,
.release = led_dts_release,
;
static int __init led_dts_init(void)
int ret;
//分配cdev设备号
ret = alloc_chrdev_region(&led_dts_dev, 0, 1, "led_dts");
if (ret != 0)
printk("alloc_chrdev_region fail!");
return -1;
//初始化cdev
led_dts_cdev = cdev_alloc();
if (!led_dts_cdev)
printk("cdev_alloc fail!");
return -1;
//设置fop操作函数
led_dts_cdev->owner = THIS_MODULE;
led_dts_cdev->ops = &led_dts_fops;
//注册cdev
cdev_add(led_dts_cdev, led_dts_dev, 1);
// 创建设备类
led_dts_class = class_create(THIS_MODULE, "led_dts_class");
if (!led_dts_class)
printk("class_create fail!");
return -1;
//创建设备节点
led_dts_device0 = device_create(led_dts_class, NULL, led_dts_dev, NULL, "led0");
if (IS_ERR(led_dts_device0))
printk("device_create led_dts_device0 fail!");
return -1;
return 0;
static void __exit led_dts_exit(void)
// 将设备从内核删除
cdev_del(led_dts_cdev);
// 释放设备号
unregister_chrdev_region(led_dts_dev, 1);
// 删除设备节点
device_destroy(led_dts_class, led_dts_dev);
// 删除设备类
class_destroy(led_dts_class);
module_init(led_dts_init);
module_exit(led_dts_exit);
MODULE_AUTHOR("Mculover666");
MODULE_LICENSE("GPL");
编译脚本:
KERNEL_DIR = /home/mculover666/imx6ull/kernel/linux-imx6ull
obj-m := led_dts.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
编译:
make
二、修改设备树文件
正点原子i.MX6ULL开发板上的LED连接到 GPIO1_IO03 引脚。
添加设备树自定义描述文件
添加自定义设备树描述文件 arch/arm/boot/dts/imx6ull-atk-emmc-custom.dtsi
,以后自定义的节点添加都在该文件中进行。
在设备树描述文件arch/arm/boot/dts/imx6ull-atk-emmc.dts
中引用我们的自定义文件:
#include "imx6ull-atk-emmc-custom.dtsi"
1. 添加pinctrl子节点
在 iomuxc 节点的 imx6ul-evk 子节点下,新添加一个名为 pinctrl_led0的节点,用来描述引脚复用。
pinctrl_led0: led0grp
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
>;
;
2. 添加led0设备节点
在根节点下添加led0节点,节点名为led0,如下。
led0
compatible = "gpio-led";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led0>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
;
3. 检查pin是否已经被使用
(1)全局搜索pinctrl占用:MX6UL_PAD_GPIO1_IO03
。
找到,已经被占用为电阻触摸屏(TSC)外设的pin。
直接删除节点 pinctrl_tsc。
(2)全局搜索gpio占用:gpio1 3
。
删除该段对于tsc节点的补充描述。
4. 重新编译设备树
进入内核目录:
make dtbs
使用新的设备树文件启动内核,查看是否有新添加的led0子节点。
三、编写led gpio硬件初始化函数
主要完成:
- 子系统从设备树中获取led所使用的gpio
- 初始化gpio
包含头文件:
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
定义全局变量:
struct device_node *node;
int led_gpio;
编写硬件初始化函数:
static int board_led_init(void)
int ret;
/* 设置LED所使用的GPIO */
//在设备树寻找节点
node = of_find_node_by_path("/led0");
if (!node)
printk("of_find_node_by_path fail!");
return -1;
//获取gpio属性值,得到LED使用的gpio编号
led_gpio = of_get_named_gpio(node, "led-gpio", 0);
if (led_gpio < 0)
printk("of_get_named_gpio fail!");
return -2;
printk("led-gpio num is %d", led_gpio);
// 申请gpio
ret = gpio_request(gpioled.led_gpio, "led-gpio");
if (ret < 0)
printk("gpio %d request fail!\\n", gpioled.led_gpio);
return -3;
//设置gpio方向并输出默认电平
ret = gpio_direction_output(led_gpio, 1);
if (ret < 0)
printk("gpio_direction_output fail!");
gpio_free(gpioled.led_gpio);
return -4;
return 0;
在驱动加载函数最开始调用:
// led硬件初始化
ret = board_led_init();
if (ret < 0)
printk("board_led_init fail, err is %d", ret);
return -1;
四、全局变量封装
一路写下来,整个驱动模块的全局变量非常多,用一个结构体来进行管理:
struct gpioled_dev
dev_t led_dts_dev; /*!< 设备号 */
struct cdev *led_dts_cdev; /*!< cdev对象 */
struct class *led_dts_class; /*!< 设备类 */
struct device *led_dts_device0; /*!< 设备节点 */
struct device_node *node; /*!< 设备树节点 */
int led_gpio; /*!< led使用的gpio编号 */
;
static struct gpioled_dev gpioled;
相关变量的使用也需要同步修改。
五、LED对应驱动
1. open
static int led_dts_open(struct inode *inode, struct file *fp)
/* 设置私有数据 */
fp->private_data = &gpioled;
return 0;
2. read
static int led_dts_read(struct file *fp, char __user *buf, size_t size, loff_t *off)
return 0;
3. write
static int led_dts_write(struct file *fp, const char __user *buf, size_t size, loff_t *off)
int ret;
unsigned char data_buf[1];
unsigned char led_status;
struct gpioled_dev *dev = fp->private_data;
// 拷贝用户传入数据
ret = copy_from_user(data_buf, buf, 1);
if (ret < 0)
printk("led write failed!\\n");
return -EFAULT;
// 控制LED
led_status = data_buf[0];
if (led_status == 0)
// 关闭LED,高电平关闭
gpio_set_value(dev->led_gpio, 1);
else if (led_status == 1)
// 打开LED,低电平打开
gpio_set_value(dev->led_gpio, 0);
return 0;
4. release
static int led_dts_release(struct inode *inode, struct file *fp)
return 0;
至此,驱动编写完成,编译:
make
六、驱动测试
1. 加载驱动模块
insmod led_dts.ko
查看是否加载成功:
查看是否注册了设备类:
查看是否有设备节点:
2. 编写测试程序
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
int led_ctrl(char *filename, int status)
int fd;
int ret;
unsigned char data_buf[1];
if (!filename)
return -1;
// 打开设备文件
fd = open(filename, O_RDWR);
if (fd < 0)
printf("open %s error!\\n", filename);
return 0;
// 写文件
data_buf[0] = status;
ret = write(fd, data_buf, sizeof(data_buf));
if (ret < 0)
printf("write %s error!\\n", data_buf);
// 关闭文件
close(fd);
return 0;
int main(int argc, char *argv[])
uint32_t interval;
// 检查参数
if (argc != 3)
printf("usage: ./test_led_blink [device] [led blink interval(s)]\\n");
return -1;
interval = atoi(argv[2]);
if (interval < 1)
interval = 1;
while (1)
led_ctrl(argv[1], 1);
sleep(interval);
led_ctrl(argv[1], 0);
sleep(interval);
编译:
arm-linux-gnueabihf-gcc test_led_blink.c -o test_led_blink
3. 测试led闪烁
在开发板上执行:
./test_led_blink /dev/led0 1
可以看到LED闪烁了起来,大功告成。
以上是关于i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED的主要内容,如果未能解决你的问题,请参考以下文章
i.MX6ULL驱动开发 | 10 - 修改LCD驱动点亮LCD显示小企鹅logo
i.MX6ULL驱动开发 | 17 - Linux中断机制及使用方法(taskletworkqueue软中断)
i.MX6ULL驱动开发 | 27 - 使用WM8960 CODEC播放音频