STM32F103C8T6第二天:按键点灯轮询法和中断法、RCC、电动车报警器(振动传感器、继电器、喇叭、433M无线接收发射模块)

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

复位

  • 系统复位
    当发生以下任一事件时,产生一个系统复位:
    *
    1. NRST引脚上的低电平(外部复位)
    2. 窗口看门狗计数终止(WWDG复位)
    3. 独立看门狗计数终止(IWDG复位)
    4. 软件复位(SW复位)
    5. 低功耗管理复位
  • 电源复位
    当以下事件中之一发生时,产生电源复位:
    *
    1. 上电/掉电复位(POR/PDR复位)
    2. 从待机模式中返回
  • 备份区复位
    备份区域拥有两个专门的复位,它们只影响备份区域。
    当以下事件中之一发生时,产生备份区域复位。
    *
    1. 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)(见6.3.9节)中的BDRST位
      产生。
    2. 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。

时钟控制

  • 什么是时钟?
    时钟打开,对应的设备才会工作。
  • 时钟来源
    • 三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
      • HSI振荡器时钟(高速内部时钟)
      • HSE振荡器时钟(高速外部时钟)
      • PLL时钟(锁相环倍频时钟)
    • 二级时钟源:
      • 40kHz低速内部RC(LSIRC)振荡器
      • 32.768kHz低速外部晶体(LSE晶体)
  • 如何使用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)

  1. 配置时钟

  2. 配置GPIO口
  3. 使能中断
  • 代码(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;
		}
	}
}


相关推荐
-Springer-12 分钟前
STM32 学习 —— 个人学习笔记5(EXTI 外部中断 & 对射式红外传感器及旋转编码器计数)
笔记·stm32·学习
LS_learner31 分钟前
树莓派(ARM64 架构)Ubuntu 24.04 (Noble) 系统 `apt update` 报错解决方案
嵌入式硬件
来自晴朗的明天1 小时前
16、电压跟随器(缓冲器)电路
单片机·嵌入式硬件·硬件工程
钰珠AIOT1 小时前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠1 小时前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
代码游侠12 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200515 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT16 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen17 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制