嵌入式系统概述
-
定义:嵌入式系统是以应用为中心,以计算机技术为基础,采用可剪裁软硬件,适用于对功能、可靠性、成本、体积、功耗等有严格要求的专用计算机系统。
-
实时系统:指系统能够在限定的响应时间内提供所需水平的服务。
-
嵌入式操作系统:VxWorks、Windows CE、Android、iOS,Linux
-
区别:
- 任务是专用而确定
- 要求实时性
- 使用实时操作系统
- 高可靠性保障
- 功耗约束
- 资源少、需要专用工具和特殊方法
-
嵌入式系统一般由嵌入式微处理器、外围硬件设备、嵌入
式操作系统(可选),以及用户的应用软件系统等四个部
分组成
嵌入式系统硬件概述【重点】
1 嵌入式微处理器
- 嵌入式微处理器(EMPU):有CPU, ROM, RAM总线等,尺寸更大
- 嵌入式微控制器(MCU):单片机,尺寸小
- DSP处理器,协处理器,可以看成外设
- 片上系统SOC,全部外设集成到一个芯片上
2 ARM与Thumb
- ARM32 按字排列,长度为32bit,ARM指令必须在ARM态下运行
- Thumb 按半字排列,长度为16bit,Thumb指令必须在Thumb态下运行
- 在CPSR T字段记录运行状态
ARM和Thumb状态切换使用bx指令
ARM=>Thumb:
LDR R0,=Label+1
BX R0
Thumb=>ARM:
LDR R0,=Label
BX R0
ARM寄存器:31(通用寄存器)+6(状态寄存器)
总线比较
USB
通用串行总线,无时序信号(异步),单主设备
I2C
主设备传输数据给从设备,
串行总线,需要时钟信号(同步),总线仲裁
多主设备
PCI/SPI
和I2C差不多, PCI使用集中仲裁
嵌入式软件编程技术【重点】
1 arm处理器模式(7种)
用户模式+特权模式
特权模式为除了系统模式(System)+异常模式(5种)
CPSR寄存器作为状态寄存器
2 arm指令
2.2 条件符
条件符 含义
GT/LT 符号数大于/小于
GE/LE 符号数大于等于/小于等于
HI/LO 无符号数大于/小于
HS/LS 无符号数大于等于/小于等于
2.3 寻址方式
寄存器寻址
MOV R1, R2 ;R2->R1
SUB R0, R1,R2 ;R1-R2 -> R0
立即数寻址
SUBS R0,R0,#1 ;R0-1 -> R0
MOV R1,#0xFF00 ;0xFF00 -> R0
寄存器偏移寻址
ADD R1, R1, R2 , ROR #0x2
; R2循环右移两位后与R1相加,结果放入R1
MOV R1, R0, LSL R2
; R0逻辑左移R2位后放入R1中
寄存器间接寻址
STR R1, [R2]
; 将R1的值存入以R2内容为地址的存储器中
SWP R1, R1, [R2]
; 交换以R2为地址的存储器内容和R1内容
基址变址寻址
op Rd, [Rn, R1]
; 零偏移,Rn+R1是操作数地址
op Rd, [Rn, FlexOffset]
; 前索引偏移,Rn+FlexOffset是地址
op Rd, [Rn, FlexOffset]!
; 带写回的前索引偏移,地址存入Rn寄存器
op Rd, [Rn], FlexOffset
; 后索引偏移,Rn是地址,Rn+FlexOffset写入Rn
多寄存器寻址
LDMIA R0, {R1, R2, R3, R4, R5}
; R1<- R0, R2<- R0+4, …, R5<-R0+16
STMIA R0, {R2-R5, R7}
; R0<-R2, R0+4<-R3, … , R0+12<-R5, R0+16<-R7
堆栈寻址
STMFD SP!, {R1-R7, LR}
; 将R1-R7,LR存放到堆栈中,这条指令一般用来保护现场
相对寻址
BL Label
; 转跳到Label标签处
2.4 指令后缀
指令后缀 | 含义 |
---|---|
B(byte) | 功能不变,操作长度变为8位(依赖CPU位数,以下相同) |
H(Halfword) | 功能不变,操作长度变为16位 |
S(signed) | 功能不变,操作数变为有符号数 |
S(S标识) | 影响CPSR里的NZCV标识位 |
2.5 条件代码
条件代码 | 含义 |
---|---|
N | 正负,N=1,结果为负,N=0,结果为正或0 |
Z | 零,Z=1结果为0,Z=0,结果非零 |
C | 进位,加法产生进位则C=1,不然为C=0; 借位,减法运算产生了借位则C=0,否则C=1 |
V | 溢出,V=1,有溢出,V=0,无溢出 |
2.3 数据传输指令
mov: 在两个寄存器之间或者立即数和寄存器之间传递数据,将后一个寄存器上的值或者立即数赋值给前一个寄存器
mvn:和mov用法一致,区别是mvn会把后一个寄存器的值或者立即数按位取反后赋值给前一个寄存器
mvn r0,#0xFF
; 后一个数取反后赋值给前一个数,r0的值为0xffffff00(32位数据)
2.4 算术指令
rsb:逆向减运算
adc:带进位的加法运算(ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值)
sbc:带进位的减法运算
rsc:带进位的反减指令
2.5 逻辑指令
and:与操作
orr:或操作
eor:异或操作
bic:位清除操作
BIC R0,R0,#%1011
;清除R0的0,1,3位,赋值给R0
2.5 比较指令
cmp:比较大小
cmn:取反比较
tst:按位与运算
teq:按位异或运算
2.6 乘法指令
mul:32位乘法
mla:32位乘加
MLA R0, R1, R2, R3
; R0 = R1*R2+R3
umull:64位无符号乘法
umlal:64位无符号乘加
smull:64位有符号乘法
smlal:64位有符号乘加
2.7 跳转指令
b指令:跳转
bl指令:跳转,保存返回地址
bx指令:跳转,切换到ARM/Thumb模式
blx指令:跳转,保存返回地址,切换到ARM/Thumb模式
BL Label
; 转跳到Label标签处
2.8 内存访问
ldr:加载指定内存地址的数据到寄存器,按照字节访问
str:加载指定寄存器数据到内存地址中,按照字节访问
STR R1, [R2]
; 将R1的值存入以R2内容为地址的存储器中
ldm: 和ldr功能一样,一次多字节多寄存器访问
stm:和str功能一样,一次多字节多寄存器访问
ldm/stm后缀 | 含义 |
---|---|
ia | increase after,后增加,表示每个操作时,先传输数据,后增加内存地址, |
ib | increase before,先增加,表示在每个操作时,先增加内存地址,再进行数据传输 |
da | decrease after:和ia一样,差别在于减少地址 |
db | decrease before:和ib一样,差别在于减少地址 |
fd | full decrease:满递减堆栈,指的是从高地址向下生长,sp指向装最后一个数据的位置 |
ed | empty decrease:空递减堆栈 |
fa | 满递增堆栈 |
ea | 空递增堆栈 |
STMIA R0, {R2-R5, R7}
; [R0]<-R2, [R0+4]<-R3, … , [R0+12]<-R5, [R0+16]<-R7
STMFD SP!, {R1-R7, LR}
; 将R1-R7,LR存放到堆栈中,这条指令一般用来保护现场
SWP:内存和寄存器互换指令,一边读一边写
SWP R1, R1, [R2]
; 交换以R2为地址的存储器内容和R1内容
swp r1,r2,[r0]
; [r0] -> r1,r2 -> [r0](记忆方式:r1是目的寄存器,所以将值写入r1)
2.9 软中断指令
swi(software interrupt),在软件层模拟产生一个中断,这个
中断会传送给CPU,常用于实现系统调用
3 可重入
- 如果某个函数可被多个任务并发调用而不会造成数据错误,则称该
函数具有可重入性 - 可重入函数可在任意时刻被中断,稍后继续运行时不会造成错误
不可重入性:
- 不可重入函数不能被多个任务共享,除非采用信号量等机制确保函
数的互斥调用,或者在代码的关键部分禁止中断
注意:可重入被多个任务并发调用,应该考虑为多个线程并发调用。因此代码中出现全局变量,或者是static的,多为不可重入函数。
4 中断
- 中断:当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断
- 软中断:通常为正在执行的进程产生的IO请求
- 硬中断:硬件产生的中断,比如键盘,网卡等
中断处理-硬件:
- 复制CPSR到SPSR_<mode>
- 设置正确的CPSR位
- 切换到<mode>
- 保存返回地址到LR_<mode>
- 设置PC跳转到相应的异常向量表入口
中断处理-软件:
- 把SPSR和LR压栈
- 把中断服务程序的寄存器压栈
- 开中断,允许嵌套中断
- 中断服务程序执行完后,恢复寄存器
- 弹出SPSR和PC,恢复执行
5 makefile
格式:
目标:[依赖模块]
命令
命令
6 嵌入式汇编
寄存器别名 | 含义 |
---|---|
A1-A4 | R0-R3,用来传递参数 |
V1-V8 | R4-R11 |
IP | R12,前一个栈指针 |
SP | R13,栈指针 |
LR | R14,返回地址 |
PC | R15 |
- text section
代码段,只读 - data section
数据段,存放已初始化的全局变量、静态变量和常量等 - bss section
block started by symbol,存放未初始化全局和静态变量
汇编mian函数编写:
.global _start
.text
_start:
; main函数主体
;函数调用
MOV R0, 1
LDR R1, [R1, 2]
BL fun
;函数返回值在R0
.end
汇编函数编写:
fun:
STMFD SP!, {R0-R4,R12,LR} ;保存现场
;函数的参数通过R0-R3传递
LDR R0, [R1, #0x4] ;获取数组的值,注意计算偏移量
STR R0, [R1, #0x4] ;修改数组的值
PUSH {R0, R1} ;变量入栈
POP {R0, R1} ;变量出栈
LDMFD SP!,{R0-R4,R12,LR}
MOV R0, R1; 返回值保存
BX LR
实现阶乘20!,结果放入R9:R8
.global _start
.text
_start:
Mov R8, #20 @低32位初始化为20
Mov R9,#0 @高32位初始化为0
Sub R0,R8,#1 @初始化计数器
Loop:
MOV R1,R9 @暂存高位值
UMULL R8,R9,R0,R8 @[R9:R8]=R0*R8
MLA R9,R1,R0,R9 @R9=R1*R0+R9
SUBS R0,R0,#1 @计数器递减
BNE Loop @计数器不为0时继续循环
.Stop:
B Stop
.end @文件结束
注意点:
- 如果函数体内再次调用函数,则一定写push {lr},后面写 pop {lr}
- 尽量不要使用栈操作,为每个变量都分配一个寄存器
- 尽量使用R0-R3,如果使用超过R3,则必须入栈
- 返回时不要忘记把返回值赋值给R0
基本块
如果是简单的if语句:
if(r0 >= r1) r0 = r1 + r2;
CMP R0, R1
ADDGE R0, R1, R2
如果if块中有较多的语句
CMP R0, R1
BLT else ;这里的条件是if取反
;条件满足时执行
else:
;条件不满足时执行
如果是循环语句:
MOV R0, #20 ;设置计数器
LOOP:
;循环体
SUBS R0, R0, #1
BNE LOOP
STOP:
;循环结束
7 混合编程
arm调用C
C语言函数 int add(int a, int b);
在arm中:
IMPORT add;声明要调用的C函数
MOV r0, 1
MOV r1, 2
BL add ;调用C函数add
C调用arm
C语言:
extern int add (int x,int y); //声明add为外部函数
void main()
{
int a=1,b=2,c;
c=add(a,b); //调用add子程序
……
}
arm
EXPORT add ;声明add子程序将被外部函数调用
add: ;求和子程序add
ADD r0,r0,r1
MOV pc,lr
内嵌汇编
__asm(
汇编语句模板:
输出部分:
输入部分:
修改部分
)
- 参数传递时无法指定寄存器,必须传递进入后使用MOV
- 输出部分: “=r” (result),result表示C语言的变量
- 输入部分:“r”(value),value表示输入的C语言变量,多个变量用逗号分隔
- 在模板中使用%0,%1的占位符,按顺序表示输出输入的变量
- 如果在汇编模板中用到了寄存器,则需要在最后注明(用逗号隔开)
例子:
#include <stdio.h>
int main(void)
{
int result ,value;
value=1;
printf("old value is %x",value);
__asm("mov %0,%1,ror #1": "=r"(result):"r"(value));
printf("new result is %x\n",result) ;
return 1;
}
嵌入式操作系统uCOS【重点】
1 调度
基于优先级,就绪态优先级最高的任务先运行
抢占式:如果中断服务中出现了高优先级任务就绪,则中断结束后直接返回高优先级任务
非抢占式:中断结束后总是返回原来的任务
临界区:执行时不允许中断
2 进程分配
- 抢占式实时操作系统
- 64个任务,8(系统保留)+56(应用程序)
- 允许每个任务有不同的栈空间
- 中断嵌套最多255层
- 优先级越高,任务编号数字越小
- 空闲任务(idle task) 当所有的任务都在等待事件发生时μC/OS-Ⅱ执行OSTaskIdle()函数
- OS_TCB:堆栈指针,状态,优先级,任务表位置,任务链表指针等,OS_TCBs全部驻留在RAM中
- uC/OS中不支持时间片轮转法
- uC/OS任务调度所花的时间为常数,与应用程序中建立的任务数无关
3 优先级计算
OSRdyTbl是一个长度为8的数组,看成一个8*8的矩阵,第一行是7->0,如果一个元素为1,表示该优先级的任务就绪。
OSRdyGrp是一个8bit的数,看成OSRdyGrp[n]表示第n行是否有就绪队列
任务号高三位是矩阵的行(0开始),任务号低3位是矩阵的列(从右边开始数)
OSMapTbl[n]就是2的n次的值,把一个值n映射到了第n个bit是1的8位数
关键是计算行列地址。
任务就绪时
OSRdyGrp |=OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |=OSMapTbl[prio & 0x07];
如果没有给OSMapTbl,则直接改成2^i次即可,比如优先级为12
12=0x1100,地址为(1,4)
OSRdyTbl[1] |= 2^4
OSRdyGrp |= 2^1=2
脱离就绪态
OSRdyTbl[prio>>3] &= ~OSMapTbl[prio&0x07];
if(OSRdyTbl[prio>>3] == 0);
OSRdyGrp &= ~OSMapTbl[prio>>3];
如果优先级12脱离就绪态,则在矩阵中的地址为(1,4)
OSRdyTbl[1] &= ~2^4;
if(OSRdyTbl[1] == 0) {
OSRdyGrp &= ~2^4;
}
寻找优先级最高的任务
根据OSRdyGrp从右开始为1的最高位就是行地址x,
在OSRdyTbl中该行从右开始为1的就是列地址y。
优先级=x*8+y
比如OSRdyGrp=01101000B,则从右开始数第一个为1的位为3,
OSRdyTbl[3] = 11100100B,则从右开始为1的是2,优先级为3*8+2=26
High3 =OSUnMapTbl[OSRdyGrp];
Low3 =OSUnMapTbl[OSRdyTbl[High3]];
Prio =(Hign3<<3)+Low3;
嵌入式软件开发环境和调试技术【了解】
交叉编译:就是在一个平台上生成另一个平台上的可执行代码
远程调试:调试器运行于通用桌面操作系统的应用程序,被调试的程序则运行于基于特定硬件平台的嵌入式操作系统(目标操作系统)
Boot Loader程序设计【重点】
在操作系统内核运行之前运行的一段小程序,Boot Loader一般存储在0地址,系统加电后,CPU将首先执行 Boot Loader 程序
字符方式与用户进行交互
典型结构
stage1 | state2 |
---|---|
硬件设备初始化 (屏蔽中断,设置CPU速度,RAM初始化,LED初始化,关闭CPU指令) | 初始化本阶段要使用到的硬件设备 |
为加载 Boot Loader 的 stage2 准备 RAM 空间 | 检测系统内存映射(memory map) |
拷贝 Boot Loader 的 stage2 到 RAM 空间中 | 将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中 |
设置好堆栈 | 为内核设置启动参数 |
跳转到 stage2 的 C 入口点 | 调用内核 |
stage 1
- 汇编
- 简单的硬件初始化
stage 2
- C语言
- 不能使用 glibc 库中的任何支持函数
- trampoline(弹簧床)编程方式:用汇编将state2的main函数包裹起来,可以处理传递进来的参数,对main函数返回值进行处理
嵌入式操作系统内核设计
内存管理
32位地址长度,4GB虚拟空间
- 段映射:12位段表(段地址长12bit,端大小为1M)
- 粗页表映射:64k或4k/页
- 细页表映射:1k/页
模块机制
-
微内核(Micro-kernel) Windows
操作系统主要组件(内存管理器、进程管理器和I/O管理器)运行在独立进程 -
单一内核(monolithic-kernel) Linux
操作系统核心组件在同一进程实现
嵌入式文件系统设计
文件访问操作:
- 系统调用create, open, read, write, close操作
- 调用VFS层函数
- 通过file_opration指针调用具体的函数
基于Flash的文件系统
NOR Flash, NAND Flash不带Flash控制器,
SD, EMMC, SSD, USB 带Flash控制器
基于Block Device的文件系统
FAT、EXT2:逻辑块的大小是512B,写入和修改时单位为块
基于MTD的文件系统:
JFFS2:不适合NAND,NAND容量较大,导致日志文件太大,内存占用过多,挂载时扫描整个FLASH内容,简历日志节点,导致耗时较长
支持数据压缩
YAFFS2:不支持数据压缩,挂载时间短
UBIFS:更适合NAND,支持write-back,写入数据放入cache,
不需要扫描整个Flash,可以部分压缩
内存文件系统
Ramdisk:制度,不支持动态擦写,按顺序存放数据
Ramfs/tmpfs:随机页访问,只读
网络文件系统
Samba
NFS
嵌入式字符设备驱动程序设计【重点】
概念:
- 对设备的初始化和释放
- 把数据从内核传到硬件/从硬件读数据到内核
- 读取应用程序传送给设备文件的数据和回送应用程序请求的数据。这需要在用户空间,内核空间,总线以及外设之间传输数据
- 检测和处理设备出现的错误、
具体分类
- 字符设备:无需缓冲直接读写
- 块设备:通过buffer或cache进行读写,支持随机访问
- 网络设备:通过BSD套接口访问
每个设备文件都对应有两个设备号
- 主设备号,标识设备的种类,使用的驱动程序
- 次设备号,标识使用同一设备驱动程序的不同硬件设备
字符设备在Linux内核中使用struct cdev结构来表示
file_operations
//用来从设备中读取数据
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//用来向设备写入数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//用来打开设备
int (*open) (struct inode *, struct file *);
//用来关闭设备
int (*release) (struct inode *, struct file *);
file
file结构体在<linux/fs.h>中定义
f_mode: 文件读写模式
f_pos: 文件读写位置
f_flags: 文件标志
f_op: 文件关联操作
private_data: 系统调用信息
inode
是内核文件系统索引节点对象,包含内核在操作文件或目录时需要的全部信息
在内核中inode结构体用来表示文件,file是表示打开文件
的结构体
- dev_t i_rdev:设备号
- struct cdev *i_cdev:指向cdev结构的指针
最小内核模块:
#define CHRDEV_MAJOR 240 // 主设备号
#define CHRDEV_MAION 0 // 次设备号
#define CHRDEV_COUNT 1 // 次设备号个数
#define CHRDEV_NAME "testchrdev"
struct led_cdev
{
struct cdev chrdevcdev;
int major;
dev_t dev;
struct class *led_dev_class;
struct semaphore led_sem;// 定义信号量
};
static struct led_cdev leddev;
ssize_t chrdev_read (struct file *file, char __user *usr, size_t size, loff_t *loff)
{
printk("%s\r\n",__func__);
return 0;
}
int chrdev_open (struct inode *inode, struct file *file)
{
if (down_trylock(&leddev.led_sem) != 0) { // 当应用程序打开文件时会尝试申请信号量
return -EBUSY; // 当信号量已经申请完时,就返回错误码
}
file->private_data = &leddev;
return 0;
}
int chrdev_release (struct inode *inode, struct file *file)
{
struct led_cdev *led_private_data = (struct led_cdev *)file->private_data;
up(&led_private_data->led_sem); // 当应用程序关闭文件时释放信号量
return 0;
}
struct file_operations fops =
{
.open = chrdev_open,
.read = chrdev_read,
.release = chrdev_release,
};
static int __init chrdev_init(void)
{
int ret = 0,error = 0;
struct device *devices;
//DEBUG_SFLR("%s\r\n",__func__);
error = alloc_chrdev_region(&leddev.dev,CHRDEV_MAION,CHRDEV_COUNT,CHRDEV_NAME); // 注册设备号
printk("MAJOR = %d MINOR = %d\r\n",MAJOR(leddev.dev),MINOR(leddev.dev));
if(error < 0){
printk("alloc_chrdev_region error\r\n");
ret = -EBUSY;
goto fail;
}
leddev.major = MAJOR(leddev.dev);
cdev_init(&leddev.chrdevcdev, &fops); // 绑定字符设备操作函数集
error = cdev_add(&leddev.chrdevcdev,leddev.dev,CHRDEV_COUNT); // 添加字符设备
if(error < 0){
printk("cdev_add error\r\n");
ret = -EBUSY;
goto fail1;
}
// 创建类,类名为testledclass
leddev.led_dev_class = class_create(THIS_MODULE, "testledclass");
if (IS_ERR(leddev.led_dev_class)){
printk("class_create error\r\n");
ret = -EBUSY;
goto fail2;
}
// 创建设备
devices = device_create(leddev.led_dev_class, NULL, MKDEV(leddev.major,0), NULL, "testled");
if(NULL == devices){
printk("device_create error\r\n");
ret = -EBUSY;
goto fail3;
}
sema_init(&leddev.led_sem,2); // 初始化信号量
return 0;
fail3:
class_destroy(leddev.led_dev_class);/* 删除类 */
fail2:
cdev_del(&leddev.chrdevcdev);/* 删除cdev */
fail1:
unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
fail:
return ret;
}
static void __exit chrdev_exit(void)
{
//DEBUG_SFLR("%s\r\n",__func__);
device_destroy(leddev.led_dev_class,MKDEV(leddev.major,0));/* 卸载设备 */
class_destroy(leddev.led_dev_class);/* 删除类 */
cdev_del(&leddev.chrdevcdev);/* 删除cdev */
unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_DESCRIPTION("xxxxxx");
MODULE_AUTHOR("xxxxxx");
MODULE_LICENSE("GPL");
内核编译:
make -C KERNER_DIR M=$shell(pwd) modules
内核装载和卸载:
lnsmod module.ko
rmmod module.ko
内核模块开发时需要的机制:了解进程间通信,
同步机制和通信
重点,几部分组成,每部分的作用、实验是重点
嵌入式块设备驱动程序设计
块设备数据存取的单位是块,块的大小通常为512字节到
32K字节不等;块设备每次能传输一个或多个块,支持随机
访问,并采用了缓存技术
字符设备以字节为单位进行读写,块设备则以块为单位
块设备还支持随机访问,而字符设备只能顺序访问
嵌入式网络设备驱动程序设计
网线=>变压器=>PHY芯片=>MAC芯片=>PCI总线
MAC芯片:实现MAC子层和LLC子层的功能,提供PCI界面
PHY芯片主要负责数据收发:CSMA/CD、模数转换、编解码、串并转换等
嵌入式数据库技术
概念
- 安装在嵌入式设备中
- 通常与操作系统和具体应用集成
特点
- 占用存储空间小
- 可靠性、可管理性和安全性
- 互操作性和可移植性
- 可剪裁性
关键技术
- 数据库微型化:关键模式优化和数据压缩
- 数据同步技术:同步冲突检测
- 系统定制能力:和应用程序结合
- 系统实时处理能力:实时应用需要数据库实时处理能力
例题
- bootloader两阶段的功能?
state1 硬件初始化、准备RAM空间,将state2代码拷贝到RAM中、设置堆栈、跳到C语言函数入口
state2 初始化本阶段硬件、检测内存映射、加载内核和根文件系统代码、设置内核启动参数、调用内核
- 解释交叉编译并举例:
在一个平台编译生成另一个平台的可执行代码
比如在arm64机器上编译生成arm32代码,在x86机器上编译生成arm32代码
- BSP,驱动程序,bootloader,HAL的含义?
BSP: ?
驱动程序:内核的一部分,提供设备的初始化和释放,设备和内核的数据交换,设备和应用软件的数据交换,出错控制
bootloader:在操作系统开始运行前运行的一小段代码
HAL: 硬件抽象层
- ucOS2的优先级任务算法具体是怎样的?以优先级12为例进行说明?
计算出12所在的行:12=0x001100,行=1,列=4,
设置就绪队列:OSRdyGrp | = 2^1, OSRdyTbl[1] |= 2^4
取消就绪队列:OSRdyTbl[1] &= ~16, if(OSRdyTbl[1]==0) OSRdyGrp &= ~2
- 以优先级21为例进行说明:
计算出21所在的行列:21=0x0001 0101,行=2,列=5
设置就绪队列:OSRdyGrp |= 2^2=4, OSRdyTbl[2] |= 2^5 = 32
取消就绪队列:OSRdyTbl[2] &= ~32, if(OSRdyTbl[2]==0) OSRdyGrp &= ~4
- 加入OSRdyGrp==100 100b, OSRdyTbl[2]=
0x12,求最高优先级:
通过OSRdyGrp右侧第一个1确定行:2,通过OSRdyTbl[2]右侧第一个1确定列:0x12=0b0001 0010, 列=1,则优先级=2*8+1=17
- 写⼀条 ARM 指令,完成操作r1 = r2 * 3
注意是一条:MUL R1, R2, 3
或者是 ADD R1, R2, R2, LSL #1
- 初始值R1=23H,R2=0FH执⾏指令BIC R0,R1,R2,LSL #1后,寄存器R0,R1的值分别是多少?
R2 LSL 1后 R2=b1 1110, BIC指令将R2对应位为1的值在R1上清0,R1=0b0010 0011
BIC 后: 0010 0001 ,R0=0x21, R1=0x23
- 说明指令STMIA r12!, {r0-r11}的操作功能
R0-> [R12], R1->[R12+4], …, R11->[R12+44], i指的是地址增加,a指的是第一个地址不增
10.以下哪种⽅式不是uc/os操作系统中任务之间通信⽅式。
(A) 信号量
(B) 消息队列
(C) 邮件
(D) 邮箱
ucOS通信方式:信号量、邮箱、消息队列,事件标志
- nand 和 nor的区别:
nand | nor | |
---|---|---|
价格 | 便宜 | 贵 |
容量 | 大 | 小 |
擦写次数 | 多 | 少 |
擦写速度 | 快 | 慢 |
读取速度 | 慢 | 快 |
读取单位 | 字节 | 字节 |
写单位 | page | 字节 |
擦除 | block | block |