IMX6学习记录(13)-LED驱动

本文详细介绍了一种基于Linux的嵌入式系统中LED驱动的开发过程,包括寄存器地址确定、时钟配置、GPIO设置及驱动程序编写。通过具体的代码示例,展示了如何映射寄存器、配置GPIO功能并实现LED的开关控制。

上面是我的微信和QQ群,欢迎新朋友的加入。

1.确定寄存器地址

时钟配置

时钟基地址:0x20c406c

GPIO MUX基地址:0x2290028

GPIO PAD基地址0x0229006c

GPIO寄存器

2.驱动程序

#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO5_IO08_BASE (0x02290028)
#define SW_PAD_GPIO5_IO08_BASE (0x0229006c)
#define GPIO5_DR_BASE (0X020AC000)
#define GPIO5_GDIR_BASE (0X020AC004)

完整驱动

#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

static int major = 232; /* 静态设备号方式的默认值 */
static int minor = 0; /* 静态设备号方式的默认值 */

module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);

#define ON  0
#define OFF 1

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO5_IO08_BASE (0x02290028)
#define SW_PAD_GPIO5_IO08_BASE (0x0229006c)
#define GPIO5_DR_BASE (0X020AC000)
#define GPIO5_GDIR_BASE (0X020AC004)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO5_IO08;
static void __iomem *SW_PAD_GPIO5_IO08;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;

static dev_t devno; /* 设备编号 */

#define DEVICE_NAME "led"

void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == ON) {
		val = readl(GPIO5_DR);
		val &= ~(1 << 8);
		writel(val, GPIO5_DR);
	}else if(sta == OFF) {
		val = readl(GPIO5_DR);
		val|= (1 << 8);
		writel(val, GPIO5_DR);
	}
}

static int led_open(struct inode *inode, struct file *file )
{ 
	printk(KERN_INFO DEVICE_NAME " opened!\n");
	return 0;
}

static int led_release(struct inode *inode, struct file *file )
{
	printk(KERN_INFO DEVICE_NAME " closed!\n");
	return 0;
}

static ssize_t led_read(struct file *file, char *buf,size_t count, loff_t *f_pos)
{
	printk(KERN_INFO DEVICE_NAME " read method!\n");
	return count;
}

static ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
	retvalue = copy_from_user(databuf, buf, count);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
	ledstat = databuf[0]; /* 获取状态值 */
	if(ledstat == ON) {
		led_switch(ON); /* 打开 LED 灯 */
	} else if(ledstat == OFF) {
		led_switch(OFF); /* 关闭 LED 灯 */
	}
	return count;
}

//static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long led_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO DEVICE_NAME " ioctl method!\n");
	return 0;
}

struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl
};

static int __init led_init(void)
{
	int ret;
	int retvalue = 0;
	u32 val = 0;
	/* 初始化 LED */
	/* 1、寄存器地址映射 */
	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO5_IO08 = ioremap(SW_MUX_GPIO5_IO08_BASE, 4);
	SW_PAD_GPIO5_IO08 = ioremap(SW_PAD_GPIO5_IO08_BASE, 4);
	GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);
	GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);
	/* 2、使能 GPIO1 时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26); /* 清除以前的设置 */
	val |= (3 << 26); /* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);
	/* 3、设置 GPIO1_IO03 的复用功能,将其复用为
	* GPIO1_IO03,最后设置 IO 属性。
	*/
	writel(5, SW_MUX_GPIO5_IO08);
	/* 寄存器 SW_PAD_GPIO5_IO08 设置 IO 属性 */
	writel(0x10B0, SW_PAD_GPIO5_IO08);
	/* 4、设置 GPIO1_IO03 为输出功能 */
	val = readl(GPIO5_GDIR);
	val &= ~(1 << 8); /* 清除以前的设置 */
	val |= (1 << 8); /* 设置为输出 */

	writel(val, GPIO5_GDIR);
	/* 5、默认关闭 LED */
	val = readl(GPIO5_DR);
	val |= (1 << 8);
	writel(val, GPIO5_DR);

	if (major > 0) { /* 静态设备号 */
		devno = MKDEV(major, minor);
		ret = register_chrdev_region(devno, 1, "led");
        printk(KERN_ALERT " 静态设备号:%d!\n",major);
	} else { /* 动态设备号 */
		ret = alloc_chrdev_region(&devno, minor, 1, "led"); /* 从系统获取主设备号 */
		major = MAJOR(devno);
        printk(KERN_ALERT " 动态设备号:%d!\n",major);
	}

	if (ret < 0) {
		printk(KERN_ERR "cannot get major %d \n", major); 
		return -1;
	}

	/* 6、注册字符设备驱动 */
	retvalue = register_chrdev(major, DEVICE_NAME, &led_fops);
	if(retvalue < 0){
		printk("register chrdev failed!\r\n");
		return -EIO;
	}
	return 0;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO5_IO08);
	iounmap(SW_PAD_GPIO5_IO08);
	iounmap(GPIO5_DR);
	iounmap(GPIO5_GDIR);
	/* 注销字符设备驱动 */
	unregister_chrdev(major, DEVICE_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jun,2839084093@qq.com");

makefile

obj-m := led.o

KERNEL_SRC := /home/jun/i.mx6/source/linux-imx-rel_imx_4.1.15_2.1.0_ga
SRC := $(shell pwd)

all:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
	rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -f Module.markers Module.symvers modules.order
	rm -rf .tmp_versions Modules.symvers

编译脚本

#!/bin/bash

make clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j4

编译

3.应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.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];
	/* 打开 led 驱动 */
	fd = open(filename, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!\r\n", argv[1]);
		return -1;
	}
	databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
	/* 向/dev/led 文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("LED Control Failed!\r\n");
		close(fd);
		return -1;
	}
	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", argv[1]);
	return -1;
	}
	return 0;
}

4.测试

mknod /dev/led c 248 0
./app /dev/led 0
./app /dev/led 1

板子上的LED正常变化

5.实现自动分配设备号

驱动源码

#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define ON  0
#define OFF 1

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO5_IO08_BASE (0x02290028)
#define SW_PAD_GPIO5_IO08_BASE (0x0229006c)
#define GPIO5_DR_BASE (0X020AC000)
#define GPIO5_GDIR_BASE (0X020AC004)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO5_IO08;
static void __iomem *SW_PAD_GPIO5_IO08;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;

#define DEVICE_NAME "led"
#define DEVICE_CNT 1

static int major = 0; /* 静态设备号方式的默认值 */
static int minor = 0; /* 静态设备号方式的默认值 */

