规则

target … : prerequisites …
command

edit : main.o kbd.o command.o display.o
insert.o : search.o files.o utils.o

cc -o main.o kbd.o comand.o display.o
insert.o search.o files.o utils.o

clean :
rm edit main.o kbd.o command.o display,o
insert.o search.o files.o utils.o

工作方式
  • 在目录中中找Makefile或makefile文件
  • 找第一个目标文件,并作为最终目标
  • 如果依赖文件不存在或所依赖的文件比较新,会执行后面所定义的命令
  • 执行终极文件
变量和隐藏规则

objects

.PHONY : clean
clean :
-rm edit $(objects)

.PHONY意思表示clean是一个”伪目标”,在rm命令前加一个小减号的意思是,也许某些文件出现问题,但不要管,继续作后面的事。
默认clean从来都是放在文件的最后

必须以[Tab]键开始

文件名

默认为Makefile,其他文件名如Make.Solars,Make.AIX需要添加-f指定

引用其他Makefile

在Makefile使用include关键字可以把别的Makefile包含进来,被包含的文件会放在当前文件的包含位置
include filename可以是当前操作系统的Shell文件模式(可以包含路径和通配符)在include前面可以
有一些空字符,但是绝不能是[Tab]键开始

规则
  1. 如果make执行时,有”-I”或”–include-dir”参数,那么make就会在这个参数的指定目录下去寻找
  2. 如果目录/include(一般是:/usr/local/bin或/usr/include)存在的法,make也会去寻找
环境变量 MAKEFILES

如果当前环境中定义了环境变量MAKEFILESS,那么make会把这个变量中的值做一个类似于include的动作。
不建议使用这个环境变量,因为一旦这个变量定义,使用make时,所有的Makefile都会受它的影响。

make的工作方式
  1. 读入所有的Makefile
  2. 读入被include的其他Makefie
  3. 初始化文件中的变量
  4. 推导隐晦式规则,并分析所有的规则
  5. 为所有的目标文件创建依赖关系链
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令
规则通配符

make支持三种通配符:”*”,”?”和[…],和Unix的B-Shell是相同的

文件搜索

