一、STM32最小系统
单片机最小系统一般有晶振电路、电源电路、复位电路以及调试电路组成。
以下以STM32F103C8T6举例
1.电源电路:
此时电容分为输入电容和输出电容:
- C5、C7为输入电容:作用是防止断电后出现电压倒置。
- C6、C8为输出电容:作用是抑制自激振荡和稳定输出电压。
2.时钟电路:
时钟电路也称晶振电路,其中有两种晶振电路,区别如下:
3.复位电路:
STM32芯片复位管脚持续为低电平时复位,STM32的NRST引脚在内部已经连接了一个上拉电阻,数据手册建议复位电路需外接一个对地电容,如果认为这个上拉电阻较小,用户也可以在复位电路中外接一个上拉电阻。
一般有两种复为形式:
- 上电复位:在上电瞬间,由于电容来两端电压不能突变,RESET出现短暂低电平,芯片自动复位,之后电容充电,充电时间由电阻和电容共同决定:
- 手动复位:按键SW1按下时,RESET和地连通,产生低电平,实现复位。
4.调试和下载电路:
1.调试接口:
STM32F103系列微控制器内核集成了SWD/JTAG调试端口(缩写为SWJ-DP)。
SWJ-DP引脚名称 | JTAG-DP | SW-DP | 引脚号 |
JTMS/SWDIO | 输入:JTAG模式选择 | 输入输出:串行数据输入输出 | PA13 |
JTCK/SWCLK | 输入:JTAG时钟 | 输入:串行时钟 | PA14 |
JTDI | 输入:JTAG数据输入 | PA15 | |
JTDO/TRACESWO | 输出:JTAG数据输出 | 异步跟踪 | PB3 |
JNTRST | 输入:JTAG模块复位 | PB4 |
2.启动方式:
在STM32F103系列微控制器中通过设置BOOT[1:0]引脚电平的高低选择三种不同启动模式,从而将STM32F103微控制器的存储空间起始地址0x00000000映射到不同存储区域的起始地址。
启动模式选择引脚 | 启动模式对应的存储介质 | 说明 | |
BOOT0 | BOOT1 | ||
0 | x | 闪存存储器,即用户闪存Flash | 闪存存储器被选为启动区域 |
1 | 0 | 系统存储器,即系统Flash | 系统存储器被选为启动区域 |
1 | 1 | 内置SRAM | 内置SRAM被选为启动区域 |
CM-3 内核在离开复位状态后的工作过程如下:
1.从地址 0x00000000 处取出栈指针 MSP 的初始值,该值就是栈顶的地址。
2.从地址 0x00000004 处取出程序指针 PC 的初始值,该值指向复位后应执行的第一条指令。
上述过程由内核自动设置运行环境并执行主体程序,因此它被称为自举过程。
虽然内核是固定访问 0x00000000 和 0x00000004 地址的,但实际上这两个地址可以被重映射到其它地址空间。以 STM32F103 为例,根据芯片引出的 BOOT0 及 BOOT1 引脚的电平情况,这两个地址可以被映射到内部 FLASH、内部 SRAM以及系统存储器中。
BOOT1 | BOOT2 | 映射到的存储器 | 0x00000000 | 0x00000004 |
地址映射到 | 地址映射到 | |||
x | 0 | 闪存存储器,即用户闪存Flash | 0x08000000 | 0x08000004 |
1 | 0 | 系统存储器,即系统Flash | 0x1FFFB000 | 0x1FFFB004 |
1 | 1 | 内置SRAM | 0x20000000 | 0x20000004 |
二、C语言基础知识
C语言的数据类型 | STM32对应的数据类型 | 说明 |
unsigned char | uint8_t | 8位无符号数据(0 ~ 255)字符型 |
unsigned short int | uint16_t | 16位无符号数据(0 ~ 65535)短整型 |
unsigned int | uint32_t | 32位无符号数据(0 ~ 232-1)长整型 |
unsigned long long | uint64_t | 64位无符号数据(0 ~ 264-1)64位整型 |
signed char | int8_t | 8位有符号数据(-128 ~ +127) |
signed short int | int16_t | 16位有符号数据(-32768 ~ +32767) |
signed int | int32_t | 32位有符号数据(-231 ~ 231-1) |
signed long long | int64_t | 64位有符号数据(-263 ~ 263-1) |
1.const
- const用于定义只读的变量,其值在编译时不能被改变。
- 使用它是为了在编译时防止变量的值被误修改,提高程序的安全性和可靠性。
- 在C99标准中,const定义的变量是全局的。
- const关键词修饰的变量在声明时必须初始化。
const uinit8_t sum = 3.14;
uint8_t abs=0;
...
sum=abs;//非法,sum只能被读取不能被赋值。
abs=sum;
2.static
- 修饰变量或函数。修饰后的变量称为静态变量。
- 在全局变量之前加上关键字static,则该全局变量被定义成为一个静态全局变量。
- 作用范围只在定义该变量的源文件内有效,其他源文件不能引用该全局变量,避免了在其他源文件中因引用相同名字的变量而引发错误,有利于模块化程序设计。
1.static定义函数
模块化的程序设计中,用static声明一个函数,则该函数只能被该模块内的其它函数调用。
#include "stm32f1xx_hal.h"
static void DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
… …
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
HAL_StatusTypeDef status = HAL_OK;
… …
if(HAL_DMA_STATE_READY == hdma->State)
{
DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
… …
}
… …
}
DMA_SetConfig()函数是本地函数,只能被stm32f1xx_hal_dma.c的其它函数调用,不能被其它模块的文件使用。
2.static定义局部变量
static除了用于静态全局变量,还用于定义静态局部变量,保证静态局部变量在调用过程中不被重新初始化。
void fun_count( )
{
static count_num = 0;
// 声明一个静态局部变量,count_num用作计数器,初值为0。
count_num ++;
printf("%d\n", count_num);
}
int main(void)
{
int i=0;
for (i = 0;i <= 5;i++)
{
fun_count( );
}
return 0;
}
在main函数中每调用一次fun_count( )函数,则静态局部变量count_num加1,而不是每次都被初始化为初值0。不加static的结果是1,加static的结果是5。
3.volatile
- 使用volatile关键字定义了一个字符型的变量i,指出i是随时可能发生变化的,每次使用的时候都必须从i的地址中读取。
- 使用volatile就是不让编译器进行优化,即每次读取或者修改值的时候,都必须重新从内存中读取或者修改,而不是使用保存在寄存器里的备份。
其可用于以下场合:
- 中断服务程序中修改的供其他程序检测的变量需要使用volatile。
-
多任务环境下各任务间共享的标志应添加volatile。
-
存储器映射的硬件寄存器通常也要加volatile进行说明。
4.extern
指明此函数或变量的定义在别的文件中,提示编译器遇到此函数或变量时去其他模块中寻找其定义。
有一种特殊用法:extern "C"进行链接指定,告知编译器这是采用C语言定义的函数。
#ifdef __cplusplus
extern "C"{
#endif
……
#ifdef __cplusplus
}
#endif
如果定义了__cplusplus(C++编译器中自定义的宏),则执行extern“C”{语句}。在C++环境下使用C函数会出现链接时找不到对应函数的情况,这时需要使用extern “C”进行链接指定,告知编译器使用C语言的命名规则来处理函数。
5.回调函数:
操作系统中的某些函数常需要调用用户定义的函数来实现其功能,由于与常用的用户程序调用系统函数的调用方向相反,因此将这种调用称为“回调”(Callback),而被系统函数调用的函数称为“回调函数”。简单来说就是在一个函数中调用另一个函数。
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin);
}
以上代码就是在GPIO中断处理函数中调用HAL_GPIO_EXTI_Callback函数。一般用于通过中断处理函数调用回调函数来实现中断服务功能。
三、HAL库的内存空间
1.内存空间的定义
#ifndef __STM32F1XX_H
#define __STM32F1XX_H
#ifdef __cplusplus
extern "C" {
#if !defined (STM32F1)
#define STM32F1
#endif /* STM32F1 */
如果没定义STM32F1,则定义STM32F1。