版权声明 本文为博主随手笔记,欢迎评论和转载
博客源址:
https://blog.csdn.net/qq_41825384/article/details/84955103
博主博客园:
https://home.cnblogs.com/u/dl04301201/
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 函数,则驱动层有对应的
led_open 函数,两者通过
file_operation结构关联起来!!!
led 的驱动程序为 :
#include
#include
#include
#include
#include
#include
#include
#include
int major;
volatile unsigned long *gpl2con = NULL;
volatile unsigned long *gpl2dat = NULL;
volatile unsigned long *gpk1con = NULL;
volatile unsigned long *gpk1dat = NULL;
static int led_open(struct inode *inode, struct file *file)
{
/*配置GPL2为输出,先清零,再配置*/
*gpl2con &= ~0x03;
*gpl2con |= 0x01;
/*配置GPK1为输出,先清零,再配置*/
*gpk1con &= ~(0x03<<4);
*gpk1con |= (0x01<<4);
return 0;
}
static int led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int val;
/*从用户拷贝到内核///copy_to_user()从内核拷贝到用户*/
copy_from_user(&val, buf, count);
if(val == 1)
{
//点灯
*gpl2dat |= 1;
//点灯
*gpk1dat |= 0x02;
}
else
{
//灭灯
*gpl2dat &= ~1;
//点灯
*gpk1dat &= ~0x02;
}
return 0;
}
static struct file_operations led_fops = {
/*这是一个宏,推向编译环境时自动创建的__this_module*/
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_drv_init(void)
{
/*注册驱动程序,告诉内核这个函数来被谁调用*/
major = register_chrdev(0, "led_drv", &led_fops);
gpl2con = (volatile unsigned long *)ioremap(0x11000100, 4);
gpl2dat = gpl2con + 1;
gpk1con = (volatile unsigned long *)ioremap(0x11000060, 4);
gpk1dat = gpk1con + 1;
return 0;
}
static void led_drv_exit(void)
{
unregister_chrdev(major, "led_drv");
iounmap(gpl2con);
iounmap(gpk1con);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("stromeli");
MODULE_DESCRIPTION("LED 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
#include
#include
#include
#include
/*
*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!
");
}
if(argc != 2)
{
printf("Usage :
");
printf("%s
", 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 点亮
熄灭LED灯