---------------------------------------------------------
力扣专题
---------------------------------------------------------
一、嵌入式开发
1、ARM的历史
ARM原本含义(Acom RISC Machine)后来Acron公司独立出了ARM部门,成立了ARM公司(Advanced PISC Machine)<高级的精简指令集计算机>
ARM公司是在英国剑桥成立的,刚开始ARM公司和别的半导体公司一样,也是自主研发芯片(arm1 arm2 arm3 arm4 arm5),但当时市面上都是8Bit/16Bit的芯片,但是ARM公司一上来就生产的是32Bit的芯片,因此生意不好,所以ARM公司做了一个决定,自己不制作芯片,只研发芯片的架构,卖芯片的设计方案,解决方案,技术授权,卖给全球的半导体厂商(苹果\三星\ST\华为...)
全球99%的智能手机和平板电脑都采用的是ARM架构,但由于只卖理念,ARM公司并不挣钱,在2016年被日本软银收购了
2、板子简介
我们使用的开发板的芯片是三星公司的芯片(S5P6818),内核的核心是ARM公司的V8架构
ARM-CORTEX-A53(8核)定制版
与S5P6818类似的还有一个芯片是S5P4418,内部的核心使用的是ARMV7架构
ARM-CORTEX-A9(4核)
S5P6818与S5P4418从外观上来看一模一样,这样打出来的电路板可以直接套用两款产品,提高了工作效率。
3、ARM嵌入式开发环境
与STM32进行对比
STM32的开发环境:
Windows(7 8 10 11) + KEIL(IAR \ VSCode) + 仿真器 \ 串口下载 + STM32CUBEMX
S5P6818的开发环境:
Linux操作系统:Red Hat \ Ubuntu \ CentOS \ Open Suse \ Dedoral \ Debian
因为在我们在开发的时候在Ubuntu系统下的解决方案比较多,容易处理,所以我们选择Ubuntu
小技巧:
在选择操作系统时,一定要选择LTS(Long-term support)版本
一般选择前面的数字是偶数的,后面的版本是04的
(因为Ubuntu操作系统是每半年发行一次,分别是4月和10月,一财年)
在Ubuntu中,我们使用lsb_release -a命令查看系统版本号
uname -a 查看系统位数与内核版本
我们在嵌入式开发不会拿高端芯片做裸板开发,因为这会使得很多资源以及芯片的性能发挥不出来,所以一定要跑操作系统,我们在开发板上移植操作系统,例如:
Linux 定制一款Linux
Android 移植谷歌公司的安卓
Ubuntu 在很多的电子设备,会跑Ubuntu操作系统(北京地铁的闭路电视)
4、嵌入式开发的结构
APP | QQ、微信、游戏、控制界面 | (C/C++的内容) |
GUI | QT(C++)、Android(Java)、GTK(C)、MINIGUI(C/C++)、LVGL(C/C++) | (Qt的内容) |
本地执行程序 | ls、cat、cp。。。 | (APUE的内容) |
lib库 | libc.so.6、libstdc++、libsqlite3、libjpeg.so | (动态库和静态库的制作) |
文件系统 | 根文件系统(rootfs) | |
Kernel | Linux、Android、Windows | |
Bootloader | 检测硬件+引导内核(X86的BIOS+GRUB) |
----------------------------------------------------------------------
硬件
S5P6818开发板 = 核心板 + 底板
核心板 = SOC(system on chip<系统级芯片 \ 片上系统>)
+ DDR(内存1GB)
+ EMMC(硬盘8GB)
+ PMU(电源管理芯片)
+PHY(网络芯片)
SOC = CPU(ARM-CORTEX-A53核心(8核 * 1.4GHz))
+ 总线(CPU访问外设)
+ 外设(GPIO\UART)
+ 内存
+ 硬盘
CPU = ARM-CORTEX-A53
+ cache(高速缓存)
+ mmu(内存管理单元<访问的地址就可以是虚拟地址了>)
+ 中断控制器。。。
底板 = reset、key、led、SD0、SD1、SIM、IR。。。
LCD、LVDS、HDMI、MIPI(显示类的接口)
UART0/1/2/3/4
麦克风\耳机接口
扩展IO
USB-OTG
----------------------------------------------------------------------
1)APP
首先是最上层的图形化应用程序,我们管这样的程序叫做APP
我们可以在高端芯片的开发板上直接连接显示屏,或通过显示类的接口外接显示器都可以,只要是可以在显示屏或者显示器上展示的应用就是APP
2)GUI
如果要开发图形化的应用程序,我们需要GUI(图形用户接口)的支持,说白了,就是一套图形库,如果没有图形库,我们写图形化应用程序就很麻烦,需要一个像素点一个像素点去画,我们嵌入式领域中常用的GUI就是Qt(在医疗设备或者工控领域)Qt本身是用C++写的,本身支持的接口也是C++接口
3)本地执行程序
我们本地的执行程序就是大家最熟悉的命令,如:touch、ls、mkdir。。。以及我们写的所有在本地可以直接运行的程序都算是本地的可执行程序。
4)lib库
在本地执行程序的下面我们是lib库,上面的GUI或者APP都会调用这一层,需要掌握如何制作静态库以及动态库,静态库和动态库的区别?
-------------------------------------------------------------------------
制作静态库,动态库
静态库与动态库的区别
-------------------------------------------------------------------------
5)文件系统
将硬盘中的二进制数据识别成各种类型的文件
6)内核
主要研究Linux内核
有一小部分公司也会使用Android内核,很多公司支持国产操作系统
7)bootloader
在内核之下就是bootloader,是一个启动程序,我们嵌入式中使用BOOTLOADER就相当于是BIOS + GURB的功能,初始化硬件 + 引导内核,我们这块使用的bootloader是通用的bootloader,叫做Uboot,uboot能支持多个平台:ARM \ X86
8)硬件
二、课程内容
1、ARM体系结构与编程
[1] LED 1-Wire UART IIC SPI 硬件接口
[2] ARM汇编
[3] ARM中的中断处理
总结:在裸板上开发,不跑操作系统
2、系统移植(porting)
[1] uboot移植
[2] Linux内核移植
[3] 根文件系统的制作
3、Linux下的驱动程序开发
硬件的知识
内核编程的基础知识
内核驱动的编程框架
字符设备的编程框架
块设备的编程框架
网络设备的编程框架
三、搭建开发环境
1、下载软件包
2、搭建上位机的串口
SecureCRT
----------------------------------------------------
安装路径不要有中文
----------------------------------------------------
3、Linux启动过程
Linux:如果做工控或医疗设备选择Linux多一些
Android:如果做消费电子或追求高精尖(追求用户体验)肯定选择安卓
因为Linux相对于Android操作系统来说更轻量级一些,占用资源更少
一般的SOC都可以跑Linux操作系统,但如果想要跑安卓,至少需要ARM-CORTEX-A8的核心
1)Linux的启动顺序
1】先启动Uboot(bootloader)
2】再启动uImage / zlmage(Linux内核)
3】最后启动rootfs(根文件系统)
在EMMC中首先是Uboot,
Uboot是不在分区里面的,uImage(内核)可以放在分区,也可以不放在分区
对于Linux操作系统来说,在EMMC中至少有一个分区,放的是根文件系统
未分区 已分区
EMMC(8G) = [Uboot ulmage | rootfs]
2)Android的启动顺序
1】先启动Uboot(bootloader)
2】再启动ulmage / zlmage(Android内核)
3】最后启动ramdisk(虚拟内存盘)
4】对安卓系统进行简单的初始化
5】初始化完成后去挂载
其实ramdisk就是安卓系统的根(这个根在内存里面)
在(ramdisk)根目录下有这样的几个目录文件,需要进行分区挂载
sysytem、data、cache、storage
对于Android系统来说硬盘分区最少是这4个,
在EMMC中首先是uboot,uboot是不在分区里面的,ulmage内核可以防灾分区也可以不放在分区,比如手机产品,uboot和内核就不能放在分区里面,如果放到分区中,就可以在手机的文件管理系统看到Uboot和内核,如果不小心删掉内核没有关系,可以通过刷机刷回来,但如果不小心把uboot删掉了,那我们就连刷机都刷不了了,因为手机刷机使用的recovery模式也是uboot提供的。
ramdisk这个根在内存中,如果ramdisk下的几个目录直接存储数据,设备重启之后,里面的数据都会被清空,所以需要挂载到EMMC上
需要把system目录挂载到system分区
(只有超级用户可以操作<系统自带的软件【电话】【短信】。。>都会安装到system分区中)
需要把data目录挂载到data分区
(普通用户可以操作的,其实data分区就是安卓系统的家目录,用户自己下载的一些文件会存放到data分区中,我们如果把data分区格式化了,就相当于是恢复了出厂设置)
需要把cache目录挂载到cache分区(存放的都是一些缓存的东西)
需要把storage目录挂载到storage分区(可以存放用户自己的一些文件)
未分区 已分区
EMMC(8G) = [Uboot ulmage | system | data | cache | storage]
4、烧写Uboot的实验
对于我们的S5P6818开发板来说,板子的启动设备有:
第一启动设备是SD0
第二启动设备是EMMC(硬盘)
第三启动设备是USB
我们拿到一块新的开发版之后,EMMC是新的,里面什么也没有,所以我们拿到开发版之后,第一次启动肯定不是从EMMC启动,只能从SD卡或者USB启动,SD卡启动更方便,SD卡里面需要有启动代码,也就是Uboot,所以我们需要把Uboot烧写到SD卡中
需要把Uboot放到SD卡的第513个字节,也就是说SD卡需要在前面空出512个字节,从SD卡的第513个字节位置写入Uboot(S5P6818规定的)
--------------------------------------------------------------------
1、为啥是第513个字节?
主要是因为S5P6818内部有一块儿ROM(read-only只读存储器),里面存储的是三星公司固化好的程序,当6818芯片启动后,先去执行ROM中的代码,执行之后,会去SD卡中读取bootloader,把bootloader读到6818内部的RAM(random-access随机存储器)当中执行bootloader的第一阶段,第一阶段的功能是初始化板卡上的DDR,然后在板卡上的DDR中执行bootloader第二阶段,bootloader必须要放到SD卡的第513个字节上,要不然6818的ROM找不到bootloader。
2、为啥不放到第0个字节?
为什么不从SD卡的第0个字节存放bootloader?
主要是因为SD卡的前512个字节(是第一个扇区)存储的是分区表(分区的开始\结束\大小。。)所以需要把bootloader准确的写入到SD卡的第513个字节
注意:不能直接把bootloader拷贝到SD卡中,这样是不能确定bootloader拷贝到了SDS卡的什么位置,直接拷贝到的位置是虚拟地址。
3、如何准确的写入到SD卡的第513个字节?
在Linux下,一切皆文件,我们在Linux操作系统中,任何东西都可以当成文件进行处理,将SD卡插入到Linux操作系统中,也就是一个文件,对这个文件,我们可以进行open打开,然后使用lseek定位,直接定位到513个字节,然后把bootloader的内容read出来,write到第513个字节。
--------------------------------------------------------------------
5、创建SD卡分区
我们先这样处理,给SD卡开头的513个字节之后,再预留100MB空间,在100MB之后,建立分区,将分区格式化成fat32格式
未分区 已分区
SD卡 = [512bytes + 100MB | ----fat32----]
1)先把SD卡插入到PC机中
需要在Linux操作系统中识别
2)首先确认一下Linux系统是否识别了SD卡
在终端中录入:ls /dev/sd*
一般来说,sda是PC机的硬盘分区,sdb或者sdc是SD卡设备,如果不确定,可以把除了SD卡以外的设备都拔掉(手机\U盘\移动硬盘)或将SD卡拔下,再查看一下哪一个消失了,就可以确定了。
注意,有/dev/sdb也有/dev/sdb1,/dev/sdb是整个SD卡设备,/dev/sdb1是SD卡里的第一个分区,一会儿我们在写入bootloader的时候需要把/dev/sdb open打开,而不是/dev/sdb1千万不要弄混了。
3)确定SD卡是否挂载到了Linux系统中
在终端录入mount命令(用于挂载的命令)
没有挂载到Linux系统中也没有关系,我们现在要卸载SD卡的分区。
4)卸载SD卡分区
在重新分区时,我们需要把挂在上的分区卸载掉
两种卸载的方法,在终端录入
【1】umount /dev/sdb1
【2】umount /media/zjd
5)重新建立分区
需要使用fdisk命令,(可以用于对磁盘<存储类设备>进行分区操作)
【1】在终端录入 sudo fdisk /dev/sdb
【2】在<命令(输入m获取帮助):>之后输入p,查看SD卡分区情况
【3】在<命令(输入m获取帮助):>之后输入d,删除分区
【4】在<命令(输入m获取帮助):>之后输入p,查看分区是否删除成功
【5】在<命令(输入m获取帮助):>之后输入n,新建一个分区
【6】在<选择(默认p)):>之后输入p,创建主分区(也可直接敲回车)
【7】在<分区号(1-4,默认 1)):>之后输入1,设置分区号(也可直接敲回车)
【8】在<第一个扇区(2048-15433727, 默认 2048):>之后输入最大扇区的1/100,设置该扇区的大小,设置完后,继续敲回车,代表当前扇区从311162到最后都是第一个分区
---------------------
可能会遇到是否移除标签,直接选“是”就好
---------------------
【9】在<命令(输入m获取帮助):>之后输入t,选择分区类型
【a】在Hex code or alias (type L to list all): 之后输入L。列出所有类型
W95 FAT32 硬盘分区格式的一种
W95 FAT32(LBA) 带有逻辑寻址模式的硬盘分区
【b】在Hex code or alias (type L to list all): 之后输入0b,我们选择W95 FAT32
【c】在<命令(输入m获取帮助):>之后输入p,查看SD卡的分区是否标记为W95 FAT32类型
【d】在<命令(输入m获取帮助):>之后输入w,保存刚才的配置
7)给SD卡新创建出来的分区进行格式化(可能需要一些时间,等待一下就好)
在终端录入sudo mkfs.vfat -F 32 /dev/sdb1 -n zjd
8)同步一下,在终端中录入sync
9)将SD卡从PC机中拔除,重新插入,看看SD卡是否可以自动挂载
6、烧写uboot到SD卡
我们想让开发板启动,首先要有一个bootloader,所以我们首先把bootloader烧写到SD卡第513个字节(前512个字节是第一扇区,里面存储的是分区表)
1)找到bootloader
<ubootpak.bin>是已经编译好的bootloader,我们需要把<ubootpak.bin>文件放到SD卡的第513个字节,可以使用C的系统函数open、close、read、write、lseek,也可以直接使用dd命令,直接吧文件内容写到SD卡的第513个字节。
2)执行<dd>命令
sudo dd if=./ubootpak.bin of=/dev/sdb seek=1
if=文件名 表示输入文件名
of=文件名 表示输出文件名
seek=blocks 表示从输出文件开头跳过blocks个块后再开始复制
注意:一定是/dev/sdb,而不是/dev/sdb1
3)使用<sync>命令同步
4)把SD卡插入开发板中
注意:插入到SD0卡的位置,只有插入到SD0卡才可以启动
对于嵌入式设备来说,标准输入(stdin)\标准输出(stdout)\标准错误输出(stderr)都是串口,因此,如果uboot调用了printf(3)函数的话,其实是往串口中打印输出的,所以我们在后面的调试程序的时候,就直接使用串口调试助手即可。
----------------------------------------------------------------------
使用高编内容,把bootloader写入到SD卡的第513个字节
----------------------------------------------------------------------