栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux 驱动篇 笔记 --字符设备 led驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux 驱动篇 笔记 --字符设备 led驱动

背景知识

linux 下任何 外设驱动都是 配置相应的寄存器

MMU(内存管理单元) 作用
  1. 完成虚拟空间到物理空间的映射
  2. 内存保护,设置寄存器的范围权限、设置虚拟存储空间的缓冲特性
内存管理 函数:

1. ioremap 函数:获取指定物理地址对应的虚拟地址

为宏 实际函数原型如下:

void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)

2、 iounmap函数: 释放掉 ioremap函数所做的映射 (卸载驱动的时候需要调用此函数)

为宏 实际函数原型如下:

void iounmap (volatile void __iomem *addr)

I/O 内存范围函数:

使用内存管理单元映射后,可直接操作虚拟地址,但是不建议如此,而是通过一下几个函数操作内存。

读:

u8 readb(const volatile void __iomem *addr) 

u16 readw(const volatile void __iomem *addr)

u32 readl(const volatile void __iomem *addr)

void writeb(u8 value, volatile void __iomem *addr)

 void writew(u16 value, volatile void __iomem *addr)

 void writel(u32 value, volatile void __iomem *addr)

实际代码  旧字符设备 led灯驱动代码编写: 指明 固定 设备号 以及子设备号 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define LED_MAJOR		200		
#define LED_NAME		"led" 	

#define LEDOFF 	0				
#define LEDON 	1				
 

#define CCM_CCGR1_BASE				(0x020C406C)	
#define SW_MUX_GPIO1_IO03_BASE		(0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0x020E02F4)
#define GPIO1_DR_BASE				(0x0209C000)
#define GPIO1_GDIR_BASE				(0x0209C004)


static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}


static int led_open(struct inode *inode, struct file *filp)
{
	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;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!rn");
		return -EFAULT;
	}

	ledstat = databuf[0];		

	if(ledstat == LEDON) {	
		led_switch(LEDON);		
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	
	}
	return 0;
}


static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}


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


static int __init led_init(void)
{
	int retvalue = 0;
	u32 val = 0;

	
	
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

	
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	
	val |= (3 << 26);	
	writel(val, IMX6U_CCM_CCGR1);

	
	writel(5, SW_MUX_GPIO1_IO03);
	
	
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	
	val |= (1 << 3);	
	writel(val, GPIO1_GDIR);

	
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);
	
	retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
	if(retvalue < 0){
		printk("register chrdev failed!rn");
		return -EIO;
	}
	return 0;
}


static void __exit led_exit(void)
{
	
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	
	unregister_chrdev(LED_MAJOR, LED_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ali");

代码功能

实现
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
};

加载驱动 led_init 

                        流程:

                        (1)寄存器地址映射

                          (2)  使能GPIO时钟

                        (3)设置GPIO复用功能 电气属性

                        (4)设置输出

                        (5)默认关闭led

                        (6)注册字符设备驱动

                          

        卸载驱动 led_exit

                        流程:

                        1:取消虚拟地址映射

                        2:卸载字符设备驱动

应用程序编辑

#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!rn");
		return -1;
	}

	filename = argv[1];

	
	fd = open(filename, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!rn", argv[1]);
		return -1;
	}

	databuf[0] = atoi(argv[2]);	

	
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("LED Control Failed!rn");
		close(fd);
		return -1;
	}

	retvalue = close(fd); 
	if(retvalue < 0){
		printf("file %s close failed!rn", argv[1]);
		return -1;
	}
	return 0;
}
运行测试 

在makefile 

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m := led.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

make -j32 即可 编译出 led.o  驱动文件

通过下面命令  编译出  应用文件

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

运行测试

depmod //第一次加载驱动的时候需要运行此命令

modprobe led.ko //加载驱动

mknod /dev/led c 200 0 //  创建节点  

./ledApp /dev/led 1 //打开 LED灯

./ledApp /dev/led 0 //关闭 LED灯

rmmod led.ko   //卸载驱动