module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);

struct cdev *led; /* cdev 数据结构 */
static dev_t devno; /* 设备编号 */
static struct class *led_class;

void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == ON) {
		val = readl(GPIO5_DR);
		val &= ~(1 << 8);
		writel(val, GPIO5_DR);
	}else if(sta == OFF) {
		val = readl(GPIO5_DR);
		val|= (1 << 8);
		writel(val, GPIO5_DR);
	}
}

static int led_open(struct inode *inode, struct file *file )
{ 
	printk(KERN_INFO DEVICE_NAME " opened!\n");
	return 0;
}

static int led_release(struct inode *inode, struct file *file )
{
	printk(KERN_INFO DEVICE_NAME " closed!\n");
	return 0;
}

static ssize_t led_read(struct file *file, char *buf,size_t count, loff_t *f_pos)
{
	printk(KERN_INFO DEVICE_NAME " read method!\n");
	return count;
}

static ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
	retvalue = copy_from_user(databuf, buf, count);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
	ledstat = databuf[0]; /* 获取状态值 */
	if(ledstat == ON) {
		led_switch(ON); /* 打开 LED 灯 */
	} else if(ledstat == OFF) {
		led_switch(OFF); /* 关闭 LED 灯 */
	}
	return count;
}

//static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long led_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO DEVICE_NAME " ioctl method!\n");
	return 0;
}

struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl
};

static int __init led_init(void)
{
	u32 val = 0;
	int ret;
	
	/* 初始化 LED */
	/* 1、寄存器地址映射 */
	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO5_IO08 = ioremap(SW_MUX_GPIO5_IO08_BASE, 4);
	SW_PAD_GPIO5_IO08 = ioremap(SW_PAD_GPIO5_IO08_BASE, 4);
	GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);
	GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);
	/* 2、使能 GPIO1 时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26); /* 清除以前的设置 */
	val |= (3 << 26); /* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);
	/* 3、设置 GPIO1_IO03 的复用功能,将其复用为
	* GPIO1_IO03,最后设置 IO 属性。
	*/
	writel(5, SW_MUX_GPIO5_IO08);
	/* 寄存器 SW_PAD_GPIO5_IO08 设置 IO 属性 */
	writel(0x10B0, SW_PAD_GPIO5_IO08);
	/* 4、设置 GPIO1_IO03 为输出功能 */
	val = readl(GPIO5_GDIR);
	val &= ~(1 << 8); /* 清除以前的设置 */
	val |= (1 << 8); /* 设置为输出 */

	writel(val, GPIO5_GDIR);
	/* 5、默认关闭 LED */
	val = readl(GPIO5_DR);
	val |= (0 << 8);
	writel(val, GPIO5_DR);

	/* 注册字符设备驱动 */
	if (major > 0) { /* 静态设备号 */
		devno = MKDEV(major, 0);
		ret = register_chrdev_region(devno, DEVICE_CNT, DEVICE_NAME);
	} else { /* 动态设备号 */
		ret = alloc_chrdev_region(&devno, 0, DEVICE_CNT, DEVICE_NAME); /* 从系统获取主设备号 */
		major = MAJOR(devno);
		minor = MINOR(minor);
	}
	
	if (ret < 0) {
		printk(KERN_ERR "cannot get major %d \n", major); 
		return -1;
	}
	printk(KERN_ALERT " major:%d! minor:%d\n",major,minor);
	
	led = cdev_alloc(); /* 分配 led 结构 */
	if (led != NULL) { 
		cdev_init(led, &led_fops); /* 初始化 led 结构 */
		led->owner = THIS_MODULE;
		if (cdev_add(led, devno, DEVICE_CNT) != 0) { /* 增加 led 到系统中 */
			printk(KERN_ERR "add cdev error!\n");
			goto error;
		}
	} else {
		printk(KERN_ERR "cdev_alloc error!\n"); 
		return -1;
	}

	led_class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(led_class)) { 
		printk(KERN_INFO "create class error\n");
		return -1;
	}

	device_create(led_class, NULL, devno, NULL, DEVICE_NAME);
    printk(KERN_ALERT "mod init!\n"); 
	return 0;

	error:
	unregister_chrdev_region(devno, DEVICE_CNT); /* 释放已经获得的设备号 */
	return ret;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO5_IO08);
	iounmap(SW_PAD_GPIO5_IO08);
	iounmap(GPIO5_DR);
	iounmap(GPIO5_GDIR);
	
	/* 注销字符设备 */
	cdev_del(led);/*移除字符设备*/
	unregister_chrdev_region(devno,DEVICE_CNT);/*释放设备号*/
	device_destroy(led_class,devno);
	class_destroy(led_class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jun,2839084093@qq.com");

创建文件夹

mkdir /lib/modules/4.1.15

磁盘空间不足

df -i

确定是inodes不足了

ls -d /tmp/sess_* | xargs -n 10 rm -fr

明天写

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值