1.使能时钟
2.初始化GPIO,比如输出功能,上/下拉,速度等。
3.是否IO复用,如果要将 IO 作为其它外设引脚使用的话就需要设置 IO 的复用功能。
4.设置GPIO输出高低电平
硬件配置:
参考IMX6ULL芯片手册<< IMX6ULL 参考手册.pdf >>
I.MX6ULL 的 IO 分为两类:SNVS 域的和通用的。SNVS( Secure Non-Volatile Storage)为安全的非易失性存储的IO,
本质上都是一样的。
I.MX6ULL 的 IO 命名与IO配置
“IOMUXC**_YY_**MUC_CTL_PADXX_XX”
前面的“YY”有 MUX 和 PAD 两种命名,分别配置不同功能。
如:GPIO1_IO00有
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 //用来配置 GPIO1_IO00 复用功能
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 //是来配置 GPIO1_IO00,包括速度设置、驱动能力设置、压摆率设置等等
后面的“XX_XX”就是 GPIO 命名,如GPIO0_IO0、UART1_TX_DATA、JTAG_MOD等
GPIO配置
上述是配置IO,GPIO 是 IO 众多复用功能中的一种,需要配置GPIO的一些功能。参考<< IMX6UL 参考手册 >>的“Chapter 26 G
eneral Purpose Input/Ouput(GPIO)”:

当 IO 用作 GPIO 的时候需要设置的寄存器,一共有八个:DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR 和 ISR.
其中 DR为数据寄存器。设置高低电平,若输出高电平 则GPIO.DR = 1;
GDIR为工作方向,即输入/输出
PSR 寄存器也是一个 GPIO 对应一个位,读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值
I.MX6U GPIO 时钟使能
参考钟《I.MX6UL 参考手册》“Chapter 18: Clock Controller Module(CCM)”,CMM 有 CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关, 其中CCM_CCGR1的 bit27:26 位控制GPIO1的时钟使能。

LED0原理图:

