一、驱动框架
1.底层驱动
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno; //设备号
static int major =231; //主设备号
static int minor =0; //次设备号
static char *module_name="pin4"; //模块名
//led_read函数
static int pin4_read(struct file *file1,char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_read\n"); //内核的打印函数和printf类似
return 0;
}
//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n"); //内核的打印函数和printf类似
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_write\n");
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
.read = pin4_read,
};
int __init pin4_drv_init(void) //真实入口
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在Dev下自动生成设备
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
return 0;
}
void __exit pin4_drv_exit(void)
{
device_destroy(pin4_class,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin4_drv_init); //入口 内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
2.上层验证
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
int fd;
fd = open("/dev/pin4",O_RDWR);
if(fd<0){
printf("open failed!\n");
perror("res");
}else{
printf("open success!\n");
}
fd = write(fd,'1',1);
}
二、在ubantu下编译
1.将做好的代码文件移动到/system/linux-rpi-4.14.y/drivers/char下面
2.打开vi Makefile在obj-y += mem.o random.o下
添加obj-m += 1.o(文件名)
3.添加完成后回到/system/linux-rpi-4.14.y下执行命令ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules进行编译
编译成功生成drivers/char/1.ko
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
CHK include/generated/bounds.h
CHK include/generated/timeconst.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
CHK scripts/mod/devicetable-offsets.h
CHK kernel/config_data.h
CC [M] drivers/char/1.o
Building modules, stage 2.
MODPOST 1523 modules
CC drivers/char/1.mod.o
LD [M] drivers/char/1.ko//表示编译成功
三、驱动代码编译和测试
1.将底层驱动文件拷贝到树莓派上
scp drivers/char/1.ko pi@192.168.43.88:/home/pi
2.将上层文件拷贝到树莓派上
首先回到上层文件所在的目录执行arm-linux-gnueabihf-gcc 2.c -o test命令进行编译上层文件,编译通过后执行scp test pi@192.168.43.88:/home/pi拷贝到树莓派上
3.在树莓派上执行命令sudo insmod 1.ko将内核加载进去
然后执行命令ls /dev/pin4或者lsmod验证是否加载内核成功,加载成功后给执行命令sudo chmod 666 /dev/pin4给其运行权限,然后运行上层文件,可以用命令dmesg |grep pin4查看是否运行成功
四、IO口操作代码编程
1.代码
底层代码
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno; //设备号
static int major =231; //主设备号
static int minor =0; //次设备号
static char *module_name="pin4"; //模块名
//volatile 指令不会因为编译器的优化而省略,且每次直接读值
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
//led_read函数
static int pin4_read(struct file *file1,char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_read\n"); //内核的打印函数和printf类似
return 0;
}
//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n"); //内核的打印函数和printf类似
//配置pin4引脚为输出引脚 bit 12-14配置成001
*GPFSEL0 &= ~(0x6 << 12); //把bit 14 bit13 配置成0
*GPFSEL0 |= (0x1 << 12);
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
//获取上层write函数的值
//根据值来操作io口,高电平或者低电平
int userCmd;
printk("pin4_write\n");
copy_from_user(&userCmd,buf,count);
//printk("get value\n");
if(userCmd == 1){
*GPSET0 |= 0x1 << 4;
printk("get 1\n");
}else if(userCmd == 0){
*GPCLR0 |= 0x1 << 4;
printk("get 0\n");
}else{
printk("undo");
}
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
.read = pin4_read,
};
int __init pin4_drv_init(void) //真实入口
{
int ret;
printk("insmod driver pin4 success\n");
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在Dev下自动生成设备
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//将物理地址转换成虚拟地址用函数ioremap,io寄存器映射成普
通内存单元进行访问
GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4);
GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);
return 0;
}
void __exit pin4_drv_exit(void)
{
iounmap(GPFSEL0);
iounmap(GPSET0);
iounmap(GPCLR0);
device_destroy(pin4_class,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin4_drv_init); //入口 内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
上层代码
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
int fd;
int cmd;
int data;
fd = open("/dev/pin4",O_RDWR);
if(fd<0){
printf("open failed!\n");
perror("res");
}else{
printf("open success!\n");
}
printf("input commd :1/0 \n 1:set pin4 high \n 0:set pin4 low\n");
scanf("%d",&cmd);
if(cmd == 1){
data = 1;
}
if(cmd == 0){
data = 0;
}
printf("data = %d\n",data);
fd = write(fd,&data,1);
}
2.代码编译
1.在目录/system/linux-rpi-4.14.y下使用指令ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules进行底层代码的编译
2.在上层代码所在目录使用指令arm-linux-gnueabihf-gcc 2.c -o test进行编译
3.在树莓派上交叉编译
1.在树莓派上执行指令:
sudo rmmod 1.ko(文件名)
rm 1.ko(文件名)
rm test(文件名)
删除树莓派上之前加载的内核
使用指令lsmod或ls /dev/pin4查看是否删除成功
2.在ubantu上使用指令scp test pi@192.168.43.88:/home/pi将上层代码拷贝到树莓派上
使用指令scp drivers/char/1.ko pi@192.168.43.88:/home/pi将底层代码拷贝到树莓派上
可在ubantu上使用指令md5sum drivers/char/1.ko(文件目录+文件名),在树莓派上使用指令md5sum 1.ko(文件名)看是否成功拷贝(成功拷贝后返回的数值一致)
3.在树莓派上执行命令sudo insmod 1.ko将内核加载进去然后执行命令ls /dev/pin4或者lsmod验证是否加载内核成功,加载成功后给执行命令sudo chmod 666 /dev/pin4给其运行权限
4.使用./test(文件名)运行程序,可以用命令dmesg |grep pin4查看是否运行成功,命令dmsg可以查看底层代码运行状态,成功运行之后可以使用命令gpio readall查看指定IO口是否有响应。
本文详细介绍了Linux字符设备驱动的编写过程,包括底层驱动和上层验证。在Ubuntu环境下,讲解了如何编译及在树莓派上部署和测试驱动。此外,还展示了如何进行IO口操作,通过ioremap实现IO寄存器的访问。最后,提供了上层应用的代码,用于控制GPIO口的高低电平状态。
&spm=1001.2101.3001.5002&articleId=115483115&d=1&t=3&u=c5e7394bff4449ca9ff3114b415e7909)
499

被折叠的 条评论
为什么被折叠?