VPATH = src:…/headers
上面的定义是指定两个目录,”src”和”…/headers”,make会按照这个顺序进行搜索。目录由”冒号”分割(当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用make的”vpath”关键字和VPATH变量很类似,但是更灵活。

  1. vpath 为符合模式的文件指定搜索目录
  2. vpath 清除符合模式的文件搜索目录
    pattern需要包含”%”字符。”%”的意思是匹配零或若干字符
    例如:”%.h”表示所有以”.h”结尾的文件,指定了要搜索的文件集指定了文件集的搜索目录
伪目标

为了避免和文件重名的这种情况,可以使用一个特殊的标记”.PHONY”来显示指明一个目标是伪目标

多目标

bigoutput litteoutput:text.g
generate text.g -$(subst output,,$@) > $@

等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput

其中,-$(subst output,,$@)中的”$”表示执行一个Makefile函数,函数名为subst
“$@”表示目标的集合,就像一个数组,”$@”依次取出目标,并执行命令

静态模式

静态模式可以更加容易的定义目标的规则,可以让我们的规则变得更加有弹性和灵活

<targets …>::<prereq-pattern …>

…

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合.
target-pattern是指明了targets的模式,也就是目标模式。
prereq-patterns是目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义

objects = foo.o bar.o

all : $(objects)

$(objects) : %.o : %.c
$(CC) -c $(CFLAGS) $< -o $@

在这个例子中,指明了目标从$object中获取,”%.o”表明要所有以”%.o”结尾的目标,命令中的”$<”表示所有的依赖目标集,”$@”表示目标集。

等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o

files = foo.elc bar.o lose.o
$(filter %.o,$(files)):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@

$(filter %.o,$(files))表示调用Makefile的filter函数,过滤”$filter“集中”%.o“的内容

自动生存依赖

大多数C/C++编译器都支持一个”-M”选项,即自动找源文件中包含的头文件,并生存一个依赖关系
cc -M main.c
其输出是:
main.o : main.c main.h

%.d: %.c
@set -e; rm -f $@;
$(CC) -M $(CPPFLAGS) $< > $@.$$$$;
sed ‘s,($).o[:],\1.0 $@:,g’ <$@.$$$$> $@;
rm -rf $@.$$$$

数据传输指令
  • 将数据从一个寄存器传递到另一个寄存器
  • 将数据从一个寄存器传递到特殊特殊寄存器
  • 将立即数传递到寄存器
  1. MOV指令
    MOV R0,R1
    MOV R0,#0X12
  2. MRS指令
    MRS RO,CPSR
  3. MSR指令
    MSR CPSR,R0
存储器访问指令

LDR R0,=0X0209C004
LDR R1,[R0]

LDR指令
LDR R0,=0X0209C004
LDR R1, [R0]

STR指令
LDR R0,=0X0209C004
LDR R1,=0X20000002
STR R1,[R0]

压栈和出栈指令

R0~R3和R12寄存器压栈
PUSH {R0~R3} 底[LR,R0,R1,R2,R3,R12]顶 向底部增长

POP {LR}
POP {R0~R3,R12}

STMFD SP!,{R0~R3,R12}
STMD SP!,{LR}

LDMFD SP!,{LR}
LDMFD SP!,{R0~R3,R12}

STM和LDM就算多存储和多加载,可以连续地读写存储器中多个连续的数据

跳转指令

  1. 直接使用跳转指令B,BL,BX等
  2. 直接向PC寄存器中写入数据

B指令会将PC寄存器的值设置为跳转目标地址。执行B指令,ARM处理器就会立即跳转到指定的目标地址。

_start:
ldr sp, = 0X80200000
b main

BL指令

BL指令相比与B指令,在跳转之前会在寄存器LR中保存当前PC的值,这就可以通过将LR寄存器的值重新加载到PC中拉

push {r0,r1}
cps #0x13 @进入SVC模式,允许其他终端再次进去

bl system_irqhandler @加载C语言中断处理函数到r2寄存器中

cps #0x12 @进入IRQ模式
pop {r0,r1}
str r0,[r1,#0x10] @中断完成,写EOIR

ADD Rd,Rn, Rm Rd = Rn + Rm
ADD Rd, Rn, #Immed
ADC Rd, Rn, Rm(带进位)
ADC Rd, Rn, #immed(带进位)
SUB Rd, Rn, Rm(带进位)
SUB Rd, #immed (带进位)
SUB Rd, Rn, #immed
SBC Rd, Rn, #immed
SBC Rd, Rm, #immed 借位

MUL Rd,Rn,Rm @32位乘法
UDIV Rd,Rn,Rm @无符号乘法
SDIV Rd,Rn,Rm @有符号除法

逻辑运算指令

AND Rd,Rn @按位与
AND Rd,Rn #immed @按位与
AND Rd,Rn,Rm

ORR Rd,Rn @按位或
ORR Rd,Rn,#immed
ORR Rd,Rn,Rm

BIC Rd,Rn @按位清除
BIC Rd,Rn,#immed
BIC Rd, Rn,Rm

ORN Rd,Rn,#immed @按位或非
ORN Rd,Rn,Rm

EOR Rd,Rn @按位异或
EOR Rd,Rn,#immed
EOR Rd,Rn,Rm

介绍

BIM(Building Information Modeling,建筑信息模型)是一种基于三维数字技术的工程数据管理方法,BIM是”在建设工程及设施全生命周期内,对其物理和功能特性进行数字化表达,并依此设计、施工、运营的过程和结果的总称”。

通俗理解:BIM就是在电脑里先”盖”一遍楼——把所有专业的设计、施工、设备信息都整合进一个三维模型中,让问题在开工前解决,让数据在建筑全寿命周期内流转

BIM系统并非单一的软件产品,而是一系列软件工具、数据标准、协同流程和管理规范的集合。它通过创建和使用数字化模型,实现建筑项目从策划、设计、施工到运维的全生命周期管理。

BIM和CAD的区别

BIM是现代的CAD-model,在说广义的CAD(Computer-Aided Design,计算机辅助设计)时,我们指的绝不仅仅是“画图软件”,而是指所有利用计算机技术辅助或增强设计创作过程的软件、算法和硬件的总称。在这个定义下,BIM是广义 CAD 在建筑工程领域发展到高级阶段的、以数据为中心的一种特定形态,也是软件工程应用的的高级范式。

广义CAD的三大范式:

  • 几何CAD 描述物体的形状与位置 解析几何、样条曲线(NURBS)、实体几何(CSG/B-rep)
  • 工程CAD 模拟物体的物理性能与行为 有限元法(FEM)、计算流体力学(CFD)、多体动力学
  • 信息CAD 管理物体的全生命周期数据 关系数据库、图论、本体论(Ontology)、参数化约束求解

包含CAID (计算机辅助工业设计),CAM (计算机辅助制造),CAE (计算机辅助工程),EDA (电子设计自动化),PLM (产品生命周期管理)
广义CAD的本质是人类将物理世界的设计逻辑,通过计算机语言进行编码、模拟和优化的过程。 BIM正是建筑行业在这个宏大背景下,特定于建筑工程领域的一种高级编码形式

相对于广义CAD,侠义CAD指的是精确描述物体在三维空间中的形状、尺寸、位置和装配关系,并高效生成用于指导生产的二维工程图。

BIM的组成

  1. 核心数据与逻辑层
    BIM系统的灵魂,负责存储、管理所有的建筑信息和业务逻辑。它决定了BIM是“智能模型”而非“3D图纸”。
    包括参数化建模引擎,数据管理子系统,协同与权限子系统

  2. 业务应用层
    子系统直接面向最终用户,解决具体的工程问题。它们读取核心层的数据,进行计算和展示。
    包括分析模拟子系统,施工模拟子系统 (4D/5D)

  • 4D模拟:将模型构件与施工进度计划(时间)关联,动态演示施工过程。
  • 5D模拟:在4D基础上关联成本数据,进行资金流的动态管控。
  1. 交互与视图层
    用户直接打交道的部分,负责将核心层的数据以人类可理解的方式呈现,并接收用户的输入。
    包括图形交互子系统,文档生成子系统

  2. 标准与接口层
    BIM系统实现互操作性和可扩展性的关键,决定了它是一个封闭的孤岛还是一个开放的平台。
    包括数据交换接口:

  • FC接口:这是最重要的接口,负责将内部私有数据模型无损地转换为开放的、通用的IFC格式,或反向解析IFC文件。
  • BCF接口:用于与其他软件交换问题和协作信息。

二次开发接口 (API/SDK)
这是BIM系统生态的核心。通过提供公开的API,允许第三方开发者或用户编写插件,扩展软件功能。例如,一个专门生成楼梯的插件,或一个自动检查规范的工具。

典型案列

用户输入->核心运算->应用分析->结果呈现->协同流转->开放生态

ARM Cortex-A7

介绍

ARM Cortex-A7是一颗高性能,低电源功耗基于ARMV7-A架构得处理器,处理器单元有1-4个计算0核心,每一个核心带有一个L1级别的子缓存系统,一个可选的GIC和一个可以选的二级cache控制器单元.

1
2
3
4
5
6
7
8
9
        L1                                    L1 data
instruction Cache data Cache
Processor

Optional Interrupt Snoop Controller Unit(SCU) Optional L2 Cache
Controller Controller

ACE master inteface

ARMCortex-A7设计

  • ARM架构
  • 高级微控制总线架构
  • Debug架构
  • 通用中断处理器架构
  • 通用定时器架构

ARM架构

基于SIMDv2扩展整形和浮点型的向量操作

注意: 高级SIMD架构扩展,相关联的实现,和软件支持是参考NEON技术

VFPv2(浮点向量计算)架构扩展是完全基于IEEE754标准

更高的安全性实现

地址范围提升到40bit

多处理功能

高级微控制器总线架构

ACE和Debug调试接口

Debug架构

基于ARMV7.1 Debug架构

通用中断控制架构

GIC(Generic Interrupt Controller) V 2.0 包括可视化扩展

通用定时架构

可视化扩展

特点

  • 完全的ARMV7-A架构实现
  • 流水线直接的和间接的指令预测
  • L1级内存系统和内存管理单元
  • L2级内存系统
  • APB调试接口
  • 支持调试追踪的ETM(Embedded Trace Macrocell)接口
  • 可选的VFPv4-D16 FPU,Media Processing Enging(MPE),NEON技术

接口

ACE APB ETM
Design For Test(DFT)
Memory Built-In Self Test(MBIST) controller

可配置项

处理器配置项 Floating-point Unit(FPU)或者Media Processing Enging(NEON),FPU或者FPU,NEON 默认(FPU NEON)

全局配置项

指令缓存大小 8KB,16KB,32KB or 64KB 默认32KB
数据缓存大小 8KB,16KB,32KB or 64KB 默认32KB
L2缓存控制单元 是否使用 默认使用
L2缓存大小 无,128KB,256KB,512KB,1024KB 默认256KB
L1数据读写周期 2个周期或者三个周期 默认2个周期
可共享的硬中断 0-480/32 默认0
处理器数量 1,2,3或者4个 2
集成GIC 是/否 否

功能

数据处理单元(DPU Data Processing Unit)

数据处理单元处理大部分的可视化的状态计算,如通用寄存器,状态寄存器,控制寄存器。它解码和执行指令,位于ARM架构下在寄存器之间操作数据位,
通过预取Prefetch Unit(PFU)指令单元,数据处理单元执行指令将要处理的数据在内存系统通过接口和数据处理单元之间传输,这将会管理加载数据和存储数据操作。

系统协控制器

系统协控制器提供配置和控制内存系统相关的功能

指令端的存储系统

  • 指令存储单元

指令存储单元(ICU-Instruction Cache Unit)包含指令存储控制器金和相关的填充缓存,Cortex A7的指令缓存系统是双路相关的,使用虚拟索引物理标记(VIPT)缓存行,每行最多可容纳8条AMR或Thumb 32位指令,或最多16条Thumb 16位指令。

  • 预取值单元

预取值单元( The Prefetch Unit - PFU )包含指令从指令缓存或者额外的内存储器,预测分支指令流的执行结果,接着通过指令传输到数据处理单元,在已知的指令周期内,最多有4条指令可以获取到,有两条指令可以传给数据处理单元.

  • 目的指令缓存分支

预取指也包含四步分支指令缓存,每个条目最多存储两个指令缓存取指,并使预测为已采用的B和BL指令影子能够被消除。它的实现是透明的,所以不会被刷新在切换到下一个上下文的时候。

  • 目的地址缓存分支

PFU也包含一个8层深的分支地址缓存系统,用来预测必然的间接的目的地址,实现也是透明的,同上。

  • 分支预测

分支预测是一个全类型预测用在历史寄存器和256条的匹配表里

  • 返回堆

指令预取值包含8个用来返回的堆来加速过程调用,对于每个过程调用,返回地址再插入硬件堆,当一个过程返回被识别到的时候,它的地址在返回堆中被弹出,实现也是透明的,同上.

数据端的存储系统

  • 数据存储单元

L1数据缓存控制器,会产生控制信号和关联的嵌入式标签,数据,不同源的内存请求,数据缓存是4路虚拟地址索引相关联的查找协议,从而实现系统中明确的地址管理。

读/取管道接口带有DPU和TLB功能

系统协处理单元会直接影响主存储器的数据缓存和间接的指令缓存通过指令缓存单元.

接口收到连续的请求从(Snoop Controller Unit-SCU)嗅探控制单元

数据缓存单元包含了合并本地和全局的独立的监视器,监视器可以被设置为独立的状态通过LDREX指令执行在自己的处理器上,也可以被清除开放成其他状态。

Cortex-A7使用MODESI协议,通过ACE修改同等的MODESI状态。为了主存储器的一致性,MODESI描述了L1的共享数据缓存状态.

M 修改/标记(Modified/UniqueDirty) 只用在缓存和脏数据
O 拥有/共享(Owned/SharedDirty) 可能用在一个缓存和它的脏数据上
E 独自/单独(Exclusive/UniqueClean) 只存在本缓存并且是干净的
S 共享/公共清除(Shared/SharedClean) 可能在一个或多个缓存是清除的
I 无效的 不在缓存

读分配模式

L1数据缓存只支持黑白规则

数据缓存重置为无效

ARMV7 虚拟存储系统不支持CP15动作来无效整个数据缓存.
  • 存储缓冲

    存储缓存保留了存储操作当有加载/存储管道的时候

  • 总线接口单元和SCU接口

    总线接口(BUS Inteface Unit-BIU)包括SCU接口和缓冲.

L1存储系统

处理器一级处理单元包括

拆分的指令和数据存储
存储系统的内存属性导出

处理器有如下特点

支持指令集和数据缓存的大小在8KB到64KB之间
伪随机存储替换策略
可禁用独立存储缓存
支持LDM和LDRD操作数据流
缓存未命中时关键字来填充
所有的缓存和关联的标签和有效的存储块都假设通过标准的内存编译器实现

L2存储系统

可选的通用中断控制器

调试

性能监视器

存储器和总线结构

系统架构

小容量产品

四个驱动单元

  • Cortex-M3内核DCode总线(D-bus),和系统总线.
  • 通用DMA1和通用DMA2

四个被动单元

  • 内部SRAM
  • 内部闪存寄存器
  • FSMC
  • AHB到APB的桥(AHB2APBx),它连接所有的APB设备

互联型产品

五个驱动单元

  • Cortex-M3内核和DCode总线(D-bus),和系统总线(S-bus)
  • 通用DMA1和通用DMA2
  • 以太网DMA

三个被动单元

  • 内部SRAM
  • 内部闪存寄存器
  • AHB到APB的桥(AHB2APx),它连接所有的APB设备

存储器组织

程序存储器,数据存储器,寄存器和输入输出端口被组织在同一个4GB的线性地址空间内.
数据字节以小端格式存放在存储器中。一个字节里的最低字节被认为是该字的最低有效字节,而最高地址字节是最高有效字节。

可访问的存储器空间被分成8个主模块,每个块为512MB
其他所有没有分配给片上的存储器和外设的存储器空间都是保留的地址空间。

存储器映像

AHB

USB OTG,以太网,CRC,闪存存储器接口,复位和时钟控制(RCC)
DMA2,DMA1,SDIO

APB2

ADC3,USART1,TIM8定时器,SPI1,TIM1定时器
ADC2,ADC1,GPIO端口G,GPIO端口F,GPIO端口E,GOIO端口D,GPIO端口C
GPIO端口B,GPIO端口A,EXTI,AFIO

APB1

DAC,电源控制(PWR),后备寄存器(BKP),bxCAN2,BxCAN1,USB/CAN共享的512字节SRAM,USB全速设备寄存器,I2C2,I2C1,UART5,
UART4,USART3,USART2,SPI3/I2S3,SPI2/I2S3,独立看门狗(WDG)
窗口看门狗(WWDG),RTC,TIM7定时器,TIM6定时器,TIM5定时器,TIM4定时器
TIM4定时器,TIM3定时器,TIM2定时器

嵌入式SRAM

STM32F10xxx内置64K字节的静态SRAM,它可以以字节,半字(16位)或全字(32位)访问.

位段

Cortex-M3存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器中每个字映射到位段存储器的一个位,在别名存储区写入一个字具有对应段区的目标位执行读-改-写操作的相同效果。

在STM32F10XXX里,外设寄存器和SRAM都被映射到一个位段区里,这允许执行单一的位段的写和读操作。

bit_word_addr = bit_band_base + (byte_offsetx32)+(bit_numberx4)

其中:

bit_word_addr是别名存储器中字的地址,它映射到某个目标位。
bit_band_base是别名区的起始地址
byte_offset是包含目标位的字节在位段的序号
bit__number是目标所在的位置(0-31)

如: 映射别名区中SRAM地址为0x20000300的字节中的位2:

0x22006008 = 0x22000000 + (0x300x32)+(2x4)

嵌入式闪存

高性能的闪存模块有以下的主要特性:

  • 高达512K字节闪存存储器的结构,闪存存储器有主存储器和信息快组成:

主存储块容量

小容量产品主存储快最大为4K x 64位,每个存储快划分为32个1k字节的页
中容量产品主存储块最大为16K x 64位,每个存储快划分为128个1K字节的页
大容量产品主存储模块最大为64K x 64位,每个存储快划分为256个2K字节的页
互联型产品主存储模块最大为32K x 64位,每个存储快划分为128个2K字节的页

信息快容量

互联型产品有2360 x 64位
其它产品有256 x 64位

闪存存储器接口特性

带预取缓存器的读接口(每字节2x64位)
选择字节加载器
闪存编程/擦除操作
访问/写保护

闪存读取

闪存的指令和数据访问是通过AHB总线完成的。预取模块是用于通过ICode总线读取指令的。仲裁是作用在闪存接口,并在DCode总线上的数据访问优先。

等待时间:可以随时更改的用于读取操作的等待状态的数量

预取缓存区: 在每一次复位以后被自动打开,由于每个缓存区的大小(64位)
与闪存的宽带相同,因此只通过需一次读闪存的操作即可更新整个缓存区的内容。由于预取缓存
区的存在,CPU可以工作在更高的主频。 CPU每次取指最多为32位的字,取一条指令时,下一条指令已经在缓存区中等待。

半周期: 用户功耗优化

  1. 等待周期
    0等待周期,当 0 < SYSCLK < 24MHz
    1等待周期,当 24MHz < SYSCLK < 48MHz
    2等待周期,当 48MHz < SYSCLK < 72MHz

  2. 半周期配置不能与使用了预分配器的AHB一起使用,时钟系统应该等于HCLK时钟。该特性只能用于
    在时钟频率为8MHz或低于8MHz时,可以直接使用的内部RC振荡器(HSI),或者是主振荡器(HSE),但不能用PLL。

  3. 当AHB预分配系数不为1时,必须置预取缓冲区处于开启状态。

  4. 只有在系统时钟(SYSCLK)小于24MHz并且没有打开AHB的预分频器(即HCLK必须等于SYSHCLK)时,才能
    执行预取缓存器的打开和关闭操作。一般而言,在初始化过程中执行预取缓冲器的打开和关闭操作,这时微控制器的时钟由8MHz的内部RC振荡器(HSI)提供。

  5. 使用DMA: DMA在DCode总线上访问闪存存储器,它的优先级比ICode上的取指高。DMA在每次传送完成后具有一个空余的周期。这些指令可以和DMA传输一起执行.

编程和擦除闪存

闪存和编程一次可以写入16位(半字)。
闪存擦除操作可以按页面擦除或完全擦除(全擦除)。全擦除不影响信息快。
为了确保不完全过度编程,闪存编程和擦除控制器块是由一个固定的时钟控制的。
写操作(编程或擦除)结束时可以出发中断。仅当闪存控制器接口时钟开启时,此中断可以用来从WFI模式退出

启动配置

启动模式选择引脚 启动模式 说明
BOOT1 x BOOT0 0 主闪存存储器 主闪存存储器被选为启动区域
BOOT1 0 BOOT0 1 系统存储器 系统存储器被选为启动区域
BOOT1 1 BOOT0 1 内置SRAM 内置SRAM被选为启动区域

在系统复位后,SYSCLK的第四个上升沿,BOOT引脚的值将被锁存。用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式

在从待机模式退出时,BOOT引脚的值将被重新锁存:因此,在待机模式下BOOT引脚应保持为需要的启动配置。在启动延迟之后,CPU从地址0x0000 0000获取堆栈顶的地址,并从启动存储器的0x000 004指示的地址开始执行代码。

因为固定的存储器映像,代码区始终从地址0x0000 0000开始(通过ICode和DCode总线访问),而数据区(SRAM)始终从地址0x2000 0000开始(通过系统总线访问)。Cortex-M3的CPU始终从ICode总线获取复位变量,即启动仅适合于从代码区开始(典型地从flash启动).STM32F103xxx微控制器实现了一个特殊的机制,系统可以不仅仅从Flash存储器或系统存储器启动,还可以从内置SRAM启动。

  • 从主闪存储器启动:主闪存存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(0x8000 0000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x0000 0000或0x0800 0000

  • 从系统存储器启动: 系统存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(互联型产品原有地址为0xFFF B000,其它产品原有地址为0x1FFF F000)访问它.

  • 从内置SRAM启动: 只能在0x2000 0000开始的地址区访问SRAM

注意: 当从内置的SRAM启动,在应用程序的初始化代码中,必须使用NVIC的异常表和偏移寄存器,重新映射向量表至SRAM中。

内嵌的自举程序

内嵌的自举程序存放在系统存储区,由ST在生产线上写入,用于通过可用的串行接口对闪存存储器进行重新编程

对于小容量,中容量和大容量的产产品而言,可以通过USART1接口启用自举程序

对于互联型产品而言,可以通过以下某个接口启用自举程序:USART1,USART2(重映像的),CAN2(重映像的)或USB OTG全速接口的设备模式(通过设备固件更新DFU协议)。USART接口依靠内部8MHz振荡器(HSI)运行。只有在外部使用8MHz,14.7456MHz或25MHz时钟(HSE)时,才能使用CAN或USB OTG接口。

CRC计算单元

小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101xx,STM32F102xx和STM32F103xx微控制器

中容量产品是指闪存存储器容量在64K至128K字节之间的STM32F101xx,STM32F102xx和STM32F103xx微控制器

互联型产品是指STM32F105xx和STM32F107xx微控制器

CRC简介

循环冗余校验码(CRC)计算单元是根据固定的生成多项式得到任一32位全字的CRC计算结果。
在其他应用中,CRC技术主要应用核实数据传输或者数据存储的正确性。标准EN/IEC 60335-1即提供了一种核实
闪存存储器完整性的方法,CRC计算单元可以在程序运行时计算出软件的标识,之后与在连接时生成的参考标识做比较,然后存放在指定的存储器空间。

CRC主要特性

使用CRC-32(以太网)多项式:0x4C11DB7

  • X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 + X8 + X7 + X5 + X2 + X + 1

一个32位数据寄存器用于输入/输出
CRC计算时间:4个AHB时钟周期(HCLK)
通用8位寄存器(可用于存放临时数据)

CRC功能描述

CRC计算单元含有1个32位数据寄存器
对该寄存器进行写操作,作为输入寄存器,可以输入要进行的CRC计算的新数据
对该寄存器进行读操作,返回上一次CRC计算结果

每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字节进行CRC计算,而不是逐字节的计算),CRC计算单元(CRC)在CRC计算期间会暂停CPU操作,
因此可以对寄存器CRC_DR进行背靠背写入或者连续地写-读操作

可以通过设置寄存器CRC_CR的RESET位来重置寄存器CRC_DR为0xFFFF FFFF。该操作不影响寄存器CRC_IDR内的数据

CRC寄存器

CRC计算单元包括两个数据寄存器和1个控制寄存器

数据寄存器

地址偏移:0x00
复位值: 0xffff ffff

位31:0 数据寄存器,写入CRC计算器的新数据时,作为输入寄存器
读取时返回CRC计算结果

独立数据寄存器(CRC_IDR)

地址偏移: 0x04
复位值: 0x0000 0000

位31:8
位7:0

通用8位数据寄存器
可用于临时存放1字节的数据
寄存器CRC_CR的RESET位生产的CRC复位对本寄存器没有影响

此寄存器不参与CRC计算,可以存放任何数据

控制寄存器

地址偏移: 0x08
复位值: 0x0000 0000

位31:1 保留
位0 RESET位
复位CRC计算单元,设置数据寄存器为0XFFFF FFFF。
只能对该位写’1’,它由硬件自动清’0’

CRC寄存器映像
1
2
3
4
偏移  	寄存器   复位值
0x00 CRC_DR 0XFFFF FFFF 31:0~31:31 数据寄存器
0x04 CRC_IDR 0x00 31:0~31:7 独立数据寄存器 31:8~31:31 保留
0x08 CRC_CR 0 31:0 RESET 31:1~31:2 保留 31:3~31:31 保留

实时操作系统是一种体积小巧,确定性强的计算机操作系统,RTOS通常用于需要在严格时间限制内对外部时间做出反应的嵌入式操作系统。

多任务处理

每个执行的程序由操作系统控制下的一个或多个线程实现,如果操作系统能够以这种方式执行多个线程,则称为多任务处理。小型的RTOS通常将线程称为任务,因为他们不支持虚拟内存,因此进程和线程之间没有区别。

多任务处理并发

多任务处理并发是指在同一时间内执行多个任务,每个任务都被分配一定的时间片来执行。操作系统会按照一定的时间间隔来切换任务,每个任务在执行时都会被操作系统调度器调度,以便在每个任务之间进行切换。

调度

调度器是内核中负责决定在特定时间应执行什么任务的部分,内核可以在任务的生命周期内多次暂停并恢复该任务。

调度策略是调度器用来决定何时执行哪个任务的算法。相对于非实时多用户系统中,调度策略通常会确保每个任务获得”公平”的处理器时间。

只有当调度算法决定执行不同的任务时,任务才会换出。这种切换可能在当前执行任务不知情的情况下发生,例如调度算法响应外部事件或定时器到期时;还可能发生在执行任务显式调用某个导致其让出,休眠或阻塞的API函数。

如果某任务让出,调度算法可能会再次选择同一任务执行。如果某任务休眠,则在指定的延迟时间到期前不可被选择。同样,如果某任务阻塞,则在特定事件发生或超时满之前将不可被选择。

操作系统内核负责管理这些任务状态和转换,确保根据调度算法和每个任务的当前状态在给定的时间选择适当的任务执行。

实时调度

实时操作系统利用与通用(非实时)系统相同的原理来实现多任务处理,但两者的目标截然不同,这一差异主要体现在调度策略上,实时嵌入式系统旨在对现实世界事件做出及时响应,这些事件通常有截止时间,实时嵌入式系统必须在此之前响应,RTOS调度策略必须确保遵守这些截止时间要求。

在小型的RTOS中实现这一目标,软件工程师必须为每个任务分配优先级。RTOS的调度策略就是确保能够执行最高优先级任务获得处理时间。如果存在多个能够运行的同等最高优先级任务,则调度策略可以选择在这些任务之间”公平”地分配处理时间。

  1. 编译as汇编语言程序

    1
    as [选项]  [-o objfile] [srcfile.s]

    其中objfile是as编译输出的目标文件名,srcfile.s是as的输入汇编语言程序名,如果没有 使用输出文件名,那么as会编译输入名称为a.out的默认目标文件

  2. AS汇编语法

    ​ 为了维持与gcc输出程序的兼容性,as汇编器使用AT&T的汇编语法(下面简称为AT&T语法)。这种语法与Intel汇编程序使用的语法(简称intel)语法很不一样,它们之间的主要区别有一下几点

    ​ AT&T 语法中立即操作数前面要加一个字符‘$’;寄存器操作数名前要加字符串百分号‘%’

    ;绝对跳转/调用(相对于程序计算器有关的跳转/调用)操作数前面要加星号’*’。而intel汇编语法均没有这些限制

    ​ AT&T语法与intel语法使用的源和目的操作数次序正好相反,AT&T的源和目的操作数是从左到右’源,目的’.例如Intel的语法‘ add exa,4’,对应 ‘addl $4,%eax‘

    ​ AT&T语法中内存操作数的长度(宽度)由操作码最有一个字符来确定,操作码后缀’b’,’w’,和‘l’ 分别指示内存引用宽度为8位字节(byte),16位字节(byte),和32位字节长(long),intel语法则通过在内存操作数前使用前缀 ‘byte prt’,’word ptr’,和’dword ptr’来达到同样目的,因此,intel的语法‘mov al,byte ptr foo’对应AT&T的语法’movb $foo,%al’

    ​ AT&T语法中立即形式的远跳转和远调用为‘l jump/call $section,$offset’ ,而intel 的是’jmp/call far section:offset’。

​ AT&T汇编器不提供对多代码程序端,UNIX类操作系统要求所有代码在一个段中

  1. 符号,语句,和常数

    ​ 符号(Symbol)是由字符串组成的标识符,组成符号的有效数字符取自于大小写字符集,数字和三个字符‘_.$’。符号不允许用数字字符开始,并且大小写含义不同。在as汇编程序中符号没有长度限制,并且符号中所有字符都是有效的,符号使用其他字符(例如空格,换行符)或者文件的开始来界定开始和结束处。

    ​ 语句(Statement)以换行符或者分割字符(“;”) 作为结束,文件最后语句必须以换行符作为结束,若在一行的最后使用反斜杠‘\’,(在换行符前),那么就可以让一条语句使用多行。当as读取到反斜杠加换行符时,就会忽略掉这两个字符。

    ​ 语句由零个或多个标号(label)开始,后面可以跟随一个确定语句类型的关键符号。标号由符号后面跟随一个冒号(‘:’) 构成,关键符号确定了语句余下部分的语义。如果该关键符号以一个‘.’开始,那么当前语句就是一个汇编命令(或称为伪指令,指示符。如果关键符号以一个字母开始,那么当前语句就是一条汇编语言指令语句。

    常数是一个数字,可分为字符常数和数字常数两类。字符常数还可分为字符串和单个字符;而数字常数可分为整数,大数和浮点数

    ​ 字符串必须用双引号扩住,并且其中可以使用反斜杠‘\’ 来转义特俗字符。例如‘\\‘ 表示一个反斜杠字符,其中第一个反斜杠是转义指示字符,说明把第二个字符看作一个普通的反斜杠字符

    ​ 汇编程序中使用单个字符常数时可以写成该字符前加一个单引号,例如“ ‘A ”表示值65,“ ’c ” 表示67

    ​ 整数数字常数有四种表示方法,即使用‘0b’或‘0B’开始的二进制数(‘0-1’) ,以‘0’开始的八进制数’(0-7)’ ;以非‘0’数字开始的十进制数(‘0-9’)和使用‘0x’或‘0X’开头的十六进制数(0-9a-fA-F)。若要表述负数,只需在前面添加负号‘-’

    大数( bignum) 是位数超过32位的二进制的数,其表示方法与整数的相同

  2. 指令语句,操作数和寻址

    ​ 指令(Instructions)是cpu执行的操作,通常指令也称作操作码(Opcode);操作数(Operand)是指令操作的对象;而地址(Address)是指定数据在内存中的位置。指令语句是程序运行时刻执行一条的语句,它通常可包含四个组成部分:

    ​ 标号(可选)

    ​ 操作码(指令助记符)

    ​ 操作数(由具体指令指定)

    ​ 注释

     

    ​ 一条指令语句可以含有0个或最多3个用逗号分开的操作数。对于具体有两个操作数的指令语句,第1个是源操作数,第二个是目的操作数,即指令操作结果保存在第二个操作数中

    ​ 操作数可以是立即数(即值是常数值的表达式),寄存器(值在cpu的寄存器中)或内存(值在内存中),一个间接操作数(indirect operand)含有实际操作数值的地址值。AT&T语法通过在操作数前面加一个(‘*’)字符来指定一个简洁操作数。只有调转/调用指令才能使用间接操作数。

    ​ 立即操作数前面需要加一个‘$’字符前缀

    ​ 寄存器名前需要加一个‘%‘字符前缀

    ​ 内存操作数由变量名或者含有变量地址的一个寄存器指定。变量名隐式指出了变量地址,并指示cpu引用该地址处内存的内容

  3. 指令操作前缀

    ​ 操作码前缀用与修饰随后的操作码,它们作用于重复字符串的指令,提供区覆盖,执行总线锁定操作,或指定操作数和地址宽度,通常操作码前缀可用于一条没有操作数的指令独占一行并且必须直接位于所影响指令之前,但是最好与它修饰的指令放在同一行上。

  4. 内存引用

    ​ intel语法的间接内存引用方式,section:[base+index*scale+disp],对应AT&T语法的形式:section:disp(base, index,scale)其中base和index是可选的32位基寄存器和索引寄存器,disp是可选的偏移值。scale是比列因子,取值范围是1,2,4和8。scale其乘上索引index用来计算操作数地址。如果没有指定scale,则scale取默认值1.section为内存操作数指定可选的段寄存器。并且会覆盖操作数使用的当前默认段寄存器,请注意,如果指定的段覆盖寄存器与默认操作的寄存器相同,则as就不会为汇编的指令再输出相同的段前缀;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    movl var ,%eax 							#把内存地址var处的内容放入寄存器%eax中
    movl %cs:var,%eax #把代码段中内存地址var处的内容放入%eax中
    movl $0x0a,%es:(%ebx) #把字节值0x0a保存到es段的%ebx指定的偏移处
    movl $var,%eax #把var的地址放入%eax中
    movl array(%esi),%eax #把array+%esi确定的内存地址处内容放入%eax中
    movl (%ebx,%esi,4),%eax #把%ebx+%esi*4确定的内存地址处的内容放入%eax中
    movl array(%ebx,%esi,4),%eax#把array+%ebx+%esi*4确定的内存地址处的内容放入%eax中
    movl -4(%ebp),%eax #把%ebp-4的内存地址处的内容放入%eax中,使用默认段%ss
    movl foo(,%eax,4) %eax #把内存地址foo+eax*4处内容放入%eax中,使用默认段%ds

合并某一个文件

1
git checkout -p zc_1218_add  ./pages/box/blind.vue

讲解: -p是–patch的缩写
zc_1218_add 是要合并分支
后面跟路径

需求文档

echo客户端

  1. 连接到服务器
  2. 发送一个或多个消息
  3. 对于每个消息。等待并接受从服务器发回的相同的消息
  4. 关闭连接

1. 通过channelHandler 实现客户端逻辑

如同服务器,客户端将拥有一个用来处理数据的ChannelInBoundHandler。在这个场景下,你将扩展SimpleChannelInBoundHandler类以处理所有必须的任务,代码如下

  • channelActive() –在到服务器的连接已建立之后被调用;
  • channelRead0()–当从服务器收到一条信息时被调用
  • exceptionCaught()–在处理过程中引发异常时被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

public class EchoClient extends SimpleChannelInboundHandler<ByteBuf> {

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8));

}

@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
//当被通知Channel是活跃当额时候,发送一条消息
System.out.println("--cient received:"+byteBuf.toString(CharsetUtil.UTF_8));

}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();



}
}

