1.字符设备

1.1设备分类

Linux 下一切皆文件:Linux 系统将文件做分类 d – p l s c b

Linu 设备分类有 3 种:

字符设备(文件类型 c) 块设备(文件类型 b) 网络设备(没有设备文件)

字符设备: 鼠标 键盘 屏幕

块设备:硬件 emmc

Linux 内核中区分设备 使用设备号(无符号的 32 位整数)

主设备号: 高 12 位 //区分不同设备设备

次设备号: 低 20 位 //区分同类设备中的具体设备

键盘和鼠标都属于输入设备,所以 主设备号都是 13, 但是他俩次设备号不一样

IP:网络号和主机号组成 32 位整数 192.168.100.15

1.2字符设备相关的内容

所有设备在 Linux 中一定有设备文件存在 所在的位置 /dev

先向内核注册设备,才会存在设备,后续内核才可以管理

字符设备驱动注册方法:

①杂项字符设备注册

②Linux2.6 方式注册

③早期经典注册

1.3杂项字符设备

特点

杂项设备主设备号固定是 10

次设备号:0~255 255 表示由内核自动分配次设备号

自动创建设备文件

杂项字符设备注册调用一次 仅可以注册一个设备

(1)注册杂项字符设备

int misc_register(struct miscdevice * misc)

杂项的核心结构体:

杂项字符设备注册步骤_设备号

文件操作指针集:

杂项字符设备注册步骤_linux_02

2.地址映射

内核中不允许直接操作硬件地址,如果需要操作地址,进行映射

void __iomem *ioremap (unsigned long phys_addr, unsigned long size)

形参: phys_addr:物理地址---寄存器地址

ize:映射的大小

返回值: void *

3.GPIO子系统

地址映射 和硬件平台关系比较大,更换开发板,需要重新查原理图、手册—寄存器

GPIO 子系统适用于任意 Linux 系统,只需要查原理图,使用更方便

gpio 口都有唯一编号,定义在 linux-3.5\arch\arm\mach-exynos\include\mach\gpio.h

1》申请 IO 口

gpio_request(unsigned gpio, const char * label)

2》设置 IO 模式

gpio_direction_input(int gpio)

gpio_direction_output(int gpio, int v) //设置为输出模式 参数 v 表示引脚默认值

3》设置 GPIO/获取 GPIO 值

int gpio_get_value(unsigned gpio); //获取 GPIO 值

void gpio_set_value(unsigned gpio, int value);// 设置 GPIO 值

4》释放

void gpio_free(unsigned gpio);

具体代码链接---->下方链接内的GPIO子系统控制LED灯压缩包内都有

https://gitee.com/military-c-language/drive

4.驱动步骤

(1)打开linux-ubuntu里的共享文件夹

建三个文件

①Makefile(用于工程模块编译)

②app.c(用于验证驱动接口是否搭建完成)

③misc.c(用于写底层驱动代码)

Makefile

obj-m +=misc.o
KDIR = /home/fuck/桌面/linux-3.5
call:
	make -C ${KDIR} M=${PWD} modules
	arm-linux-gcc app.c -o app
clean:
	make -C ${KDIR} M=${PWD} modules clean
	rm app

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
	while(1)
	{
		int fd = open("/dev/misc_test",O_RDWR);
		sleep(1);
		close(fd);
		sleep(1);
	}
	return 0;
}

misc.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/gpio.h>
unsigned int *addr = NULL;

#define LED_CON *addr
#define LED_DATA *(addr+1)
int open_test (struct inode *i,struct file *f)
{
	printk("%s 表锅我粗来了窝\n",__FUNCTION__);
	gpio_set_value(EXYNOS4X12_GPM4(0),0);
	gpio_set_value(EXYNOS4X12_GPM4(1),0);
	gpio_set_value(EXYNOS4X12_GPM4(2),0);
	gpio_set_value(EXYNOS4X12_GPM4(3),0);
	//四个灯泡子全亮
	return 0;
}
int close_test (struct inode *i,struct file *f)
{
	printk("%s 表锅我走了窝\n",__FUNCTION__);
	gpio_set_value(EXYNOS4X12_GPM4(0),1);
	gpio_set_value(EXYNOS4X12_GPM4(1),1);
	gpio_set_value(EXYNOS4X12_GPM4(2),1);
	gpio_set_value(EXYNOS4X12_GPM4(3),1);
	//四个灯泡子全熄灭
	return 0;
}

struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = open_test,
	.release = close_test
};

struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR, //次设备号
	.name = "misc_test",   //设备文件 misc_test
	.fops = &ops
};

static int __init misc_init(void)
{
	int ret = misc_register(&misc);
	if(ret == 0){
		printk("注册成功\n");
		ret = gpio_request(EXYNOS4_GPD0(0),"ajun_led");
		printk("request ret = %d\n",ret);
		gpio_direction_output(EXYNOS4_GPD0(0),0);
	}
	else
		printk("注册失败\n");
	return 0;
}


static void __exit misc_exit(void)
{
	misc_deregister(&misc);
}

module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

make编译一下,然后到

杂项字符设备注册步骤_linux_03

连接开发板,在开发板调试串口中进行下列操作

加载内核

杂项字符设备注册步骤_字符设备_04

运行app来验证灯是否一秒一闪烁,一秒一闪烁就说明没毛病了

杂项字符设备注册步骤_设备号_05

完结撒花!!!