1.基础知识
CPU:CPU
内存:SRAM
硬盘:FLASH
主板:外设
仪器仪表:电源/示波器/焊台
家用电器:空调/冰箱/洗衣机
工业控制:机器人/PLC/电梯
汽车电子:GPS/ABS/胎压监测
:数据与程序储存在同一存储器,分时复用
CoreMark分数:https://www.eembc.org/coremark/scores.php
ARM公司官网:https://www.arm.com/
ARM开发者官网:https://developer.arm.com/
STM32:
ST:意法半导体 M:MCU/MPU 32:32位
ST累计推出了:5大类、18个系列、1000多个型号的Cortex内核微控制器
ST中文社区网:https://www.stmcu.org.cn
ST官网: https://www.st.com
正点原子论坛:www.openedv.com/forum.php
STM32 主要分两大块, MCU 和 MPU, MCU 不能跑 Linux,而 MPU可以跑 Linux。
芯片的基本参数:
1,主频/FLASH/SRAM
2,工作电压/最大电流
3,IO引脚接入电压范围
4,单个IO引脚最大电流
外设串口资源:
NVIC(嵌套向量中断控制器):
SysTick(系统滴答定时器):
RCC(复位和时钟控制):
GPIO(通用IO):
AFIO(复用IO):
EXTI(外部中断):
TIM(定时器):
ADC(模数转换器):
DMA(直接内存访问):
USART(同/异步串口通信):2
I2C(通信):2
SPI(通信):4
CAN(通信):2
USB(通信):2
RTC(实时时钟):
CRC(校验):
PWR(电源控制):
BKP(备份寄存器):
IWDG(独立看门狗):
WWDG(窗口看门狗):
DAC(数模转换器):
SDIO(SD卡接口):6
FSMC(可变静态存储控制器):
USB OTG(USB主机接口):
标准波特率系列:110,300,600,1200,1800,2400,4800,9600,14.4K,19.2K,28.8K,33.6K,56Kbps
define :预处理命令,它用于宏定义
常见的格式:define 标识符 字符串
标识符:宏名。
ifdef 条件编译:条件编译命令最常见的形式为:
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译, 否则编译程序段 2。 其中#else 部分也可以没有
extern 外部申明:
extern :置于变量或者函数前,以表示变量或者函数的定义在别的文件中。 extern 申明变量可以多次,但定义只有一次
typedef:为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。定义结构体的类型别名和枚举类型。
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
…
} GPIO_TypeDef;
声明结构体类型:
struct 结构体名
{
成员列表;
}变量名列表;
在结构体申明的时候可以定义变量,也可以申明之后定义:struct 结构体名字 结构体变量列表 ;
结构体成员变量的引用方法是:结构体变量名字.成员名
指针:本质是指向一个地址,从而可以访问一片内存区域。
申明指针我们一般以 p 开头:char * p_str = “This is a test!”;
p_num:变量的地址
*p_num:指向的地址所存储的值
&p_num:指针自身的地址
存储器本身是没有地址信息的,存储器分配地址的过程就叫存储器映射。
给寄存器的地址命名的过程就叫寄存器映射。
寄存器的地址=外设基地址+地址偏移量
=总线基地址(BUS_BASE_ADDR)+总线基地址的偏移量(PERIPH_OFFSET)+外设基地址的偏移量(REG_OFFSET)
例如:GPIOB_ODR地址 = AHB1总线基地址 + GPIOB外设偏移量 + 寄存器偏移量
外设基地址在stm32参考手册2.3存储器映射中查看
/*GPIOB_ODR 寄存器的地址为: 0x4002 0414, 假设我们要控制GPIOB 的 16 个 IO 口都输出 1*/
#define GPIOB_ODR *(unsigned int *)(0x40020414)
GPIOB_ODR = 0XFFFF;
/*这个宏定义过程就可以称之为寄存器的映射。*/
固件包目录结构:
API 函数的帮助文档:Drivers\STM32F4xx_HAL_Driver\ chm 格式的文档
使用HAL库:
修改stm32f4xx_hal_conf.h:
修改外部高速晶振频率
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /* 外部高速振荡器的值8MHZ,单位 HZ */
#endif /* HSE_VALUE */
#if !defined (LSE_VALUE)
#define LSE_VALUE ((uint32_t)32768) /* 外部低速振荡器的值32.768KHZ,单位 HZ */
#endif /* LSE_VALUE */
滴答定时器的中断优先级一定要比中断高。
使用断言:取消USE_FULL_ASSERT注释,使能断言->实现assert_failed()函数,在sys.c文件里->->->->
HAL库中断处理:
使用某一外设中断->初始化一个外设->开启外设的中断设置外设中断优先级->定义中断函数并调用HAL库的中断处理->编写中断回调函数的内容->判断有中断发生?->
是:判断目标中断源?->
是:执行中断回调函数
否:顺序执行主程序功能->判断有中断发生?(执行中断回调函数)
否:顺序执行主程序功能->判断有中断发生?(执行中断回调函数)
外设的控制句柄结构体:PPP_HandleType ->初始化结构体的参数:PPP_InitType ->驱动的初始化:HAL_PPP_Init()->实现外设初始化细节的接口:Hal_PPP_Mspinit()->完成外设时钟、IO 等细节差异的设置->使能外设中断:HAL_NVIC_SetPriority()、HAL_NVIC_EnableIRQ()->定义中断处理函数:PPP_IRQHandler->判断和处理中断标记:HAL_ppp_function_IRQHandler()->调用自定义的中断回调接口:HAL_PPP_ProcessCpltCallback()->->
STM32 启动过程:
启动模式复位方式有三种:上电复位,硬件复位和软件复位
内部 FLASH启动方式:读取内部 FLASH的 0x08000000 地址空间存储的内容,赋值给栈指针 MSP,作为栈顶地址,再读取内部 FLASH的 0x08000004 地址空间存储的内容,赋值给程序指针 PC,作为将要执行的第一条指令所在的地址
内部 SRAM 启动方式:
关于 MicroLIB 更多知识可以看官方介绍 http://www.keil.com/arm/microlib.asp
.map 文件分析:主要包含了交叉链接信息。
整个工程的函数调用关系、FLASH 和 RAM 占用情况及其详细汇总信息。
map 文件的基础概念:
Section:描述映像文件的代码或数据块,简称程序段
RO:Read Only 的缩写,包括只读数据(RO data)和代码(RO code)两部分内容,占用 FLASH 空间
RW:Read Write 的缩写,包含可读写数据(RW data,有初值,且不为 0),占用FLASH(存储初值)和 RAM(读写操作)
ZI:Zero initialized 的缩写,包含初始化为 0 的数据(ZI data),占用 RAM 空间
.text:相当于 RO code
.constdata:相当于 RO data
.bss:相当于 ZI data
.data:相当于 RW data
时钟系统
时钟树:在 MCU 设计时就设计了专门用于控制时序的电路,在芯片设计中称为时钟树设计
输入时钟源:包括 HSI,HSE,LSI,LSE。 HSI、HSE 高速时钟,LSI 和 LSE 是低速。HSE 和 LSE 是外部时钟源
锁相环:自动控制系统中常用的一个反馈电路
主要作用:输入时钟净化和倍频
系统时钟 SYSCLK:为整个芯片提供了时序信号
时钟树:
时钟是具有周期性的脉冲信号,最常用的是占空比50%的方波
外部时钟:
uint8_t sys_stm32_clock_init(uint32_t plln, uint32_t pllm, uint32_t pllp, uint32_t pllq)
SYSCLK=HSE/pllm*plln/pllp
内部时钟:
时钟源、锁相环:HAL_RCC_OscConfig()
系统时钟、总线:HAL_RCC_ClockConfig()
使能外设时钟:__HAL_RCC_PPP_CLK_ENABLE()
扩展外设时钟(RTC/ADC/USB):HAL_RCCEx_PeriphCLKConfig()
系统时钟配置步骤:
1,配置HSE_VALUE:HAL库外部晶振频率,stm32xxxx_hal_conf.h
2,调用SystemInit()函数(可选):在启动文件中调用, 在system_stm32xxxx.c定义
3,选择时钟源,配置PLL:通过HAL_RCC_OscConfig()函数设置
4,选择系统时钟源,配置总线分频器:通过HAL_RCC_ClockConfig()函数设置
5,配置扩展外设时钟(可选):通过HAL_RCCEx_PeriphCLKConfig()函数设置
1.HAL_RCC_OscConfig()函数(F4/F7)
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
typedef struct
{
uint32_t OscillatorType; /* 选择需要配置的振荡器 */
uint32_t HSEState; /* HSE 状态 */
uint32_t LSEState; /* LSE 状态 */
uint32_t HSIState; /* HSI 状态 */
uint32_t HSICalibrationValue; /* HSI 校准微调值,范围0x0~0x1F */
uint32_t LSIState; /* LSI 状态 */
RCC_PLLInitTypeDef PLL; /* PLL 结构体 */
}RCC_OscInitTypeDef;
typedef struct
{
uint32_t PLLState; /* PLL 状态 */
uint32_t PLLSource; /* PLL 时钟源 */
uint32_t PLLM; /* PLL 分频系数 M */
uint32_t PLLN; /* PLL 倍频系数 N */
uint32_t PLLP; /* PLL 分频系数 P */
uint32_t PLLQ; /* PLL 分频系数 Q */
}RCC_PLLInitTypeDef;
2.HAL_RCC_ClockConfig()函数(F4/F7)
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
typedef struct
{
uint32_t ClockType; /* 要配置的时钟(SYSCLK/HCLK/PCLK1/PCLK2) */
uint32_t SYSCLKSource; /* 系统时钟源 */
uint32_t AHBCLKDivider; /* AHB 时钟预分频系数 */
uint32_t APB1CLKDivider; /* APB1 时钟预分频系数 */
uint32_t APB2CLKDivider; /* APB2 时钟预分频系数 */
}RCC_ClkInitTypeDef;
uint32_t FLatency
#define FLASH_LATENCY_0 FLASH_ACR_LATENCY_0WS /* FLASH 0个等待周期 */
#define FLASH_LATENCY_1 FLASH_ACR_LATENCY_1WS /* FLASH 1个等待周期 */
#define FLASH_LATENCY_2 FLASH_ACR_LATENCY_2WS /* FLASH 2个等待周期 */
...
#define FLASH_LATENCY_15 FLASH_ACR_LATENCY_15WS /* FLASH 15个等待周期 */