字符设备驱动(一)---led

一、总体架构

  

二、硬件电路

  1.硬件原理图

  

  

  2.寄存器配置

  

三、代码编写

  写linux的设备驱动操作的是系统的虚拟地址,并不是像裸机程序一样操作的是物理地址。

  物理地址要映射成虚拟地址,就要用到ioremap函数,用来把物理地址映射成虚拟地址。

  3.1 确定主设备号

    执行命令 cat proc/devices 可以查看到已经用了的主设备号,给自己的设备定义设备号的时候,可以选用没有被设备使用的主设备号。或者可以写0,让系统自动给我们的设备分配主设备号。

  3.2 设备节点的创建方法

    a. 手动创建

      

    b. 自动创建

      应用程序里面应用udev机制

      mdev机制:mdev会根据系统的信息,sys目录下有很多系统的信息。注册一个驱动程序的时候,会在此目录下生成设备的信息,mdev可以根据设备的信息自动创建设备节点。

      

      

  3.3 源码

  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");
View Code

 

 

 

  

转载于:https://www.cnblogs.com/kele-dad/p/6899233.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值