导图社区 嵌入式系统组成(程序运行原理)
嵌入式系统的组成(硬件 软件)分层,程序运行原理的四大阶段,知识点系统且全面,希望对大家有用。
编辑于2024-05-14 11:39:09嵌入式系统组成
软件
应用层
为什么
找到问题(痛点)
找到一个社会问题
针对应用需求,用来实现用户预期目标的软件,完成整个产品的功能
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
一个可以提供将各个模块整合调用的应用程序(软件分层的一层)
整体内部框架是怎样的?
内部组成,划分为原子
流程图
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
怎么做
功能1:
具体的实现步骤
1.
2.
3.
功能2:
C++
默认的宏
增加对C++项目引用的支持
当在一个C++项目引用这个头文件时,编译器能知道要按照C语言对此文件进行编译
__cplusplus
#ifdef __cplusplus extern "C" { #endif ...头文件内容 #ifdef __cplusplus } #endif
功能3:
中间层
为什么
找到问题(痛点)
找到一个社会问题
对于纯软件的应用程序,与硬件和操作系统都无关,为了屏蔽底层OS的异构型,便于移植
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
无
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
无
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
一个可以提供某种应用服务/算法的应用程序(软件分层的一层)
整体内部框架是怎样的?
内部组成,划分为原子
举例:某算法
业务逻辑代码
接口代码
算法层代码
网络编程
TCP/IP协议栈
LWIP
定义
Light weight IP
轻量级
保持TCP协议主要功能的基础上减少对RAM的占用,只需十几KB和40K左右的ROM
特点
小型开源
有无操作系统都可以
适合在低端的嵌入式系统中使用
参考网站
http://savannah.nongnu.org/projects/lwip/
源码下载
http://download-mirror.savannah.gnu.org/releases/lwip/
contrib
lwip
http://download.savannah.gnu.org/releases/lwip/
源码分析
contrib
移植和应用的demo、移植所需的头文件
lwip
内核源码文件
5个文件
移植
无os
步骤
移植准备工作
基础工程
基于内存管理
UAMART组件
LWIP文件下载
lwip-x.x.x.zip
contrib-x.x.x.zip
LWIP数据包和网络接口管理
数据包
使用pbuf结构体描述协议栈使用中的数据包
对于网络接口的描述
netif结构体
用于和网卡连接
添加网卡驱动
不同网卡硬件对应不同驱动
添加LWIP源文件
添加中间文件
作用
将网卡驱动和LWIP连接起来
添加arch
cc.h
主要完成协议栈内部使用的数据类型的定义
如果使用os的话还有临界代码区保护等
cpu.h
用来定义CPU的大小端模式
perf.h
是和系统测量与统计相关的文件
我们不使用任何的测量和统计
sys_arch.h
实现获取时间的函数sys_now
lwip_localtime为全局变量,用来为LWIP提供时钟
sys_arch.c
使用操作系统会用到
添加LWIP通用文件
lwip_comm.c
lwip_comm.h
将LWIP源码和以太网驱动库结合的桥梁
文件由对应的芯片厂家提供
lwipopts.h
用来裁剪和配置LWIP
添加ethernetif.h
由对用的芯片厂家提供
LWIP源码修改
修改LWIP源文件名字
sys.c和sys.h和工程其他文件名字冲突
修改ethernetif.c文件
子主题
修改mem.c和memp.c
修改icmp.c
支持硬件帧校验
LWIP的裁剪与配置
lwipopts.h
用来裁剪和配置LWIP
编程接口
raw
TCP
优点
效率高
缺点
不适合大数据量等场合
带os
步骤
在无os的基础上修改
修改cc.h
使用os的临界区保护
OS_CRITICAL_METHOD
添加includes.h头文件
修改lwipopts.h
NO_SYS=0
使用os
SYS_LIGHTWEIGHT_PROT=1
对LWIP中关键代码做保护
LWIP_NETCONN = 1
使能NETCONN编程方式
LWIP_SOCKET = 1
使能SOCKET编程方式
修改sys_arch.h
定义了消息邮箱的数量和每个消息邮箱的大小
定义了4种数据类型
sys_sem_t
信号量
sys_mutex_t
互斥信号量
sys_mbox_t
消息邮箱
sys_thread_t
线程ID
修改sys_arch.c
定义了使用到的信号量、消息邮箱的操作函数
sys_msleep
延时函数
sys_now
获取系统时间函数
修改lwip_comm.c
编程接口
NETCONN
SOCKET
TCP/IP网络系统
引入和解决
解决问题
连通性
上网用户之间可以传递信息
共享
信息
上网查阅资料
软件
下载一些网上的软件
硬件
共用打印机
知识点
名词解析
网络(network)
若干结点和连接这些结点的链路组成
作用
把许多计算机连在一起
分类
计算机网络
用包含计算机的网络加上许多路由器组成的互联网
备注
一定有计算机,才可构成有用的网络
互联网(网络的网络 network of networks)
网络和网络通过路由器互连
备注
网络互连不止把计算机物理连接,而且默认在计算机上安装了适当的软件,这样计算机才能通过网络传输信息
种类
因特网(Internet)
世界上最大的互连网络(把许多网络连在一起)
发展的三个阶段
从单个的网络ARPANET向互联网发展的过程
建成了三级结构的因特网
主干网、地区网和校园网(企业网)
逐渐形成了多层次ISP结构的因特网
用户上网和ISP的关系
组成
边缘部分
由所有连接在因特网上的主机组成
用户直接使用,用来进行通信(传送数据、音频或视频)和资源共享
在网格边缘的端系统中运行的程序之间的通信方式通常可划分为两大类
客户服务器方式(C/S方式)
客户(client)和服务器(server)都是指通信中所涉及的两个应用进程
描述的是进程之间服务与被服务的关系
客户是服务请求方,服务器是服务提供方
服务请求方和服务提供方都要使用网络核心部分所提供的服务
特点
客户程序
被用户调用后运行,在通信时主动向远地服务器发起通信。客户程序必须知道服务器程序的地址
不需要特殊的硬件和很复杂的操作系统
服务程序
是一种专门用来提供某种服务的程序,可同时处理多个远地或本地客户的请求
系统启动后自动调用并一直不断地运行着,被动的等待并接收来自各地的客户的通信请求。因此服务器程序不需要知道客户程序的地址
一般需要强大的硬件和高级的操作系统支持
对等方式(P2P方式) peer-to-peer
指两个主机在通信时并不区分哪一个是服务请求方还是服务提供方
只要两个主机都运行了对等连接软件(P2P软件),他们就可以进行平等的、对等连接通信
核心部分
由大量网络和连接这些网络的路由器组成
为边缘部分提供服务的(提供连通性和交换)
重要功能
路由器是实现分组交换的关键构件,其任务是转发收到的分组
电路交换
步骤
建立连接(占用通信资源)->通话(一直占用通信资源)->释放连接(归还通信资源)
特点
在通话的全部时间内,通话的两个用户始终占用端到端的通信资源
计算机数据是突发的,使用电路交换,真正传输数据的时间占比很低,这就浪费了通信线路资源
分组交换
特点
采用存储转发技术
优点
报文
把要发送的整块数据称为一个报文
步骤
发送报文前,先把较长的报文划分成为一个个更小的等长数据段,例如1024bit
每一个数据段前,加上一些必要的控制信息组成的首部,构成一个分组(包),分组的首部成为包头
分组是在因特网中传输的数据单元
路由器接收到一个分组,先暂时存储下来(内存),再检查其首部,查找转发表,按照首部中的目的地址,找到合适的接口转发出去,把分组交给下一个路由器,最终找到目标主机
两部分的示意图
备注
连接在Internet上的计算机成为主机(host)
Internet和Internet的区别
小写开头
互联网或互连网(通用名词)
泛指由多个计算机网络互连而成的网络
在这些网络之间的通信协议可以是任意的
大写开头
因特网(专有名词)
指当前全球最大的、开放的、由众多网络相互连接而成的特定计算机网络
采用TCP/IP协议族作为通信的规则
计算机网络的类别
不同作用范围的网络
广域网WAN(Wide Area Network)
作用范围
几十到几千公里
也称远程网
所属部分
核心部分
作用
通过长距离运送主机所发送的数据
特点
连接广域网各结点交换机的链路一般都是高速链路,具有较大的通信容量
城域网MAN(Metropolitan Area Network)
作用范围
一般是一个城市,跨越几个街区甚至整个市,5~50km
作用
可以为一个或几个单位所拥有,但也可以是一种公用设施,用来将多个局域网互连
特点
很多城域网采用以太网技术
局域网LAN(Local Area Network)
作用范围
一般用微型计算机或工作站通过告诉通信线路相连,速率通常10Mb/s以上,局限在较小的范围(1km)
特点
现在一个学校或企业大都拥有许多个互连的局域网(校园网/企业网)
个人区域网PAN(Personal Area Network)
作用范围
在个人工作地方把属于个人使用的电子设备用无线技术连接起来的网络(无线个人区域网),10m左右
不同使用者的网络
公用网
指电信公司(国有或私有)出资建造的大型网络
公用
所有愿意按电信公司的规定缴纳费用的人都可以使用这种网络
专用网
这是某部门为单位的特殊业务工作需要而建造的网络
特点
这种网络不向本单位以外的人提供服务
用来把用户接入到因特网的网络
接入网AN(Access Network)
计算机网络的性能
性能指标
速率
带宽
吞吐量
时延
发送时延
传输时延
处理时延
排队时延
时延带宽积
往返时间RTT
利用率
非性能
费用
质量
标准化
可靠性
可扩展和可升级性
易于管理和维护
计算机网络体系结构
引入和解决
连接在网络上的两台计算机要互传文件
解决
通过分层的概念将一个大而复杂的问题转换为小问题,分开处理。所以推出了OSI
计算机网络体系结构的形成
协议和划分层次
网络协议
为进行网络中的数据交换而建立的规则、标准或约定
具有五层协议的体系结构
引入
由于OSI复杂不适用,引入了另一种TCP/IP体系结构,综合OSI和TCP/IP的优点,采用一种只有五层协议的体系结构
各层
应用层
功能
为用户的应用进程提供服务
协议
概念
每个协议都是为了解决某一类应用问题,应用层的具体内容就是规定应用进程在通信时所遵循的协议
许多都是基于客户服务器方式
分类
支持万维网应用的HTTP协议
支持电子邮件的SMTP协议
支持文件传输的FTP协议
概述
是因特网上使用得最广泛得文件传送协议
应用
屏蔽了各计算机系统的细节,适用于在异构网络中任意计算机之间传送文件
文件共享协议分类
复制整个文件
FTP(TCP)
端口21
TFTP(UDP)
只支持文件传输而不支持交互
优点
可用于UDP环境
代码所占得内存较小
3种平台环境搭建
ubuntu
安装tftp-hpa和tftpd-hpa
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
创建文件夹&赋予权限
mkdir /home/cq/linux/tftpboot
chmod 777 /home/cq/linux/tftpboot
配置tftp
新建目录
mkdir /etc/xinetd.d/
新建&修改文件
touch /etc/xinetd.d/tftp
启动tftp服务
sudo service tftpd-hpa start
修改文件/etc/default/tftpd-hpa
TFTP_DIRECTORY为创建的tftp目录
重启tftp服务
sudo service tftpd-hpa restart
查看tftp状态
service tftpd-hpa status
windows
下载软件tftpd64
百度网盘-工具
开发板
一般自带
命令格式
busybox
参数
-g
get
下载文件
-p
put
上传文件
-r
remote
后面跟server即PC机tftp服务器根目录中的文件名or上传server后重命名的文件名
-l
local
后跟存在于client的源文件名or下载client后重命名的文件名
更名
下载
tftp -g -l mb.txt -r y.txt 192.168.10.100
从server端(192.168.10.100)下载根目录下的y.txt到client端并更名为mb.txt
上传
tftp -p -r mb.txt -l y.txt 192.168.10.100
从client端上传y.txt到server端(192.168.10.100)的根目录下并更名为mb.txt
不更名
下载
tftp -g -l/r y.txt 192.168.10.100
从server端(192.168.10.100)下载根目录下的y.txt到client端
上传
tftp -p -r/l y.txt 192.168.10.100
从client端上传y.txt到server端(192.168.10.100)的根目录下
linux其他
下载
tftp -4 192.168.10.200 -c get a.txt
上传
tftp -4 192.168.10.200 -c put a.txt
联机访问:多个程序同时对一个文件进行存取
NFS(网络文件系统)
应用于TCP/IP网络上
允许应用进程打开一个远地文件,并能在该文件得某一个特定得位置开始读写数据
远程登录
TELNET
在所在地通过TCP连接注册(登录)到远地得另一个主机上(使用主机名或IP地址)
特点
基于TCP
服务是透明的
端口
23
SSH
基于TCP
提供安全远程登录及其他安全网络服务的协议
端口
22
万维网www
大规模的、联机式的信息储藏所Web
用链接的方法能非常方便地从因特网上的一个站点访问另一个站点,从而主动按需获取信息
URL
用来表示从因特网上得到的资源位置和访问这些资源的方法
相当于一个文件名在网络范围的扩展
格式
http
超文本传送协议
定义了浏览器怎么向万维网服务器请求万维网文档,以及服务器怎样把文档传送给浏览器
端口80
域名系统DNS
概述
是因特网使用的命名系统,用来把便于人们使用的机器名字转换为IP地址(名字系统)
因特网的域名结构
域名服务器
用于解析域名,完成域名到Ip的转换
运输层
功能
负责向两个主机中进程之间的通信提供服务
根据其首部中的目的端口号把数据交付给应用层的目的应用进程
特点
因为一个主机可同时运行多个进程,因此有复用和分用的功能
复用
多个应用进程可同时使用下面运输层的服务
分用
运输层把收到的信息分别交付给上面应用层中相应的进程
协议
传输控制协议TCP(Transmission Control Protocol)
概述
数据传输单位
报文段(segment)
TCP的连接
TCP连接的端点
套接字(socket)或插口
端口号拼接到IP地址即构成套接字
例如
IP是192.3.4.5,端口是80,那么套接字就是192.3.4.5:80
TCP连接是由被通信两端的两个套接字确定
可靠传输的工作原理
停止等待协议
每发送完一个分组就停止发送,等待对方的确认,收到确认再发下一个分组
优点
简单
缺点
信道利用率低
连续ARQ协议(滑动窗口)
位于发送窗口内的5个分组都可以连续发送出去,而不需要等到对方确认
接收方采用累积确认。接收方不必对收到的分组逐个发送确认,而是可以在收到几个分组后,对按需到达的最后一个分组发送确认
优点
容易实现,即使确认丢失也不必重传
缺点
不能向发送方反映出接收方已经正确收到的所有分组信息
TCP可靠传输的实现
以字节为单位的滑动窗口
超时重传时间的选择
自适应算法
选择确认SACK
引入
收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,只传送缺少的数据,不重传已经正确到达接受方的数据
流量控制
概念
让发送方的发送速率不要太快,要让接收方来得及接收
方法
利用滑动窗口实现流量控制
必须考虑传输效率
拥塞控制
原理
拥塞:对资源的需求大于可用的资源
防止过多的数据注入网络,这样可以使网络中的路由器或链路不致过载
集中拥塞控制方法
慢开始和拥塞避免
快重传和快恢复
随机早期检测RED
网络层的策略对TCP拥塞控制影响最大的就是路由器的分组丢弃策略
TCP的运输连接管理
三阶段
连接建立
数据传送
连接释放
采用客户服务器方式
TCP的连接建立
三次握手建立TCP连接
1. B的TCP服务器进程先创建传输控制块TCB,准备接收客户进程的连接请求
A:close(关闭)
B:listen(监听)
2. A的TCP客户进程首先创建传输控制块TCB,然后向B发出连接请求报文段,首部中的同步位SYN=1(不能携带数据,但消耗一个序号),同时选择一个初始序号seq=x
A:SYN-SENT(同步已发送)
B:listen(监听)
3. B收到连接请求的报文段后,同意建立连接,向A发送确认。(确认报文段中SYN=1和ACK=1 确认号是ack=x+1,同时为自己选择一个初始序号seq=y,此报文段也不能携带数据)
B:SYN-RCVD(同步收到)
4. A的TCP客户进程收到B的确认后,还要向B给出确认
参数
ACK=1
确认号ack=y+1
自己的序号seq=x+1
状态
A:ESTABLISHED(已建立连接)
理由
为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误
举例
A发出的第一个连接请求报文段并没丢失,而是在某个网络结点长时间滞留了,以致延误到连接释放以后的某个时间到达B
5. B收到A的确认后
A/B:ESTABLISHED(已建立连接)
TCP的连接释放
数据传输结束后,双方都可释放连接
步骤
1. A的应用进程先向其TCP发送连接释放报文段,并停止再发送数据,主动关闭TCP连接
参数
FIN=1
序号seq=u
等于前面已经传送过的数据的最后一个字节的序号+1
状态
A:FIN-WAIT-1(终止等待1)
备注
FIN报文段即使不携带数据,它也消耗一个序号
2. B收到连接释放报文段后发出确认,通知B高层应用进程
参数
确认号是ack=u+1
自己的序号seq=v
等于前面已经传送过的数据的最后一个字节的序号+1
ACK=1
状态
B:CLOSE-WAIT(关闭等待)
TCP连接A->B连接释放了,处于半关闭状态
3. A收到B的确认后,等待B发出的连接释放报文段
状态
A:FIN-WAIT2(终止等待2)
4. 此时B还可以向A发送数据,若没有传送的数据后,B发出连接释放报文段,等待A的确认
参数
FIN=1
确认号ack=u+1
和上次发的确认号一样
B的序号为w
在半关闭状态B可能又发送了一些数据变成了w
状态
B:LAST-ACK(最后确认)
5. A收到B的连接释放报文后,必须对此发出确认。此时必须经过时间等待计时器设置的时间2MSL后,A才能进入CLOSED
参数
ACK=1
确认号ack=w+1
自己的序号seq=u+1
状态
A:TIME-WAIT(时间等待)
6. 经过2MSL时间后
状态
A:CLOSED(关闭)
B:CLOSED(关闭)
理由
为了保证A发送的最后一个ACK报文段能够到达B
如果A发送的确认丢失,还可以在这个时间进行重传
防止”已失效的连接请求报文段“出现在本连接
TCP的有限状态机
首部格式
字节数
20+4N
各字段
源端口(2B)和目的端口(2B)
完成分用功能
序号(4B)
含义
指的是本报文段所发送的数据的第一个字节的序号,下一个报文段字节的编号接着递增。(报文段序号)
序号范围
2^32
字节流中的每一个字节都按顺序编号
确认号(4B)
含义
期望收到对方下一个报文段的第一个数据字节的序号
定义
数据偏移(4bit)
含义
指出TCP报文段的数据起始处距离TCP报文段的起始处有多远
实际就是首部长度
单位
32位字(及4个字节)
长度
4bit最大十进制为15,所以最大值为60字节(15*4)
选项长度<=40字节
保留(6bit)
后续使用,默认0
6个控制位,各占(1bit)
紧急URG
URG=1
紧急指针字段有效
告诉系统此报文段中有紧急数据,应尽快传送,而不是按照排队顺序
和紧急指针配合使用
确认ACK
ACK=1
确认号字段有效
在连接建立后所有传送的报文段都必须置一
ACK=0
确认号无效
推送PSH
功能
当两个应用进程进行交互式的通信时,有时在一段的应用进程希望在键入一个命令后立即就能够收到对方的响应
使用
发送方TCP把PSH置1,并立即创建一个报文段发送出去
接收方TCP接收到PSH=1的报文段,就尽快交付给接收应用进程,而不再等待到整个缓存填满再交付
复位RST
RST=1
表明TCP连接中出现严重差错,必须释放连接,然后再重新建立传输连接
拒绝一个非法的报文段或拒绝打开一个连接
同步SYN
在连接建立时用来同步序号
SYN=1含义
表示这是一个连接请求或连接接收报文
SYN=1 ACK=0
表明这是一个连接请求报文段,对方若同意建立连接,则在响应报文段中使用SYN=1 ACK=1
终止FIN
用来释放一个连接
FIN=1
表明此报文段的发送方的数据已经发送完毕,并要求释放传输连接
窗口(2B)
指的是发送本报文段的一方的接收窗口。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量
因为接收方的数据缓存有限
窗口值作为接收方让发送方设置其发送窗口的依据
现在允许对方发送的数据量
检验和(2B)
加上12字节的伪首部
紧急指针(2B)
指出了紧急数据的末尾在报文段中的位置
选项
长度可以变,最大40字节
特点
面向连接的
每一条TCP连接只能有两给端点(点对点,一对一)
不提供广播/多播服务
提供可靠的交付
提供全双工通信
因为设有发送/接收缓存,所以双方的进程可以在任何时候发送数据
面向字节流
应用程序一次发送给TCP的数据块大小不等,但是TCP只是把这些数据当作无格式的字节流。TCP根据对方给出的窗口值和当前网络拥塞的程度决定一个报文段应该含有多少个字节
用户数据报协议UDP(User Datagram Protocol)
概述
只在IP的数据报服务之上增加了很少一点的功能,这就是复用和分用的功能以及差错检测的功能
数据传输单位
用户数据报
原理
当运输层从IP层收到UDP数据报,根据首部中的目的端口,把UDP数据报通过相应的端口,上交最后的终点------应用进程
如果不存在目的端口号,就丢弃报文,并由ICMP发送“端口不可达”差错报文给发送方
首部格式8字节(4字段)
源端口
在需要对方回信时选用
目的端口
在终点交付报文时必须使用
长度
用户数据报长度,最小8
检验和
检验UDP用户数据报在传输中是否出错。有错丢弃
计算校验时,在最前加上12字节的伪首部(不存在,只在校验时有效)
特点
无连接的
不保证提供可靠的交付,只能提供尽最大努力交付
面向报文的
发送方的UDP对应用程序交下来的报文,既不拆分也不合并,在添加首部后就向下交付给IP层
应用程序必须选择合适大小的报文,不然会降低IP层的效率
没有拥塞控制
网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用很重要
IP电话
实时视频会议
支持一对一、一对多、多对一、多对多的交互通信
首部开销小
8个字节,比TCP的20个字节首部要短
当某些实时应用需要使用UDP,但是因为没有拥塞功能,可能会引起网络拥塞。那么可以在应用进程不影响应用的实时性的前提下,增加一些提高可靠性的措施(如采用前向纠错或重传已丢失的报文)
端口
解决问题
因为分用的原因,要给每一个实体进程一个标志
概念
根据具体功能来定义端口号,而不是根据进程的存在与否定义
类型
16位
意义
只具有本地意义,为了标识本计算机应用层中各个进程和运输层交互时的层间接口
分类
服务器使用的端口号
熟知端口号0~1023
登记端口号1024~49151
客户端使用的端口号49152~65535
应用和应用层协议主要使用的运输层协议
网络层
功能
负责为分组交换网上的不同主机提供通信服务
发送数据时,网络层把运输层产生的报文段或用户数据报分装成分组或包进行传送
选择合适的路由
使源主机运输层传下来的分组能够通过网络中的路由器找到目的主机
特点
网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务
协议
IP
因为是IP协议,所以分组叫做IP数据报/数据报
在各式各样的网络构成的互连网上运行
格式
和IP配套使用的四个协议
地址解析协议ARP(Adress Resolution Protocol)
每个主机都设有一个ARP高速缓存(ARP cache),里面有本局域网上的各主机和路由器的IP地址到硬件地址的映射表。
适用于同一个局域网
逆地址解析协议RARP(Reverse Adress Resolution Protocol)
网际控制报文协议ICMP(Internet Control Message Protocol)
为了更有效的转发IP数据报和提高交付成功的机会
网际组管理协议IGMP(Internet Group Management Protocol)
因特网的路由选择协议
需要何种算法来获得路由表中的各项目
IP及其配套协议
虚拟互联网络
逻辑互联网络,互连起来的各种物理网络的异构型本来是客户存在的,但是我们利用IP协议就可以使这些性能各异的网络在网络层上看起来好像是一个统一的网格
IP地址
概念
就是给因特网上的每一个主机(或路由器)的每一个接口分配一个在全世界范围是唯一的32位的标识符
结构
IP地址 ::= {<网络号>,<主机号>}
各种IP地址的网络号字段和主机号字段
A
网络号可指派126个(2^7-2),去掉全0和全1
主机号最大主机数2^24-2
全0的主机号字段表示该IP地址是本主机所连接到的单个网络地址
全1表示所有的,表示该网络上的所有主机
B
可指派的网络数2^14-1
扣掉128.0.0.0
最小网络地址128.1.0.0
每一个网络上的最大主机数2^16-2
扣掉主机号全0和全1
C
可指派的网络总数2^21-1
最小网络地址192.0.1.0
扣掉192.0.0.0
每一个网络上的最大主机数2^8-2
扣掉主机号全0和全1
A、B和C都是单播地址(一对一通信)
D类地址用于多播
三个历史阶段
分类的IP地址
将IP地址划分为若干个固定类,每一类地址都由两个固定长度的字段(网络号+主机号)组成
网络号
表示主机(路由器)所连接到的网络
一个网络号在整个因特网范围内必须是唯一的
网络号字段全为0的IP地址是个保留地址,意思是“本网络”
网络号为127(01111111)保留作为本地软件环回测试本地主机的进程之间的通信之用
主机号
标志该主机(路由器)
一个主机号在它前面的网络号所指明的网络范围内必须是唯一的
子网的划分
构成超网
点分十进制
备注
IP地址和硬件地址区别
物理地址是数据链路层和物理层使用的
IP地址是网络层及以上各层使用的一种逻辑地址(软件实现)
划分子网
解决的问题
使单位内部使用更加灵活,但是减少了能够连接在网络上的主机数
IP两级变三级
本质
把IP地址的主机号部分位变为子网号,网络号不变
子网掩码
解决的问题
路由器如何把数据报转发到子网上
为了更便于查找路由表
原理
255.255.255.0(C类)和目的IP逐位相与,得到子网的网络地址
默认值
A
255.0.0.0
B
255.255.0.0
C
255.255.255.0
IP多播
概念
一对多通信
单播和多播比较
虚拟专用网VPN
专用地址
解决问题
用于两个远距离的专用网通信
网络地址转换NAT
解决问题
专用网内部的主机想和因特网上的主机通信
工作原理
NAPT
多本地主机共用一个全球IP
数据链路层
功能
将网络层交下来的IP数据报组装成帧(framing),在两个相邻结点间的链路上“透明”的传送帧中的数据
在数据链路层接收数据时,控制信息使接收端能够知道一个帧从帧的哪里开始和结束,提取数据部分,上交网络层
专用协议
点对点协议PPP
概念
用户计算机和ISP进行通信时所使用的数据链路层协议
使用的信道
点对点信道
广播信道
数据传输图
物理层
功能
透明的传输比特流
传输的单位
比特
特点
传送比特时应从首部开始传送
传输媒体
导向传输媒体
电磁波沿固体媒体传播
分类
双绞线
电话系统
同轴电缆
有线电视网的居民小区
光缆
非导向传输媒体
电磁波在自由空间传播(无限传输)
备注
packet(分组或包)往往是作为任何一层传送的数据单元
路由器在转发分组时最高只用到网络层而没有使用到运输层和应用层
TCP/IP分层具体协议
数据传输过程
具有不同网络号的局域网必须使用路由器进行互连
两个路由器相连,常常不分配IP地址(无编号网络/无名网络)
两个计算机的进程通信,必须知道对方的IP地址(为了找到对方的计算机)和对方的端口号(找到对方的应用进程)
将网络互连起来使用的一些中间设备
物理层
转发器
数据链路层
网桥/桥接器
网络层
路由器
TCP/IP体系的选择
网络层以上
网关
基本使用
内存管理
数据处理
shell
用于用户和内核的交互
shell是一种命令行解释器,等待输入命令,然后执行命令,返回结果
执行的规则
如果第一个单词是内置shell命令,则执行
若不是shell命令,则shell就会假设这是一个可执行文件的名字,要加载和执行这个文件
shell脚本
主题
概念:将多条命令放到一个文件中,类似于Windows批处理,以.sh结尾
特点: 第一行必须是:#!/bin/bash (表示使用bash) 可以提供数组、循环、条件判断
语法
read 读取键盘输入值
用途
人机交互
语法
read [options] [var1] [var2]
var用来存储数据的变量,一个/多个
输入的值数量>实际变量数量
额外的值全部保存在最后一个变量
输入的值数量=实际变量数量
输入的值数量<实际变量数量
多出来的变量值为空
选项
空
无指定变量,回车之前输入的值保存在shell变量REPLY中
-n
读取num个字符,而不是整行字符
-p
显示提示信息
-s
静默模式,不会在屏幕上显示输入的字符
-t
设置超时时间(s),在指定时间没输入完成,read返回一个非0推出状态,表示读取失败
用法
是否继续(Y/N)
p
n
在规定10s时间内静默输入密码
s
条件判断
if
if ... fi 语句
if ... else ... fi 语句
if ... elif ... fi 语句
备注
expression 和方括号([ ])之间必须有空格,否则会有语法错误
[]里面的字符串/变量要加双引号“”,[[]]可以不加
&&和|| 简单的判断
符号解析
$
shell仅支持整形、数值计算使用$((表达式))
脚本语法
test命令
功能
查看文件是否存在
test -e $filename && echo "$filename exist" || echo "$filename no exist" 输入filename文件,使用test查看文件是否存在,test -e $filename为真,则执行echo "$filename exist" test -e $filename为假,则执行echo "$filename no exist"
判断数值,字符,文件
test $name1 == $name2 判断name1是否等于name2
其他命令
&&和||命令:
cmd1 && cmd2 1.当cmd1执行正确,cmd2才执行 2.当cmd1执行错误,cmd2不执行
cmd1 || cmd2 1.当cmd1执行正确,cmd2不执行 2.当cmd1执行错误,cmd2执行
默认变量
$0~$n:表示shell脚本的参数,包括shell脚本命令本身,本身为$0
$# :#表示最后一个参数
$@ :表示$1 $2 $3...
$?
功能
显示最后命令的退出状态
0表示没有错误
其他任何值表明有错误
上一个函数的返回值
作用
用于判断上一个命令的状态
@
在命令前面加上@就不会在终端输出命令了
|
管道:将左边的输出作为右边的输入
条件判断
case $1 in "a") echo "a" ;; "b") echo "a" ;; *) echo "qita" ;; esac
函数
function fname(){ //函数代码段 }
调用函数直接输入函数名 fname
函数带参数 直接fname a b 表示两个参数
循环
while do done 表示条件成立就循环
while [条件] do //循环代码段 done
until do done 表示条件不成立就循环
until [条件] do //循环代码段 done
for do done循环
for var in con1 con2 con3... do //循环代码段 done
var表示变量,con表示执行的次数
for((初始值;限制值;执行步长)) do //循环代码段 done
其他语法
$name 不用括号括起来
=左右不能有空格
#! /bin/bash -e
每条指令之后后,都可以用#?去判断他的返回值,零就是正确执行,非零就是执行有误,加了-e之后,就不用自己写代码去判断返回值,返回非零,脚本就会退出
1.初识Linux shell
什么是Linux
Linux发行版
小结
2.走进shell
终端模拟
terminfo数据库
Linux控制台
xterm终端
Konsole终端
GNOME Terminal
小结
3.基本的bash shell命令
启动shell
shell提示符
bash手册
浏览文件系统
文件和目录列表
处理文件
处理目录
查看文件内容
小结
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
算法流程图
怎么做
功能1:
具体的实现步骤
1.
2.
3.
功能2:
功能3:
操作系统层
为什么
找到问题(痛点)
找到一个社会问题
功能较多时,有些功能需要快速响应,利用OS实现多个功能宏观并行执行(多任务/多线程)
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
饭店点餐,比如来了10桌客人,每桌点了6个菜。 1. 如果做完一桌再做另一桌,最后几桌客人早跑了。(裸机) 2.1 应该是一个桌客人一个菜,然后每桌的第二个菜。。。客户能接受这个速度,饭店在客户可接受的时间范围内,提高做菜效率(分时OS) 2.2 但是特殊情况下,有一桌是老板,这时候需要提高这桌的优先级(不然不给小费),只要老板让上下一菜,必须马上在规定时间做出来,这时候就要把老板的菜插队做出来再做其他的(RTOS)
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
一个可以提供多任务的应用程序(软件分层的一层)
整体内部框架是怎样的?
内部组成,划分为原子
具体OS
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
具体OS
怎么做
具体OS
驱动层
BSP是针对操作系统的,不同的操作系统BSP也不同
为什么
找到问题(痛点)
找到一个社会问题
问题:代码杂乱,为了使纯软件层便于跨平台移植,引入分层的思想
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
驱动代码和应用逻辑代码揉在一块
移植性强
将硬件和软件分离开,使软件无需关注硬件,调用驱动层提供的API即可
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
衣柜。第一层放上衣,第二层放裤子,第三层放鞋。
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
一个可以提供所有硬件驱动的程序(软件分层的一层)
整体内部框架是怎样的?
内部组成,划分为原子
片外板内(外部设备驱动)
RS485
外部存储器
掉电丢失
RAM 随机存取存储器 (人们常说的内存,访问速度比较快)
SRAM
DRAM
分类
SDRAM
它是同步动态存储器,利用单一的系统时钟同步所有的地址数据和控制信号。使用SDRAM不但能提高系统表现,还能简化设计、提供高速的数据传输,在嵌入式系统中经常使用
DDR SDRAM
区别
同时钟频率下的数据传输速度
DDR加倍
上升和下降沿都传输数据
特定类型的RAM
DPRAM:双端口RAM
优点
通信速度快、实时性强、接口简单,而且两边处理器都可主动进行数据传输
CAM:内容寻址RAM
工作机制
同时将一个输入数据项与存储在CAM 中的所有数据项自动进行比较,判别该输入数据项与CAM 中存储的数据项是否相匹配,并输出该数据项对应的匹配信息
优点
数据检索能提高系统性能
FIFO:先进先出队列
FIFO 多 用 于 数 据 缓 冲
NVRAM
...
区别
数据存储位置
SRAM
利用双稳态触发器来保存信息的
只要不掉电,信息是不会丢失的
DRAM
利用MOS(金属氧化物半导体)电容存储电荷来储存信息的
因此必须通过不停地给电容充电来维持信息
定期刷新
SRAM
无需刷新
供电就会保持一个值
DRAM
需要定期刷新
电容器会因漏电出现电荷丢失
组成
SRAM
6个晶体管
DRAM
1个晶体管和一个电容器
DRAM的成本、集成度、功耗等明显优于SRAM
掉电不丢失
ROM(只读存储器)
ROM(不可编程)
PROM(可编程)
EPROM(可擦除可编程)
E2PROM(电可擦除可编程)
24C02
为什么
外部存储设备
是什么
串行 EEPROM 存储器
大小:256字节
WP:写保护引脚,高电平只读,低电平可读写 A0/A1/A2:设备地址可编程部分,1 0 1 0 A2 A1 A0 0写/1读
怎么做
FLASH
非易失闪存技术 编程原理:只能1->0,所以编程前要擦除对应的块
NOR FLASH
NM25Q128
为什么
是什么
SPI接口的FLASH
特点: 最大SPI时钟:104MHz(双输出208MHz,四输出416MHz) 容量:128MBit = 16Mbyte 块(容量->块):256个 一个块大小:16*1024/256=64Kbyte 扇区(块->扇区):16个 一个扇区大小:64/16=4Kbyte 页(扇区->页):16 一页大小:4*1024/16=256字节
CS 即片选信号输入,低电平有效 DO 是 MISO 引脚,在 CLK 管脚的下降沿输出数据 DI 是 MOSI 引脚,主机发送的数据、地址和命令从 SI 引脚输入到芯片内部,在 CLK 管脚的上升沿捕获捕获数据 CLK 是串行时钟引脚,为输入输出提供时钟脉冲 WP 是写保护管脚,高电平可读可写,低电平仅仅可读 HOLD 是保持管脚,低电平有效
怎么做
读操作时序图
页写时序图
扇区擦除时序图
光/磁介质存储器
各种传感器
核外片内(芯片级驱动/主机驱动)
RCC
为所有外设提供时钟
ram
概念
临时存储设备
作用
处理器执行程序时,用来存放程序和程序处理的数据
内存状态
用户空间(用户态)
应用程序
调用内核态的方法
系统调用
不会直接调用系统调用函数,会通过API函数间接调用,比如POSIX、API和c库
异常(中断)
陷入
内核空间(内核态)
操作系统内核和驱动程序
层次结构的存储设备
在处理器和一个较大较慢的设备(主存)之间插入一个更小更快的存储设备(高速缓存存储器)
框图
处理器内的寄存器文件属于L0
主要思想
一个层次上的存储器作为下一层次上的存储器的高速缓存
flash(内部)
DMA
框图
为什么
为了提高CPU效率
1.当慢速外设通信比较多 2.数据量比较大的时候 3.中断使用明显会影响系统性能的时候 4.大量高速A/D转换
是什么
参考网址 https://blog.csdn.net/as480133937/article/details/104927922 https://www.stmcu.org.cn/module/forum/forum.php?mod=viewthread&tid=622515
本质是无需CPU参与,通过硬件为RAM与I/O设备开辟一条直接传输数据的通路。外设先给DMA控制器发送DMA请求,DMA收到信号后,会给外设发一个应答信号,外设应答后且DMA控制器收到应答后,就会启动DMA传输,直到传输完毕
一般数据操作: CPU把内存的数据读出来到寄存器,然后进行进一步操作,当只是移动数据时,再从寄存器读取到内存中
怎么做
初始化流程
子主题
BDMA
参考网址
https://www.cnblogs.com/armfly/p/12171106.html
UART(通用异步收发器)
常用
USART(通用同步异步收发器)
框图
为什么
为了实现数据通信
是什么
一种设备间常用的串行通信方式,串口按位(bit)发送和接收字节
分类
同步通信
异步通信
怎么做
串口通信协议
波特率
发送和接收双方需要约定一个波特率
选择
波特率选择为传输比特数的2/4倍
数据帧
需要提前约定好数据帧格式
起始位(1)
0
有效数据位(5、6、7、8)
低位(LSB)在前,高位(MSB)在后
校验位(1)
LSB(低位)在前MSB(高位)在后
奇校验
(有效数据位+校验位)的1个数为奇数
10011001 1
校验位补1凑成奇数
偶校验
(有效数据位+校验位)的1个数为偶数
10011001 0
校验位补1凑成奇数
0校验
不管有效数据中的内容是什么,校验位总为0
1校验
不管有效数据中的内容是什么,校验位总为1
无校验
数据帧不包含校验位
停止位(0.5、1、1.5、2)
1
寄存器
数据寄存器(USART_DR)
写入数据
自动存储在TDR
读取数据
自动提取RDR数据
TTL
USB
转换芯片: 1.CH340
RS232
RS485
转换芯片: 1.TP8485E/SP3485
AB:总线接口
RO:接受输出端
DI:发送数据收入端
RE:接收时能信号(低电平有效)
DE:发送使能信号(高电平有效)
TIM(基本定时器)
TIM6和TIM7
框图
16位可编程预分频器(1~65536)
为什么
定时功能
触发DAC同步电路
在更新事件(计数器溢出)时产生中断/DMA请求
是什么
16位自动重装载累加计数器
怎么做
步骤
1. 找到时钟源 TIM_clk的大小
SysClk /2 *2= 72MHz
系统时钟 APB1 2分频 2倍频
2. 确定延时的大小
延时300ms
3. 根据公式可得有两个变量
要注意两个变量的范围
预分频寄存器psc:控制频率
自动重装寄存器arr
控制斜率
WWDG
框图
为什么
对于独立看门狗来说,可以在产生复位前任意时刻进行喂狗。但是有隐患,有可能程序跑乱了又回到正常的地方(或者跑乱的时候正好喂了狗),这种情况下独立看门狗就检测不出来了。使用窗口看门狗,可以根据程序正常执行的时间设置看门狗的一个时间窗口,保证不会提前或者滞后喂狗,这样就可以检测出程序异常跳过程序段的情况
是什么
怎么做
比如一个程序执行需要大约10ms,那么在执行这个程序前初始化窗口看门狗,窗口设置在10ms左右,执行完函数后进行喂狗。若复位,说明没有按照正常的程序执行
RTC
框图
为什么
可以给我们提供哪些功能,解决什么社会痛点
提供日历功能(当前时间和日期)
上闹钟
是什么
基本原理是什么
本质是一个定时器。会产生秒中断,有后备区域用于掉电情况后继续累加秒数。寄存器存储的是秒数,这个秒数可以定义为unix时间戳(从1970年01月01日 0:00:00开始到现在的秒数),根据公式可以得出当前时间日期
怎么做
每个芯片不一样,以一个为例,大多数是差不多的,归根到底就是操作寄存器。对应的功能怎么实现
IIC
Inter-Integrated Circuit 内部集成电路
为什么
引入
现实有什么需求
板子间低速的器件要进行通讯
类比生活中的例子
生活中人与人之间进行交流的工具(语言/电话/短信/微信...)
和同类比较的优势
spi
布线简单
是什么
本质
是一个工具?程序?函数?协议?
一种同步串行通信协议
起始信号(1bit)+一个字节(8bit,第一个字节包括7bit的从机地址+1bit读写位)+应答信号(1bit)+一个字节(bit)+应答信号(1bit)+...+一个字节(bit)+应答信号(1bit)+停止信号
通信时序
主机写数据到从机
0:写
主机由从机读数据
1:读
起始终止信号
数据有效性
SCL高电平SDA数据有效
地址及传输方向
设备地址7或10位
响应
ACK:应答信号
低电平
NACK:非应答信号
高电平
在主机的第9个时钟,释放SDA控制权,由数据接收端控制;
框架
所属层级
站在更高一层,看它的所处位置。不是庐山真面目,只缘身在此山中
通信协议
整体内部框架是怎样的?
原理
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
不懂
怎么做
1. 需求分析
功能模块化设计: 将收集到的需求,进行归类,总结和分析,将这些需求概括为一个个单独的功能(拆分成原子),每一个功能,做成一个单独的功能模块
2. 软件架构图
分层设计: 层与层之间不能跨层调用 模块与模块各自独立,无依赖关系,各模块独立编译 模块提供统一的接口API供上层调用,模块的内外接口分明 模块的功能只能增,不能改 各个功能模块层也还可以进行继续分层,比如接口层、驱动层、硬件层
应用层
将各个业务逻辑进行整合调用,完成整个产品的功能
业务逻辑层
包括产品整体功能的各个业务流程,通过调用功能模块层的API实现
功能模块层
包括实现具体功能的函数,通过调用驱动层API实现相应功能,同时提供可调用的API给业务逻辑层
系统层
硬件驱动层
目的
在于封装掉硬件的细节,使在其上层的软件具备跨平台的移植性
包含板载硬件资源正常运行所需的所有驱动程序并提供API给功能模块调用
3. 流程图
4. 写代码
高内聚低耦合: 做到模块化思维(外漏API),最大限度的复用
mw_i2c
API1:初始化
步骤1
步骤1.1
步骤1.2
...
步骤2
步骤3
...
API2:起始信号
API3:终止信号
API4:等待ACK应答到来
产生ACK应答
不产生ACK应答
API5:发送一个字节
API6:读取一个字节
SPI
框图
为什么
连接板内的高速器件进行通讯
是什么
本质上是一种串行通信协议
CPOL(时钟极性):设置空闲时的SCK电平(NSS为高电平时) CPOL=0:SCK空闲为低电平 CPOL=1:SCK空闲为高电平 CPHA(时钟相位):数据线(MOSI和MISO)采样的时刻(即数据线上的数据稳定了,被放到了数据寄存器中) CPHA=0:数据线在SCK时钟的奇数边沿被采样 CPHA=1:数据线在SCK时钟的偶数边沿被采样
奇数边沿上升沿采样时序图
起始信号&终止信号 起始:NSS高->低 终止:NSS低->高 数据有效性 MISO和MOSI在每个时钟周期传输1bit,数据的输入和输出是同时进行的
偶数边沿下降沿采样时序图
奇数边沿下降沿采样时序图
偶数边沿上升沿采样时序图
怎么做
CAN
Controller Area Network:控制器域网
框图
为什么
线束很多的场景进行通信(汽车上的线束庞大,引入can,多个ecu挂在can总线上组成局域网进行通讯)
是什么
本质上是一种异步串行通信协议
CAN收发器将mcu过来的tx0/1数据,转换为对应协议的差分信号到CANH/CANL线上
显性电平逻辑0:CAN_High - CAN_Low > 0.9V 隐性电平逻辑1:CAN_High - CAN_Low < 0.5V 优先级特性:显性(逻辑0)的优先级比隐性(逻辑1)高 该优先级的特性,可以用来进行多主机的仲裁
数据帧: 1.帧起始(1bit):表示数据开始 2.仲裁段():表示该帧优先级的段 ID【ID越小,优先级越高】(标准:11bit 扩展:29bit):确定报文 0x26d RTR 是否是远程帧(0,数据帧 1,远程帧) IDE 标识符选择为(0,使用标准标识符 1,使用扩展标识符) SRR 代替远程请求位,为隐性位,代表了标准帧中的RTR 3.控制段(6bit):表示数据的字节数及保留位的段 DLC(4bit)数据段的字节数(有效值0~8) 4.数据段:数据的内容,一帧可发送0~8byte的数据 MSB(最高位)开始输出 5.CRC段(16bit):检查帧的传输错误的段 6.ACK段(2bit):表示确认正常接受的段 7.帧结束(7bit):表示数据帧结束的段
怎么做
硬件连接
设备的CAN1口接到CAN卡上 CAN0的usb接到电脑上
2: CANL 7:CANH
上位机模拟发送CAN协议数据
dbc文件通过工具转换为CAN协议数据
(Database CAN)CAN协议的数据库,帮助解释二进制的帧信号,以便分析报文信息
上位机配置
安装CAN-bus-ZCANPRO_Setup软件
程序解析
模式配置
正常模式
需要2块开发板进行通讯
回环模式
需要一块开发板,tx接到了rx,不能两块板子进行通讯
接收到CAN协议数据后进行解析
收到数据
TIM(通用定时器)
TIM2~TIM5
框图
为什么
定时功能
PWM输出:脉冲宽度调制
框图
频率
由自动重载寄存器的值TIMx_ARR决定
占空比
由捕获/比较寄存器的值TIMx_CCRx决定
输入捕获:对输入信号的上升沿/下降沿/双边沿进行捕获
应用
测量输入信号的脉冲宽度
原理
初始化定时器
上升沿捕获
捕获中断
定时器更新中断
定义并清零捕获状态和捕获值
t1时间到了以后,检测到第一次上升沿
清空捕获状态和捕获值
捕获状态-置位获取到上升沿标志
定时器计数清零
清楚设置,并设置下降沿捕获
t1~t2在高电平持续中间
定时器更新中断没触发一次,捕获状态+1
t2到了以后,检测到下降沿
在捕获中断中
置位捕获完整一次的标志位
获取当前捕获值
清楚设置,并设置上升沿捕获,进行下一次捕获
主函数中
判断获取了一次完整的高电平
取出溢出定时器的次数
计算高电平时间
(溢出次数*65536+当前捕获值)*定时器增加1的周期
定时器增加1的周期: arr+1=clk/(psc+1) 当clk=72MHz,psc=72 arr+1=1000000 代表装载值 1s 累计1000000 1ms累计1000 1us 累计1
清空捕获状态和捕获值
测量PWM输入信号的频率和占空比
是什么
怎么做
TIM(高级定时器)
为什么
输出指定个数的PWM
输出比较模式
互补输出带死区控制
PWM输入模式
GPIO(通用型输入输出)
General-purpose input/output
为什么
控制和采集外部器件的信息的外设,负责输入输出
是什么
很多个引脚
STM32F103ZET6
16(每组16个IO)*7(GPIOA~G共7组)
基本结构图
寄存器
端口配置寄存器
CRL
CRH
端口数据寄存器
IDR
ODR
端口置位/复位寄存器
BSRR
端口复位寄存器
BRR
端口锁定寄存器
LCKR
怎么做
功能模式
输入浮空
IO口的电平由外部电路决定
操作
上下拉电阻断开
施密特触发器打开
输出禁止
应用场景
按键检测
输入上拉
操作
上拉电阻打开
可节省一个外部上拉电阻,但是内部上拉电阻阻值大,不适合做电流驱动
施密特触发器打开
输出禁止
应用场景
不适合做电流型驱动
输入下拉
操作
下拉电阻打开
可节省一个外部下拉电阻,但是内部下拉电阻阻值大,不适合做电流驱动
施密特触发器打开
输出禁止
应用场景
不适合做电流型驱动
模拟输入
需要用到芯片内部的模拟电路单元
操作
上下拉电阻断开
施密特触发器关闭
双MOS关闭
应用场景
ADC、DAC、MCO等操作模拟信号的外设
开漏输出
结果
只能输出低电平或者高阻态,想输出高电平需要外接上拉电阻
操作
P-MOS截止,N-MOS打开
应用场景
IIC通讯(IIC_SDA)
其他需要进行电平转换的场景
推挽输出
结果
能输出高低电平
操作
P-MOS打开,N-MOS打开。但同一时间只有一个导通
应用场景
驱动能力强,可做电流驱动
推挽输出和开漏输出区别
1. 推挽驱动能力更强,因为是直接输出VDD
2. 开漏输出用于双向通信,比如IIc
开漏式复用功能
结果
引脚状态由对应的外设控制
推挽式复用功能
结果
引脚状态由对应的外设控制
IO表现形式
会罗列所有的引脚,每个引脚对应两个地方 (32章 IO复用章节)
IOMUXC_SNVS_SW_MUX_CTL_PAD_BOOT_MODE0
存放复用功能
IOMUXC_SNVS_SW_PAD_CTL_PAD_BOOT_MODE0
存放电气特性
对比STM32
管脚名字:PA0~PA15。。。
管脚名字
PAD_BOOT_MODE0
EXTI
框图
为什么
是什么
怎么做
FIFO
为什么
主要用于不同时钟域之间的数据传输。也可以用于不同数据宽度。
是什么
FIFO(first input first output)先进先出,是一种先进先出的数据缓冲器,无外部的读写地址线,只能顺序读写,指针自动加1。
怎么做
参数:
FIFO的宽度:一次读写的数据位,可设。
FIFO的深度:可以存储多少宽度的数据。
读写:满时不去写,空时不去读。
分类:同步和异步的。
核内(内核驱动)
NVIC(嵌套向量中断控制器)
框图
cortex-M
11个系统异常
内核触发
1个不可屏蔽中断(NMI)
用途
在多数情况下,NMI 会被连接到一个看门狗定时器
是电压监视功能块
输入信号线
1个滴答定时器中断
System timer
<=240个外部中断请求(IRQ)
使用几个由芯片厂商决定
为什么
可以管理和分配中断请求,确保处理器正确响应外部事件
功能
可嵌套中断支持
作用范围
覆盖了所有的外部中断和绝大多数系统异常
概念
高优先级的异常会抢占低优先级的异常
向量中断支持
概念
当开始响应一个中断后,CM3会自动定位一张向量表,并且根据中断号从表中找出ISR 的入口地址,然后跳转过去执行
向量表
在复位后,该寄存器的值为0。因此,在地址0 处必须包含一张向量表,用于初始时的 异常分配
动态优先级调整支持
软件可以在运行时期更改中断的优先级
中断延迟大大缩短
引入了好几个新特性
自动的现场保护和恢复
其他
中断可屏蔽
既可以屏蔽优先级低于某个阈值的中断/异常
设置BASEPRI 寄存器
全体封杀
设置PRIMASK 和FAULTMASK 寄存器
是什么
位于Cortex-M内的一种控制器
中断优先级分组
为什么
为了配置优先级和抢占
是什么
NVIC_Type uint8_t IP[240U]
240个8bit的寄存器,每个可屏蔽中断占用8bit,共表示240个可屏蔽中断。但是8bit只会占用高4bit,包含抢占和响应优先级,具体分配通过寄存器配置
1.抢占优先级高的可打断抢占优先级低的中断 2.抢占优先级相同,响应优先级高的中断不能打断响应优先级低的中断 3.抢占和响应相同时,中断向量表的数值越小,优先级越高 4.理论上优先级可设置2的8次方0~255,但是只用了高4bit,因此0~16(数值越小优先级越高)
STM32F103占用60个,即IP[0]~IP[59]对应0~59
怎么做
设置NVIC中断优先级分组
寄存器
SCB->AIRCR
SCB_Type 提供系统控制以及系统执行信息,包括配置,控制,上报系统异常等
STM32HAL
HAL_NVIC_SetPriorityGrouping
怎么做
输出
内部异常源
框图
个数
16-1-4=11
分类
Systick Timer
1.系统睡眠模式下也能工作
外部中断输入(相对于内核说的,包含串口、定时器中断等)
框图
个数
240
NVIC功能配置
设置NVIC中断优先级分组
寄存器
SCB->AIRCR
SCB_Type 提供系统控制以及系统执行信息,包括配置,控制,上报系统异常等
STM32 HAL
HAL_NVIC_SetPriorityGrouping
一般设置为分组2 (NVIC_PRIORITYGROUP_2)
设置中断优先级
寄存器
NVIC_Type IP
STM32 HAL
HAL_NVIC_SetPriority
中断使能
寄存器
NVIC_Type ISER
STM32 HAL
HAL_NVIC_EnableIRQ
中断除能
寄存器
NVIC_Type ICER
STM32 HAL
HAL_NVIC_DisableIRQ
当前中断是否在执行
寄存器
NVIC_Type IABR
STM32 HAL
HAL_NVIC_GetActive
挂起当前中断,执行同级或更高级别的中断
寄存器
NVIC_Type ISPR
内核
NVIC_SetPendingIRQ
将挂起的中断解挂
寄存器
NVIC_Type ICPR
内核
NVIC_ClearPendingIRQ
芯片为例
根据原有NVIC中断,从中选择性添加部分中断,并重新命名与排序
Systick Timer
1.系统睡眠模式下也能工作
为什么
用于提供时基,多为操作系统所使用
用于延时
是什么
一个24bit向下递减的计数定时器
按照时钟源频率向下递减
怎么做
4个寄存器配置
SysTick控制及状态寄存器(0xE000_E010)
CLKSOURCE:时钟源
SYSTICK_CLKSOURCE_HCLK_DIV8
0-外部时钟(STCLK)
SYSTICK_CLKSOURCE_HCLK
1-内部时钟(FLCK)
SysTick重装载数值寄存器(0xE000_E014)
SysTick当前数值寄存器(0xE000_E018)
SysTick校准数值寄存器(0xE000_E01C)
延时
不修改原有的初始化:时钟摘取法
MPU(存储器保护单元)
引入
它会使嵌入式系统变得更加健壮,更加可靠
作用
可以对特权级访问和用户级访问分别施加不同的访问限制
L1/L2高速缓存
解决问题
针对处理器和主存之间的差异,设计出更小更快的存储设备
本质
SRAM
作用
用来暂时的集结区域,存放处理器在不久的将来可能会需要的信息
框图
用途
可以使用高速缓存提高程序的性能,加快访问速度
调试
引导操作系统
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
包含板载硬件资源正常运行所需的所有驱动程序并提供API给功能模块调用
怎么做
功能1:无操作系统
具体的实现步骤
1.
对驱动进行再次分层
2.
对每个驱动进行模块化,提供API
3.
功能2:带操作系统
1.
对驱动进行再次分层
2.
对每个驱动进行模块化,提供OS需要的API
硬件
片外
RS485
外部存储器
掉电丢失
RAM
掉电不丢失
ROM
FLASH
光/磁介质存储器
6轴传感器
温湿度传感器
led灯
...
组成
核外片内
框图
RCC
为所有外设提供时钟
框图
系统时钟经过APB1 2分频为36MHz,又经过2倍频为72MHz
为什么
因为纯粹的组合电路会发生“毛刺现象”,故使用时序电路替代,达到精确控制输出的效果
处理器运行时间基准
是什么
时钟树
因为各种外设需要的频率不同
输入
HSE
外接石英/陶瓷谐振器,频率为 4MHz~16MHz。当前使用8MHz
LSE
外接 32.768kHz 石英晶体,主要作用于 RTC 的时钟源
HSI
芯片上电默认启动
由内部 RC 振荡器产生,频率为 8MHz
LSI
由内部 RC 振荡器产生,频率为 40kHz,可作为独立看门狗的时钟源
IWDG
框图
为什么
为了防止程序卡死,定时产生一个复位信号,除非在时间到来前重新赋值(比如一只狗半小时叫一次因为饿了,除非半小时内喂一次)
是什么
12bit递减计数器,减到0后会产生一个复位信号
输入
LSI时钟
40KHz内部低速时钟
输出
复位信号
怎么做
输出
AHB
APB1
APB2
SDIO
FSMC
Cortex内核
存储器
DMA的HCLK时钟
MCO:时钟信号输出
可配
SYSCLK
HSI
HSE
除 2 的 PLL 时钟
配置
PLL
三个设置
PLLXTPRE:HSE 分频器作为 PLL 输入
0:HSE不分频
1:HSE2分频
PLLSRC:PLL 输入时钟源
0:HSI的2分频
1:HSE
PLLMUL:PLL 倍频系数(2~16)
USB
SYSCLK
框图
配置
时钟源选择: CFGR 的位 SW[1:0]
HSI
8MHz/2*16=64MHz
HSE
PLLCLK
AHB分频
APB1分频
APB2分频
修改主频
1. 配置 HSE_VALUE,这里外部晶振是8MHz
2. 调用SystemInit
外部存储器配置
中断向量表地址配置
3. 在main函数中调用用户写的时钟设置函数
sys_stm32_clock_init
使用外设时开启对应的时钟
怎么做
具体外设
ICode
内核<->内部flash
总线矩阵
DCode
内核
System
内核
DMA
DMA
BDMA
AHB系统总线
APB1
UART(通用异步收发器)
USART(通用同步异步收发器)
TIM(基本定时器)
TIM6和TIM7
WWDG
RTC
I2C
Inter-Integrated Circuit 内部集成电路
SPI
CAN
Controller Area Network:控制器域网
DAC
USB
APB2
ADC
USART
SPI
TIM(通用定时器)
TIM2~TIM5
TIM(高级定时器)
GPIO(通用型输入输出)
General-purpose input/output
EXTI
其他
内部存储器
ram
flash
其他总线
FIFO
组成
相关知识点
启动方式
硬件启动方式选择
BOOT_MODE1和BOOT_MODE0 (顺序是mode1 mode0)
00
从FUSE启动
01
串行下载
通过 USB 或者UART 将代码下载到板子上的外置存储设备中,我们可以使用 OTG1这个USB 口向开发板上的 SD/EMMC、NAND 等存储设备下载代码
10
内部BOOT模式
芯片会执 行内部的 boot ROM 代码,这段 boot ROM 代码会进行硬件初始化(一部分外设),然后从 boot 设 备(就是存放代码的设备、比如 SD/EMMC、NAND)中将代码拷贝出来复制到指定的 RAM 中, 一般是 DDR
BOOT ROM初始化内容
初始化时钟
设置内核时钟为396MHz
使能MMU和cache
内部 boot ROM 为了加快执行速度会打开 MMU 和 Cache,下载镜像的时候 L1 ICache 会打 开,验证镜像的时候 L1 DCache、L2 Cache 和 MMU 都会打开。一旦镜像验证完成,boot ROM 就会关闭 L1 DCache、L2 Cache 和 MMU
中断向量偏移会被设置到 boot ROM 的起始位置,当 boot ROM 启动了用户代码以后就可 以重新设置中断向量偏移了。一般是重新设置到我们用户代码的开始地方,关于中断的内容后 面会详细讲解
从BOOT_CFG设置的外置存储中,读取image,然后做相应的处理
启动设备
根据BOOT_CFG1[7:0]、BOOT_CFG2[7:0]和 BOOT_CFG4[7:0]这 24 个配置 IO,这 24 个配置 IO 刚 好对应着 LCD 的 24 根数据线 LCD_DATA0~LCDDATA23,当启动完成以后这 24 个 IO 就可以 作为 LCD 的数据线使用
有很多不用到的引脚会默认为0,所以需要配置的只有6个引脚接到了拨码开关
11
保留
启动头文件
以SD为例,烧写到SD卡的load.imx文件在SD卡中的偏移是0x400,也就是1024
然后boot rom会从根据拨码开关找到启动设备,再从启动设备的偏移找到load.imx加载,根据头添加把程序放到指定的RAM链接地址
load.imx
头部(3Kb)
IVT(32字节)
包含了一些地址信息
镜像程序的入口点、指向 DCD 的指 针和一些用作其它用途的指针
Boot Data(12字节)
镜像要拷贝到哪个地址,拷贝的大小是多少
DCD(最大1768字节) (设备配置数据)
配置6ULL内部寄存器
DDR初始化
...其他配置
bin文件
bin文件在load.imx和SD的起始位置不一样
load.imx起始地址为3kb即0xc00 SD卡的起始地址为4kb(3kb+1kb(SD卡特有的))即0x1000
核内
框图
cm3
总线接口
贯穿整个系统的是一组电子管道,携带信息字节并负责在各个部件间传递 传递的单位:字word(字中的字节长是一个基本的系统参数,各系统不一致)
框图
存储器映射图
代码存储区总线
负责对代码存储区的访问
I‐Code
取指
是一条基于 AHB‐Lite 总线协议的 32 位总线。取指以字的长度执行,即使是对于16 位指令也如此。因此CPU 内核可以一次取出两条 16 位 Thumb 指令
D‐Code
查表
是一条基于 AHB‐Lite 总线协议的 32 位总线
系统总线
用于访问内存和外设 覆盖的区域包括SRAM,片上外设,片外RAM,片外扩展设备,以及系统级存储区的部分空间
是一条基于 AHB‐Lite 总线协议的 32 位总线。和D‐Code总线一样,所有的数据传送都是对齐的
私有外设总线
负责一部分私有外设的访问,主要就是访问调试组件
AHB
CM3内部的AHB外设
NVIC
FPB
DWT
指令追踪宏单元(ITM)
APB
32 位总线
CM3内部的APB设备
内核外的设备
嵌入式跟踪宏单元(ETM)
ETM 可以不断地发出跟踪信息,这些信息通过一个被称为“跟踪端口接口单元(TPIU)”的模块而送到内核的外部,再在芯片外面使用一个“跟踪信息分析仪”,就可以把TIPU 输出的“已执行指令信息”捕捉到,并且送给调试主机——也就是PC
ROM Table
调试接口(调试访问接口(DAP))
三种外接调试端口(DP)
SWJ‐DP
既支持传统的JTAG 调试,也支持新的串行线调试协议
SW‐DP
支持新的串行线调试协议
JTAG‐DP
ARM CoreSignt 产品家族
支持传统的JTAG 调试
TPIU
处理单元
组成
控制器OC
指令寄存器IR
作用
暂存当前正在执行的指令
R15:程序计数寄存器PC
指向当前的程序地址 指令计数器
指令译码器ID
作用
对现行指令分析,确定要完成的操作及寻址方式
运算器
ALU
执行算术(AU)和逻辑(LU)操作
备注
可能含有一个以上AU,去执行浮点运算
FPU(Floating Point Uint)
寄存器
寄存器组
是一个小的存储设备,由一些字长大小的寄存器组成,这些寄存器每个都由唯一的名字
框图
R0-R12:通用寄存器
32位通用寄存器,用于数据操作
R0‐R7(低组寄存器)
绝大多数的16bitThumb指令
32bit的thumb-2
R8‐R12(高组寄存器)
很少的16bitThumb指令
32bit的thumb-2
Banked R13: 两个堆栈指针SP
任一时刻只能使用其中的一个。 堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的
主堆栈指针(MSP)
复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包 括中断服务例程)
进程堆栈指针(PSP)
由用户的应用程序代码使用
R14:连接寄存器LR
为了减少访问内存的次数(当呼叫一个子程序时,由R14 存储返回地址)
当你在使用BL(分支并连接,Branch and Link)指令时, 就自动填充LR 的值
R15:程序计数寄存器PC
指向当前的程序地址 指令计数器
如果修改它的值,就能改变程序的执行流
特殊功能寄存器
它们只能被专用的MSR 和MRS 指令访问,而且它们也没有存储器地址
框图
程序状态字寄存器组(PSRs或xPSR)
应用程序APSR
记录ALU标志(0标志、进位标志、负数标志、溢出标志)
执行EPSR
执行状态
中断号IPSR
当前正服务的中断号
中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
用于控制异常的使能和除能
控制寄存器(CONTROL)
用于定义特权级别,还用于选择当前使用哪个堆栈指针
操作模式和特权级别
工作原理
解释(执行)存储在主存中指令的引擎(从系统通电开始,直到断电,重复执行相同的基本任务)
框图
取指(Fetch)
根据指令计数器(程序计数器PC)从存储器中取出各条指令,放在指令寄存器IR中
译码(Decode)
指令译码器ID确定了应该进行的操作
执行(Execute)
通过操作控制器OC向相应的部件发出控制信号,执行命令(运算单元+存储单元)
若操作数是地址,则需要再次去cache或者内存去数据
写回(Writeback)
更新指令计数器指向下一条指令
循环
NVIC(嵌套向量中断控制器)
Systick(滴答计时器)
MPU(存储器保护单元)
MMU(存储器保护单元)
L1/L2高速缓存
解决问题
针对处理器和主存之间的差异,设计出更小更快的存储设备
本质
SRAM
作用
用来暂时的集结区域,存放处理器在不久的将来可能会需要的信息
框图
用途
可以使用高速缓存提高程序的性能,加快访问速度
调试
跟踪
组成
相关知识点
按指令集分类
RISC(精简指令集)
特点
减少指令集
指令单周期执行
目标代码大
种类
ARM(闭源)
概念:英国ARM公司是全球领先的半导体知识产权(IP)提供商。全世界超过95%的智能手机和平板电脑都采用ARM架构 [1] 。ARM设计了大量高性价比、耗能低的RISC处理器、相关技术及软件
架构
ARMv1
ARMv2
ARMv3
ARMv4
ARM7:没有MMU(内存管理单元),只能叫做MCU(微控制器),不能运行诸如Linux、WinCE等这些现代的多用户多进程操作系统,因为运行这些系统需要MMU,才能给每个用户进程分配进程自己独立的地址空间。ucOS、ucLinux这些精简实时的RTOS不需要MMU,当然可以在ARM7上运行
ARMv5
ARM9:是嵌入式CPU(处理器),带有MMU,可以运行诸如Linux等多用户多进程的操作系统,应用场合也不同于ARM7
ARMv6
ARM11:是嵌入式CPU(处理器),带有MMU,可以运行诸如Linux等多用户多进程的操作系统,应用场合也不同于ARM7
ARMv7
ARM-Cortex
A系列:面向尖端的基于虚拟内存的操作系统和用户应用
R系列:针对实时系统
M系列:对微控制器
框图
举例
STM32
Cortex-M0
Cortex-M0+
Cortex-M3
STM32F103系列
Cortex-M4
STM32F407系列
STM32F429系列
Cortex-M7
STM32F746系列
Cortex-M4/M7
STM32H745xx
Tumb-2
ARMv8
ARMv8.5
苹果A15芯片
苹果虽然也采用了ARM架构,但CPU、GPU核全部是自研的,苹果可以自由发挥,完全不受ARM的限制
MIPS
概念:MIPS公司
PowerPC
概念:IBM公司
RISC-V(开放)
CISC(复杂指令集)
特点
减少目标代码的数量
增强指令的能力
指令复杂,指令周期长
种类
X86
intel
AMD
按体系结构分类
冯.诺伊曼结构
将程序指令存储器和数据存储器合并在一起的存储器结构
程序指令和数据的宽度相同
哈佛结构
将程序指令和数据分开存储
指令和数据可以有不同的数据宽度
采用了独立的程序总线和数据总线
概要
CPU核数
单核
单个CPU
仅有一个执行单元
多核
8核8线程
有8个执行单元
8核16线程
有16个执行单元
实际8个CPU,但是一个CPU两个通路,模拟两个核,但是性能肯定没两个核强大
机器对于多字节的存储方式
大小端
为什么?
内存地址的最小寻址单位是字节
当数据类型大于1字节时(short/int),数据在内存中应该如何存储
是什么?
大端
低地址存放整数的高位字节
大低高
小端
低地址存放整数的低位字节
小低低
怎么做?
举例:将0x12345678存放到0x00开始的地址上
常用性能评估标准
MIPS
每秒百万条指令
用来计算同一秒内系统的处理能力,即每秒执行了多少百万条指令
DMIPS
Dhrystone Million Instructions executed Per Second
每秒执行百万条指令,整数运算
Dhrystone
整数运算测试程序下的MIPS
主要用于测整数计算能力
他表示了在Dhrystone这样一种测试方法下的MIPS
举例
一个处理器达到 200 DMIPS的性能 是指:这个处理器测整数计算能力为(200*100万)条指令/秒
ARM各架构整型运算能力对比-DMIPS/MHz
A系列
R系列
M系列
MFLOPS
Million Floating-point Operations per Second
主要用于测浮点计算能力
采用Whetstone 主要用于测浮点计算能力
内核(arm【cortex-m7】/arm【cortex-a7】)
芯片厂商-特定功能芯片(stm32(ST)/imx6ull(NXP))
开发板厂商(zd,afl,yh)
程序运行的基本原理
编写好的程序经过编译器生成可执行二进制文件 二进制文件加载到内存中
1. 编写源码
编辑器
source insight
vscode
2. 编译阶段(将源码编译成对应平台二进制文件)
自动化编译工具
make工具
为什么
找到问题(痛点)
找到一个社会问题
当工程中源文件数量很多时,使用编译器命令编译源文件太麻烦。所以想到发明一种自动编译化工具
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
只有一种
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
无
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
一个可以通过Makefile文件来完成自动编译工作的程序
整体内部框架是怎样的?
内部组成,划分为原子
makefile的语法规则
核心规则
target ... :prerequisite ... command
target
目标文件
obj中间文件
执行文件
标签
伪目标
prerequisite
要生成target所需要的文件或是目标
command
make需要执行的命令
任意的shell命令
执行command的条件
prerequisites文件的日期要比targets文件的日期要新
target不存在
注意
命令要以tab键作为开头
告诉make两件事
文件的依赖关系
如何生成目标文件
第一条规则中的第一个目标是终极目标
特殊用法
目标: command
目标后面不添加依赖,执行make不会去执行command,只有在执行“make 目标”,command才会执行
可以定义一些不用编译的命令,比如删除中间文件,程序打包,备份
clean: rm -rf xxx
指定特定的makefile文件
make -f xxx
make --file xxx
如果命令太长,你可以使用反斜框(‘\’)作为换行符
条件判断
ifeq
作用
用于判断两个值是否相等
用法
ifeq ("$(origin V)", "command line")
V变量如果在命令行定义,那么两个变量就相等了,因为origin返回定义的位置
举例
ifep(A,B) xxx else xxx endif
判断AB是否相等
条件关键字
变量
quiet
为空
显示整个命令
Q
控制编译的时候是否在终端输出完整的命令
Q
规则
$(Q)$(MAKE) $(build)=tools
V=0
@make$(build)=tools
不会在终端输出命令了
V=1
make$(build)=tools
在终端会输出完整的命令
quiet
规则
$($(quiet)$(cmd))
quiet取值
quiet为空
完整命令
quiet为quiet_
短命令
quiet为silent_
不输出命令
举例
sym
cmd_sym
输出完整命令
quiet_cmd_sym
输出短命令
功能一样,区别在于make执行的时候输出的命令不同
函数
origin
告诉你变量哪来的
规则
$(origin <variable>)
variable 是变量名,origin 函数的返回值就是变量来源,因此$(origin V)就是变量 V 的来源
如果变量V是在命令行定义的,那么origin的返回值就是command line
filter
过滤函数
规则
$(filter <pattern...>,<text>)
表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式
返回值
符合pattern的字符串
$(filter 4.%,$(MAKE_VERSION))
表示在MAKE_VERSION中找到符合4.%的字符,并返回4.%
MAKE_VERSION通过make -v查看
MAKE_VERSION=4.1
返回4.1
filter-out
反过滤函数
格式
$(filter-out PATTERN…,TEXT)
返回值
空格分割的“TEXT”字串中所有不符合模式“PATTERN”的字串
举例
firstword
作用
获取首单词
规则
$(firstword <text>)
取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词
举例
$(firstword x$(MAKEFLAGS))
当MAKEFLAGS=-rR xxx xxx
返回xrR
去掉-
if
作用
在函数上下文中实现条件判断的功能
格式
$(if CONDITION,THEN-PART,[ELSE-PART])
返回值:有效表达式的计算结果
CONDITION
非空
则条件为真,就将第二个参数“ THEN_PATR”作为函数的计算表达式
为空
将第三个参数“ ELSE-PART”作为函数的表达式
foreach
作用
循环(类似for)
格式
$(foreach <var>,<list>,<text>)
返回值
text每个字符串组成整个字符串(用空格隔开)
举例
dep_files := $(foreach f, $(objs), .$(f).d)
objs := main.o sub.o
返回值
.mian.o.d .sub.o.d
wildcard
作用
展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表
格式
$(wildcard PATTERN...)
patsubst
作用
替换文件后缀
格式
$(patsubst <pattern>,<replacement>,<text> )
举例
$(patsubst %.c,%.o, a.c b.c)
“a.o b.o”
关键字
export
export VARIABLE ……
导出变量给子make
unexport
unexport VARIABLE……
不导出变量给子make
特殊
两个特殊变量除非使用“unexport”声明, 否则的话在整个make的执行过程中,它们的值始终自动的传递给子make
SHELL
MAKEfLAGS
.PHONY
作用
防止在Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突
伪目标
不是一个文件,只是一个标签
如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,可以使用伪目标
all : prog1 prog2 prog3 .PHONY : all
用法
.PHONY:clean
表示clean是一个伪目标,和目录中实际的同名文件无关,告诉make执行clean的指令
obj-y
指定根目录下要编进程序去的文件、子目录
include
作用
包含别的Makefile
包含的文件会展开放到当前位置
用法
include <filename>
filename
若未制定相对路径和绝对路径
会在当前目录下寻找
有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找
如果目录<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找
include foo.make *.mk $(bar)
备注
如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。
-include <filename>
vpath
作用
可以指定不同的文件在不同的搜索目录
用法
vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>
vpath %.h ../headers
要求make在“../headers”目录下搜索所有以“.h”结尾的文件
vpath <pattern>
清除符合模式<pattern>的文件的搜索目录
vpath
清除所有已被设置好了的文件搜索目录
备注
我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make会按照vpath语句的先后顺序来执行搜索
vpath %.c foo:bar vpath % blish
而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录
变量
分类
特殊变量
MAKECMDGOALS
记录了命令行参数指定的终极目标列表
举例
make are you ok
MAKECMDGOALS= are you ok
VPATH
作用
make若在当前目录下未找到文件,会到指定的目录找寻文件
用法
VPATH = src:../headers
代表src和../headers两个路径
用:隔开
系统级的变量
MAKEFLAGS
作用
用来传递MAKE选项的
如果在命令行中make后面有选项,则在此处,会在原来make选项后加上 -rR --include-dir=$(CURDIR)
举例
命令行中执行的make --no-print-directory
则此处的MAKEFLAGS值为 --no-print-directory -rR --include-dir=当前目录
只要定义了就会传递到底层Makefile中,除非unexport
环境变量
MAKEFILES
如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理
备注
不介意使用
自动化变量
$@
目标文件
$^
依赖集合
$<
依赖集合第一个文件
内置的标准变量
CURDIR
表示Makefile 所在的当前目录目录
备注
如果在 make 中使用 --directory 或 -C 参数指定目录,那么 make 就会切换到所指定的目录去寻找 Makefile 文件
MAKE_VERSION
make 命令的版本号,也就是执行 make -v 时看到的版本号
未分类变量
GREP_OPTIONS
表示grep搜索的时候shell下应该有搜索属性或者选项
高亮
export GREP_OPTIONS=’–color=XXX’
CFLAGS
C编译器选项
LDFLAGS
告诉链接器从哪里寻找库文件
LDFLAGS=-L/usr/lib
LIBS
要链接的库文件
LIBS=-lpthread
引用变量的方式
${}和${}无区别
使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile
赋值
=
变量的值是整个makefile中最后被指定的值
VIR_A = A VIR_B = $(VIR_A) B VIR_A = AA B = AAB
:=
直接赋值,赋予当前位置的值
VIR_A := A VIR_B := $(VIR_A) B VIR_A := AA B = AB
?=
如果该变量没有被赋值,则赋予等号后的值
VIR ?= new_value VIR = new_value
VIR := old_value VIR ?= new_value VIR = old_value
+=
表示将等号后面的值添加到前面的变量上
静态模式
作用
更加容易地定义多目标的规则
用法
<targets ...>: <target-pattern>: <prereq-patterns ...> <commands>
targets
定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern
指明了targets的模式,也就是的目标集模式
prereq-parrterns
目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义
举例
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”
自动生成依赖性
-M
作用
自动找寻源文件中包含的头文件,并生成一个依赖关系
用法
cc -M main.c
备注
如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来
通配符
*
?
[...]
让make自动推导
作用
它可以自动推导文件以及文件依赖关系后面的命令
只要make看到一个.o文件他就会自动把对应的.c文件加在依赖关系中
用法
main.o : defs.h
相当于 main.o : main.c defs.h cc -c main.c
是一种隐晦规则
清空目标文件的规则
写法
.PHONY:clean clean: -rm xxx
rm前加了-,表示也许某些文件出现问题,但不要管,继续做后面的事
编译选项
CC
C编译器的名称,默认为cc
CXX
C++编译程序。默认是"g++"
-I
指定头文件路径
sed
set -e
替换
sed -e 's/wang/w/g;s/xu/x/g' user.txt,
's/wang/w/g;s/xu/x/g'的意思,s代表search,g是尽可能多的匹配,有多少替换多少,因而意思是将user.text文本中的wang 替换成w,xu 替换成x
tr '[:upper:]' '[:lower:]
将所有的大写字母替换为小写字母
include和snclude
读取指定文件的内容
sinclude 读取的文件如果不存在的话不会报错
word
统计单词的个数
$(words <text>)
LC_X
LC表示locale
是根据计算机用户所使用的语言,所在国或地区及当地文化习俗所定义的一个runtime语言环境。
C是系统默认的locale
LC_COLLATE=C
一组变量
LC_COLLATE
表示比较规则和习惯
LC_NUMERIC
表示数字
LC_ALL
若设置了该值,则会覆盖前面所有的LC_*设置,除了LC_LANG
make 在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
make工具的执行过程
执行make命令,会寻找当前目录下的Makefile或makefile文件
make的常用参数
-C DIRECTORY, --directory=DIRECTORY
在执行前先切换到 DIRECTORY 目录
-f FILE, --file=FILE, --makefile=FILE
从 FILE 中读入 makefile
-I DIRECTORY, --include-dir=DIRECTORY
在 DIRECTORY 中搜索被包含的 makefile
make的特性
1. 支持递归调用
比如在Makefile文件中使用make可以执行其他的Makefile,一般都是子目录的Makefile
2. make若没指定目标,就会使用默认目标
找到Makefile文件中第一个目标文件aaa(target),并把这个文件作为最终的目标文件
若aaa文件不存在或aaa后面的依赖文件修改时间比aaa新,那么就会执行command生成aaa
若aaa的依赖文件存在,会在当前文件中找目标为.o文件的依赖性,若找到,则根据command生成.o文件
c和h文件是存在的,make会生成.o文件,然后再用.o文件生成终极目标aaa
怎么做
功能1:
具体的实现步骤
1.
2.
3.
功能2:
编写通用的Makefile
支持多个子目录
Makefile
#1. 修改CROSS_COMPILE(工具链前缀) #2. 修改CFLAGS(头文件路径) #3. 修改LDFLAGS(库的路径) #4. 修改TARGET(最后目标) #5. 修改obj-y(整个编译需要的源文件和目录) # 指定工具链的前缀(arm-linux-gnueabihf-)===用户改 # CROSS_COMPILE = arm-linux-gnueabihf- CROSS_COMPILE = arm-oe-linux-gnueabi- AS = $(CROSS_COMPILE)as # 把汇编文件生成目标文件 LD = $(CROSS_COMPILE)ld # 链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者一个可执行文件 CC = $(CROSS_COMPILE)gcc # 编译器,对 C 源文件进行编译处理,生成汇编文件 # CPP = $(CC) -E CPP = $(CROSS_COMPILE)g++ AR = $(CROSS_COMPILE)ar # 打包器,用于库操作,可以通过该工具从一个库中删除或则增加目标代码模块 NM = $(CROSS_COMPILE)nm # 查看静态库文件中的符号表 STRIP = $(CROSS_COMPILE)strip # 以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码 OBJCOPY = $(CROSS_COMPILE)objcopy # 复制一个目标文件的内容到另一个文件中,可用于不同源文件之间的格式转换 OBJDUMP = $(CROSS_COMPILE)objdump # 查看静态库或则动态库的签名方法 # 导出变量给到子Makefile export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP # 定义C编译器的选项===用户改 CFLAGS := -march=armv7-a -marm -mfpu=neon -mfloat-abi=hard -O2 -Wa,--noexecstack -fexpensive-optimizations \ -frename-registers -fomit-frame-pointer -ftree-vectorize -finline-functions -finline-limit=64 \ -Wno-error=maybe-uninitialized -Wno-error=unused-result -include quectel-features-config.h -std=gnu99 # 追加头文件搜索目录===用户改 CFLAGS += -I$(shell pwd)/csdk/inc \ -I$(shell pwd)/csdk/interface \ -I$(shell pwd)/log \ -I$(shell pwd)/../../include \ -I$(SDKTARGETSYSROOT)/usr/include \ -I$(SDKTARGETSYSROOT)/usr/include \ -I$(SDKTARGETSYSROOT)/usr/include/data \ -I$(SDKTARGETSYSROOT)/usr/include/dsutils \ -I$(SDKTARGETSYSROOT)/usr/include/qmi \ -I$(SDKTARGETSYSROOT)/usr/include/qmi-framework \ -I$(SDKTARGETSYSROOT)/usr/include/ql-common-tools # 定义库路径===用户改 LDFLAGS := -L./ \ -L$(shell pwd)/../../lib \ -lrt ${QL_EXP_LDLIBS} \ -lql_sys_log \ -L$(SDKTARGETSYSROOT)/usr/lib \ -lpthread \ -lm \ -lstdc++ # 定义库文件===用户改 USR_LIB := $(shell pwd)/../../lib/libql_peripheral.a \ $(shell pwd)/../../lib/libql_common_api.a \ $(SDKTARGETSYSROOT)/usr/lib/libdsi_netctrl.so \ $(SDKTARGETSYSROOT)/usr/lib/libdsutils.so \ $(SDKTARGETSYSROOT)/usr/lib/libqmiservices.so \ $(SDKTARGETSYSROOT)/usr/lib/libqmi_cci.so \ $(SDKTARGETSYSROOT)/usr/lib/libqmi_common_so.so \ $(SDKTARGETSYSROOT)/usr/lib/libqmi.so \ $(SDKTARGETSYSROOT)/usr/lib/libmcm.so \ $(SDKTARGETSYSROOT)/usr/lib/libql_mgmt_client.so \ /usrdata/sixents/lib/gao/libsixents_core_sdk.so \ /usrdata/sixents/lib/gao/libsixents_rtklib.so # ./lib/libsixents_ins.so export CFLAGS LDFLAGS TOPDIR := $(shell pwd) export TOPDIR # 最终目标===用户改 TARGET := sixents_app_test # 本次整个编译需要源文件和目录===用户改 # 这里的“obj-y”是自己定义的一个格式,和“STRIP”这些一样,*但是 一般内核会搜集 ”obj-”的变量* obj-y += sixents_initial.o obj-y += csdk/src/ obj-y += log/ # 第一个目标 all : start_recursive_build $(TARGET) @echo $(TARGET) has been built! # 处理第一个依赖,**转到 Makefile.build 执行** start_recursive_build: make -C ./ -f $(TOPDIR)/Makefile.build $(TARGET) : built-in.o $(CPP) $(CFLAGS) $(LDFLAGS) -o $(TARGET) built-in.o $(USR_LIB) clean: rm -f $(shell find -name "*.o") rm -f $(TARGET) distclean: rm -f $(shell find -name "*.o") rm -f $(shell find -name "*.d") rm -f $(TARGET)
用户配置
Makefile_build
# 伪目标 PHONY := __build __build: # 清空需要的变量 obj-y := subdir-y := EXTRA_CFLAGS := # 包含同级目录Makefile include Makefile # 获取当前 Makefile 需要编译的子目录的目录名 # obj-y := main.o sub.o a/ b/ # $(filter %/, $(obj-y)) : a/ b/ # __subdir-y : a b # subdir-y : a b __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) # 把子目录的目标定为以下注释 # a/built-in.o b/built-in.o subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o) # 获取当前目录需要编进程序的文件名作为,并写为目标 cur_objs : main.o sub.o cur_objs := $(filter-out %/, $(obj-y)) # 使修改头文件 .h 后,重新make后可以重新编译(重要)dep_files : .main.o.d .sub.o.d(存放依赖文件的头文件) dep_files := $(foreach f,$(cur_objs),.$(f).d) # 列出存在的文件 dep_files := $(wildcard $(dep_files)) # 包含头文件 ifneq ($(dep_files),) include $(dep_files) endif # PHONY : __build a b PHONY += $(subdir-y) # 第一个 目标 __build : $(subdir-y) built-in.o # 优先编译 子目录的内容 $(subdir-y): make -C $@ -f $(TOPDIR)/Makefile.build # 链接成 目标 built-in.o : $(cur_objs) $(subdir_objs) $(LD) -r -o $@ $^ dep_file = .$@.d # 生成 cur_objs 目标 %.o : %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $< .PHONY : $(PHONY)
把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
sub
Makefile
#===用户改 EXTRA_CFLAGS := CFLAGS_sixents_sdk_app.o := obj-y += sixents_sdk_app.o
指定哪些文件或者子目录需要编译
功能3:
cmake工具
为什么
找到问题(痛点)
找到一个社会问题
工程较大时,编写Makefile比较复杂,故引入语法简单的cmake来生成Makefile
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
整体内部框架是怎样的?
内部组成,划分为原子
CMakeLists.txt的语法
指定cmake的最小版本
cmake_minimum_required
用法
cmake_minimum_required (VERSION 3.12.0)
备注
cmake编译版本大于这里指定的版本
放在整个文件最开头
cmake指定编译器的方式
在CMakeLists.txt文件中指定
这两条命令应该放在文件的开始位置(cmake_minimum_required命令之下,其他命令之上),否则可能无效
set(CMAKE_C_COMPILER "/xxx/xxx/gcc")
set(CMAKE_CXX_COMPILER "/xxx/xxx/g++")
在编译时指定
cmake .. -DCMAKE_CXX_COMPILER=/usr/local/bin/gcc
指定/usr/local/bin/gcc路径下的gcc来编译
设置工程名
project
用法
PROJECT(projectname [CXX] [C] [JAVA])
[CXX] [C] [JAVA] 表示项目支持的语言,一般忽略这部分,默认情况下支持所有语言
project(cheche)
其他
隐式定义
<projectname>_SOURCE_DIR
CMakeLists.txt文件所在目录
<projectname>_BINARY_DIR
执行cmake的目录
举例
假若projectname为cheche,则cheche_BINARY_DIR
备注
cheche_BINARY_DIR和CMAKE_BINARY_DIR值一样
projectname修改后,所有使用了这两个变量的CMakeLists.txt脚本的相应位置都需要更改
启用汇编语言支持
enable_language()
用法
enable_language(<lang> [OPTIONAL] )
CXX
C
Fortran
Asm
其他
隐式定义
编译器的编译选项
针对所有类型编译器的
add_compile_options
用法
add_compile_options(-std=c++11 -Wall -Werror)
作用
添加编译参数
备注
这里添加的选项会在所有的编译器中运用
此命令添加的参数是递归的
在多层目录结构中,根文件下设置选项后,在所有的子目录编译时都会运用
针对指定类型的编译器
通过设置CMAKE_CXX_FLAGS来配置
作用
针对C++编译器的参数选项
默认保存环境变量CXX_FLAGS的内容,但是如果直接修改这个参数值,那么系统会忽略原CXX_FLAGS的内容
用法
set(CMAKE_CXX_FLAGS -std=c++11 -Wall -Werror )
备注
这个变量只在当前文件有效,如果项目中有多个模块,多个编译文件,那么需在每一个CMakeLists.txt文件中都添加对应的命令和参数
编译器的编译定义
target_compile_definitions
作用
为指定target增加编译定义
举例
target_compile_definitions(target PUBLIC FOO)
add_compile_definitions
用法
add_compile_definitions(<definition> ...)
作用
预编译命令会添加到COMPILE_DEFINITIONS目录属性中
举例
add_compile_definitions(FOO)
-std=gnu99
-g
add_definitions
举例
add_definitions(-DFOO)
概要
为当前以下层路径的所有源文件和target增加编译定义
添加头文件路径
include_directories
用法
include_directories(xxx)
include_directories(${PROJECT_SOURCE_DIR}/include)
源文件
扫描指定文件夹下的所有源文件,并将源文件以列表的形式存放在变量中
aux_source_directory
用法
AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES)
向当前项目添加存放源文件的子目录
add_subdirectory
用法
ADD_SUBDIRECTORY(directory [BINARY_DIR] [EXCLUDE_FROM_ALL])
[BINARY_DIR] 指定生成的二进制文件的存放位置
[EXCLUDE_FROM_ALL] 将这个目录从编译中排除
库
生成库
add_library
格式
ADD_LIBRARY(<name> [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 [source2...])
name
指定生成的库文件名字,会自动加上前缀和后缀
[STATIC | SHARED | MODULE]
STATIC
生成静态库
SHARED
生成动态链接库
MODULE
这个生成的库注重于动态加载,而不是编译链接(官网说的),但是我实测和 SHARED 没什么区别
source
源码列表
举例
add_library(${PROJECT_NAME} STATIC sub.c sub.h)
备注
[EXCLUDE_FROM_ALL] 这个库不会被默认构建,除非有其他的组件依赖或者手工构建
生成最终共享库的文件名为,libname.so。会自动在库名前加lib
指定库路径的方式
link_directories
用法
include_directories(xxx)
link_directories(${PROJECT_SOURCE_DIR}/lib)
g++命令的-L选项的作用
环境变量中增加LD_LIBRARY_PATH的路径
生成可执行文件
add_executable
用法
ADD_EXECUTABLE(name sourceFile)
sourceFile
单个文件
ADD_EXECUTABLE(${PROJECT_NAME} main.cpp)
多个文件
ADD_EXECUTABLE(${PROJECT_NAME} main.cpp pow.cpp)
源文件变量
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILE} ${LIB_FILE})
add_executable(executable main.c xxx.c)
作用
用sourceFile源文件生成一个名为name的可执行文件
sourceFile可以是单个文件,也可以是定义的文件列表变量
(可执行文件or库)链接库
不需要全路径
target_link_libraries
用在add_executable之后
用法
TARGET_LINK_LIBRARIES(可执行文件名 库名(不带-l)) #大小写随便
三种
target_link_libraries(executable cc)
默认链接动态库
target_link_libraries(executable libcc.so)
链接动态库
target_link_libraries(executable libcc.a)
链接静态库
参数
-Wl,-Map=${targt_map_file}
生成map文件
target_link_libraries(${PROJECT_NAME} m -Wl,-Map=${targt_map_file})
m
数学库
备注
有时需要link_directories指定库路径
依赖库的顺序
依赖的库 被依赖的库
target_link_libraries(executable a b)
a依赖b
link_directories
添加需要链接的库文件目录(全路径)
link_libraries
用在add_executable之前
用法
link_libraries(xxx)
link_libraries("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
其他
变量
获取变量值
${变量名}
设置变量
set
作用
用来定义变量的值,相当于编程语言中的赋值操作(VAR=VALUE)
用法
SET(VAR [VALUE])
定义SRC_LIST变量为三个cpp文件的列表
SET(SRC_LIST main.cpp sqrt.cpp pow.cpp)
预定义变量
当前文件路径
${CMAKE_CURRENT_SOURCE_DIR}
安装路径
${CMAKE_INSTALL_PREFIX}
默认值为 /usr/local
项目编译路径
PROJECT_BINARY_DIR/CMAKE_BINARY_DIR/_BINARY_DIR
运行cmake命令的目录,即工程编译发生的路径
项目路径(CMakeLists.txt)
PROJECT_SOURCE_DIR/CMAKE_SOURCE_DIR/_SOURCE_DIR
为包含PROJECT()命令的最近一个CMakeLists.txt文件所在的文件夹路径
概要
CMAKE也隐式地定义了另外两个变量
项目名
${PROJECT_NAME}
编译选项
生成对应的版本
CMAKE_BUILD_TYPE
用法
SET(CMAKE_BUILD_TYPE "Debug")
debug
cmake会使用变量CMAKE_CXX_FLAGS_DEBUG和CMAKE_C_FLAGS_DEBUG中的字符串作为编译选项生成Makefile
SET(CMAKE_BUILD_TYPE "Release")
release
cmake会使用变量CMAKE_CXX_FLAGS_RELEASE和CMAKE_C_FLAGS_RELEASE选项生成Makefile
编译链接标志
指定编译标志
CMAKE_FLAGS变量
CMAKE_C_FLAGS
C语言编译器选项
对应于环境变量CFLAGS
CMAKE_CXX_FLAGS
C++语言编译器选项
对应于环境变量CXXFLAGS
指定特定构建类型的编译标志,这些编译标志将被加入到 CMAKE_C_FLAGS 或 CMAKE_CXX_FLAGS 中去
CMAKE_C_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
CMAKE_CXX_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
链接标志相关变量
CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
CMAKE_MODULE_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
CMAKE_SHARED_LINKER_FLAGS
链接动态库指定的参数
CMAKE_SHARED_LINKER_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
CMAKE_STATIC_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS_[DEBUG|RELEASE|MINSIZEREL|RELWITHDEBINFO]
系统名字
CMAKE_SYSTEM_NAME
Linux
FreeBSD
Windows
指定二进制文件保存路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
概要
指定最终二进制文件(可执行文件及库文件,不包含中间文件)的位置
打印信息
message
用法
MESSAGE([SEND_ERROR|STATUS|FATAL_ERROR] "Content")
SEND_ERROR
报错误信息,不终止CMake构建过程
STATUS
普通输出信息
FATAL_ERROR
报错误信息,终止CMake构建过程
作用
相当于编程中的打印指令(python的print、C++的std::cout)
复制文件
将SOURCE目录文件or文件夹复制到BINARY目录
file(COPY ${PROJECT_SOURCE_DIR}/resource DESTINATION ${PROJECT_BINARY_DIR})
install
用法
INSTALL(FILES|PROGRAM|DIRECTORY|TARGETS xxx DESTINATION xxx)
DESTINATION后可以写绝对路径,也可以写相对路径。相对路径是${CMAKE_INSTALL_PREFIX}/<相对路径>
安装目录时,指定路径为directoryName是安装该文件夹到目标目录,指定路径为directoryName/是安装该文件夹中的所有文件到目标目录中
作用
可以指定是安装文件、非目标文件的可执行程序、目录、可执行文件
备注
CMake的指令可以大写、小写或大小混写,但是一般约定俗称都用大写
CMake指令的参数用括号括起来,参数之间用空格隔开,参数是大小写相关的,不要乱写
CMake参数也可以用分号“;”隔开,但不是约定俗成的用法,不建议使用
文件名带空格
处理办法是给文件名加引号
SET(HELLO_SRC_FILE "hello world.cpp")
将hello world.cpp文件赋值给HELLO_SRC_FILE变量
清除工程
make clean
清除Makefile生成的中间文件
rm -rf *
清除CMake生成的中间文件
外部构建
创建一个build文件夹存放中间生成文件
使用cmake ..
路径要指定到存放CMakeLists.txt的地方
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
1. 编写CMakeLists.txt文件 2. 用CMake命令将CMakeLists.txt文件转化为Make所需要的Makefile文件 3. 用make命令编译源码生成可执行程序或共享库
怎么做
功能1:
具体的实现步骤
1. 编写通用的CMakeLists.txt
CMakeLists.txt
# 指定cmake编译的最低版本 cmake_minimum_required(VERSION 3.12.0) # 指定工具链 # set(CMAKE_C_COMPILER "arm-oe-linux-gnueabi-gcc") # set(CMAKE_CXX_COMPILER "arm-oe-linux-gnueabi-g++") set(CMAKE_C_COMPILER "gcc") set(CMAKE_CXX_COMPILER "g++") # 设置工程名 project(sixents_core_sdk_app) #使能汇编 # enable_language(ASM) # 设置库的名字 set(LIB_NAME "sixents_core_sdk") #让gdb能够调试程序 SET(CMAKE_BUILD_TYPE "Debug") SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") # 设置编译器的编译选项 # add_compile_options(-march=armv7-a -marm -mfpu=neon -mfloat-abi=hard -O2 -Wa,--noexecstack -fexpensive-optimizations -frename-registers -fomit-frame-pointer -ftree-vectorize -finline-functions -finline-limit=64 -Wno-error=maybe-uninitialized -Wno-error=unused-result -include quectel-features-config.h -std=gnu99) add_compile_options(-fPIC) # 增加编译定义 # add_definitions() # 包含头文件 include_directories(${CMAKE_SOURCE_DIR}/SourceCode/csdk) include_directories(${CMAKE_SOURCE_DIR}/SourceCode/inc) include_directories(${CMAKE_SOURCE_DIR}/SourceCode/platform/Linux64) # 包含库路径(方式2) # link_directories(/usrdata/sixents/lib/) # 指定源文件到变量 aux_source_directory(${CMAKE_SOURCE_DIR}/SourceCode/csdk PROJECT_SRCS1) aux_source_directory(${CMAKE_SOURCE_DIR}/SourceCode/platform/Linux64 PROJECT_SRCS2) set(PROJECT_ALL_SRCS ${PROJECT_SRCS1} ${PROJECT_SRCS2}) # 链接库文件(方式1) # link_libraries("/usrdata/sixents/lib/libsixents_core_sdk.so") # 生成库文件 #1. static_library # add_library(${LIB_NAME} STATIC ${PROJECT_ALL_SRCS}) #2. share_library add_library(${LIB_NAME} SHARED ${PROJECT_ALL_SRCS}) # 生成可执行文件 # add_executable(${PROJECT_NAME} ${PROJECT_ALL_SRCS}) # 链接库文件(方式2 增加库的名字) target_link_libraries(${LIB_NAME} m)
build.sh
rm -rf build mkdir build cd build # cmake .. -G "Unix Makefiles" -DANDROID_ABI=arm64-v8a -DANDROID_NATIVE_API_LEVEL=19 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE:PATH="/home/sixents/cheq/toolchain/android-ndk-r24-linux/android-ndk-r24/build/cmake/android.toolchain.cmake" cmake .. -DCMAKE_BUILD_TYPE=Release make
功能2:
功能3:
编译工具链
为什么
找到问题(痛点)
找到一个社会问题
我们要去控制硬件,必须使用机器语言。但是作为人类,机器语言不适合我们直接编程。所以发明方便人可编程的语言(高级语言)。高级语言到机器语言需要一个翻译官,就引入了编译器
和同类比较的优势
找到问题后,有很多解决方案。那么为什么选择你?你相比较同类有什么优势(差异化)
根据处理器(指令集)选择
工具链种类
都是运行在X86架构上的
一般编译器
gcc
编译X86架构的代码,可执行文件是在X86架构上运行的
交叉编译器
为什么
现在没有比较成熟的ARM架构的编译器,所以需要在另外的架构上的编译器去编译ARM架构的代码,生成可以在ARM架构上运行的可执行文件
是什么
程序
怎么做
分类
arm-linux-gnueabihf-gcc(交叉编译器)
下载网址
https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabihf/
安装和配置
安装配置参考网址
https://blog.csdn.net/cq17805982133/article/details/124251555?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165319967916781818733784%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165319967916781818733784&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-124251555-null-null.nonecase&utm_term=linux&spm=1018.2226.3001.4450
把下载好的文件上传到Ubuntu
拷贝,解压
//1.若没有此路径,则创建 sudo mkdir /usr/local/arm //2.拷贝工具链到/usr/local/arm路径下 sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/ //3.解压 sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
这里的路径无所谓,主要是把这个路径添加到环境变量就行
配置环境变量,重启Ubuntu使能环境变量
//打开配置文件 sudo vi /etc/profile //最后面添加如下信息 export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
//重启Ubuntu reboot
验证版本
//验证版本 arm-linux-gnueabihf-gcc -v
aarch64-linux-gnu-gcc(交叉编译器)
下载网址
https://releases.linaro.org/components/toolchain/binaries/6.3-2017.05/aarch64-linux-gnu/
类比生活中的例子
有些知识点比较专业/抽象,所以可以结合现实中的场景类比理解
一个中国人要和一个英国人交流,但是不会英文,这时候就需要一个翻译官当中间人把中文翻译成英文,让英国人知道我要说什么
是什么
从高层次看,属于哪一大类
站在更高一层,看它的所处位置。不识庐山真面目,只缘身在此山中
是一个可以把高级语言翻译成机器语言的程序
整体内部框架是怎样的?
内部组成,划分为原子
编译过程相关工具
预处理cpp
预处理指令
一类#开头的指令
文件包含
将其他头文件包含到当前源文件中,以便使用其中定义的函数和变量
#include
https://blog.csdn.net/cq17805982133/article/details/121861024?spm=1001.2014.3001.5501
应用场景
<>
#include <stdio.h>
对应的编译器会到系统路径下查找头文件
gcc -E - -v < /dev/null
arm-linux-gnueabihf-gcc -E - -v < /dev/null
""
#include "stdio.h"
1. 编译器首先在当前目录下查找头文件,如果没有找到 2. 再到系统路径下查找
举例
#include <stdio.h>
宏定义
将一段代码片段或值,用一个标识符代替的方式
#define
应用场景
场景1
方便地复用一些重复使用的代码片段
#define MAX_SIZE 100
场景2
仅定义一个宏,用于条件编译判断
#define MAX_SIZE
举例
#define MAX_SUM 6
条件编译
根据给定的条件选择性地编译代码
#ifdef
若宏定义了,则执行
应用场景
可以为不同的平台、不同的需求编写适配的代码
#ifdef WIN32 // Windows平台的代码 #elif defined LINUX // Linux平台的代码 #else // 其他平台的代码 #endif
#ifndef
若宏未定义则执行
应用场景
用于头文件中,防止头文件重复包含
#ifndef _SIXENTS_LOG_H #define _SIXENTS_LOG_H ... #endif
#if
如果常量表达式的值为真,则执行
应用场景
判断宏定义是否为真
#define WINDOWS 1 #if (WINDOWS == 1) /* some windows code */ #elif (WINDOWS == 2) /* some linux code */ #else /* some other code */ #endif
其他
在后面取消之前定义的宏定义
#undef
用于条件编译
#error
自定义一条编译错误的信息
#warning
自定义一条编译警告信息
执行步骤
1. 展开include包含的头文件
2. 替换宏定义内容
3. 处理条件编译
4. 去注释
操作指令
方式1:gcc -E cc.c -o cc.i
-E表示让编译器在预处理后就退出
方式2:cpp cc.c -o cc.i
编译cc1
执行步骤
1. 将文本文件hello.i翻译成hello.s(汇编程序)
汇编的每一条语句都是以一种标准的文本格式确切的描述了一条低级机器语言指令
文本格式
人能看懂的
机器语言指令
处理需要执行的指令
一条汇编语言对应一条机器指令
将C程序的指令和数据分开,分别映射到不同的程序段中
2. 优化
等级 -O
1. 中间代码的优化
.此优化不依赖于特定的计算机
删除通用表达式,循环优化(代码外推,强度降低,变换循环控制条件,已知数量的合并等),复制传播以及删除无用的赋值,以及还有很多
2. 目标代码的生成
依赖计算机
与机器的硬件结构密切相关. 最重要的是考虑如何充分利用存储在机器硬件寄存器中的相关变量的值,以减少对内存的访问次数. 另外,如何根据机器硬件执行指令(如流水线,RISC,CISC,VLIW等)的特性对指令进行一些调整,以使目标代码更短,执行效率相对较高
-std=c99 或 -std=gun99
gcc 默认使用的是 C89 的标准,而 C89 的标准不支持在 for 中定义循环变量,而在 for 循环中需要定义循环变量的话,需要在 C99 标准中才支持,因此需要增加 -std=c99 或 -std=gun99 参数才能编译通过
-O
优点:程序运行更快 缺点:编译时间更长了/堆代码调试更困难
-O0 表示没有优化
-O1 为缺省值,提供基础级别的优化
-O2 提供更加高级的代码优化,会占用更长的编译时间
是性能优化和使用方便之间的妥协
-O3 提供最高级的代码优化
-Wall
编译后显示所有警告
操作指令
gcc -S cc.c -o cc.s
-S指让编译器在编译之后停止,不执行后续过程,生成汇编代码cc.s
汇编as
执行步骤
1. 将hello.s汇编语言代码翻译成目标机器语言指令
为每个源文件生成对应的目标文件
2. 打包这些指令成为一种叫做可重定位目标程序的格式,并将结果保存到目标文件hello.o(二进制文件)
操作指令
方式1:as cc.s -o cc.o
方式2:gcc -c cc.s -o cc.o
知识点
汇编代码可见的处理器状态
程序计数器
表示将要执行的下一条指令在存储器中的地址
整数寄存器文件
个数
包含8个
存储的位数
分别存储32位的值
存储的内容
地址
对应于C的指针
整数数据
有的寄存器用来记录某些重要的程序状态,而其他的寄存器用来保存临时数据,例如过程的局部变量
条件码寄存器
作用
实现控制流中的条件变化,比如if或while
存放内容
保存最近执行的算术指令的状态信息
浮点寄存器文件
个数
8个位置
存放内容
浮点数据
程序存储器
存放内容
程序的目标代码
操作系统需要的信息
用来管理过程调用和返回的运行时栈,以及用户分配的存储器块
用虚拟地址寻址,然后操作系统负责管理虚拟地址,将虚拟地址转换成物理地址
机器指令
作用
只执行非常基本的操作
特点
常用的字节少,不常用的多
指令格式:可以将字节唯一的解码成机器指令
举例
将两个存放在寄存器中的数字相加
在存储器和寄存器之间传递数据
条件分支转移到新的指令地址
可重定位文件
将这些不同的信息按照不同的属性,以“节(section)”也叫“段(segment)”的形式进行存储(ARM)
.bss(未初始化的数据段)
会被初始化为0,由于data段存0值没有必要,因为程序运行data段占用内存空间,bss段只预留一个位置,节省内存空间
存放未初始化的全局变量/局部静态变量
可读/可写
.data(数据段)
存放各种全局变量或静态数据
可读/可写
.rdata/.rodata(只读数据段)
存放字符串常量/只读变量(const修饰)
只读
.text(代码段)
存放程序的机器指令
只读
.symtab(符号表段)
存放符号表
特点
每个目标文件都有一个相应的符号表,记录目标文件中用到的所有符号,每个符号都有相应的值,叫符号值。(符号值可以是符号对应的数据在段中的偏移量,可以是该符号的对齐属性)
在链接中,我们将函数和变量统称为符号(Symbol),函数或变量名就是符号名(Symbol Name)
其他一些特殊的段
存放调试信息的段
特点
每个section和指令的地址都是相对与0的偏移,因为具体存放在内存哪里,编译阶段并未知道,链接时才确定
程序运行时的堆和栈在运行时决定,和目标文件无关
目标文件的格式和平台相关,Linux的.o是ELF格式,Windows的.obj是PE/COFF格式
一般低地址放代码,高地址放数据
链接ld
执行步骤
1. 将各个独立的目标地址空间编排到一个统一的地址空间中,生成一个完整的与实际物理内存相符合的内存映像文件(在有MMU的系统中可以为每个任务单独分配一个地址空间)
链接器的配置原则(ARM):
地址映射本着“RO第一,RW第二,ZI最后”
同一模块,代码的配置优于数据
之后按着名字字母的顺序来配置
操作指令
ld -o cc.out cc.o ...libraries...
备注
目标代码中必须有一个main函数
生成-Map
方式1
gcc -o target main.c add.c -Wl,-Map,target.map
方式2
ld -Map target.map main.o add.o -o target
方式3
LDFLAGS += -Wl,-Map=output.map
静态库和动态库的区别
静态ar
生成
1. g++ -c add.cpp sub.cpp
2. ar -r libmath.a add.o sub.o
使用
g++ main.cpp -lmath -L./
-l选项用于指定要链接的库名
-L选项用于指定库文件的搜索路径
动态
生成
1. g++ -fPIC -c add.cpp sub.cpp
-fPIC必须加
保证so库能被正确加载
2. g++ -shared -o libmath.so add.o sub.o
使用
方式1
export LD_LIBRARY_PATH=.
将当前目录添加到库文件的搜索路径中
g++ main.cpp -lmath -L./ -Wl,-rpath=./
-Wl选项用于将后面的选项传递给链接器,-rpath选项用于指定运行时动态库的搜索路径
方式2
g++ main.cpp ./libmath.so
其他小工具(调试)
反汇编
引入
把可执行文件翻译成汇编指令,调试代码
本质
确定可执行文件所表示的指令序列的工具
原理
根据目标文件中的字节序列来确定汇编代码
OBJDUMP
objdump -d xxx.o
gdb
引入和解决
解决的问题
程序出现bug,可以实时调试
知识点
命令使用
非调试命令
启动GDB
gdb 可执行文件
gdb -q 可执行文件
-q参数可以屏蔽一些gdb版本等相关信息,使得页面看起来干净些
查看代码(默认显示10行)
l/list
查看当前文件前十行
l 行号
查看当前文件第几行
l 函数名
查看当前文件的某个函数
l 文件名:行号
跨文件
l 文件名:函数名
跨文件
代码的行数(针对list)
show list/listsize
查看list的行数
set list/listsize 行数
设置list的行数
设置断点
b/break 行号
在某行设置断点
b/break 函数名
在某函数设置断点
b 文件名:行号
跨文件
b 文件名:函数名
跨文件
b 行号 if i==9
设置条件断点
当程序中的i满足时进入断点
delete/del 断点编号
info/i b/breakpoints
查看所有的断点信息
dis/display 断点编号
设置断点无效
ena/enable 断点编号
设置断点生效
退出GDB
q/quit
调试命令
运行GDB程序(开始调试的第一步)
start
程序停在第一行
run
遇到断点才停
继续运行,到下一个断点停
c/continue
向下执行一行代码(遇到函数不会进入函数体)
n/next
变量操作
打印变量值
p/print 变量名
打印变量类型
ptype 变量名
向下执行一行代码(遇到函数会进入函数体)
s/step
finish(跳出函数体)
函数体里没有断点
自动变量操作
自动打印指定变量的值
display num
查看自动打印变量的信息
i/info display
取消自动打印指定变量
undisplay 编号
其他操作
set var
set var a=3
变量=变量值
until(跳出循环体)
循环里没断点是前提
远程GDB
应用场景
能在A电脑上能通过gdb调试B服务器上的程序
A电脑为开发环境,B电脑为服务器,在A电脑上开发好程序后部署到服务器B上运行,结果出现了bug。我希望能在A电脑上能通过gdb调试B服务器上的程序(服务器上没有代码,无法调试,当然也可以选择拷贝源代码,但某些情况是不允许的)
使用步骤
在B上安装gdbserver
yum install gdb-gdbserver
拷贝gdbserver到B
备注
gdbserver要满足在B环境上运行的
在B上启动gdbserver,监听端口,等待A连接
gdbserver + A_IP:1234(端口号) + 执行程序名字 + 执行程序参数
IP:A的IP
端口随便设置一个
./gdbserver 192.168.1.200:1234 可执行文件
在A启动GDB
gdb
在A连接服务器
target remote 服务器B的IP:端口
开始调试
其他IDE
vscode
基本使用
准备工作
关掉编译器的优化现象
-o
打开调试选项 -g
在可执行文件中加入源代码信息
’-wall‘在尽量不影响程序行为的情况下打开,提示所有的warning
基本流程
启动GDB
打断点
运行
退出GDB
框架内部的实现原理是怎样的?
大致了解即可,可以当成黑盒。入口出口(接口)必须要明白,因为要使用 比如汽车,可以不知道内部怎么构成的,原理等,但是你得会使用,比如油门刹车,功能
1. 每句C语句都必须被转换成一系列低级机器语言指令
2. 这些指令按照一种可执行目标程序的格式进行打包,然后以二进制磁盘文件的形式存放起来
怎么做
功能1:
具体的实现步骤
编译器的安装和配置
1. 把下载好的文件上传到Ubuntu
2. 拷贝、解压
3. 配置环境变量,重启Ubuntu
4. 验证编译器版本
功能2:
功能3:
重点总结
编译的过程
生成的文件内部构成
动态库和静态库
3. 下载阶段(将二进制文件下载到flash)
方式1:下载器(调试器)
方式2:uart下载(外接USB转TTL->UART)不能调试
通过约定好的引脚端口、使用约定好的通讯方式、把代码写入芯片内部的特定位置
ISP
in-system programming,在线系统编程下载
利用芯片出厂的boot loader,将串口数据写入指定的flash
怎么做
工程
APP
IAP
in-applicatin programing,在线应用编程下载
用于程序的升级
怎么做
工程1
bootloader
工程2
APP
4. 程序运行(CPU读取flash的数据到内存,开始运行内存中数据和指令)
处理单元工作原理
分类
纯C语言开发
windows
dev-c++
编写源码+编译源码+下载+运行
单片机(裸机orRTOS)开发
windows
keil
将编译相关的过程封装到了IDE(集成开发环境),用户可以直接通过软件一键实现编译
编写源码+编译源码+下载+运行
iar
编写源码+编译源码+下载+运行
Linux开发
windows下搭建Linux开发环境VMware+Ubuntu
虚拟机+Linux发行版本
无可用的软件进行开发
编辑器
vim
vscode
编译工具链
根据处理器下载对应的工具链
下载
下载到嵌入式平台的文件系统
运行
./hello
Windows开发
windows
IDE
类比
厨房/嵌入式系统
内存(内存地址和值)
中心主题
时间
UTC
全世界公用的一个时间,各个地方时间根绝这个来调整时区
UNIX
从UTC时间1970年01月01日 0:00:00开始到现在的秒数
UNIX->本地时间
UNIX->UTC->本地时间
https://tool.ip138.com/timestamp/
GPS时间戳
从UTC原子钟时间1980年1月6日到现在的秒数
GPS->UNIX
与unix两者相差:err=315964800秒 GPS时间表示:周数Weeks和周内秒Secs,转成秒: sec = Weeks *7 * 24 * 3600 +Secs 和unix的时间转为同一时间基准: s = sec + err - 闰秒次数 即 unix_timestamp = gps_timestamp + 315964800 - LEAPSEC 其中315964800 为两个时间起始相差的固定秒数 LEAPSEC :闰秒
通讯协议pk
通信速率
传信率(比特率)
是什么
每秒传输的信息量,即每秒传输的二进制位数,单位bit/s
传码率(波特率)
是什么
每秒传输的码元个数,单位Baud/s
码元是信号被调制后的概念,每个码元都可以表示一定 bit 的数据信息 量。举个例子,在 TTL 电平标准的通信中,用 0V 表示逻辑 0,5V 表示逻辑 1,这时候这个码 元就可以表示两种状态。如果电平信号 0V、2V、4V 和 6V 分别表示二进制数 00、01、10、11, 这时候每一个码元就可以表示四种状态
比特率 = 波特率 * log 2 M
其中 M 表示码元承载的信息量。我们也可以理解 M 为码元的进制数
特率为 100 Baud,即每秒传输 100 个码元
如果码元采用十六进制编码
比特率 = 波特率 *log 2 16=波特率 *4
如果码元采用二进制编码
比特率 = 波特率 *log 2 2=波特率*1
存储单位
k和K、B和b
kb:表是1000b,即1000个二进制位; kB:表是1000B,即1000个byte或1000个Byte; Kb:表示1024b,即1024个二进制位; KB:表示1024B,即1024个byte或1024个Byte;
k和K
小k
1000
大K
1024
b和B
小b
1bit
大B
1Byte(8bit)
芯片分类
MPU:微处理器
Micro Processor Unit
为什么?
因为嵌入式产品的普及,需要一个运算核心控制硬件。所以就由CPU(计算机运算核心统称)演变而来,在CPU基础上保留了与嵌入式应用紧密相关的功能硬件
是什么?
特点
强处理力
怎么做?
举例
ARM的Cortex-A芯片
cortex-A7内核
向下增长(从高地址向低地址增长)
MCU:微控制器(MPU+外围器件)
为什么?
早期在工业控制领域,需要一种体积小的芯片直接放到仪器内部。所以就产生了集成化很高的MCU
是什么?
组成
将整个计算机系统集成到一块芯片中
MPU
外围电路
存储器
接口与总线
串口
RS-232
RS-422
RS-485
串行数据接口标准
IIC
SPI
USB
数据传输率高、易扩展、支持即插即用和热插拔的优点
以太网接口
PCI和PCI-E
SD和SDIO
CPLD和FPGA
显示设备
显示屏
特点
集成化高,体积小
可放在仪表内部。可以直接加简单的外围器件(电阻,电容)就可以运行代码了
存储量小,输入输出接口简单,功能较低
一般跑跑裸机,最多跑RTOS(多线程),不会跑Linux(多进程),运行简单的代码
怎么做?
举例
stm32f103zet6
ST
软件包
外设驱动库
基于库的大量例程
技术文档
参考手册(Reference Manual)
对芯片的每个外设的具体描述和功能介绍
USART
SPI
DMA
数据手册(Data Sheet)
画PCB
芯片引脚定义
电气特性
机械封装
料号定义
勘误手册(Errata Sheet)
描述了芯片某些功能的局限性,并给出解决方法
闪存编程手册(Flash Programming Manual)
芯片的片上flash操作指南,比如芯片的擦除,编程,闪存读写保护,选项字节信息等
内核编程手册(Cortex-M Programming Manual)
对内核的系统控制块的介绍
NVIC
SysTick
应用笔记(Application Note)
针对不同应用主题的描述性文档,部分笔记还会有配套的固件例程
用户手册(User Manual)
一般对某个软件库的说明文档
Cortex-M3/M4权威指南
https://www.armbbs.cn/forum.php?mod=viewthread&tid=2161
www.st.com
搜索芯片型号就行
Soc:片上系统(MCU+系统)
为什么?
随着功能越来越多,MCU集成化与MPU强处理力各优点二合一
是什么?
定制功能版本的MCU/MPU,专用芯片
组成
MPU/CPU+特定功能外设
MCU+特定功能模块外设
特点
SOC可以跑Linux或者QNX系统,最大的差别就是多核,多进程多线程。QNX也是实时操作系统,但是可以实现多进程的,多线程的功能,Linux的分时系统
怎么做?
举例
iMX6ULL(NXP)
ARM Cortex A7
Sip(多Soc)
按应用领域
通用处理器(GPP)
Soc
数字信号处理器(DSP)
概念
专门用来对离散时间信号进行极快的处理计算,提高编译效率和执行速度
包含
独立的硬件乘法器
乘法指令一般在单周期内完成
作用
针对通信、图像、语音和视频处理等领域的算法而设计
优化了卷积、数字滤波、FFT(快速傅里叶变换)、相关矩阵运算等算法中的大量重复乘法
分类
浮点DSP
用硬件来实现,可以在单周期内完成,因而其浮点运算处理速度高于定点 DSP
定点DSP
定点运算模拟浮点运算
融合,数字信号控制器(DSC)MCU+DSP
专用处理器(ASP)及ASIC
网络处理器
音视频编解码器
...