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)
杂项的核心结构体:
文件操作指针集:
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编译一下,然后到
连接开发板,在开发板调试串口中进行下列操作
加载内核
运行app来验证灯是否一秒一闪烁,一秒一闪烁就说明没毛病了
完结撒花!!!