目录
1、嵌入式c编程基础知识
1.1 C的关键词
关于static:
关于define:
关于const:
关于inline:
关于volatile:
1.2 关于大小端
手写库函数 strcpy,strncpy,memcpy,memset
https://blog.csdn.net/tsh123321/article/details/52263707
题目:
1、在一台64位的机器上,使用32位编译,Garfield 变量占用多少内存空间?64位编译又是如何?
2. 描述下面XXX 这个宏的作用。(总分10分)
1.3 总线回顾
1.3.1 SPI
1.3.2 I2C
1.4 CPU架构(x86与x86_64,Intel与ARM)
1.4.1 RISC与CISC造成的区别
1.5 STM32-ARM知识
1.5.1 stm32启动分析
面试问题:如果单片机启动不了,可能的原因?
1.6 Linux启动过程
1.6.1 BIOS自检
1.6.2 bootloader
1.6.3 系统初始化
2、编程
2.1、输入与输出
2.2、编程题
请写一个函数,将一个16进制字符串转换为数字
3、简述
4、汇编
嵌入式软件工程师经典笔试题
程序编译时各部分介绍:
动态存储区、静态存储区、堆和栈的区别
空指针和void *类型指针
联合体判断大小端
修饰函数:使函数只能被其所在的文件访问,其他c文件不能访问static修饰的函数;
修饰变量:
define 的括号里的参数表示变量
常量指针和指针常量:
char *str = "Hello World";
//定义常量指针,即指针指向的对象是常量
const char *pstr = str;
printf("%c\n", *pstr);
//*pstr = 'S';//error,指针指向的对象不可变
printf("%c\n", *++pstr);//right,指针本身可变
//定义指针常量 ,指针是 常量
char* const ppstr = str;
//ppstr++;//error
*ppstr = 'N';//right
参考:inline
简单概括,inline修饰的内联函数在编译器编译时,将原本程序中的函数 调用步骤直接替换成了函数本体,因此减少了原本调用步骤产生的额外开销;但是缺点就是导致了代码膨胀!
volatile为什么要修饰中断里的变量
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
大端:数据的高位存在内存的低字节,典型的如ARM、PowerPC
小端:数据的低位存在内存的高字节,典型的如Intel
注意:大端小端是字节存储位置的不同,而不是每一个bit不同
例如:a=0x12345678
将a分别在大端处理器和小端处理器中写入内存0x000,那么其存储如下:
内存地址 0x0000 0x0001 0x0002 0x0003 大端 0x78 0x56 0x34 0x12 小端 0x12 0x34 0x56 0x78 那么如果在内存地址0x0000-0x0003中分别存储0x12 0x34 0x56 0x78,
则小端读取得到:0x12345678
而大端读取得到:0x78563412
因为字节序只是对内置的数据类型而言(int、short、double、long....char),而对于char而言由于其本身只有一个字节则字节序和存储模式对其不影响。(因此字符串在跨平台传输时不用考虑字节序)
因此网络通讯时常常要将字节序转化成网络字节序(大端)
再举一个例子:
struct stucA{ unsigned int a:1; unsigned int b:2; unsigned int c:3; unsigned int d:4; unsigned int e:5; unsigned int f:6; unsigned int g:11; }A; memset(&A,0,sizeof(A)); A.d=11;
在小端中:a:0,b:00,c:000,d:1101,e,f,g都为0,因为小端中数据低位存在内存低位,因此数据存储(高位地址->低位地址)为:
0000 0000 0000 0000 0000 0010 1100 0000,即使从低位到高位分别为 0xc0,0x02,0x00,0x00;
在大端中:因为数据的高位存在内存的低位,则数据存储(高位地址->低位地址)为:
0000 0011 0100 0000 0000 0000 0000 0000,按照每位数字来看,刚好与小端模式完全相反,但是!,大端中字节的读取方式也是高位数据在低位地址,因此 得到数据(从低位地址到高位地址依次显示)为 0x00 0x00 0x02 0xc0.
可以用以下代码测试:
#include
#include #include using namespace std; struct strA { unsigned int a : 1; unsigned int b : 2; unsigned int c : 3; unsigned int d : 4; unsigned int e : 5; unsigned int f : 6; unsigned int g : 11; }; struct ch { char c[4]; }; union test { strA A; ch C; }T; int main() { memset(&T, 0, sizeof(T)); T.A.d = 11; for (int i = 0; i < 4; i++) printf("%02x\n", (unsigned)(unsigned char)T.C.c[i]);//以16进制输出,不足两位前面补0 getchar(); }
struct CAT_s{
int ld;//32
char Color;、、
unsigned short Age;
char *Name;
void(*Jump)(void);
}Garfield;
数据类型 | 32位编译器/字节 | 64位编译器/字节 | 备注 |
char | 1 | 1 | 例如:0xff |
short | 2 | 2 | 例如:0xffff |
int | 4 | 4 | 例如:0xffff ffff |
long | 4 | 8 | |
float | 4 | 4 | |
char* | 4 | 8 | 实际指向的是一个地址,地址字节数由编译器决定 |
long long | 4 | 8 | |
double | 8 | 8 | |
long double | 10/12 | 10/16 | 有效字节10位,但 为了对齐实际分配12/16字节 |
所以使用32位编译器时:
- 1个int 4字节;
- char、unsigned shore 共3字节,为了对齐,算4字节;
- char* 4字节;void * 4字节,
合计16字节
所以使用64位编译器时(8字节/64位对齐):
- 1个int 4字节,char、unsigned shore 共3字节,为了对齐,合计算8字节;
- char* 8字节;
- void * 8字节,
合计24字节
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define XXX(ptr,type,member) ({\
const typeof(((type*)0)->member)*__mptr=(ptr);\
(type*)(char*)__mptr – offsetof(type,member));})
offsetof(type,member));})
第一条语句:
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
TYPE代表一个结构体,MEMBER代表结构体成员,因此这条宏定义强行将结构体起始地址定义为0,那么member就代表成员地址的偏移量;
步骤如下:
第二条语句:
3、在一个多任务嵌入式系统中,有一个CPU 可直接寻址的32位寄存器REGn ,地址为 0x1F000010,编写一个安全的函数,将寄存器REGn 的指定位反转(要求保持其他bit 的值不变)
void bit_reversal(uint32_t nbit)
{
*((volatial unsigned int *)0x1F000010)^=0x01<
((volatial unsigned int *)0x1F000010)为地址定义,例如(int *)0x1F111111,代表可寻址的地址。
4、 有10000个正整数,每个数的取值范围均在1到1000之间,变成找出从小到大排在第 3400(从0开始算起)的那个数,将此数的值返回,要求不使用排序实现。(总分10分)
输入参数 数组a,数组长度n ,要查找的第index个数;返回:0 -未找到 i-第index数的数值
int fun_find(int *a, int n, int index)
{
int c = 0;
int count[1001] = {0};
for (int i = 0; i < n; i++)
{
count[a[i]]++;
}
for (int i = 0; i < 1001; i++)
{
c += count[i];
if (c >= index)
return i;
}
return 0;
}
接口:SDI、SDO、SCLK、CS
特点:有主从之分、全双工、高速、速度遵从事实标准
面试题之坑:SPI速率是多少?
答:。SPI是一种事实标准,由Motorola开发,并没有一个官方标准。已知的有的器件SPI已达到50Mbps。具体到产品中SPI的速率主要看主从器件SPI控制器的性能限制。
- SPI的最大时钟频率
- CPU处理SPI数据的能力
- 输出端驱动能力(PCB所允许的最大信号传输速率)
因此最多可以挂载2^7=128个设备,同一种设备最多挂在2^3=8个。I2C传输速率有三个标准!
最大的区别:Intel采用小端结构,复杂指令集(CISC);ARM采用大端结构,精简指令集(RISC)
Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。
因此STM32启动文件,主要做了3个工作:
其中,复位向量的地址根据BOOT的不同而不同。
启动模式选择引脚 |
启动模式 | 说明 | 地址 | |
BOOT1 | BOOT0 | |||
X | 0 | 主闪存存储器 | 主闪存存储器作为启动区域 | 0x8000000 |
0 | 1 | 系统存储器 | 系统存储器作为启动区域 | |
1 | 1 | 内置SRAM | 内置SRAM作为启动 | 0x2000000 |
这篇博文讲的很详细:STM32F407启动流程浅析
启动汇编代码如下:
总的概括起来,stm32启动步骤如下:
;******************** (C) COPYRIGHT 2013 STMicroelectronics ********************
;* File Name : startup_stm32f40_41xxx.s
;* Author : MCD Application Team
;* Version : V1.3.0
;* Date : 08-November-2013
;* Description : STM32F40xxx/41xxx devices vector table for MDK-ARM toolchain. STM32F40/41XX器件向量表
;* This module performs: stm32启动以后模块执行以下操作
;* - Set the initial SP 设置初始化的SP指针
;* - Set the initial PC == Reset_Handler 设置初始PC--->指向Reset_Handler
;* - Set the vector table entries with the exceptions ISR address 设置具有异常ISR地址的向量表条目
;* - Configure the system clock and the external SRAM mounted on STM324xG-EVAL board to be used as data memory (optional, to be enabled by user)
配置系统时钟和外部SRAM并挂载到STM324xG-EVAL板用作数据存储器(可选,可由用户设置)
;* - Branches to __main in the C library (which eventually 分支跳转到C库中的_main(最终调用main())
;* calls main()).
;* After Reset the CortexM4 processor is in Thread mode, 复位后CortexM4处理器处于线程模式,
;* priority is Privileged, and the Stack is set to Main. 优先级是Privileged,并且Stack被设置为Main。
;* <<< Use Configuration Wizard in Context Menu >>>
;*******************************************************************************
;
; Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
; You may not use this file except in compliance with the License.
; You may obtain a copy of the License at:
;
; http://www.st.com/software_license_agreement_liberty_v2
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
;*******************************************************************************
; Amount of memory (in bytes) allocated for Stack 为堆栈分配的内存量(以字节为单位)
; Tailor this value to your application needs 根据您的应用程序需求定制此值
; Stack Configuration
; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
Stack_Size EQU 0x00000400 设置堆栈大小为0X400(1k) EQU伪指令,作用是左边的符号名代表右边的表达式
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定义栈段:名称为STACK,未初始化,可读写,ELF 的栈段按2^3=8对齐
Stack_Mem SPACE Stack_Size 分配一片连续的存储区域并初始化为 0,栈空间:0x400个字节
__initial_sp ;栈顶空间地址(低地址)?
; Heap Configuration ;堆定义
; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ;堆空间起始地址
Heap_Mem SPACE Heap_Size ;堆空间:0x200个字节
__heap_limit ;堆空间结束地址
PRESERVE8 ;PRESERVE8 指令指定当前文件保持堆栈八字节对齐
THUMB ;告诉汇编器下面是32位的Thumb指令,如果需要汇编器将插入位以保证对齐
; Vector Table Mapped to Address 0 at Reset ;中断向量表定义;向量表在复位时映射到地址0
实际上是在CODE区(假设STM32从FLASH启动,则此中断向量表实际起始地址即为0x8000000,映射到0)
AREA RESET, DATA, READONLY ;定义一块数据段,只可读,段名字是RESET
EXPORT __Vectors ;EXPORT:在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
EXPORT __Vectors_End ;在程序中声明一个全局的标号__Vectors_End
EXPORT __Vectors_Size ;在程序中声明一个全局的标号__Vectors_Size ;DCD(DCDU)用于分配一片连续的字存储单元并用指定的数据初始化。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
假如: 0x8000000地址存放的是栈顶地址__initial_sp,
0x8000004地址存放的是复位中断向量Reset_Handler(STM32使用32位总线,因此存储空间为4字节对齐)。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
大BOSS:向量表 这个向量表的编写是有讲究的,跟硬件一一对应不能乱写的,CPU找入口地址就靠它了,bin文件开头就是他们的地址
__Vectors DCD __initial_sp ; Top of Stack ;该处物理地址值存储---栈顶地址
__initial_sp所表示的地址值,即为 __Vetors 标号所表示的值
DCD Reset_Handler ; Reset Handler
上电后根据boot引脚来决定PC位置,比如boot设置为flash启动,则启动后PC跳到0x08000000。
此时CPU会先取2个地址,第一个是栈顶地址,第二个是复位异常地址,故有了上面的写法,这样就跳到reset_handler。
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
当发生硬件错误中断时,调转到中断错误处理函数--->stm32f4xx_it.c--->
void HardFault_Handler(void) //可以发现这个函数是一个什么也不做的死循环,所以发生硬件
{ //错误时代码会跑死
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
//CODE可以添加 如果有必要,可以在这种中断处理函数中添加自己的coding
}
}
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window WatchDog ;以下为外部中断向量表
DCD PVD_IRQHandler ; PVD through EXTI Line detection
DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line
DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line0
DCD EXTI1_IRQHandler ; EXTI Line1
DCD EXTI2_IRQHandler ; EXTI Line2
DCD EXTI3_IRQHandler ; EXTI Line3
DCD EXTI4_IRQHandler ; EXTI Line4
DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0
DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1
DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2
DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3
DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4
DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5
DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6
DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s
DCD CAN1_TX_IRQHandler ; CAN1 TX
DCD CAN1_RX0_IRQHandler ; CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; External Line[9:5]s
DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9
DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10
DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; External Line[15:10]s
DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line
DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line
DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12
DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13
DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0
DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1
DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2
DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3
DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4
DCD ETH_IRQHandler ; Ethernet
DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line
DCD CAN2_TX_IRQHandler ; CAN2 TX
DCD CAN2_RX0_IRQHandler ; CAN2 RX0
DCD CAN2_RX1_IRQHandler ; CAN2 RX1
DCD CAN2_SCE_IRQHandler ; CAN2 SCE
DCD OTG_FS_IRQHandler ; USB OTG FS
DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5
DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6
DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7
DCD USART6_IRQHandler ; USART6
DCD I2C3_EV_IRQHandler ; I2C3 event
DCD I2C3_ER_IRQHandler ; I2C3 error
DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out
DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In
DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI
DCD OTG_HS_IRQHandler ; USB OTG HS
DCD DCMI_IRQHandler ; DCMI
DCD CRYP_IRQHandler ; CRYP crypto
DCD HASH_RNG_IRQHandler ; Hash and Rng
DCD FPU_IRQHandler ; FPU
__Vectors_End ;Vectors结束
__Vectors_Size EQU __Vectors_End - __Vectors ;得到向量表的大小,304个字节也就是0x130个字节(以上代码累加) 4*(142-66)
AREA |.text|, CODE, READONLY ;定义一个代码段,可读,段名字是.text
;|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段
; Reset handler (复位中断发生以后执行以下过程) ;利用PROC、ENDP这一对伪指令标记程序开始、结束,把程序段分为若干个过程,
使程序的结构加清晰
Reset_Handler PROC
EXPORT Reset_Handler [WEAK] ;WEAK(弱声明)声明其他的同名标号优先于该标号被引用,
就是说如果外面声明了的话,调用外面的对应函数
IMPORT SystemInit ;IMPORT:(导出符号表供外部调用(系统初始化))伪指令用于通知编译器要使用的标号在其他的源文件中定义
IMPORT __main ;导出符号表
LDR R0, =SystemInit ---->存储器到寄存器的数据加载 system_stm32f4xx.c 中的void SystemInit(void)
设置微控制器系统初始化嵌入式Flash接口,PLL并更新 SystemFrequency变量。
BLX R0 ;带链接的跳转,同时切换指令集,跳到SystemInit
LDR R0, =__main ;系统初始化之后跳转到main函数执行
BX R0 ;切换指令集,main函数不返回跳到__main,进入C的世界
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK] 不可屏蔽中断处理函数
B .
ENDP
HardFault_Handler\ ;\意为换行连接符
PROC
EXPORT HardFault_Handler [WEAK] 硬件错误处理函数
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC (默认的处理函数---->导出符号表)
;输出异常向量表标号,方便外部实现异常的具体功能,
[WEAK]是弱定义的意思,如果外部定义了,优先执行外部定义,否则下面的函数定义(与上面相同,上面为定义,这边为导出)
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Stream0_IRQHandler [WEAK]
EXPORT DMA1_Stream1_IRQHandler [WEAK]
EXPORT DMA1_Stream2_IRQHandler [WEAK]
EXPORT DMA1_Stream3_IRQHandler [WEAK]
EXPORT DMA1_Stream4_IRQHandler [WEAK]
EXPORT DMA1_Stream5_IRQHandler [WEAK]
EXPORT DMA1_Stream6_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM10_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK]
EXPORT TIM8_UP_TIM13_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT DMA1_Stream7_IRQHandler [WEAK]
EXPORT FSMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_DAC_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Stream0_IRQHandler [WEAK]
EXPORT DMA2_Stream1_IRQHandler [WEAK]
EXPORT DMA2_Stream2_IRQHandler [WEAK]
EXPORT DMA2_Stream3_IRQHandler [WEAK]
EXPORT DMA2_Stream4_IRQHandler [WEAK]
EXPORT ETH_IRQHandler [WEAK]
EXPORT ETH_WKUP_IRQHandler [WEAK]
EXPORT CAN2_TX_IRQHandler [WEAK]
EXPORT CAN2_RX0_IRQHandler [WEAK]
EXPORT CAN2_RX1_IRQHandler [WEAK]
EXPORT CAN2_SCE_IRQHandler [WEAK]
EXPORT OTG_FS_IRQHandler [WEAK]
EXPORT DMA2_Stream5_IRQHandler [WEAK]
EXPORT DMA2_Stream6_IRQHandler [WEAK]
EXPORT DMA2_Stream7_IRQHandler [WEAK]
EXPORT USART6_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_OUT_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK]
EXPORT OTG_HS_WKUP_IRQHandler [WEAK]
EXPORT OTG_HS_IRQHandler [WEAK]
EXPORT DCMI_IRQHandler [WEAK]
EXPORT CRYP_IRQHandler [WEAK]
EXPORT HASH_RNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
以下为只是定义而没有实现的空函数
WWDG_IRQHandler
PVD_IRQHandler
TAMP_STAMP_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Stream0_IRQHandler
DMA1_Stream1_IRQHandler
DMA1_Stream2_IRQHandler
DMA1_Stream3_IRQHandler
DMA1_Stream4_IRQHandler
DMA1_Stream5_IRQHandler
DMA1_Stream6_IRQHandler
ADC_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM9_IRQHandler
TIM1_UP_TIM10_IRQHandler
TIM1_TRG_COM_TIM11_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTC_Alarm_IRQHandler
OTG_FS_WKUP_IRQHandler
TIM8_BRK_TIM12_IRQHandler
TIM8_UP_TIM13_IRQHandler
TIM8_TRG_COM_TIM14_IRQHandler
TIM8_CC_IRQHandler
DMA1_Stream7_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Stream0_IRQHandler
DMA2_Stream1_IRQHandler
DMA2_Stream2_IRQHandler
DMA2_Stream3_IRQHandler
DMA2_Stream4_IRQHandler
ETH_IRQHandler
ETH_WKUP_IRQHandler
CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler
OTG_FS_IRQHandler
DMA2_Stream5_IRQHandler
DMA2_Stream6_IRQHandler
DMA2_Stream7_IRQHandler
USART6_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
OTG_HS_EP1_OUT_IRQHandler
OTG_HS_EP1_IN_IRQHandler
OTG_HS_WKUP_IRQHandler
OTG_HS_IRQHandler
DCMI_IRQHandler
CRYP_IRQHandler
HASH_RNG_IRQHandler
FPU_IRQHandler
B .
ENDP
ALIGN ;默认是字对齐方式,也说明了代码是4字节对齐的
;*******************************************************************************
; User Stack and Heap initialization 用户堆栈初始化
;*******************************************************************************
IF :DEF:__MICROLIB ;判断是否使用DEF:__MICROLIB(micro lib),如果勾选了micro lib
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE ;如果没有勾选micro lib
IMPORT __use_two_region_memory ;两区堆栈空间,堆和栈有各自的空间地址
EXPORT __user_initial_stackheap
__user_initial_stackheap
;标号__user_initial_stackheap,表示用户堆栈初始化程序入口
;此处是初始化两区的堆栈空间,堆是从由低到高的增长,栈是由高向低生长的,
两个是互相独立的数据段,并不能交叉使用。
LDR R0, = Heap_Mem ;保存堆起始地址
LDR R1, =(Stack_Mem + Stack_Size) ;保存栈结束地址
LDR R2, = (Heap_Mem + Heap_Size) ;保存堆结束地址
LDR R3, = Stack_Mem ;保存栈起始地址
BX LR
ALIGN
ENDIF
答:
参考文章:https://blog.csdn.net/u014379540/article/details/52243187
https://www.cnblogs.com/bluestorm/p/5981435.html
Linux启动过程可以简单由下图描述:
BIOS(basic Input/Output System),是计算机操作系统输入输出管理的一部分,目前大多存储与板上Flash-EEPROM上,里面内容可升级
BIOS分为两部分:
bootloader分为stag1和stag2两个阶段.
stag1:
- 初始化硬件设备(复位,设置CPU为超级保护模式,屏蔽看门狗,初始化内存控制器,...初始化串口打印信息...)
- 代码重定位,为加载stag2分配RAM空间(代码重定位主要检查自己是否在内存中。如果是跳到堆栈段(stack_setup代码段)设置堆栈,不是就加载自己到RAM空间。)
- 加载stag2到RAM
- 设置堆栈跳转到第二段代码入口
stag2:
- 初始化本阶段要使用到的硬件设备
- 检测系统内存映射(所谓内存映射就是指在整个4GB 物理地址空间中有哪些地址范围被分配用来寻址系统的RAM 单元。个人觉得是因为有一段地址用于DMA直接寻址,不经过cpu的mmu单元)
- 加载内核映像和根文件系统映像
- 设置内核的启动参数
- 启动内核
init进程起来后,系统启动的控制权移交给init进程。
/sbin/init进程是所有进程的父进程,当init起来之后,它首先会读取配置文件/etc/inittab,进行以下工作:
1)执行系统初始化脚本(/etc/rc.d/rc.sysinit),对系统进行基本的配置,以读写方式挂载根文件系统及其它文件系统,到此系统基本算运行起来了,后面需要进行运行级别的确定及相应服务的启动;
2)确定启动后进入的运行级别;
3) 执行/etc/rc.d/rc,该文件定义了服务启动的顺序是先K后S,而具体的每个运行级别的服务状态是放在/etc/rc.d/rcn.d(n=0~6)目录下,所有的文件均链接至/etc/init.d下的相应文件。
4)有关key sequence的设置
5) 有关UPS的脚本定义
6)启动虚拟终端/sbin/mingetty
7)在运行级别5上运行X
这时呈现给用户的就是最终的登录界面。
至此,系统启动过程完毕:)
输出各种进制:
int m = 255;
printf("m的十六进制:%x\n", m);
printf("m的十进制: %d\n", m);
printf("m的八进制: %o\n", m);
m的十六进制:ff
m的十进制: 255
m的八进制: 377
如果输入包含空格,例如输入“how are you”,用gets代替scanf;
字符串操作:
头文件string.h
内存分配:
头文件 malloc.h
算法:
#include
#include
int hextoint(char c)
{
if (c >= 'a'&&c <= 'z')
return (int)(c - 'a' + 10);
else if (c >= 'A'&&c <= 'Z')
return (int)(c - 'A' + 10);
else if (c >= '0'&&c <= '9')
return (int)(c - '0' + 0);
else return 0xffffffff;//错误
}
int stringtoint(char* a)
{
int sum = 0;
int n = strlen(a);
for (int i = 0; i
或者更简单的方法:
#include
#include
using namespace std;
int main()
{
int t;
while (cin >> hex >>t)
cout << t << endl;
return 0;
}
3.1. 简述处理器中断处理的过程(中断向量、中断保护现场、中断嵌套、中断返回等)。
中断向量:中断服务程序的入口地址。作用是:请求中断
当某一中断源需要CPU为其进行中断服务时,就输出中断请求信号,使中断控制系统的中断请求触发器置位,向CPU请求中断。系统要求中断请求信号一直保持到CPU对其进行中断响应为止。
中断响应:
CPU对系统内部中断源提出的中断请求必须响应,而且自动取得中断服务子程序的入口地址(入口地址一般存储在中断向量地址上),执行中断 服务子程序。对于外部中断,CPU在执行当前指令的最后一个时钟周期去查询INTR引脚,若查询到中断请求信号有效,同时在系统开中断(即IF=1)的情 况下,CPU向发出中断请求的外设回送一个低电平有效的中断应答信号,作为对中断请求INTR的应答,系统自动进入中断响应周期。
保护现场:
主程序和中断服务子程序都要使用CPU内部寄存器等资源,为使中断处理程序不破坏主程序中寄存器的内容,应先将断点处各寄存器的内容压入堆栈保护起来,再进入的中断处理。现场保护是由用户使用PUSH指令来实现的。
中断服务:
中断服务是执行中断的主体部分,不同的中断请求,有各自不同的中断服务内容,需要根据中断源所要完成的功能,事先编写相应的中断服务子程序存入内存,等待中断请求响应后调用执行。
恢复现场
当中断处理完毕后,用户通过POP指令将保存在堆栈中的各个寄存器的内容弹出,即恢复主程序断点处寄存器的原值。
中断返回
在中断服务子程序的最后要安排一条中断返回指令IRET,执行该指令,系统自动将堆栈内保存的 IP/EIP和CS值弹出,从而恢复主程序断点处的地址值,同时还自动恢复标志寄存器FR或EFR的内容,使CPU转到被中断的程序中继续执行
中断嵌套
是指中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行,这个过程就是中断嵌套。
2. 简述处理器在读内存的过程中,CPU 核、cache 、MMU 如何协同工作?画出CPU 核、 cache 、MMU(内存管理单元) 、内存之间的关系示意图加以说明(可以以你熟悉的处理器为例)。(总分10分)
3、请说明总线接口USRT 、I2C 、USB 的异同点(串/并、速度、全/半双工、总线拓扑等)
【C语言】编译链接的详细过程
程序编译,链接过程