1)添加pinctrl节点
使用开发板自带的LED灯进行操作,打开dts文件,在iomuxc_snvs下创建一个“pinctrl_led的子节点”
```
pinctrl_leds: ledgrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0X10B0
>;
};
```
2)添加LED设备节点
在根节点"/"创建LED灯节点,节点名称为**"gpioled"**,节点内容如下:
```
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "100ask-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_leds>;
led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
```
3)检查PIN是否被其他外设使用
检查 PIN 有没有被其他外设使用包括两个方面:
①、检查 pinctrl 设置(看是否有其他的使用这个,MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03,有使用的可以注释掉)。
②、如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。(同时把使用这个重复的也注释掉)
4)编译dtb
5)编写驱动程序gpioled.c
```C++
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
// gpioled设备结构体
struct gpioled_dev {
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
// 设置私有数据
filp->private_data = &gpioled;
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data; // 修正:file -> filp
retvalue = copy_from_user(databuf, buf, cnt);
if (retvalue < 0) {
printk("kernel write failed!\r\n"); // 修正:kernal -> kernel
return -EFAULT;
}
ledstat = databuf[0];
if (ledstat == LEDON) { // 修正:ledtest -> ledstat
// 打开led灯
gpio_set_value(dev->led_gpio, 0);
} else if (ledstat == LEDOFF) {
// 关闭led灯
gpio_set_value(dev->led_gpio, 1);
}
return cnt; // 返回实际写入的字节数
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
// 驱动入口函数
static int __init led_init(void)
{
int ret = 0;
// 设置led所使用的gpio
// 1. 获取设备节点:gpioled
gpioled.nd = of_find_node_by_path("/gpioled");
if (gpioled.nd == NULL) {
printk("gpioled node can not found\r\n");
return -EINVAL;
} else {
printk("gpioled node has been found\r\n");
}
// 2. 获取设备树中的gpio属性,得到led所使用的led编号
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if (gpioled.led_gpio < 0) {
printk("can not get led-gpio\r\n");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
// 3. 设置GPIO5_IO03为输出,并且输出高电平,默认关闭led灯
ret = gpio_direction_output(gpioled.led_gpio, 1);
if (ret < 0) {
printk("can not set gpio!\r\n");
}
/* 注册字符设备驱动 */
/* 1. 创建设备号 */
if (gpioled.major) {
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
} else {
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
// 2. 初始化cdev
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
// 3. 添加一个cdev
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
// 4. 创建类
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
return PTR_ERR(gpioled.class);
}
// 5. 创建设备
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device)) {
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init); // 修正:module -> module_init
module_exit(led_exit); // 修正:module -> module_exit
MODULE_LICENSE("GPL");
```
6)编写测试程序led_test.c
```C++
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#define LEDOFF 0
#define LEDON 1
int main(int argc,char ** argv)
{
int fd,retvalue;
char *filename;
unsigned char databuf[1];
if(argc !=3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd == -1)
{
printf("can not open file %s\n",argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]);
retvalue = write(fd,databuf,sizeof(databuf));
if(retvalue == -1){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
retvalue = close(fd);
if(retvalue < 0){
printf("close %s failed\r\n",argv[1]);
return -1;
}
return 0;
}
```
6)编写测试程序led_test.c
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#define LEDOFF 0
#define LEDON 1
int main(int argc,char ** argv)
{
int fd,retvalue;
char *filename;
unsigned char databuf[1];
if(argc !=3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd == -1)
{
printf("can not open file %s\n",argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]);
retvalue = write(fd,databuf,sizeof(databuf));
if(retvalue == -1){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
retvalue = close(fd);
if(retvalue < 0){
printf("close %s failed\r\n",argv[1]);
return -1;
}
return 0;
}
7)写makefile文件
```
KERN_DIR = /home/xxl/100ask_imx6ull_mini-sdk/Linux-4.9.88 # 板子所用内核源码的目录
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o led_test led_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order led_test
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
obj-m += gpioled.o
```
8)总结
配置电气属性
配置设备
编写makefile
make编译得到ko文件和可执行文件
移植到开发板
insmod操作,将.ko文件注册进入设备
可以通过lsmod查看是否注册成功,同时也可以切换到 cd /dev 设备是否注册
然后就可以开始运行,示例:./ledtest /dev/gpioled 1led_test