链接:我的 github
流程图画法
-
流程图画法
代码
#include <stdio.h> #include "stm32f10x.h" // 初始化GPIO引脚 void init_GPIO(void) { GPIO_InitTypeDef GPIO_InitStructure; // 开启GPIOA和GPIOC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); // 配置GPIOA引脚为输入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置GPIOC引脚为输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } int main(void) { init_GPIO(); while (1) { // 读取传感器A的状态 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET) { // 读取传感器B的状态 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == Bit_SET) { // 读取传感器C的状态 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2) == Bit_SET) { // 水位危险状态 GPIO_SetBits(GPIOC, GPIO_Pin_12); // 点亮红色信号灯F3 } else { // 水位正常工作状态 GPIO_SetBits(GPIOC, GPIO_Pin_13); // 点亮绿色信号灯F1 } } else { // 水位异常工作状态 GPIO_SetBits(GPIOC, GPIO_Pin_14); // 点亮黄色信号灯F2 } } else { // 水位异常工作状态 GPIO_SetBits(GPIOC, GPIO_Pin_14); // 点亮黄色信号灯F2 } } }
GPT写的
#include "stm32f10x.h" #define SensorA GPIO_Pin_0 #define SensorB GPIO_Pin_1 #define SensorC GPIO_Pin_2 #define LED_F1 GPIO_Pin_0 #define LED_F2 GPIO_Pin_1 #define LED_F3 GPIO_Pin_2 void GPIO_Configuration(void); int main(void) { GPIO_Configuration(); while (1) { if (GPIO_ReadInputDataBit(GPIOA, SensorA) == Bit_SET && GPIO_ReadInputDataBit(GPIOA, SensorB) == Bit_RESET) { GPIO_SetBits(GPIOC, LED_F1); GPIO_ResetBits(GPIOC, LED_F2); GPIO_ResetBits(GPIOC, LED_F3); } else if (GPIO_ReadInputDataBit(GPIOA, SensorA) == Bit_RESET && GPIO_ReadInputDataBit(GPIOA, SensorB) == Bit_RESET) { if (GPIO_ReadInputDataBit(GPIOA, SensorC) == Bit_SET) { GPIO_ResetBits(GPIOC, LED_F1); GPIO_SetBits(GPIOC, LED_F2); GPIO_ResetBits(GPIOC, LED_F3); } else { GPIO_ResetBits(GPIOC, LED_F1); GPIO_ResetBits(GPIOC, LED_F2); GPIO_ResetBits(GPIOC, LED_F3); } } else { GPIO_ResetBits(GPIOC, LED_F1); GPIO_ResetBits(GPIOC, LED_F2); GPIO_SetBits(GPIOC, LED_F3); } } } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); // 初始化PA0、PA1、PA2为浮空输入 GPIO_InitStructure.GPIO_Pin = SensorA | SensorB | SensorC; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化PC0、PC1、PC2为推挽输出 GPIO_InitStructure.GPIO_Pin = LED_F1 | LED_F2 | LED_F3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure); }
复习正文
ARM:Advanced RISC Machines
RISC:Reduced Instruction Set Computer
CISC:Complex Instruction Set Computer
嵌入式系统:Embedded System
单片机系统:Single Chip Microcomputer System
知识产权:Intellectual Property
GPIO:通用输入/输出
GPIO:General-Purpose Input/Output
AFIO:备用功能输入/输出
AFIO:Alternate-Function Input/Output
嵌入式系统
基本特点:嵌入性,专用性,计算机系统
硬件特点:电路高度集成,体积较小,低成本,低功耗系统调试复杂,实时性,可靠性
软件特点:实时性,可靠性,软件剪裁和固化,代码高效
嵌入式系统组成
嵌入式处理器的中央处理器有 SoC、DSP、MPU、MCU 类型
嵌入式片上系统 SoC — System-on-a-Chip
嵌入式微处理器 EMPU — Microprocessor Unit
嵌入式微控制器 MCU — Microcontroller Unit
嵌入式数字信号处理器 EDSP — Digital Signal Processor
MPU 存储保护单元
MMC 存储管理单元
FPU 浮点处理单元
嵌入式开发阶段:分析、设计、实现
嵌入式开发系统的硬件部分包括:宿主机、目标机、在线仿真器
嵌入式系统开发,通常采用的模式是:宿主机-目标机开发模式、基于宿主机的软件模拟开发模式
交叉编译:指宿主机的开发软件将源程序编译生成目标机的机器代码,而不是运行于宿主机本身的可执行代码。
低功耗模式
-
Sleep 睡眠模式
- 系统时钟和外设时钟仍然工作,可以通过中断唤醒处理器
-
Stop 停止模式
- CPU、Flash 以及 SRAM 都处于停止状态,只有少数外设能够继续运行,如:RTC、WWDG
-
Standby 备用模式
- 所有内部时钟都被关闭,只有 I/O 口和 RTC 能够继续工作
stm32 支持的复位类型
备份复位
电源复位
系统复位
嵌入式操作系统
实时操作系统
uC/OS-III
VxWorks
FTOS
通用操作系统
MS-DOS
Unix
Thumb 指令是 16 位
ARM 指令是 32 位
处理器
Cortex-A:基于 ARMv7-A 体系结构,A 表示应用(Application)
- 高性能开放应用程序平台
Cortex-R:基于 ARMv7-R 体系结构,R 表示实时(Real-time)
- 高端嵌入式系统
Cortex-M:基于 ARMv7-M 和 ARMv6-M 体系结构,M 表示微控制器(Microcontroller)
-
实时,小规模,成本低,低功耗,中断延迟短
-
stm32 基于 CM3,哈佛结构
CM3 核心外设
NVIC 可嵌套向量中断控制器:支持 240 个中断请求,256 级优先权
SCB 系统控制模块
SysTick 系统时钟
MPU 存储保护单元
CM3 两个操作状态:Thumb 状态、调试状态
CM3 两个操作模式:异常处理模式、线程处理模式
CM3 两个特权级别:特权级、非特权级
CM3 寄存器
16个 32 位通用寄存器,R0-R15
R0-R7:低端寄存器(Low)
R8-R12:高端寄存器(High)
R13 是 SP 堆栈指针寄存器
R14 是 LR 连接寄存器
R15 是 PC 程序计数器
特殊寄存器:
PSR 程序状态寄存器
APSR 应用程序状态寄存器
EPSR 中断程序状态寄存器
EMR 异常屏蔽寄存器
CONTROL 控制寄存器
定时器中:
PSC 分频寄存器
ARR 自动重载寄存器
存储器映射
k 为寄存器位数
w 为所给的寄存器地址
A 为位带别名区的地址
存储区中地址映射
- 0x2000 0000~0x200F FFFF(1MB 大小)的存储空间(**位带区)**可被映射(膨胀)到地址范围 0x2200 0000~0x23FF FFFF(32MB 大小)的存储空间(位带别名区)
对应关系为 A=0x2200 0000+(W-0x2000 0000)×32+k×4
- 0x4000 0000~0x400F FFFF(1MB 大小)的存储空间**(位带区)**可被映射(膨胀)到地址范围 0x4200 0000~0x43FF FFFF(32MB 大小)的存储空间(位带别名区)
对应关系为 A=0x4200 0000+(W-0x4000 0000)×32+k×4
SRAM 启动:只能从 0x2000 0000 访问
系统存储器启动:可以从 0x1FFF F000 访问
主 Flash 启动:可以从 0x0800 0000 访问
启动代码的主要工作包括:
- 堆和栈的定义及初始化
- 异常(中断)向量表的建立(设置堆栈指针 SP 初值和程序计数器 PC 初值)
- 默认异常(中断)处理程序
启动文件的内容依次是:
- 定义堆和栈大小
- 定义中断向量表
- 定义默认的中断处理程序
- 用户初始化堆和栈
启动模式:
MDK 目录结构:
- Keil_v5\ARM\ARMCC\bin 保存主要的程序文件。例如,armcc.exe是C/C+编译程序,armasm.exe是汇编程序,armlink.exe 是连接程序,fromelf.exe 是映像转换、生成下载文件的程序
- Keil_v5\ARM\ARMCC\include 保存 C/C++ 语言的标准头文件(包含文件),如 stdio.h 是 C 语言的标准输入/输出头文件
- Keil_v5\ARM\BIN 保存动态链接库 DLL
- Keil_v5\ARM\Hlp 保存各种帮助文件
- Keil_v5\ARM\Pack 是驱动程序包(库)所在的文件目录
- Keil_v5\ARM\Pack\ARM 保存 ARM 公司提供的基本驱动程库,如:core_cm3.h 是 CMSIS Cortex-M3 核心外设访问层头文件(CMSIS Cortex-M3 Core Peripheral Access Layer Header File)
- 之前安装的 ST 公司 STM32F1xx 驱动程序库保存于 Keil_v5\ARM\Pack\Keil 目录,其中有启动代码文件、外设驱动,程序的头文件和源程序文件等
HEX 是符合 Intel 标准的十六进制格式机器代码文件
core_cm3.h,实现了内核的寄存器映射
stm32f10x.h,实现了片上外设的所有寄存器的映射
stm32f10x_it.c,内核中断服务函数
stm32f10x_conf.h,外设的头文件
startup_stm32f10x_ld.s,启动文件
system_stm32f10x.c,初始化系统时钟、扩展外部存储器用的函数
xl:超高密度产品,FLASH = 768KB~1MB
hd:高密度产品,FLASH = 256KB~512KB
md:中等密度产品,FLASH = 64KB~128KB
ld:低密度产品,FLASH = 16KB~32KB
STM32F103ZET6 结构
SRAM = 64KB
FLASH = 512KB
2 个基本定时器,TIM6,TIM7
4 个通用定时器,TIM2/3/4/5
2 个高级定时器,TIM1,TIM8
2 个 DMA 控制器(共 12 个通道)
3 个 SPI 串行外设接口
2 个 IIC 内部集成电路 I2C 接口
144 个引脚
5 个串口,3 个 USART 和 2 个 UART
1 个 USB
1 个 CAN
3 个 12 位 ADC,每个 ADC 通道数目为 16
1 个 12 位 DAC,支持( 8 位或) 12 位的数字量输入
1 个 SDIO 接口
1 个 FSMC 接口
112 个通用 IO 口
2 个看门狗:独立看门狗 IWDG、窗口看门狗 WWDG
ADC 中:
注入通道组可以安排最多 4 个通道
注入通道有 4 个数据寄存器
规则通道组中可以安排最多 16 个通道
规则通道只有 1 个数据寄存器
DAC 数字量写入方式:
- 8 位数据右对齐
- 12 位数据左对齐
- 12 位数据右对齐
看门狗的作用:防止程序发生死循环,或者说程序跑飞
APB1 操作于低速 36MHz,APB2 操作于全速(最高 72MHz)
32 位的 RTC,32 位的可编程计数器, 2 32 = 4 , 294 , 967 , 295 秒 ≈ 49 , 710 天 ≈ 136 年 2^{32}= 4,294,967,295 秒 ≈ 49,710 天 ≈ 136 年 232=4,294,967,295秒≈49,710天≈136年
SysTick 定时器是一个 24 位的递减计数器,可计数从 0 到 2 24 − 1 2^{24}-1 224−1 的整数值,即 0 至 16,777,215
RS-232 有 9 针、25 针连的接口,全双工
RS-485,半双工
STM32 外部中断的触发方式:
双边沿触发
当外部信号引脚上出现上升沿或下降沿时都会触发外部中断
上升沿触发
只有当外部信号引脚上出现上升沿时才会触发外部中断
下降沿触发
则只有当外部信号引脚上出现下降沿时才会触发外部中断
STM32 微控制器由 GPIO 引脚触发的外部中断时:
不同的 GPIO 端口不能同时中断
同一个 GPIO 端口内的多个引脚可以同时配置
如:
PA0 和 PB0 两个引脚不能同时中断
各种工作模式
输入模式
- 浮空输入(GPIO_Mode_IN_FLOATING)
- 上拉输入(GPIO_Mode_IPU)
- 下拉输入(GPIO_Mode_IPD)
- 模拟输入(GPIO_Mode_AIN)
输出模式
- 通用开漏输出(GPIO_Mode_Out_OD)
- 复用开漏输出(GPIO_Mode_AF_OD)
- 通用推挽输出(GPIO_Mode_Out_PP)
- 复用推挽输出(GPIO_Mode_AF_PP)
-
按键作为输入功能时,为检测按键输入状态,对应的引脚应使用浮空输入工作模式
-
某 GPIO 引脚复用为 ADC 功能输入引脚时,该引脚应使用模拟输入工作模式
-
进行异步串行通信前,
USARTx_TX
引脚需要进行 GPIO 配置,在全双工通信制式时,应配置为复用推挽输出 GPIO_Mode_AF_PP 工作模式 -
进行异步串行通信前,
USARTx_RX
引脚需要进行 GPIO 配置,在全双工通信制式时,应配置为上拉输入 GPIO_Mode_IPU 或浮空输入 GPIO_Mode_IN_FLOATING 的工作模式 -
当 GPIO 的 I/O 引脚配置为输入模式时,不需要配置输出速度
-
开漏输出时,引脚处于高阻态,需要外部上拉电阻将引脚拉高到逻辑高电平
开漏输出适用于多路引脚共享同一信号线的情况
-
推挽输出时,引脚连接到电源,可以提供较大的输出电流,因此不需要外部上拉电阻
推挽输出适用于直接驱动其他数字电路的情况
通常有 5 种方式使用某个引脚功能,它们的配置方式如下:
- 作为普通 GPIO 输入:浮空输入、上拉输入、下拉输入,同时不要使能该引脚对应的所有复用功能模块
- 作为普通 GPIO 输出:推挽输出、开漏输出,同时不要使能该引脚对应的所有复用功能模块
- 作为普通模拟输入:模拟输入,同时不要使能该引脚对应的所有复用功能模块
- 作为内置外设的输入:浮空输入、上拉输入、下拉输入、模拟输入,同时使能该引脚对应的某个复用功能模块
- 作为内置外设的输出:通用推挽输出、通用开漏输出、复用推挽输出、复用开漏输出,同时使能该引脚对应的所有复用功能模块
与或非
a &= ~(1<<6); // 位与 实现复位,将整型变量a的D6位清零、其他位不变
a |= (1<<6); // 位或 实现置位,将整型变量a的D6位置位、其他位不变
a ^= (1<<6); // 位异或实现求反,将整型变量a的D6位取反、其他位不变
GPIO 配置
(LED 是低电平点亮)
void LED_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个 GPIO_InitTypeDef 型结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 打开 GPIOC 外设时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; // 选择要控制的引脚,PC3 引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置引脚模式,推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速率输出速率,50MHZ
GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化选择的引脚
// 置位指定的数据端口位
GPIO_SetBits(GPIOC, GPIO_Pin_3); // 让引脚输出高电平,LED灭
// GPIO_ResetBits(GPIOC, GPIO_Pin_3); // 让引脚输出低电平,LED亮
}
GPIO 寄存器
RCC->APB2ENR |= 1<<2; // 添加 使能PORTA时钟,在需要使用某外设时,需要先开启该外设相应的时钟,否则外设不工作
RCC->APB2ENR |= 1<<5; // 添加 使能PORTD时钟
GPIOA->CRH &= 0XFFFFFFF0;
GPIOA->CRH |= 0X00000003;// PA8 推挽输出
GPIOA->ODR |= 1<<8; // PA8 输出高
GPIOD->CRL &= 0XFFFFF0FF;// 端口配置低位
GPIOD->CRL |= 0X00000300;// 端口配置低位,PD.2推挽输出
GPIOD->ODR |= 1<<2; // 端口输出,PD.2输出高
GPIOB->CRL &= 0XFF0FFFFF;// 端口配置低位,用CRL寄存器配置GPIOB端口的低8位,先将该位清0
GPIOB->CRL |= 0X00300000;// 端口配置低位,要求不改变其他位,故采用或运算其他位设置乃0,将该位设置为推挽输出
GPIOB->ODR |= 1<<5; // 端口输出,让灯初始化之后是灭的,所以同高电平(看一下原理图就明白了)
GPIOB->ODR |= 1<<5; // 端口输出,目的是使连接该PB.5处于高电平,即使得灯一开始处于熄灭状态
GPIOE->ODR &= !(1<<5); // 端口输出,目的是使连接该PE.5处于低电平,即使得灯一开始处于亮灯状态
RCC_APB2ENR |= (1<<3); // 开启 GPIOB 端口时钟
GPIOB_CRL &= ~(0x0F<<(4*0)); // 清空控制 PB0 的端口位,~(0x0F<<(4*0)) 和 ~0x0F 没有区别
GPIOB_CRL &= ~(0x0F<<(4*1)); // 清空控制 PB1 的端口位
GPIOB_CRL |= (1<<4*0); // 配置 PB0 为通用推挽输出,速度10M
GPIOB_ODR &= ~(1<<0); // PB0 输出 低电平
// 以下等价
GPIOx->BRR = 0x01
GPIOx->BSRR = 0x01<<16 // 通过 0x01 左移 16 位来控制高 16 位
- ODR 全称 Output Data Register,用于设置 GPIO 的 输出电平状态
- CRL 全称 Configuration Register Low,用于配置 GPIO 的低 8 位管脚的 I/O 方向、模式等参数
- IDR 全称 Input Data Register,用于读取 GPIO 输入电平状态
- ODR 控制管脚的高、低电平,低 16 位有效,写 1 高电平, 写0 低电平
- BSRR 控制管脚的高、低电平,32 位有效,低 16 位写 1 高电平,高 16 位写 1 低电平
- BRR 控制管脚的低电平,低 16 位有效,写 1 低电平
- BRR、BSRR、ODR 用来控制 16 位针脚的
- BRR、ODR 的高 16 位不可用,只使用低 16 位控制针脚
- BSRR 的高 16 位和低 16 位皆可用,用来控制 16 位针脚
- 用 ODR 的步骤 "读取->运算->写入"
- 用 BSRR、BRR 去改变管脚状态的时候,没有被中断打断的风险,不用关中断,更快更简单
- F1 和 F4 系列都有 ODR 和 BSRR,但 F4 取消了 BRR,为了代码通用,尽量不使用 BRR
// 使 GPIOE 的第5管脚输出低电平,使GPIOE端口的第11管脚输出高电平
// 如果使用端口输出数据寄存器GPIOE_ODR,按照“读出-修改-写回”策略,其语句为:
GPIOE_ODR &= ~(1uL<<5); // unsigned long
GPIOE_ODR |= (1uL<<11);
// 使 GPIOE 的第5管脚输出高电平,使用语句:
GPIOE_BSRR = (1uL<<5);
// 使 GPIOE 端口的第11管脚输出低电平,使用语句:
GPIOE_BSRR = (1uL<<11)<<16;
// PB0 引脚为低、复位、逻辑0
GPIOB->ODR &= ~(0x1);
// PB0 引脚为高、置位、逻辑1(会覆盖,只有Pin0为1,其他全为0)
GPIOB->ODR = (0x1);
// PB0 输出低电平
GPIOB_ODR |=(0<<0);
// GPIOx->ODR
// 将 PB5 设置为高电平状态:
GPIOB->ODR |= GPIO_PIN_5;
// GPIOx->CRL
// 将 PA3 设置为输入模式:
GPIOA->CRL &= ~(GPIO_CRL_MODE3 | GPIO_CRL_CNF3);
// GPIOx->IDR
// 读取引脚 PC4 的状态:
uint8_t status = GPIOC->IDR & GPIO_PIN_4 ? 1 : 0;
// GPIOx->BSRR
// 将 PB7 设置为低电平状态:
GPIOB->BSRR = GPIO_PIN_7 << 16; // 1000 0000
// GPIOx->BRR
// 将 PB5 置为 0:
GPIOB->BRR |= (0 << 5);
// 某端口写入1,可用:
GPIOx->ODR
GPIOx->BSRR
// 某端口写入0,可用:
GPIOx->ODR
GPIOx->BSRR
GPIOx->BRR
GPIOA->ODR |= 0x01;
// 等价于
GPIO_SetBits(GPIOA, GPIO_Pin_0);
// 同时写
GPIO_Write(GPIOB, GPIO_Pin_3); // 打开蜂鸣器
GPIO_Write(GPIOB, 0); // 关闭蜂鸣器
// 写一个
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET); // 将GPIOA.5设置为高电平 1
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET); // 将GPIOA.5设置为低电平 0
// 置1和0
GPIO_SetBits(GPIOA, GPIO_Pin_5); // 置1
GPIO_ResetBits(GPIOA, GPIO_Pin_5); // 置0
// 用于电平翻转
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
RTC 函数:
RTC_IT_SFC :用于处理 RTC 计数器达到预设的值时触发的中断请求
RTC_IT_ALRA :用于处理 RTC 报警 A 中断
RTC_IT_ALRB :用于处理 RTC 报警 B 中断
RTC_IT_WUT :用于处理 RTC 唤醒定时器(Wakeup Timer)中断
RTC_IT_TS :用于处理 RTC 时间戳(Timestamp)中断
RTC 可以在特定的时间间隔内生成中断,使系统能够定期执行某些任务
中断服务程序(ISR)是在发生中断时调用的函数
对于 RTC 中断,ISR 通常被称为 RTC_IT_SFC,或实时时钟中断服务函数调用
ISR 可以执行诸如使用当前时间更新显示器或根据时间触发其他操作等任务
RTC 中断:
RTC_Second:每秒钟产生中断一次
RTC_Alarm:当RTC的闹钟匹配到指定时间时产生中断
RTC_Overflow:当RTC计数器溢出时产生中断
RCC 的含义是 Reset and Clock Control(复位和时钟控制):
RCC_CR 时钟控制寄存器
RCC_CFGR 时钟配置寄存器
RCC_CIR 时钟中断寄存器
RCC_APB2RSTR APB2外设复位寄存器
RCC_APB1RSTR APB1外设复位寄存器
RCC_AHBENR AHB外设时钟使能寄存器
RCC_APB2ENR APB2外设时钟使能寄存器
RCC_APB1ENR APB1外设时钟使能寄存器
RCC_BDCR 备份域控制寄存器
RCC_CSR 控制/状态寄存器
RCC_AHBRSTR AHB外设时钟复位寄存器
RCC_CFGR2 时钟配置寄存器2
串口配置
void GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // USART1_TX 发送
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); // GPIOA 初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // USART1_RX 接收
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // GPIOA 初始化
}
9 发 T 推
10 收 R 浮
串口接收数据出错:
FE 帧错误 (Framing error):当接收到的数据帧中的停止位与期望值不匹配时,就会产生帧错误。这通常是由于噪声、信号干扰或波特率不匹配等原因导致的。
PE 奇偶校验错误 (Parity error) :在使用奇偶校验模式的情况下,如果接收到的数据帧中的奇偶校验位与期望值不匹配,则会产生奇偶校验错误。
ORE 帧丢失/溢出错误 (Overrun error):当接收缓冲区已满,但还有新的数据传输过来时,之前未读取的数据将被覆盖,从而导致帧丢失错误。
NE 无效字节/噪声错误(Noise error) :当在数据帧中接收到一个无效字节(非ASCII码)时,就会产生无效字节错误。
USART 是通用同步/异步收发器(Universal Synchronous/Asynchronous Receiver-Transmitter),支持同步和异步两种通信模式,并且还具有硬件流控制功能
UART 是通用异步收发器(Universal Asynchronous Receiver-Transmitter),仅支持异步通信
LPUART 是低功耗 UART(Low Power UART)的缩写,它也支持异步通信,并且具有更低的功耗和更高的性能
异步通信,是以字符或者字节为单位组成字符帧进行传输
字符帧格式:起始位(1bit)+数据位(8/9bit)+(奇偶校验位)+停止位(1bit)+空闲位(任意)
某次 UART 通信任务中,已知发送规则 115200-8-N-1-N 波特率 115200bps,8 个数据位,无奇偶校验位,1 个停止位,无硬件流控
则发送小数点(. 的 ASCII 值为 46)时 接收端 收到的正确数据帧应为:
接收方:0 01110100 1
第一位是起始位,8 位是数据位(00101110),最后一位是停止位
USART 寄存器
USART_SR (USART Status Register) :控制串行通信过程中 状态 的寄存器,例如是否有数据可以读取或者发送完成等
USART_DR (USART Data Register) :存储要发送或接收的 数据 的寄存器
USART_BRR (USART Baud Rate Register) :用于设置 USART 的 波特率/传输速率 的寄存器
USART_CR1 (USART Control Register 1) :包括了 USART 的主要控制位,例如启用或禁用 USART、设置数据位数、奇偶校验选项、停止位数以及使能中断等
USART_CR2 (USART Control Register 2) :用于配置 USART 的硬件流控制功能,如使能 CTS(清除发送)、RTS(请求发送)信号线以及选择时钟极性等
USART_CR3 (USART Control Register 3) :用于配置 USART 的其他特性,例如使能半双工模式、使能错误中断、使能LIN模式等
USART 标志位
串口通信的标志位(flag),用于指示串口硬件的状态
1.USART_FLAG_TC -- USART Transmission Complete Flag :传输完成标志,表示USART的数据发送已经完成
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待上次发送结束
USART_ClearFlag(USART1, USART_FLAG_TC);
2.USART_FLAG_TXE -- Transmit Data Register Empty Flag :发送寄存器空标志,表示USART的发送缓冲区为空并且可以接受下一个字节进行发送
3.USART_FLAG_IDLE -- USART Idle Line Detected Flag :线路空闲标志,表示USART的接收线路处于空闲状态
4.USART_FLAG_RXNE -- USART Read Data Register Not Empty Flag :接收缓冲非空标志,表示USART的接收缓冲区中有可读数据
<数据从接收移位寄存器进入接收数据寄存器时>
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)==RESET); // 是否已接收数据
USART_ClearFlag(USART1, USART_FLAG_RXNE); // 清除接受标志
USART 控制信号
USART_CK :USART 时钟信号
在串行通信中,发送方和接收方需要有统一的时钟来进行数据传输,USART_CK就是用于提供这个时钟信号的
USART_RTS :请求发送(Ready To Send)信号,也称为硬件流控制信号
当缓冲区已满或者发送方不能立即发送数据时,接收方可以使用该信号暂停数据传输,避免数据丢失或缓冲区溢出
USART_CTS :清除发送(Clear To Send)信号,也称为硬件流控制信号
当接收方准备好接收数据时,它会将该信号置为低电平,发送方检测到后才会继续发送数据,以避免数据丢失或缓冲区溢出
USART 相关函数
1.USART_Cmd() :使能USART函数,用于启动USART的传输和接收操作
USART_Cmd(USART1, ENABLE); // 串口使能
2.USART_Init() :初始化USART函数,用于配置USART的波特率、数据位数、停止位数、校验位等参数
USART_Init(USART1, &USART_InitStructure); // 串口初始化
3.USART_ReceiveData() :接收数据函数,用于从USART的接收缓冲区读取一个字节或者多个字节的数据
Res = USART_ReceiveData(USART1); // 读取接收到的数据
4.USART_SendData() :发送数据函数,用于将一个字节或者多个字节的数据发送到USART的发送缓冲区
USART_SendData(USART1, (unsigned char)ch); // 发送数据到串口
RS-232 接口
使用联络信号时,需要连线:
- RTS 和 CTS:避免数据发送过快导致接收端无法及时处理
- TxD 和 RxD:将数据从一个设备传输到另一个设备
- DTR 和 DSR:告诉对方是否准备好进行数据传输
- GND 和 VCC
不使用联络信号时,需要连线:
- TxD 和 RxD
- GND 和 VCC
时钟源:
-
HSE 外部高速时钟源,它的频率通常为 4MHz 到 25MHz 之间。提供更高的时钟频率,适用于需要更高计算能力的应用场景。由于外部环境的影响,稳定性相对较差
以外部晶振作为时钟源,晶振频率可取范围 4-16Mhz,一般采用 8Mhz
-
HSI 内部高速时钟源,它的频率通常为 8MHz 或 16MHz,提供较高的时钟精度和稳定性。在某些应用场景下,HSI 可以直接为 CPU 提供时钟信号,从而简化系统设计
由内部 RC 振荡器产生,频率为 8Mhz,但不稳定
-
LSE 外部低速时钟源
以外部晶振为时钟源,主要供给 RTC 模块,一般采用 32.768KHz
-
LSI 内部低速时钟源,频率通常为 40kHz,是一种低功耗的时钟源。适合用于处理需要低速时序的事件,例如低功耗模式下的 RTC 等
由内部 RC 振荡器产生,也主要供给 RTC 模块,频率大约为 40KHz
PLL(Phase-Locked Loop)时钟源是一种可以根据输入时钟信号进行频率合成的电路,其输出频率为输入频率的整数倍,通常用于提供与外部时钟源不同的系统时钟频率。使用 PLL 时钟源可以提高系统性能和节约能源,但需要注意时钟稳定性和抖动等问题。
PLL 不是 STM32F1 系列芯片的系统时钟源,但它可以作为一个辅助时钟源来提供更高的时钟频率
PLL(Phase Locked Loop)可以将输入时钟信号经过倍频或分频等处理后输出高频时钟。
在 STM32F1 系列芯片中,PLL 可以由 HSE 或 HSI 作为输入时钟源,并通过配置寄存器来设置其倍频系数和除数,从而得到所需的输出时钟频率。
PLL 为锁相环倍频输出,其时钟输入源可选择为 HSI/2、HSE、HSE/2。倍频可选择为 2~16 倍,但是其输出频率最大不得超过 72MHz
NVIC
CM3/4 内核支持 256 个中断,包括 16 个内核中断,240 个外部中断,拥有 256 级的可编程中断设置
STM32 支持 84 个中断,包括 16 个内核中断,68 个可屏蔽中断(包含 EXTI、TIM、ADC、USART、SPI、I2C、RTC 等多个外设),具有 16 级可编程中断优先级,使用 NVIC 统一管理中断,每个中断通道都拥有 16 个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
STM32F103 系列只有 60 个可屏蔽中断
优先级:
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队
- 抢占优先级和响应优先级均相同的按中断号排队(中断号见 STM32F10xxx 向量表)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 只设置一次
// 在引脚配置中设置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 串口1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能通道
NVIC_Init(&NVIC_InitStructure); // 初始化
EXIT
STM32 所有 I/O 端口都可以配置为 EXTI 外部中断模式,用来捕捉外部信号
外部中断 EXTI 的信号可以配置为上升沿、下降沿皆可触发中断请求
EXTI_InitTypeDef EXTI_InitStructure; // 定义一个 EXTI_InitTypeDef 结构体
EXTI_InitStructure.EXTI_Line = EXTI_Line12 | EXTI_Line14; // EXTI输入线,建议采用 浮空输入/带上拉输入/带下拉输入
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 设置 EXTI 线路为中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿中断
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能
EXTI_Init(&EXTI_InitStructure); // 初始化 EXIT
TIM 计时方式:
- 向上计数模式:计数器从 0 计数到自动装入的值(TIMx_ARR),然后重新从 0 开始计数,并产生一个计数器溢出事件
- 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到 0,然后从自动装入的值(TIMx_ARR)重新开始,并产生一个计数器向下溢出事件
- 中央对齐模式(向上/向下计数):计数器从 0 开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到 1 并且产生一个计数器溢出事件,然后再从 0 开始重新计数
PWM(Pulse Width Modulation)简称脉宽调制/脉冲宽度调制
占空比=脉宽时间/总周期时间
频率很高时,看不到闪烁,占空比越大,LED 越亮
频率很低时,可看到闪烁,占空比越大,LED 越亮
在频率一定时,可以用不同占空比改变 LED 灯的亮度,使其达到一个呼吸灯的效果
控制通信波特率的寄存器被称为波特率发生器(Baud Rate Generator)
- 在 STM32 微控制器中,波特率发生器可以使用一个浮点分频因子
- 8250 UART 是 Intel 8086 系列微处理器中,波特率发生器只能使用整数分频因子
-
对于 STM32 微控制器的 GPIO 模块描述中,不正确:I/O引脚的每一组端口都有 16 个引脚
-
要设置引脚输出高电平,需要控制:ODR 输出数据寄存器
-
设置多个引脚同时输出不同电平时,使用输出数据寄存器更方便(X)
-
指针的基类型决定了指针能够访问的存储空间(√)
-
引脚的模拟工作模式只是针对 A/D 采集模块和 D/A 转换模块所对应的引脚(X)
-
如果要设置引脚 PB13 输出低电平,正确:HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET)
-
下面不属于引脚初始化数据类型的成员变量是:Period
-
对于同一个端口的多个引脚而言,如果配置参数相同,可以使用按位或的方式来同时这些引脚(√)
-
在工程实际中,一般采用直接延时的方式来去掉按键的抖动(X)
-
硬件抽象层基于库函数或者寄存器方式封装出可移植性更好的接口函数(√)
-
在下列的数据传输方式中,适合处理突发事件的传输方式是(中断方式)
-
下列不属于 STM32 微控制器外部中断的触发方式是(低电平触发)
-
在设置 STM32 微控制器由 GPIO 引脚触发的外部中断时,PA0 和 PB0 两个引脚可以同时中断(X)
-
HAL 库对中断进行了二次封装,用户只需要编写中断的回调函数,不需要进行中断标志的清除(√)
-
HAL 库初始化时,将中断优先级分组设置为第(4)组
-
定时器设置为递减计数时,发生溢出后,计数器的重载值由哪一个寄存器决定:自动重载寄存
-
假设定时器 2 的定时器时钟 TIM_CLK 为 50MHz,预分频寄存器 PSC 的值为 9999,自动重载寄存器 ARR 的值也为 9999,则定时器的更新时间(定时时间)为:2000ms
-
在 CubeMX 软件中,如果将定时器的参数 Period 设置为 0,定时器将不会启动(√)
-
定时器属于计数器的一种,它是对周期固定的脉冲进行计数(√)
-
预分频寄存器的作用是扩大定时器的定时范围,并获取精确的时钟(√)
操控原理:三个 LED 等均为共阳极接法,PB 口的相应引脚如果输出低电平,则灯亮,输出高电平,则灯灭。
拥有1~3个12位ADC
分辨率:12位分辨率。不能直接测量负电压,所以没有符号位,即其最小量化单位LSB=Vref+/2的12次方
转换时间:转换时间可编程,采样一次至少要用14个ADC时钟周期,而ADC的时钟频率最高为14Mhz,也就是说,它的采样时间最短为1μs。足以胜任中、低频数字示波器的采样工作。
ADC工作原理:逐次比较型ADC
ADC1除了支持外部模拟输入,还可以支持2个内部信号源(温度传感器和内部参考电压)
每个ADC具有16个模拟输入通道ADCx_IN0 ~ ADCx_IN15
最多4个通道连接到注入通道,最多16个通道连接到规则通道
注入组的通道具有优先转换权,可以打断规则组通道正在进行的转换
规则组通道的转换结果只有一个数据寄存器
因为ADC转换模块只有一个ADC功能核心,它能够支持这么多通道的数据转换,用的是分时复用的方法。
因为STM32的ADC为12位,寄存器为16位,所以需要选择对齐方式。ADC_CR2寄存器中的ALIGN位(位11)选择转换后数据储存的对齐方式。数据可以左对齐或右对齐。
计算
ADC 转化:
采样时间 = 采样周期 + 12.5 A D C _ C L K 采样变化频率 采样时间= \frac{采样周期+12.5}{ADC\_CLK \ 采样变化频率} 采样时间=ADC_CLK 采样变化频率采样周期+12.5
最小的采样/转化时间:
1.5 + 12.5 14 M h z = 1 μ s \frac{1.5+12.5}{14Mhz} = 1μs 14Mhz1.5+12.5=1μs
采样周期 只能以下这些:
000 : 1.5周期
001 : 7.5周期
010 : 13.5周期
011 : 28.5周期
100 : 41.5周期
101 : 55.5周期
110 : 71.5周期
111 : 239.5周期
已知 STM32 外设总线 APB2 的时钟为 72Mhz
,APB1 时钟为 36Mhz,
调用库函数RCC_ADCCLKConfig(RCC_PCLK2_Div8);
实现 ADC 的时钟配置
已知内部温度传感器模拟输入推荐采样时间是 17.1μs
调用库函数 ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, 参数 4);
实现规则通道配置时,参数 4(采样周期)可选择的最小值应该是:ADC_SampleTime_239Cycles5
72 / 8 = 9 M H z 72/8 = 9MHz 72/8=9MHz
17.1 ∗ 9 = 153.9 个周期,则至少需要 239.5 个周期 17.1*9 = 153.9个周期,则至少需要 \ 239.5 \ 个周期 17.1∗9=153.9个周期,则至少需要 239.5 个周期
假设 定时器2 的定时器时钟 TIM_CLK 为 50Mhz,与分频寄存器 PSC 的值为 9999,自动重载寄存器 ARR 的值也为 9999,则定时器的更新时间(定时时间)为:2000ms
假设定时器时钟 TIMx_CLK 为 100Mhz,pwm 输出为 PWM1 模式,当自动重载值 ARR=999,捕获/比较值 CCR=200,有效电平为高电平,向上计数时,输出 PWM 信号的占空比为:20%
根据 PWM1 模式
当计数器计数值 < CCR 时,PWM 输出为高电平
当计数器计数值 >= CCR 时,PWM 输出为低电平
当 TIMx_CNT 计数值 < 200 时,PWM 输出为高电平
当 TIMx_CNT 计数值 >= 200 时,PWM 输出为低电平
自动重载值为 999,即当计数器计数值达到 999 时,会自动重新从 0 开始计数
一个完整的 PWM 周期的总时长为:
T = (ARR + 1) / TIMx_CLK
= (999 + 1) / 100Mhz = 10us
PWM 高电平的持续时间为:
Thigh = CCR / TIMx_CLK
= 200 / 100Mhz = 2us
PWM 的占空比为:
duty cycle = 20%