一、 嵌入式系统内核结构认知
1、内核结构大可分为:用户态、内核态、硬件;三部分组成。
2、用户态是怎样调用app一层层进到内核态再到驱动硬件干活的?
3、如图:
4、简单分析说明:
首先在用户态空间;C库提供很多app能够支持内核干活的接口,从而方便应用者开发,比如说,一套应用程式运行下调用open函数到内核会发生一次软中断(中断号为0x80),从用户态进入到内核态;C库的函数调用了系统调用函数(sys_call),然后sys_call再调用虚拟文件系统(VFS)的sys_open函数,从上层函数调用将参数(设备号)一层一层传到sys_open函数,接着通过参数(设备号),驱动链表会索引到某台设备或某个文件来打开;从而达到驱动设备的效果。
二、字符设备驱动实现一步骤
1、添加驱动
2、调用驱动
3、编写驱动程序
1、编写驱动程序
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_resd函数
static ssize_t pin4_resd(struct file *file,char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_resd\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"); //内核的打印函数和printf类似
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");
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");
1.1、基于字符设备驱动框架编写IO口驱动程序:
详细参看;字符设备驱动实现二之微机总线物理地址寄存器配置
1.2、编译驱动文件:
进到内核源码树目录/linux-rpi-4.14.y/drivers/char
cd /home/linfeng/SYSTEM/linux-rpi-4.14.y/drivers/char
修改Makefile添加编译驱动文件
vi Makefile
添加驱动:
obj-m += pin4driver2.o
1.3、编译驱动文件:
回到源码树目录进行模块的方式编译进内核
cd /home/linfeng/SYSTEM/linux-rpi-4.14.y/
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
2、将驱动程序添加到内核
设备号、设备名、设备驱动函数
在树莓派终端将驱动加载到内核
sudo insmod pin4driver2.ko
查看到/dev/pin4是否生成
pi@raspberrypi:~ $ ls /dev/pin4
/dev/pin4
pi@raspberrypi:~ $
pi@raspberrypi:~ $ ls /dev/pin4 -l
crw------- 1 root root 231, 0 Mar 4 05:04 /dev/pin4
给驱动添加所有用户均可读写的权限
sudo chmod 666 /dev/pin4
卸载驱动命令
sudo rmmod pin4driver2
3、调用驱动
上层调用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
fd = open("/dev/pin4",O_RDWR);
if(fd <0){
printf("open failed\n");
}else{
printf("open sucess\n");
}
write(fd,'1',1);
close(fd);
return 0;
}
运行程序
查看内核打印信息
程序运行后查看是否成功调用
pi@raspberrypi:~ $ dmesg |grep pin4
[ 5071.630883] pin4_open
[ 5071.631201] pin4_write
pi@raspberrypi:~ $