1. 点亮LED灯详解(307.11)
- 标号一样的导线在物理上是连接在一起的。
- 将 PB8 或 PB9 拉低,就可以实现将对应的 LED 灯点亮。
- 常用的GPIO HAL库函数:
c
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);//I/O口的初始化配置
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState
PinState);//对I/O口写高写低
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);//翻转I/O口的状态
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();//时钟:使GPIOB能工作,节约能耗,资源最大化的利用
- 结构体 GPIO_InitTypeDef 定义:
c
typedef struct
{
uint32_t Pin;//引脚编号
uint32_t Mode;//输入|推挽输出|开漏输出
uint32_t Pull;//上拉|下拉|不拉
uint32_t Speed;//低速|中速|高速
} GPIO_InitTypeDef;
2. 按键点亮LED灯(轮询法)(308.12)
- 输入(按键):
- KEY1:PA0
- KEY2:PA1
- 输出(LED灯):
- LED1:PB8
- LED2:PB9
- 代码(key_test/MDK-ARM)
c
//main.c
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define KEY_ON 0
#define KEY_OFF 1
/* USER CODE END PM */
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{//检测按键的状态
if(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET)
{ /* 按键按下 */
while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET);//消除按键抖动(防抖操作)
return KEY_ON;
}else
{ /* 按键松开 */
return KEY_OFF;
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{//按下key时,翻转led的状态
/* USER CODE END WHILE */
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);
/* USER CODE BEGIN 3 */
if(Key_Scan(GPIOA, GPIO_PIN_0) == KEY_ON)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
if(Key_Scan(GPIOA, GPIO_PIN_1) == KEY_ON)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
}
}
3. 复位和时钟控制(RCC)(309.13)
- reset and clock control
复位
- 系统复位
当发生以下任一事件时,产生一个系统复位:
*- NRST引脚上的低电平(外部复位)
- 窗口看门狗计数终止(WWDG复位)
- 独立看门狗计数终止(IWDG复位)
- 软件复位(SW复位)
- 低功耗管理复位
- 电源复位
当以下事件中之一发生时,产生电源复位:
*- 上电/掉电复位(POR/PDR复位)
- 从待机模式中返回
- 备份区复位
备份区域拥有两个专门的复位,它们只影响备份区域。
当以下事件中之一发生时,产生备份区域复位。
*- 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)(见6.3.9节)中的BDRST位
产生。 - 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。
- 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)(见6.3.9节)中的BDRST位
时钟控制
- 什么是时钟?
时钟打开,对应的设备才会工作。 - 时钟来源
- 三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
- HSI振荡器时钟(高速内部时钟)
- HSE振荡器时钟(高速外部时钟)
- PLL时钟(锁相环倍频时钟)
- 二级时钟源:
- 40kHz低速内部RC(LSIRC)振荡器
- 32.768kHz低速外部晶体(LSE晶体)
- 三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
- 如何使用CubeMX配置时钟
4. 中断相关概念(310.14)
什么是中断?
- 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入
处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
什么是 EXTI?
- 外部中断/事件控制器 (EXTI) 管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测
器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可
以单独配置为中断或者事件,以及触发事件的属性。
- EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而
产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。 - EXTI初始化结构体:
c
typedef struct
{
//中断/事件线
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or
disabled.
This parameter can be any combination value
of @ref EXTI_Lines */
//EXTI 模式
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref
EXTIMode_TypeDef */
//触发类型
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge
for the EXTI lines.
This parameter can be a value of @ref
EXTITrigger_TypeDef */
//EXTI 控制
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected
EXTI lines.
This parameter can be set either to ENABLE
or DISABLE */
}EXTI_InitTypeDef;
- 中断/事件线:
c
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16
Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17
Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18
Connected to the USB OTG FS Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19
Connected to the Ethernet Wakeup event */
#define EXTI_Line20 ((uint32_t)0x00100000) /*!< External interrupt line 20
Connected to the USB OTG HS (configured in FS) Wakeup event */
#define EXTI_Line21 ((uint32_t)0x00200000) /*!< External interrupt line 21
Connected to the RTC Tamper and Time Stamp events */
#define EXTI_Line22 ((uint32_t)0x00400000) /*!< External interrupt line 22
Connected to the RTC Wakeup event */
- EXTI 模式:
c
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //产生中断
EXTI_Mode_Event = 0x04 //产生事件
}EXTIMode_TypeDef;
- 触发类型:
c
typedef enum
{
EXTI_Trigger_Rising = 0x08, //上升沿
EXTI_Trigger_Falling = 0x0C, //下降沿
EXTI_Trigger_Rising_Falling = 0x10 //上升沿和下降沿都触发
}EXTITrigger_TypeDef;
- EXTI 控制:
使能 EXTI ,一般都是使能, ENABLE
什么是优先级?
抢占优先级和响应优先级的区别:
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行
什么是优先级分组?
Cortex-M3 允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此 STM32 把指定中断优先级的寄存器位减少到 4 位,这 4 个寄存器位的分组方式如下:
- 第0组:所有4位用于指定响应优先级
- 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
- 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
- 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
- 第4组:所有4位用于指定抢占式优先级
什么是NVIC?
STM32 通过中断控制器 NVIC(Nested Vectored Interrupt Controller)进行中断的管理 。NVIC 是属于 Cortex 内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理,但是 SYSTICK 不是由 NVIC 控制的。
c
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority; //抢断优先级
uint8_t NVIC_IRQChannelSubPriority; //响应优先级
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
什么是中断向量表?
- 每个中断源都有对应的处理程序,这个处理程序称为中断服务程序,其入口地址称为中断向量。所有中
断的中断服务程序入口地址构成一个表,称为中断向量表;也有的机器把中断服务程序入口的跳转指令构成
一张表,称为中断向量跳转表。
5. 按键点亮LED灯(中断法)(311.15)
- 配置时钟
- 配置GPIO口
- 使能中断
- 代码(4.exti_test/MDK-ARM)
c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断服务函数
{
switch(GPIO_Pin)
{
HAL_Delay(50);//为了消抖
case GPIO_PIN_0:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)//消抖:如果下降沿之后的50ms还是低(消除意外抖动带来的低电平情况)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//翻转LED灯状态
break;
case GPIO_PIN_1:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
break;
}
}
6. 电动车报警器项目概述(312.16)
项目需求
- 点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警,吓退小偷。
- 点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警,否则系统一直发出尖叫,让车主尴
尬。
项目框图
硬件清单
- 振动传感器
- 继电器
- 高功率喇叭
- 433M无线接收发射模块
- 杜邦线
7. 振动传感器介绍及实战(313.17)
振动传感器介绍
- 单片机供电VCC GND接单片机
- 产品不震动,输出高电平,模块上的DO口
- 产品震动,输出低电平,绿色指示灯亮
- AO口不用
编程实现
- 需求:当振动传感器接收到振动信号时,使用中断方式点亮LED1。
- CubeMX 的必要配置
- 代码(5.alert_project/MDK-ARM)
c
//重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//一根中断线上接有多个中断源,判断中断源是否来自PA4
if(GPIO_Pin == GPIO_PIN_4)
{
//如果检测到PA4被拉低
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET)
{
//则点亮LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
else
{
//如果未检测到PA4被拉低,则关闭LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
}
}
- 如果直接在中断服务函数里调用 HAL_Delay 函数,则会造成系统卡死。
- 原因:程序初始化时默认把滴答定时器的中断优先级设为最低,其它中断源很容易打断它导致卡死。
- 解决:在 main 函数里使用以下函数提高滴答定时器的中断优先级(提升至0):
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
- 并且将 EXTI4 的中断优先级设置比滴答定时器的中断优先级高,比如 2 。
8. 继电器介绍及实战(314.18)
继电器工作原理
- 单片机供电VCC GND接单片机,VCC需要接3.3V,5V不行!
- 最大负载电路交流250V/10A,直流30V/10A
- 引脚 IN 接收到低电平时 ,开关闭合。(PB8,同时led1亮)
- 电源的负极---负载的负极
- 电源的正极、负载的正极分别接到继电器的 com 和 NO 口(可反)
9. 433M无线发射接收模块介绍及实战(315.19)
433M无线发射接收模块介绍
- 单片机供电VCC GND接单片机
- 接收到信号,接收模块对应针脚输出高电平
- 有D0 D1 D2 D3,对应遥控器的ABCD
编程实现
- 需求:按下遥控器A按键,LED1亮1秒;按下遥控器B按键,LED2亮1秒。
- D0 -- PA5
- D1 -- PA6
- 修改 cubemx 工程配置:
- 代码(5.alert_project/MDK-ARM)
c
//重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case GPIO_PIN_5:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
{// 如果检测到PA5被拉高(即按键A被按下)
// 则将PB8拉低,LED1亮1秒
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
else
{// 未检测到PA5被拉高,则LED1灭
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
break;
case GPIO_PIN_6:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)
{// 如果检测到PA6被拉高(按键B被按下)
// 则将PB9拉低,LED2亮1秒
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}
else
{// 未检测到PA5被拉高,则LED1灭
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}
break;
}
}
10. 电动车报警器项目设计及(316.20)
项目设计
//如果检测到PA4被拉低(小偷偷车),并且警报模式打开
//则将PB7拉低,继电器通电,喇叭一直响
// 如果检测到PA5被拉高(按键A按下),设定为开启警报模式
// 则将PB7拉低(喇叭响),2秒后恢复电平(喇叭不响),表示进入警报模式
// 同时将标志位设置为ON
// 如果检测到PA6被拉高(按键B按下),设定为关闭警报模式
// 则将PB7拉低(喇叭响),1秒后恢复电平(喇叭不响),表示关闭警报模式
// 同时将标志位设置为OFF
编程实现
- cubemx工程修改
- 代码(5.alert_project/MDK-ARM)
c
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define J_ON 1
#define J_OFF 2
/* USER CODE END PM */
//重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static int mark = J_OFF;//只在首次初始化时分配内存,且在函数调用之间保留其值
switch(GPIO_Pin)
{
// 如果检测到PA4被拉低(发生振动 即小偷偷车),并且警报模式打开
case GPIO_PIN_4:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET && mark == J_ON)
{
// 则将PB7拉低,继电器通电,喇叭一直响
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
}
break;
// 如果检测到PA5被拉高(按键A被按下),设定为开启警报模式
case GPIO_PIN_5:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
{
// 则将PB7拉低(喇叭响),2秒,表示进入到警报模式
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
// 同时将标示位设置为ON
mark = J_ON;
}
break;
// 如果检测到PA6被拉高(按键B被按下),设定为关闭警报模式
case GPIO_PIN_6:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)
{
// 则将PB7拉低(喇叭响),1秒,表示关闭警报模式
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
// 同时将标示位设置为OFF
mark = J_OFF;
}
}
}