新字符设备 led灯驱动代码2 编写:

区别于旧字符设备:

通过以下函数 自动获取 设备号

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

通过以下函数 释放获取的设备号

void unregister_chrdev_region(dev_t from, unsigned count)

修改原理代码  新代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 


#define NEWCHRLED_CNT			1		  	
#define NEWCHRLED_NAME			"newchrled"	
#define LEDOFF 					0			
#define LEDON 					1			
 

#define CCM_CCGR1_BASE				(0x020C406C)	
#define SW_MUX_GPIO1_IO03_BASE		(0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0x020E02F4)
#define GPIO1_DR_BASE				(0x0209C000)
#define GPIO1_GDIR_BASE				(0x0209C004)


static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


struct newchrled_dev{
	dev_t devid;			
	struct cdev cdev;		
	struct class *class;		
	struct device *device;	
	int major;				
	int minor;				
};

struct newchrled_dev newchrled;	


void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}


static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &newchrled; 
	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;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!rn");
		return -EFAULT;
	}

	ledstat = databuf[0];		

	if(ledstat == LEDON) {	
		led_switch(LEDON);		
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	
	}
	return 0;
}


static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}


static struct file_operations newchrled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};


static int __init led_init(void)
{
	u32 val = 0;

	
	
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

	
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	
	val |= (3 << 26);	
	writel(val, IMX6U_CCM_CCGR1);

	
	writel(5, SW_MUX_GPIO1_IO03);
	
	
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	
	val |= (1 << 3);	
	writel(val, GPIO1_GDIR);

	
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

	
	
	if (newchrled.major) {		
		newchrled.devid = MKDEV(newchrled.major, 0);
		register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
	} else {						
		alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);	
		newchrled.major = MAJOR(newchrled.devid);	
		newchrled.minor = MINOR(newchrled.devid);	
	}
	printk("newcheled major=%d,minor=%drn",newchrled.major, newchrled.minor);	
	
	
	newchrled.cdev.owner = THIS_MODULE;
	cdev_init(&newchrled.cdev, &newchrled_fops);
	
	
	cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);

	
	newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.class)) {
		return PTR_ERR(newchrled.class);
	}

	
	newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.device)) {
		return PTR_ERR(newchrled.device);
	}
	
	return 0;
}


static void __exit led_exit(void)
{
	
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	
	cdev_del(&newchrled.cdev);
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); 

	device_destroy(newchrled.class, newchrled.devid);
	class_destroy(newchrled.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

区别与旧字符设备:

使用cdev 结构体 表示一个字符设备

struct cdev { 
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

        

成员变量 ops  为字符设备操作函数集合

成员变量 dev 设备号
 

驱动 加载函数  led_init(void) 中 的 注册字符设备过程 分为以下4 小步

               (1)创建设备号

                        register_chrdev_region 或 alloc_chrdev_region 

                (2)初始化字符设备

                        cdev_init(struct cdev *cdev, const struct file_operations *fops)     

                (3)向linux 系统添加一个字符设备

                (4)创建一个类 ()

                          class_create(THIS_MODULE, NEWCHRLED_NAME);

                (5)创建设备

                       device_create(struct class *class,

                                                struct device *parent,

                                                dev_t devt,

                                                void *drvdata, const char *fmt, ...)

                通过(4)(5) 2步骤 可以自动创建设备 节点 省去在modprobe 后通过mknod 这步。

驱动卸载函数

        注销字符设备过程 分为以下4 小步

        (1)cdev_del(&newchrled.cdev);   删除字符设备

        (2)unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);     注销设备号 

        (3)device_destroy  析构 设备 对象

        (4)class_destroy     析构 类对象  

运行测试 

        相对于旧字符设备 不需要在 手动创建节点

        modprobe newchrled.ko //加载驱动

        ./ledApp /dev/newchrled 1 // 打开led灯 

        ./ledApp /dev/newchrled 1 // 关闭led灯 

        rmmod newchrled.ko  卸载驱动

​​​​​​​

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/882526.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号