1. 什么是嵌入式系统?
---- 以应用为中心,以计算机技术为基础,软硬件可裁减,以适应应用系统对功能、可靠性、成本、
---- 体积和功耗等有严格要求的专用计算机系统。
---- 嵌入的意义:嵌入到目标系统中的计算机系统
2. 最小系统由哪些部分组成?
---- MCU/MPU,电源系统,时钟系统,复位系统,下载调试系统,FLASH,RAM
3. 什么是 ARM?
---- 高级精简指令集机器,体系结构
---- 英国的一家芯片公司,使用 chipless 模式销售。
4. Cortex-A 系列用于什么产品?
---- 应用型:手机,平板
5. Cortex-R 系列用于什么产品?
---- 实时场景:汽车制动系统、汽车安全气囊,打印机,网络设备
6. Cortex-M 系列用于什么产品?
---- 价廉物美,价格低、性能高:无人机,智能手表
7. Cortex-M3 的体系结构是什么?
---- ARMv7M
8. STM32F103RC 的内核是什么?
---- Cortex-M3
9. 每个芯片一般包含哪几个层次的型号/版本号?
---- 芯片型号,如 STM32F103RCT6
---- 内核版本号,如 Cortex-M3
---- 体系结构版本号,如 ARMv7M
10. mcuisp 工具烧录需要什么文件?
---- .hex 文件
11. J-Link 烧录需要什么文件?
---- .bin 或 .axf
12. Keil 可以烧录程序到开发板吗?
---- 可以,通过 ST-Link 或 J-Link,等等
13. 由 .axf 文件生成 .bin 文件的命令?
---- Keil安装路径\ARM\ARMCC\bin\fromelf.exe --bin -o 输出文件路径\文件名.bin 输出文件路径\文件名.axf
---- C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Objects\demo.bin .\Objects\demo.axf
---- Keil 默认生成 .axf 文件,如果需要生成 .hex 文件,必须勾选 Output 选项卡下的 Create HEX file 选项
---- 如果要生成 .bin 就必须执行 fromelf 命令
====================================================================================
1. 交叉开发
在办公系统中进行软件开发(编辑、编译),下载程序到目标系统中运行、调试,如果发现 BUG,
回到办公系统中再次编辑、编译,然后再次下载,这样重复在两个系统中进行的开发称为交叉开发。
交叉开发工具:
软件:Keil,usb 烧录器 mcuisp.exe,
硬件:USB 线,ST-Link 烧录调试设备,J-Link 烧录调试设备
2. 软件开发前的准备工作
环境搭建:开发软件 Keil、IAR 等。安装板级支持包。
烧录工具:软件 keil/mcuisp,硬件 ST-Link、J-Link、USB 线
硬件:产品(开发板、传感器)
需求:完成的功能、功耗等
资料:原理图,MCU 芯片用户手册、数据手册、编程手册,其他外设、传感器手册。
获取渠道:官网、百度、谷歌、供应商
3. GPIO 通用 IO
GPIO 是 MCU 中的一种设备,通过它可以直接控制 MCU 的 IO 口(引脚)
STM32F103RCT6 有 GPIOA, GPIOB, GPIOC, GPIOD 共 4 个 GPIO 接口。
每个 GPIO 接口最多可以管理 16 个 IO 口。
对 GPIO 接口的控制,可以通过 7 个寄存器来实现:
. 两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH)
. 两个16位数据寄存器(GPIOx_IDR,GPIOx_ODR)
. 一个32位置位/复位寄存器(GPIOx_BSRR),
. 一个16位复位寄存器(GPIOx_BRR)
. 一个32位锁定寄存器(GPIOx_LCKR)
复用功能:一脚多用(通用功能,外设功能)。比如:
14 号引脚,通用 IO 是 PA0,外设功能有:
WKUP
USART2_CTS
ADC123_IN0
TIM2_CH1_ETR
TIM5_CH1
TIM8_ETR
4 种输入模式:
─ 输入浮空(既不上拉也不下拉,状态未定,由外部电路决定输入状态)
─ 输入上拉(内部接上拉电阻)
─ 输入下拉(内部接下拉电阻)
─ 模拟输入
4 种输出模式:
─ 开漏输出(用于 GPIO 通用功能)
─ 推挽式输出(用于 GPIO 通用功能)
─ 推挽式复用功能(用于外设)
─ 开漏复用功能(用于外设)
开漏可以实现:线与功能,电流型驱动。
一般输出数字信号时配置为推挽,需要“线与”功能时配置为开漏。
一个 IO 口需要 4 个位来配置模式,16 个 IO 口需要 64 个位,因此共需要两个 32 位寄存器,
CRL 寄存器配置 IO0 ~ IO7,CRH 寄存器配置 IO8 ~ IO15。
对输出数据寄存器 ODR 写的值,可以通过 ODR 读取。
为什么要通过 BSRR 寄存器和 BRR 寄存器来操作 ODR?
确保对 ODR 的写入是原子的,中途不会被中断,因此软件不需要因为对 ODR 的写入而禁止中断。
当对GPIOx_ODR的个别位编程时,软件不需要禁止中断:在单次APB2写操作里,可以只更改一个或多个位。
这是通过对“置位/复位寄存器”(GPIOx_BSRR,复位是 GPIOx_BRR)中想要更改的位写’1’来实现的。
没被选择的位将不被更改。
标准库中将对寄存器的操作封装在函数中,因此调用标准库函数也就操作了寄存器。
4. volatile 的作用
编译器可能对变量有以下优化:
. 对同一变量的连续多次赋值时,可能只保留最后一次赋值,而优化掉前面几步的赋值。如:
a = 1; // 优化删除
a = 2; // 优化删除
a = 3; // 优化删除
a = 4; // 只保留这一次赋值
. 重新排列对变量操作的语句
. 对变量的访问,为了加快速度,将变量保存到高速缓存中,之后从高速缓存中访问这个变量
使用 volatile 修饰的变量,编译器就不会进行以上优化。
5. 复用功能重映射
一个功能可以由多个引脚去完成,称为复用功能重映射。
复用功能重映射需要使用 AFIO 寄存器,因此需要使能 AFIO 时钟。
调用 void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) 函数进行重映射:
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);
6. KEIL 自带的模拟器是使用软件来模拟芯片的通用 IO,无法模拟外设。
1. 位带/位段
位带区域:可以直接操作某个位,但不能直接操作需要通过它的别名区域进行操作
有两个位带区域:
位带区域 位带别名区域
0x20000000-0x200FFFFF(1M) 0x22000000-0x23FFFFFF(32M) SRAM地址
0x40000000-0x400FFFFF(1M) 0x42000000-0x43FFFFFF(32M) 外设寄存器地址
通过位带别名区域的一个字(32位)来操作位带区域的一个位(32:1)
字节:8 位
半字:16 位,2 个字节
字: 32 位,4 个字节
位带区域映射到别名区域的公式:
别名区域位映射字偏移量 bit_word_offset = (byte_offset x 32) + (bit_number x 4)
别名区域位映射字地址 bit_word_addr = bit_band_base + bit_word_offset
其中 bit_band_base 分别是 0x22000000 和 0x42000000
byte_offset 是字节偏移量,即字节相对于起始地址的偏移量,如:0x40013800 中的 0x13800 即是。
bit_number 位编号
2. 启动模式
三种启动模式:用户闪存存储器,系统存储器,SRAM
无论哪一种启动模式,最终都是映射到 0x00000000 地址执行。
用户闪存存储器保存我们录到 MCU 的程序。
系统存储器保存芯片生产线上烧录的启动程序。
SRAM 是将程序复制到其内运行。
选择启动模式:
boot1 boot0 启动位置 存储地址
x 0 用户闪存存储器 0x08000000
0 1 系统存储器 0x1FFFFFFF
1 1 SRAM 0x20000000
3. 启动流程
0x00000000 地址读取栈顶指针(加载到 SP 寄存器)
0x00000004 地址读取复位处理函数 Reset_Handler(),并跳转执行(加载到 PC 寄存器)
Reset_Handler() 函数中先调用 SystemInit() 函数,结束后返回;再调用 main() 函数,永不返回。
SystemInit() 函数中主要做三件初始化工作:设置系统时钟,配置 FLASH,配置外部 SRAM。
4. 复位
三种复位方式:系统复位、电源复位、备份域复位。
系统复位将复位除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器以外的所有寄存器
当以下事件中的一件发生时,产生一个系统复位:
1. NRST管脚上的低电平(外部复位)
2. 窗口看门狗计数终止(WWDG复位)
3. 独立看门狗计数终止(IWDG复位)
4. 软件复位(SW复位)
5. 低功耗管理复位
可通过查看RCC_CSR控制状态寄存器中的复位状态标志位识别复位事件来源。
其中软件复位:
通过将Cortex™-M3中断应用和复位控制寄存器(SCB_AIRCR)中的SYSRESETREQ位置’1’,可实现软件复位。
其中低功耗管理复位:
在以下两种情况下可产生低功耗管理复位:
1. 在进入待机模式时产生低功耗管理复位:
通过将用户选择字节中的nRST_STDBY位置’1’将使能该复位。这时,即使执行了进入待机
模式的过程,系统将被复位而不是进入待机模式。
2. 在进入停止模式时产生低功耗管理复位:
通过将用户选择字节中的nRST_STOP位置’1’将使能该复位。这时,即使执行了进入停机模
式的过程,系统将被复位而不是进入停机模式。
电源复位:
当以下事件中之一发生时,产生电源复位:
1. 上电/掉电复位(POR/PDR复位)
2. 从待机模式中返回
电源复位将复位除了备份区域外的所有寄存器。
备份域复位:
当以下事件中之一发生时,产生备份区域复位。备份域复位只影响备份区域。
1. 软件复位,备份区域复位可由设置备份区域控制寄存器RCC_BDCR中的BDRST位产生。
2. 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。
core_cm3.h 中定义了以下软件复位函数:
#define SCB_AIRCR_VECTKEY_Pos 16
#define SCB_AIRCR_PRIGROUP_Pos 8
#define SCB_AIRCR_PRIGROUP_Msk (7ul << SCB_AIRCR_PRIGROUP_Pos)
#define SCB_AIRCR_SYSRESETREQ_Pos 2
#define SCB_AIRCR_SYSRESETREQ_Msk (1ul << SCB_AIRCR_SYSRESETREQ_Pos)
static __INLINE void NVIC_SystemReset(void)
{
SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | /* 对 VECTKEY 域写 0x5FA 关键字 */
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | /* 保持优先级分阻不变 */
SCB_AIRCR_SYSRESETREQ_Msk); /* 将 SYSRESETREQ 位置 1 */
__DSB(); /* 设置内存屏障,确保之前的变量访问完成 */
while(1); /* 等待复位 */
}
对 SCB->AIRCR 寄存器写操作时,必须对高 16 位(VECTKEY域)写 0x5FA 关键字。
5. 时钟
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
● HSI振荡器时钟(内部高速 RC 时钟,如 8MHz)
● HSE振荡器时钟(外部高速晶振时钟,如 8MHz)
● PLL(锁相环)时钟(引入时钟源进行倍频产生系统所需的高速时钟)
这些设备有以下2种二级时钟源:
● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/
待机模式下自动唤醒系统。(LSI 内部低速 RC 时钟源)
● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。(LSE 外部低速晶振时钟源)
当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功耗。
SystemInit() 函数内调用 SetSysClock() 函数设置系统时钟。
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* 如果以上宏都没定义,则使用 HSI 作为系统时钟源 */
/* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
以上宏只使能了 SYSCLK_FREQ_72MHz,说明系统使用 72MHz 时钟。
SetSysClockTo72() 函数中实现以下功能:
1. 使能 HSE
2. 等待外部高速时钟 HSE 稳定
3. 配置 FLASH
4. 配置总线时钟:HCLK = SYSCLK / 1,PCLK2 = HCLK / 1,PCLK1 = HCLK / 2。
其中 HCLK 是 AHB 总线时钟,PCLK2 是 APB2 总线时钟,PCLK1 是 APB1 总线时钟
5. 配置 PLL 为 HSE 的 9 倍频:PLLCLK = HSE * 9 = 72 MHz
6. 使能 PLL
7. 等待 PLL 就绪
8. 选择 PLL 为系统时钟源 SYSCLK
9. 等待 PLL 成为系统时钟源
各总线频率:
SYSCLK = 72MHz
HCLK = SYSCLK / 1 = 72MHz
PCLK2 = HCLK / 1 = 72MHz
PCLK1 = HCLK / 2 = 72 / 2 = 36MHz
6. 系统嘀嗒定时器
是一个 24 位递减计数的定时器。
CTRL 寄存器可:
使能/关闭定时器 (第 0 位)
使能/禁用中断 (第 1 位)
配置时钟源(AHB, AHB/8)(第 2 位)
获取/清除到期标志 (第 16 位)
LOAD 寄存器保存计数值,开始计数时将其值加载到 VAL 寄存器中。
假如计数值为 N,如果连续计数就设置为 "N - 1",如果是单次计数就设置为 "N"。
注意这是一个 24 位的寄存器,因此可设置的最大值为 0x00FFFFFF。
VAL 寄存器用于递减计数,它的初始值从 LOAD 寄存器加载。当计数到 0 时,会自动设置 CTRL 的 16 位。
注意这是一个 24 位的寄存器,因此最大加载值为 0x00FFFFFF。