一.linux文件系统:
1.什么是文件系统
文件系统是操作系统用于明确存储设备组织文件的方法。 以上说的方法:就是文件管理系统(程序),简称文件系统
文件系统(文件管理系统的方法)的种类有哪些?
FAT VFAT NTFS EXT1/2/3/4 HFS ....
树莓派查看文件系统的命令: df -T
vfat : boot(bootloader, kernel)
ext4 : 根目录
tmpfs : 内存文件系统
2. 什么是分区?
windows: 随意(面向普通用户PC),目录即分区(比如c
盘d盘) C(装系统的位置)也可以随意在C盘存放文件. D盘(用户随意发挥)
Linux: 按照功能来分区,每个分区严格存放文件(开发者)
嵌入式系统可以分为4个区,分别是
bootloader: 启动代码
para: 启动代码向内核传递参数的位置
kerne: 内核分区
根分区等 文件系统结构
3. 什么是文件系统目录结构?
常规认知:从根目录开始的一个树状结构按功能划分,和windows不同,不是分区,和windows不同
4. 什么虚拟文件系统Virtual File System ?
Linux中一切东西都存放在唯一的虚拟文件系统(VSF)中这个虚拟文件系统是树状结构从一个根目录(boot)开始,每次系统启动都会在内存中自动生成一个虚拟文件系统。 vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接口。
5. 虚拟文件系统有什么作用?
简化应用程序员的开发不管是什么文件类型,不管文件是磁盘还是设备,都只用open read write统一操作。程序员在编程时要打开不同文件系统分区的不同文件时通过VSF用统一的方式。
二.linux内核结构:
三.Linux应用程序控制硬件的过程:
应用程序通过函数库提供的的接口(open,read等)通过一个软中断从用户态进入内核态,并触发系统调用接口(sys_call),系统调用接口通过虚拟文件系统(VSF)用文件名,设备号在驱动链表中找到相应的驱动设备,通过驱动设备控制硬件。
问:c库中的open,read等接口如何找到内核中的驱动:
open软中断触发系统调用(sys_call)
VFS找到sys_open
通过设备名设备号在内核驱动链表中找到相应驱动并调用其中的open。
四.微机总线地址物理地址和虚拟地址:
总线地址:cpu能访问的内存范围(如32位系统就可以访问2的32次方大小的内存;64位系统可以访问2的64次方大小的内存)
物理地址:硬件硬盘上的实际地址,绝对地址。
虚拟地址:逻辑(基于算法.软件层面的)地址,为假地址,物理地址中的程序是在虚拟地址中运行的,虚拟地址可以比物理地址大。
五.树莓派寄存器相关:
树莓派中有6个功能寄存器分别为GPFSEL(0~5), 六个寄存器每个控制9个IO口分别为pin(0~9),每个寄存器有4个字节32位,每3位控制一个IO口,当三位为000时,IO为输入当三位为001时IO为输出。
树莓派有两个置1寄存器为GPSET0控制pin0~pin31这32个IO口每一个bit控制一个IO口;GPSET1控制32~53的IO口,每一个bit控制一个IO口。
树莓派有两个清0寄存器GPCLR0和GPCLR1和置一寄存器一样。
六.寄存器按位操作:
按位与(&):有0就为0,全1才是1;当要给相应位置0时就与上0。
按位或(|):有1就为1,全0才是0;当要给相应位置1时就或上1。
列:要设置树莓派的pin4口为输出口,需要将寄存器GPFSEL0的bit12~14位配置成001:
*GPFSEL0 &= ~(0x6 << 12); //将14 13 位配置成0;0x6的二进制数是110,先取反为001,然后左移12位再进行与操作就使bit14,bit13变为0但是bit12不确定。
*GPFSEL0 |= (0x1 << 12); // 将12 位配置成1;0x1的二进制数是001,左移12位或操作后使bit12置1。
让pin4口输出高电平,需要将寄存器GPSET0的bit4位配置成1:
*GPSET0 | = 0x1 << 4;001左移4位后或操作。
让pin4口输出低电平,需要将寄存器GPSET0的bit4位配置成0:
*GPCLR0 | = 0x1 << 4;
七.树莓派驱动编译步骤:
1.编写驱动代码
2.放入Linux源码下的dirver/char(字符设备)文件夹下
3.修改该文件夹下的makefile文件(对照其中的文件修改)
4.回到源码下输入ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules命令编译驱动代码生成xxxx.ko文件就是驱动程序
5.用交叉编译工具arm-linux-gnueabihf-gcc pin4test.c -o pin4test编译上层代码,并将驱动程序和编译好的上层代码传入树莓派。
6.insmod pin4drive.ko命令加载驱动;lsmod查看驱动;sudo chmod 666 /dev/pin4命令给生成的驱动文件访问权限;dmesg命令查看内核打印情况。
八.pin4IO口驱动代码:
#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 unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
//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); //将14 13 位配置成0;
*GPFSEL0 |= (0x1 << 12); // 将12 位配置成1;
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
int usercmd;
//获取上层write的函数值;
copy_from_user(&usercmd,buf,count);//buf为用户空间的缓存
//根据值来操作IO口输出高电平或低电平;
if(usercmd == 1){
printk("set 1\n");
*GPSET0 | = 0x1 << 4;
}else if(usercmd == 0){
printk("set 0\n");
*GPCLR0 | = 0x1 << 4;
}else{
printk("undo\n");
}
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write
};
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_fops加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在~/dev/路径下自动生成设备类
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
//void * =ioremap(物理地址,寄存器大小);//一个寄存器四个字节32位;
GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//物理地址转换成虚拟地址,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");