一、总体架构
二、硬件电路
1.硬件原理图
2.寄存器配置
三、代码编写
写linux的设备驱动操作的是系统的虚拟地址,并不是像裸机程序一样操作的是物理地址。
物理地址要映射成虚拟地址,就要用到ioremap函数,用来把物理地址映射成虚拟地址。
3.1 确定主设备号
执行命令 cat proc/devices 可以查看到已经用了的主设备号,给自己的设备定义设备号的时候,可以选用没有被设备使用的主设备号。或者可以写0,让系统自动给我们的设备分配主设备号。
3.2 设备节点的创建方法
a. 手动创建
b. 自动创建
应用程序里面应用udev机制
mdev机制:mdev会根据系统的信息,sys目录下有很多系统的信息。注册一个驱动程序的时候,会在此目录下生成设备的信息,mdev可以根据设备的信息自动创建设备节点。
3.3 源码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /* 2 * ===================================================================================== 3 * Filename: led.c 4 * Description: 5 * Version: 1.0 6 * Created: 2017年05月22日 11时40分23秒 7 * Author: YOUR NAME (), 8 * Organization: 9 * ===================================================================================== 10 */ 11 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/fs.h> 15 #include <linux/class.h> 16 #include <linux/io.h> 17 18 /* 1.建立模块 19 * 1.1 模块初始化模板 20 * 1.1.1 映射寄存器 21 * 1.1.2 注册设备驱动,告诉内核 22 * 1.1.1.1 主设备号 23 * 1.1.3 设备类的建立 24 * 1.1.1.2.1 创建变量:设备类和设备 25 * 1.1.1.2.2 创建设备类 26 * 1.1.1.2.3 在设备类下创建设备 27 * 1.2 模块退出模板 28 * 29 * 2.建立文件操作 30 * 2.1 文件打开和关闭函数 31 * 2.1.1 文件打开函数 32 * 2.2 文件读写函数 33 * 2.2.1 文件读函数 34 * 2.2.2 文件写函数 35 * 2.2.2.1 设置GPIO引脚输出的数据 36 * 2.2.2.1.1 从用户空间读取数据 37 * 2.3 创建文件操作结构体 38 */ 39 40 #define DEVICE_NAME "leds" /* 加载模式后,执行"cat /proc/devices"命令看到设备的名称 */ 41 #define LED_MAJOR 231 /* 主设备号 */ 42 static unsigned long gpio_va; //次设备号 43 44 /* 1.1.1.2.1 创建变量:设备类和设备 */ 45 static struct class *leds_class; //创建led类 46 static struct class_device *leds_class_devs[4];//在led类下面创建设备 47 48 49 static char leds_status = 0x0; 50 static DECLARE_MUTEX(leds_lock); //定义赋值 51 52 //volatile unsigned long *gpfcon = NULL; 53 //volatile unsigned long *gpfdat = NULL; 54 #define GPIO_OFT(x) ((x) - 0x56000000) 55 #define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050))) 56 #define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054))) 57 58 /* 2.1.1 文件打开函数 */ 59 static int led_open(struct inode *inode, struct file *filp) 60 { 61 int minor = MINOR(inode->irdev);//获取次设备号 62 63 switch(minor) 64 { 65 case 0: 66 GPFCON &= ~((0x3 << (4 * 2)) | (0x3 << (5 * 2)) | (0x3 << (6 * 2))); 67 GPFCON |= ((0x1 << (4 * 2)) | (0x1 << (5 * 2)) | (0x1 << (6 * 2))); 68 69 //初始化状态:LED灯全亮,gpf引脚输出0 70 GPFDAT &= ~((1<<4) | (1<<5) | (1<<6)); 71 72 down(&leds_lock); 73 leds_status = 0x0; 74 up(&leds_lock); 75 break; 76 case 1: 77 GPFCON &= ~(0x3 <<(4 * 2)); 78 GPFCON |= (1 << (4 * 2)); 79 80 //初始化状态:LED4亮,gpf4引脚输出0 81 GPFDAT &= ~(1 << 4); 82 83 down(&leds_lock); 84 leds_status &= ~(1 << 0); 85 up(&leds_lock); 86 break; 87 case 2: 88 GPFCON &= ~(0x3 << (5 * 2)); 89 GPFCON |= (1 << (5 * 2)); 90 91 GPFDAT &= ~(1 << 5); 92 93 down(&leds_lock); 94 leds_status &= ~(1 << 1); 95 up(&leds_lock); 96 break; 97 case 3: 98 GPFCON &= ~(0x3 << (6 * 2)); 99 GPFCON |= (1 << (6 * 2)); 100 101 GPFDAT &= ~(1 << 6); 102 103 down(&leds_lock); 104 leds_status &= ~(1 << 2); 105 up(&leds_lock); 106 break; 107 } 108 109 return 0; 110 } 111 112 113 /* 2.2.1 文件读函数 */ 114 static ssize_t led_read(struct file *filp, char __user *buff, ssize_t count, loff_t *oops) 115 { 116 int minor = MINOR(filp->f_dentry->d_inode->i_rdev);//获取次设备号 117 char val; 118 119 switch(minor) 120 { 121 case 0: 122 copy_to_user(buff, (const void *)&leds_status, 1); 123 break; 124 case 1: 125 down(&leds_lock); 126 val = leds_status & 0x1; 127 up(&leds_lock); 128 copy_to_user(buff, (const void *)&val, 1); 129 break; 130 case 2: 131 down(&leds_lock); 132 val = (leds_status>>1) & 0x1; 133 up(&leds_lock); 134 copy_to_user(buff, (const void *)&val, 1); 135 break; 136 case 3: 137 down(&leds_lock); 138 val = (leds_status>>2) & 0x1; 139 up(&leds_lock); 140 copy_to_user(buff, (const void *)&val, 1); 141 break; 142 } 143 return 1; 144 } 145 146 /* 2.2.2 文件写函数 */ 147 static ssize_t led_wirte(struct file *filp, const char __user *buff, ssize_t count, loff_t *oops) 148 { 149 int minor = MINOR(file->fdentry->d_inode->i_rdev); 150 char val; 151 152 /* 2.2.2.1 设置GPIO引脚输出 */ 153 /* 2.2.2.1.1 从用户空间读取设置的数据 */ 154 copy_from_user(&val, buf, conut); 155 156 switch(minor) 157 { 158 case 0: 159 if (0 == val) //亮灯 160 GPFDAT &= ~((1 << 4) | (1 << 5) | (1 << 6)); 161 else//灭灯 162 GPFDAT |= (1 << 4) | (1 << 5) | (1 << 6); 163 down(&leds_lock); 164 leds_status = val; 165 up(&leds_lock); 166 break; 167 case 1: 168 if (val == 0) 169 { 170 GPFDAT &= ~(1<<4); 171 down(&leds_lock); 172 leds_status &= ~(1<<0); 173 up(&leds_lock); 174 } 175 else 176 { 177 GPFDAT |= (1 << 4); 178 down(&leds_lock); 179 leds_status |= (1<<0); 180 up(&leds_lock); 181 } 182 break; 183 case 2: 184 if (val == 0) 185 { 186 GPFDAT &= ~(1 << 5); 187 down(&leds_lock); 188 leds_status &= ~(1 << 1); 189 up(&leds_lock); 190 } 191 else 192 { 193 GPFDAT |= (1 << 5); 194 down(&leds_lock); 195 leds_status |= (1 << 1); 196 up(&leds_lock); 197 } 198 break; 199 case 3: 200 if(val == 0) 201 { 202 GPFDAT &= ~(1 << 6); 203 down(&leds_lock); 204 leds_status &= ~(1 << 2); 205 up(&leds_lock); 206 } 207 else 208 { 209 GPFDAT |= (1 << 6); 210 down(&leds_lock); 211 leds_status |= (1 << 2); 212 up(&leds_lock); 213 } 214 break; 215 } 216 217 return 1; 218 } 219 220 struct file_operations led_drv_fops = { 221 .owner = THIS_MODULE; 222 .open = led_open; 223 .release = led_release; 224 .read = led_read; 225 .write = led_wirte; 226 } 227 228 229 /* 1.1 模块初始化函数 */ 230 static int __init led_init(void) 231 { 232 int ret; 233 int minor = 0; 234 235 /* 1.1.1 映射寄存器 */ 236 gpio_va = ioremap(0x56000000, 0x100000); 237 if (!gpio_va) { 238 return -EIO; 239 } 240 241 242 /* 1.1.2 注册字符设备,将主设备号与file_operations结构联系起来 */ 243 /* LED_MAJOR可以设为0,表示由内核自动分配主设备号 */ 244 ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &led_drv_fops); 245 if (ret < 0) { 246 printk(DEVICE_NAME " can't register major number!!!\n"); 247 return ret; 248 } 249 250 /* 1.1.3 设备类的创建 */ 251 leds_class = class_create(THIS_MODULE, "leds"); 252 if (IS_ERR(leds_class)) 253 return PTR_ERR(leds_class); 254 255 /* 1.1.4 在leds类下面创建一个设备 */ 256 /* mdev 会自动创建一个/dev/leds 设备节点 */ 257 leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds"); 258 if (unlikely(IS_ERR(leds_class_devs[0]))) 259 return PTR_ERR(leds_class_devs[0]); 260 for(minor = 1; minor < 4; minor++) 261 { 262 leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor); 263 if (unlikely(IS_ERR(leds_class_devs[minor]))) 264 return PTR_ERR(leds_class_devs[minor]); 265 } 266 267 printk(DEVICE_NAME " initialized\n"); 268 return 0; 269 } 270 271 /* 1.2 模块退出函数 */ 272 static void __exit led_exit(void) 273 { 274 int minor; 275 for(minor = 0; minor < 4; minor++) 276 { 277 class_device_unregister(leds_class_devs[minor]); 278 } 279 class_destroy(leds_class); 280 unregister_chrdev(LED_MAJOR, DEVICE_NAME);//卸载 281 iounmap(gpfcon); 282 283 return 0; 284 } 285 286 module_init(led_init);//定义一个结构体,结构体内有一个函数指针,指向led_init这个入口函数 287 module_exit(led_exit); 288 289 MODULE_LICENSE("Dual BSD/GPL");