首页 > 技术文章 > Linux驱动程序入门 三

dl04301201 2018-12-10 20:25 原文

版权声明 : 本文为博主随手笔记,欢迎评论和转载。 https://www.cnblogs.com/dl04301201/p/10098864.html 

 

LED驱动程序

第一步:看懂 PCB 原理图和 芯片datasheet

第二步:寻找对应 Pin 的寄存器地址

 第三步:匹配有效的信息

 

下面以 iTOP4412 ARM9开发板为例 :

找出对应的 LED 引脚

 

 

通过 KP_COL0 和 VDD50_EN 匹配芯片上对应的 Pin :

 

 

 

从上图可知 : KP_COL0 对应在 chip 上的 Pin :GPL2_0  ; VDD50_EN  对应在 chip 上的 Pin :GPK1_1 

 

然后通过 chip datasheet 去匹配对应的寄存器 : 

 

 

从上图可知GPL2对应的个寄存器的功能。可知这些 datasheet 都是英文的,建议志同道合的伙伴们记得去提升一下自己的英语文化水平。

 从匹配的信息来看,需要的是 GPL2_0

GPL_CON 寄存器 :

从上图可知 GPL2CON 的 Base Address  :0x1100_0000

地址对应的偏移为:Base Address + 0x1000

重置值为 : 0x0000_0000

 LED, 那么则须把 GPL2CON[0]设置成 : 0x1 = Output --->输出模式

GPL_DAT 寄存器 : 

Description 处要仔细读, 里面涉及了对应寄存器的用法。

GPK1_1 配信息的原理同上!!!!

综述 : 

GPL2_CON      地址为 : 0x11000100 

GPL_DAT         地址为:0x11000104

GPK1_CON      地址为 : 0x11000060 

GPK_DAT         地址为:0x11000064

 

驱动原理 :就是操作 open read write ioctl close 等函数,所以在 Linux驱动程序入门 二 时说过,学驱动很简单!

用户层 open read write ioctl  
file_operation   || || || ||  
驱动层 led_open led_read led_write led_ioctl  

 

 

 

 

 

 用户层有一个 open 函数,则驱动层有对应的 led_open 函数,两者通过 file_operation结构关联起来!!!

led 的驱动程序为 : 

 1 #include <linux/module.h>
 2 #include <linux/kernel.h>
 3 #include <linux/fs.h>
 4 #include <linux/init.h>
 5 #include <linux/delay.h>
 6 #include <asm/uaccess.h>
 7 #include <asm/io.h>
 8 #include <linux/device.h>
 9 
10 int major;
11 
12 volatile unsigned long *gpl2con = NULL;
13 volatile unsigned long *gpl2dat = NULL;
14 
15 volatile unsigned long *gpk1con = NULL;
16 volatile unsigned long *gpk1dat = NULL;
17 
18 static int led_open(struct inode *inode, struct file *file)
19 {
20     /*配置GPL2为输出,先清零,再配置*/
21     *gpl2con &= ~0x03;
22     *gpl2con |= 0x01;
23     
24     /*配置GPK1为输出,先清零,再配置*/
25     *gpk1con &= ~(0x03<<4);
26     *gpk1con |=  (0x01<<4);
27     return 0;
28 }
29 static int led_write(struct file *filp, 
30         /const char __user *buf, size_t count, loff_t *ppos)
31 {
32     int val;
33     
34     /*从用户拷贝到内核///copy_to_user()从内核拷贝到用户*/
35     copy_from_user(&val, buf, count);   
36     if(val == 1)
37     {
38         //点灯
39         *gpl2dat |= 1;
40         //点灯
41         *gpk1dat |= 0x02;        
42     }
43     else
44     {
45         //灭灯
46         *gpl2dat &= ~1;
47         //灭灯
48         *gpk1dat &= ~0x02;
49     }
50     return 0;
51 }
52 static struct file_operations led_fops = {
53     /*这是一个宏,推向编译环境时自动创建的__this_module*/
54     .owner = THIS_MODULE,           
55     .open = led_open,
56     .write = led_write,
57 };
58 static int led_drv_init(void)
59 {
60     /*注册驱动程序,告诉内核这个函数来被谁调用*/
61     register_chrdev(0, "first_drv", &first_drv_fops); 
62     
63     gpl2con = (volatile unsigned long *)ioremap(0x11000100, 4);
64     gpl2dat = gpl2con + 1;
65     
66     gpk1con = (volatile unsigned long *)ioremap(0x11000060, 4);
67     gpk1dat = gpk1con + 1;
68 
69     return 0;
70 }
71 static void led_drv_exit(void)
72 {
73     unregister_chrdev(major, "first_drv");
74     device_unregister(firstdrv_class_dev);
75     class_destroy(firstdrv_class);
76     iounmap(gpl2con);
77     iounmap(gpk1con);
78 }
79 
80 module_init(led_drv_init);
81 module_exit(led_drv_exit);
82 
83 MODULE_LICENSE("Dual BSD/GPL");
84 MODULE_AUTHOR("stormeli");
85 MODULE_DESCRIPTION("LEDdriver");

对应的Makefile,即编译内核为 : 

#!/bin/bash
obj-m += led_drv.o

KDIR := /home/topeet/Android/Android4.0/iTop4412_Kernel_3.0

PWD ?= $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf *.o
    

用户层程序为 :  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

/*
 *firstdrvtest on
 *firstdrvtest off
 */
int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    fd = open("/dev/led", O_RDWR);
    
    if(fd < 0)
    {
        printf("can't open!\n");
    }
    
    if(argc != 2)
    {
        printf("Usage :\n");
        printf("%s <on|off>\n", argv[0]);
        return 0;
    }
    
    if(strcmp(argv[1], "on") == 0)
    {
        val = 1;
    }
    else
    {
        val = 0;
    }
    
    write(fd, &val, 4);
    return 0;
}

编译过程为:

一、编译驱动程序 : 使用 make 指令,得到 .ko 文件,即模块驱动文件

 

二、编译用户程序 : 使用 arm-none-linux-guneabi- gcc -o led_device led_device.c -static 指令编译

得到可执行文件 led_device

 

把编译得到的 可执行文件 led_device 和 led_drv.ko 文件拷贝进 iTOP4412 板子里(可使用的方法有 U盘拷贝, nfs协议传输,tftp协议传输)

 

板子上的操作:

加载内核模块的指令 : insmod xxx.ko

查看内核模块的指令:lsmod

卸载内核模块的指令:rmmod xxx

 

通过 insmod xxx.ko 加载内核模块, 然后 lsmod 查询,是否加载成功, 再使用 cat /proc/device 查看分配的 主设备号 跟 次设备号「此关于设备文件的创建」

「驱动匹配得有 设备文件『由用户层可知创建 /dev/led 文件跟驱动文件

创建设备文件的指令为 : mknod /dev/led c 主设备号 次设备号             c「代表是字符 character 设备驱动」

最后执行用户可执行文件 ./led_device 

 

 

可知 led_drv 主设备号为 248, 次设备号为 0 

 

 

板子 LED 点亮

 

 

推荐阅读