DSO 数字信号输出,用来控制 LED。
R9 510R 和 R11 510R
- R9、R11 是电阻编号。
- 510R 表示 510 欧姆,用于限流保护 LED。
DCDC 3V3
这是 3.3V 电源,来自某个 DC-DC 转换器,给 LED 电路供电。GND
地线。PWR
可能是一个电源指示或电源控制引脚。GPIO 3 即GPIO1_IO03为控制LED0引脚
图中三角形加箭头为发光二极管,代表正负极,发光二极管只能从阳极流向负极,当 GPIO1_IO03 输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03 的输出电平,输出 0 就亮,输出 1 就灭。
按照 8.1 小节中讲的,我们需要对 GPIO1_IO03 做如下设置:
- 寄存器映射
1****、使能 GPIO1 时钟
GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制,将这两个位都设置位 11 即
可。本教程所有例程已经将 I.MX6U 的所有外设时钟都已经打开了,因此这一步可以不用做。
2****、设置 GPIO1_IO03 的复用功能
找到 GPIO1_IO03 的复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为
0X020E0068,然后设置此寄存器,将 GPIO1_IO03 这个 IO 复用为 GPIO 功能,也就是 ALT5。
3****、配置 GPIO1_IO03
找到 GPIO1_IO03 的配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03”的地址为
0X020E02F4,根据实际使用情况,配置此寄存器。
4****、设置 GPIO
我们已经将 GPIO1_IO03 复用为了 GPIO 功能,所以我们需要配置 GPIO。找到 GPIO3 对
应的 GPIO 组寄存器地址,在《IMX6ULL 参考手册》的 1357 页,如图 8.3.1 所示
无操作系统的LED 驱动 (裸机类似STM32)
ToVirtual() 的作用是当系统启动了硬件 MMU 之后,根据物理地址和虚拟地址的映射关系,将寄存器的物理地址转化为虚拟地址
1 #define reg_gpio_ctrl *(volatile int *)(ToVirtual(GPIO_REG_CTRL)) //指针指向的值 将物理地址转换为虚拟地址
2 #define reg_gpio_data *(volatile int *)(ToVirtual(GPIO_REG_DATA))
3 /* 初始化 LED */
4 void LightInit(void)
5 {
6 reg_gpio_ctrl |= (1 << n); /* 设置 GPIO 为输出 */
7 }
8
9 /* 点亮 LED */
10 void LightOn(void)
11 {
12 reg_gpio_data |= (1 << n); /* 在 GPIO 上输出高电平 */
13 }
14
15 /* 熄灭 LED */
16 void LightOff(void)
17 {
18 reg_gpio_data &= ~ (1 << n); /* 在 GPIO 上输出低电平 */
19 }
linux操作系统的LED驱动
LED为字符设备
结构体 file_operations、cdev,
Linux 内核模块声明用的 MODULE_AUTHOR、MODULE_LICENSE、module_init、module_
exit,
字符设备注册、分配和注销的函数 register_chrdev_region()、alloc_chrdev_
region()、unregister_chrdev_region() 等。
驱动中包含 light_init ()、light_cleanup ()、light_read()、light_write() 等函数
1 #include .../* 包含内核中的多个头文件 */
2 #define reg_gpio_ctrl *(volatile int *)(ToVirtual(GPIO_REG_CTRL)) //指针指向的值 将物理地址转换为虚拟地址
2 #define reg_gpio_data *(volatile int *)(ToVirtual(GPIO_REG_DATA))
3 /* 初始化 LED */
4 void light_init(void)
5 {
6 reg_gpio_ctrl |= (1 << n); /* 设置 GPIO 为输出 */
7 }
8
9 /* 点亮 LE1 ```````````````````````````` */
10 void light_on(void)
11 {
12 reg_gpio_data |= (1 << n); /* 在 GPIO 上输出高电平 */
13 }
14
15 /* 熄灭 LED */
16 void light_off(void)
17 {
18 reg_gpio_data &= ~ (1 << n); /* 在 GPIO 上输出低电平 */
19 }
/* 设备结构体 */
3 struct light_dev {
4 struct cdev cdev; /* 字符设备 cdev 结构体 */
5 unsigned char vaule; /* LED 亮时为 1,熄灭时为 0,用户可读写此值 */
6 };
7 struct light_dev *light_devp;
8 int light_major = LIGHT_MAJOR;
9 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
10 MODULE_LICENSE("Dual BSD/GPL");
11 /* 打开和关闭函数 */
12 int light_open(struct inode *inode, struct f ile *filp)
13 {
14 struct light_dev *dev;
15 /* 获得设备结构体指针 */
16 dev = container_of(inode->i_cdev, struct light_dev, cdev);
17 /* 让设备结构体作为设备的私有信息 */
18 filp->private_data = dev;
19 return 0;
20 }
21 int light_release(struct inode *inode, struct f ile *f ilp)
22 {
23 return 0;
24 }
25 /* 读写设备 : 可以不需要 */
26 ssize_t light_read(struct f ile *f ilp, char __user *buf, size_t count, loff_t *f_pos)
28 {
29 struct light_dev *dev = filp->private_data; /* 获得设备结构体 */
30 if (copy_to_user(buf, &(dev->value), 1))
31 return -EFAULT;
32 return 1;
33 }
34 ssize_t light_write(struct f ile *f ilp, const char __user *buf, size_t count, loff_t *f_pos)
{
37 struct light_dev *dev = f ilp->private_data;
38 if (copy_from_user(&(dev->value), buf, 1))
39 return -EFAULT;
40 /* 根据写入的值点亮和熄灭 LED */
41 if (dev->value == 1)
42 light_on();
43 else
44 light_off();
45 return 1;
46 }
47 /* ioctl 函数 */
48 int light_ioctl(struct inode *inode, struct f ile *f ilp, unsigned int cmd, unsigned long arg)
50 {
51 struct light_dev *dev = f ilp->private_data;
52 switch (cmd) {
53 case LIGHT_ON:
54 dev->value = 1;
55 light_on();
56 break;
57 case LIGHT_OFF:
58 dev->value = 0;
59 light_off();
60 break;
61 default:
62 /* 不能支持的命令 */
63 return -ENOTTY;
64 }
65 return 0;
66 }
67 struct file_operations light_fops = {
68 .owner = THIS_MODULE,
69 .read = light_read,
70 .write = light_write,
71 .ioctl = light_ioctl,
72 .open = light_open,
73 .release = light_release,
74 };
75 /* 设置字符设备 cdev 结构体 */
static void light_setup_cdev(struct light_dev *dev, int index)
77 {
78 int err, devno = MKDEV(light_major, index);
79 cdev_init(&dev->cdev, &light_fops);
80 dev->cdev.owner = THIS_MODULE;
81 dev->cdev.ops = &light_fops;
82 err = cdev_add(&dev->cdev, devno, 1);
83 if (err)
84 printk(KERN_NOTICE "Error %d adding LED%d", err, index);
85 }
86 /* 模块加载函数 */
87 int light_init(void)
88 {
89 int result;
90 dev_t dev = MKDEV(light_major, 0);
91 /* 申请字符设备号 */
92 if (light_major)
93 result = register_chrdev_region(dev, 1, "LED");
94 else {
95 result = alloc_chrdev_region(&dev, 0, 1, "LED");
96 light_major = MAJOR(dev);
97 }
98 if (result < 0)
99 return result;
100 /* 分配设备结构体的内存 */
101 light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL);
102 if (!light_devp) {
103 result = -ENOMEM;
104 goto fail_malloc;
105 }
106 memset(light_devp, 0, sizeof(struct light_dev));
107 light_setup_cdev(light_devp, 0);
108 light_gpio_init();
109 return 0;
110 fail_malloc:
111 unregister_chrdev_region(dev, light_devp);
112 return result;
113 }
114 /* 模块卸载函数 */
115 void light_cleanup(void)
116 {
117 cdev_del(&light_devp->cdev); /* 删除字符设备结构体 */
118 kfree(light_devp); /* 释放在 light_init 中分配的内存 */
119 unregister_chrdev_region(MKDEV(light_major, 0), 1); /* 删除字符设备 */
120 }
121 module_init(light_init);
122 module_exit(light_cleanup);
设备树下的 platform 驱动编写
在设备树中创建设备节点
毫无疑问,肯定要先在设备树中创建设备节点来描述设备信息,重点是要设置好 compatible 属性的值,因为 platform 总线需要通过设备节点的 compatible 属性值来匹配驱动!这点要切记。比如,我们可以编写如下所示的设备节点来描述我们本章实验要用到的 LED 这个设备: linux/arc/arm/boot/dts/imx6ull-alientek-emmc.dts
gpioled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "atkalpha-gpioled";
5 pinctrl-names = "default";
6 pinctrl-0 = <&pinctrl_led>;
7 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
8 status = "okay";
9 };
platform驱动程序编写
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of_gpio.h>
12 #include <linux/semaphore.h>
13 #include <linux/timer.h>
14 #include <linux/irq.h>
15 #include <linux/wait.h>
16 #include <linux/poll.h>
17 #include <linux/fs.h>
18 #include <linux/fcntl.h>
19 #include <linux/platform_device.h>
20 #include <asm/mach/map.h>
21 #include <asm/uaccess.h>
22 #include <asm/io.h>
23 /***************************************************************
24 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
25 文件名 : leddriver.c
26 作者 : 左忠凯
27 版本 : V1.0
28 描述 : 设备树下的 platform 驱动
29 其他 : 无
30 论坛 : www.openedv.com
31 日志 : 初版 V1.0 2019/8/13 左忠凯创建
32 ***************************************************************/
33 #define LEDDEV_CNT 1 /* 设备号长度 */
34 #define LEDDEV_NAME "dtsplatled" /* 设备名字 */
35 #define LEDOFF 0
36 #define LEDON 1
37
38 /* leddev 设备结构体 */
39 struct leddev_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
struct device_node *node; /* LED 设备节点 */
int led0; /* LED 灯 GPIO 标号 */
47 };
48
49 struct leddev_dev leddev; /* led 设备 */
51 /*
52 * @description : LED 打开/关闭
53 * @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
54 * @return : 无
55 */
56 void led0_switch(u8 sta)
57 {
if (sta == LEDON )
gpio_set_value(leddev.led0, 0);
else if (sta == LEDOFF)
gpio_set_value(leddev.led0, 1);
62 }
63
64 /*
65 * @description : 打开设备
66 * @param – inode : 传递给驱动的 inode
67 * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
68 * 一般在 open 的时候将 private_data 指向设备结构体。
69 * @return : 0 成功;其他 失败
70 */
71 static int led_open(struct inode *inode, struct file *filp)
72 {
filp->private_data = &leddev; /* 设置私有数据 */
return 0;
75 }
76
77 /*
78 * @description : 向设备写数据
79 * @param - filp : 设备文件,表示打开的文件描述符
80 * @param - buf : 要写给设备写入的数据
81 * @param - cnt : 要写入的数据长度
82 * @param – offt : 相对于文件首地址的偏移
83 * @return : 写入的字节数,如果为负值,表示写入失败
84 */
85 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
86 {
int retvalue;
unsigned char databuf[2];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;}
ledstat = databuf[0];
if (ledstat == LEDON) {
led0_switch(LEDON);
} else if (ledstat == LEDOFF) {
led0_switch(LEDOFF);}
return 0;
}
106
107 /* 设备操作函数 */
108 static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
112 };
113
114 /*
115 * @description : flatform 驱动的 probe 函数,当驱动与
116 * 设备匹配以后此函数就会执行
117 * @param - dev : platform 设备
118 * @return : 0,成功;其他负值,失败
119 */
120 static int led_probe(struct platform_device *dev)
121 {
printk("led driver and device was matched!\r\n");
/* 1、设置设备号 */
if (leddev.major) {
leddev.devid = MKDEV(leddev.major, 0);
register_chrdev_region(leddev.devid, LEDDEV_CNT,LEDDEV_NAME);
}
else {
alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT,LEDDEV_NAME);
leddev.major = MAJOR(leddev.devid);
}
131
132 /* 2、注册设备 */
133 cdev_init(&leddev.cdev, &led_fops);
134 cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
135
136 /* 3、创建类 */
137 leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
138 if (IS_ERR(leddev.class)) {
139 return PTR_ERR(leddev.class);
140 }
141
142 /* 4、创建设备 */
143 leddev.device = device_create(leddev.class, NULL, leddev.devid,
NULL, LEDDEV_NAME);
144 if (IS_ERR(leddev.device)) {
145 return PTR_ERR(leddev.device);
146 }
147
148 /* 5、初始化 IO */
149 leddev.node = of_find_node_by_path("/gpioled");
150 if (leddev.node == NULL){
151 printk("gpioled node nost find!\r\n");
152 return -EINVAL;
153 }
154
155 leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
156 if (leddev.led0 < 0) {
157 printk("can't get led-gpio\r\n");
158 return -EINVAL;
159 }
160
161 gpio_request(leddev.led0, "led0");
162 gpio_direction_output(leddev.led0, 1); /*设置为输出,默认高电平 */
163 return 0;
164 }
165
166 /*
167 * @description : remove 函数,移除 platform 驱动的时候此函数会执行
168 * @param - dev : platform 设备
169 * @return : 0,成功;其他负值,失败
170 */
171 static int led_remove(struct platform_device *dev)
172 {
173 gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭 LED */
174
175 cdev_del(&leddev.cdev); /* 删除 cdev */
176 unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
177 device_destroy(leddev.class, leddev.devid);
178 class_destroy(leddev.class);
179 return 0;
180 }
181
182 /* 匹配列表 */
183 static const struct of_device_id led_of_match[] = {
184 { .compatible = "atkalpha-gpioled" },
185 { /* Sentinel */ }
186 };
187
188 /* platform 驱动结构体 */
189 static struct platform_driver led_driver = {
190 .driver = {
191 .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
192 .of_match_table = led_of_match, /* 设备树匹配表 */
193 },
194 .probe = led_probe,
195 .remove = led_remove,
196 };
197
198 /*
199 * @description : 驱动模块加载函数
200 * @param : 无
201 * @return : 无
202 */
203 static int __init leddriver_init(void)
204 {
205 return platform_driver_register(&led_driver);
206 }
207
208 /*
209 * @description : 驱动模块卸载函数
210 * @param : 无
211 * @return : 无
212 */
213 static void __exit leddriver_exit(void)
214 {
215 platform_driver_unregister(&led_driver);
216 }
217
218 module_init(leddriver_init);
219 module_exit(leddriver_exit);
220 MODULE_LICENSE("GPL");
221 MODULE_AUTHOR("zuozhongkai");
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 351134995@qq.com