ubuntu18.04版本安装
sudo apt install build-essential 安装gcc g++
使用如下命令进行arm-linux-gcc的安装:
sudo apt-get install gcc-arm-linux-gnueabihf
使用如下命令进行arm-linux-g++的安装:
sudo apt-get install g+±arm-linux-gnueabihf
————————————————
版权声明:本文为CSDN博主「.JPEG」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wu10188/article/details/86542418
让程序猿搭建环境太搞笑了,轻松easy!
=======================================================================================================================
uname -r 查看树莓派版本
witch rm 查看rm命令的所在目录
树莓派等芯片带操作系统的启动过程
C51,STM32(裸机)》》》》》》C直接操控底层寄存器实现相关业务。 业务流程型的裸机代码
遥控灯: while(1)
垃圾桶:WemosD1 LOOP
恩智浦智能车: stm32
X86,Intel windows
启动过程: 电源 -》 BIOS -》windows内核-》C,D盘-》 程序启动(QQ)
嵌入式产品: 树莓派,mini2440, mini6410,nanopi,海思,RK(瑞芯微)------人脸识别打卡器,智能家居主控。。。
启动过程: 电源-》BootLoader(引导操作系统启动)-》Linux内核-》文件系统(根据功能性来组织文件夹,带访问权限)-》KTV点歌机,
安卓
启动过程: 电源-》 fastBoot/Bootloader/-》linux内核-》文件系统-》虚拟机-》HOME应用程序-》点某图标打开某APP
BootLoader: 一阶段 让CPU 跟内存,FLASH, 串口,IIC,IIS, 数据段,打交道,驱动这些设备(汇编和C结合)
二阶段: 引导Linux内核启动 (纯C)
=======================================================================================================================
树莓派Linux源码目录树分析
[https://www.cnblogs.com/senior-engineer/p/4929703.html](https://www.cnblogs.com/senior-engineer/p/4929703.html)
大约1.3w个C文件 1100w行代码
Linux是开源,免费,LInux开源社区工作者共同维护,爱好
Linux是一个开源的,支持多架构多平台代码 =非常牛逼
可以执行非常高
但是Linux内核编译出来一般就几M. 4M
因为支持多平台,多架构,所以编译之前要配置,配置成适合的目标平台来用
ARM
海思 友善之臂 RK 树莓派 nanoPi
X86
PowerPC
MIPS
=======================================================================================================================
4. 树莓派Linux源码配置
驱动代码的编写
驱动代码的编译需要一个提前编译好的内核
编译内核就必须配置( 让CPU 跟内存,FLASH, 串口,IIC,IIS, 数据段,打交道,是否驱动这些设备)
配置的最终目标会生成 .config文件,该文件指导Makefile去把有用东西组织成内核
最后进行内核编译
厂家配linux内核源码,比如说买了树莓派,树莓派linux内核源码
如何配置树莓派的Linux内核
驱动两种加载方式:make menuconfig [*]或者[M]通过空格改变模式
* 编译进内核 zImage包含了驱动
M 模块方式生成驱动文件xxx.ko 系统启动后,通过命令inmosd xxx.ko 加载
内核配置:基于第一种方式
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig
指定ARM架构 指定编译器 树莓派 主要核心指令
=========================================================================================================================
树莓派Linux内核编译
5.1 编译:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 zImage modules dtbs
-j4指定用多少电脑资源进行编译
zImage生成内核镜像
modules要生成驱动模块
dtbs生成配置文件
tree 查看目录树
5.2 编译成功后,看到源码树目录多了vmlinux,失败则无此文件
成功后,目标zImage镜像arch/arm/boot底下
5.3 打包zImage成树莓派可用的xxx.img
./scripts/mkknlimg arch/arm/boot/zImage ./kernel_new.img
dmesg 查看设备接入信息的命令
5.4 数据拷贝
mkdir data1 和data2
插入u盘后,需要挂载U盘,U盘数据才能用
sudo mount /dev/sdb1 data1 sdb1一个fat分区,是boot相关的内容,kernel的img,U盘
sudo mount /dev/sdb2 data2 一个是ext4分区,也就是树莓派linux系统的根目录分区。
cd /home/lth/lessonpi/linux-rpi-4.14.y
安装modules, 设备驱动文件: hdmi usb wifi io ... 安装驱动文件放在/home/lth/data2 ,即树莓派linux系统的根目录分区
sudo ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make INSTALL_MOD_PATH=/home/lth/data2 modules_install
安装更新 kernel.img 文件,注意镜像名字是kernel7.img
先备份
cd /home/lth/data1
cp kernel7.img kernel7OLD.img
du kernel7.img 和 kernel7OLD.img查看文件的大小
cd /home/lth/lessonpi/linux-rpi-4.14.y
再把编译新生成的拷贝到data1,起名kernel7.img
cp kernel_new.img /home/lth/data1/kernel7.img
md5sum kernel7.img 和kernel_new.img 查看文件的文件号,文件是否在复制过程被损坏,没被损坏,文件号一样
拷贝配置文件
cp arch/arm/boot/dts/.*dtb* /home/lth/data1
cp arch/arm/boot/dts/overlays/.*dtb* /home/lth/data1/overlays/
cp arch/arm/boot/dts/overlays/README /home/lth/data1/overlays/
uname -r 查看树莓派版本
=============================================================================================================================
6. 文件系统
1. 什么是文件系统?
常规认知: 根目录
文件系统是操作系统用于明确存储设备组织文件的方法。
以上说的方法:就是文件管理系统(程序),简称文件系统
文件系统是操作系统中负责管理持久数据的子系统,说简单点,就是负责把用户的文件存到磁盘硬件中,因为即使计算机断电了,
磁盘里的数据并不会丢失,所以可以持久化的保存文件。
文件系统的基本数据单位是文件,它的目的是对磁盘上的文件进行组织管理,那组织的方式不同,就会形成不同的文件系统。
2. 文件系统(文件管理系统的方法)的种类有哪些?
FAT VFAT NTFS EXT1/2/3/4 HFS ....
树莓派查看文件系统的命令: df -T
vfat : boot(bootloader, kernel)
ext4 : 根目录
tmpfs : 内存文件系统
3. 什么是分区?
windows: 随意(面向普通用户PC),目录即分区
C(装系统的位置)也可以随意在C盘存放文件. D盘(用户随意发挥)
Linux: 按照功能来分区,每个分区严格存放文件(开发者)
嵌入式系统可以分为4个区,分别是
bootloader、 启动代码
para、 启动代码向内核传递参数的位置
kernel、 内核分区
根分区等 文件系统结构
在linux系统下的根目录下的文件可能来自不同的分区,导致文件的内存地址不一定连续
而在Windows系统下每个分区下的根目录文件的地址是连续的
2. 什么是文件系统目录结构?
常规认知: 根目录,不是分区,和windows不同
2. 什么虚拟文件系统Virtual File System ?
vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接口,
3. 虚拟文件系统有什么作用?
简化应用程序员的开发
不管是什么文件类型,不管文件是磁盘还是设备,都只用open read write统一操作
即open操作通过vfs打开不同分区上不同类型的文件,vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接口,
=============================================================================================================================
Linux内核驱动基础框架
应用程序(基础C加C库)通过函数库(C库),
C库提供了应用程序支配内核干活的接口,虚拟文件系统和系统调用接口其实是重合的。
下面我们以如何打开一个文件来说明具体的步骤——应用程序调用open,read,write这些函数(来自于C库),C库和系统调用接口打交道,
系统调用接口可以操作底层的各种东西,可以做进程线程相关的东西,也可以访问各种不同的文件系统,我们在应用程序中调用open ,read ,write函数,
在系统调用接口就会调用sys_open,sys_read ,sys_write,然后根据open,read,write后的参数来决定对谁操作,如磁盘 设备 文件
shell是一个应用程序,
在linux终端输入指令按下回车后,通过shell去解释指令让内核干活。
shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。
它接收用户输入的命令并把它送入内核去执行,是一个命令解释器。
另外,shell编程语言具有普通编程语言的很多特点,用这种编程语言编写的shell程序与其他应用程序具有同样的效果
======================================================================================
总结
最后,我们进入一般的应用。应用是一个程序,它可以
1.直接调用系统函数
2.调用库函数
3.运行shell脚本
这些应用可以由多种语言开发。最常见的是C语言。
Linux利用内核实现软硬件的对话。
通过系统调用的这个接口,Linux将上层的应用与下层的内核分离,隐藏了底层的复杂性,也提高了上层应用的可移植性。
库函数利用系统调用创造出模块化的功能,
Shell则提供了一个用户界面,并让我们可以利用shell的语法编写脚本,以整合程序。
========================================================================================
9、地址
unix设计与实现
启蒙书 内核设计文档
地址总线 (Address Bus;又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/地方的实体位址。
简单来说就是CPU能够访问内存的范围
现象:装了32位的win7系统,明明内存条8g,可是系统只识别了3.8G,装了64位才能识别8G
32位能表示访问的大小为4G,即2的32次方bit,
树莓派 1G 946M
2、物理地址
硬件的实际地址或绝对地址
3、虚拟地址
逻辑(基于算法的地址(软件层面的地址,假地址))地址称为虚拟地址
当实际地址只有1个G,而程序大于实际地址时,需要虚拟地址来映射,通过算法或MMU单元虚拟扩充地址,把1个G虚拟到4G
BCM2835 树莓派3b的型号 他是ARM-cotexA53架构
====================================================================================
DMA直接内存访问
10、 led驱动的代码是在驱动框架的基础上增加
led驱动配置对应的io功能
1、配置寄存器起始地址映射,地址联系对应的寄存器
volatile unsigned int* GPFSEL0=(volatile unsigned int*)ioremap(0x3f200000,4);
2、寄存器地址指向的值进行io配置,即配置寄存器对应的功能
*GPSET0|=0x1<<4;
LED驱动所需寄存器和基本原理
但linux必须先用指针映射寄存器地址,再配置功能
寄存器功能选择 14-12位 相当于stm32寄存器函数版本
0x 3f2 0000 GPFSEL0 GPIO Function Select 0 功能选择 输出/输入 (GPIO function select registers)32位
io起始地址 14-12位 001 = 引脚4输出模式
0x 3f20 001C GPSET0 GPIO Pin Output Set 0 输出 电平
//寄存器指针赋值的是io起始地址,*GPSET0|=0x1<<4;改变寄存器地址指向的值进行io配置
GPSET0=(volatile unsigned int*)ioremap(0x3f20001C,4);
0=not effect
1=set gpio n
0x 3f20 0028 GPCLR0 GPIO Pin Output Clear 0 清零
0=not effect
1=set gpio n
(volatile unsigned int*)0x20001001;volatile的作用,本条指令确保地址不会因编译器的优化而省略或者编译器重新分配地址,且要求每次直接读值。
地址
GPFSEL0=(volatile unsigned int*)ioremap(0x3f200000,4);//ioremap函数把物理地址0x3f200000映射为int* 4个字节的虚拟地址
==========================================================================================
在linux系统编写LED驱动相当于在stm32编写gpio的初始化的寄存器位配置和配置输出电平
但linux必须先用指针映射寄存器地址,再配置功能
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能
//GPIOF9,F10³õʼ»¯ÉèÖÃ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//输出引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//设置模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//³õʼ»¯
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);