在客户端,当channelRead0()方法完成时,已经有传入消息,并且已经处理完成它了,当该方返回时,SimpleChannelInBoundHandle负责释放指向保存该消息的ByteBuf的内存引用

在EchoServerHandler中,扔需要将传入的消息回送给发送者,而write()操作是异步的,直到channelRead()
方法返回后可能仍然没有完成,为此,EchoClient扩展了 ChnelInboubdHandlerAdapter,其在这个时间点上不会释放信息
在channelRead-Complete()方法中,当writeAndFlush()方法被调用时被释放

引导客户端

需求

引导客户端类似于引导服务器,不同的是,客户端是使用主机和端口参数来连接远程地址,也就是echo服务器的地址,而不是绑定到一个一直被监听的端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

package org.example;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

public class EchoClient {

private final String host;

private final int port;

public EchoClient(String host,int port){
this.host = host;
this.port = port;
}

public void start() throws Exception{

EventLoopGroup group = new NioEventLoopGroup();

try {
//创建bootstrap
Bootstrap b = new Bootstrap();
//指定EventLoopGroup 以处理客户端事件,需要适用于NIO的实现
b.group(group).channel(NioSocketChannel.class)
//适用于NIO传输的的channel类型
.remoteAddress(new InetSocketAddress(host,port))
//设置服务器的InetSocketAddress地址
.handler(new ChannelInitializer<SocketChannel>() {
// 在创建Chnannel时向pipLine添加一个EchoClient实例
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoHandler());
}
});
// 连接到远程节点,阻塞直到连接完成
ChannelFuture f = b.connect().sync();
//阻塞,直到Channel关闭
}finally {
group.shutdownGracefully().sync();
}

}

public static void main(String[] args) throws Exception {
new EchoClient("localhost",8002).start();
}
}

rust tcp客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::net::TcpStream;
use std::str;
use std::io::{self, BufRead, BufReader, Read, Write};
fn main() {
let stream = TcpStream::connect("127.0.0.1:8888").expect("Could not connect Server");
loop {
let mut input = String::new();
let mut buffer:Vec<u8>=Vec::new();
io::stdin().read_line(&mut input).expect("Failed to write to server");
let mut reader = BufReader::new(&stream);
reader.read_until(b'\n',&mut buffer).expect("could not read info buffer");
println!("{}",str::from_utf8(&buffer).expect("Could not write buffer as string"));
}
}
0%