基于STM32_HAL库的电动车报警器项目
基于STM32_HAL库的电动车报警器项目
文章目录
- 基于STM32_HAL库的电动车报警器项目
- 一、项目需求
- 二、硬件连接
- 三、项目实现
- 3.1 CubeMX配置:
- 3.2 代码实现:
一、项目需求
- 按下433M遥控器B按键,系统进入警戒模式,一旦检测到震动(小偷偷车)则警报响起,吓退小偷。
- 按下433M遥控器A按键,系统退出警戒模式,再怎么摇晃系统都不会报警,否则一直响的话车主会很尴尬😅
二、硬件连接
下面是具体的各模块与单片机之间的硬件连接,其中PA0、PA5、PA6是外部中断功能,PB7是GPIO输出:
三、项目实现
3.1 CubeMX配置:
-
配置Debug
-
打开RCC配置时钟,将高速时钟配置成Crystal/Ceramic Resonator
-
点击配置时钟选项,把时钟配置成高速外部时钟HSE,并且通过PLLCLK倍频到72MHZ,按下回车之后就为芯片内部的功能分配好了对应的时钟频率。
-
然后配置PB7为GPIO_Output模式,并且初始化为低电平,因为继电器模块是高电平触发的
-
配置PA0为外部中断引脚,并且配置为下降沿触发,连接外部的震动传感器
-
配置PA5和PA6为外部中断引脚,并且配置为上升沿触发,连接外部433M无线遥控的两个信号
-
打开NVIC,使能EXT0、EXT5、EXT6,并且设置“抢占优先级”为2级,本来之前我们的抢占优先级默认是0的,但是由于我们在代码的中断服务函数中使用了HAL_Delay()函数,程序初始化时默认把滴答定时器的中断优先级设为最低,其它中断源很容易打断它导致卡死,也就是说当HAL_Delay()函数在进行中的时候,如果又发生了震动触发了外部中断,就会又立刻执行这个中断处理程序,导致喇叭一直在响无法停下。所以我们只能:在 main 函数里使用以下函数提高滴答定时器的中断优先级(提升至0)
HAL_NVIC_SetPriority(SysTick_IRQn,0,0); //将 SysTick 定时器中断的抢占优先级和响应优先级都设置为 0(即最高优先级)
并且在CubeMX中将其他的中断优先级设置比滴答定时器优先级低,比如说:2
3.2 代码实现:
我们首先来看一下使用CubeMX生成的初始化代码:
/* main.c */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
上面这段代码是由CubeMX生成的时钟相关的代码。
/* gpio.c */
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : PA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA5 PA6 */
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
上面这段代码是由CubeMX配置生成的GPIO输出,以及外部中断初始化相关的代码。
/* stm32f1xx_it.c */
/**
* @brief This function handles EXTI line0 interrupt.
*/
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
/**
* @brief This function handles EXTI line[9:5] interrupts.
*/
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
上面这段代码里面的两个函数,分别处理EXT0和EXT5&EXT6,他们都调用了HAL_GPIO_EXTI_IRQHandler
()函数,我们按下F12查看此函数:
/* stm32f1xx_hal_gpio.h */
/**
* @brief This function handles EXTI interrupt request.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin);
}
到这里我们终于看到外部中断处理函数了,我们只需要重新的写__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
就可以了,下面就是我们在main函数写的业务代码,从而实现我们的项目需求:
#include "main.h"
#include "gpio.h"
#define J_ON 1 // 定义警报标志位
#define J_OFF 0 // 定义警报标志位
void SystemClock_Config(void);
/* 重写中断服务函数,如果检测到EXIT中断请求则进入该函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static int mark = J_OFF; // 默认是取消警报标志
// 一根线上可以接多个中断源,判断中断到底来自于哪一个
switch(GPIO_Pin){
// 如果检测到PA0被拉低(小偷偷车)并且警报模式打开
case GPIO_PIN_0:
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET && mark == J_ON){
// 将PB7拉低,继电器通电,喇叭一直响
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
}
break;
// 如果检测到PA5被拉高(按键B按下)设定为进入警报模式
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_SET);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
// 同时将标志位设置为ON
mark = J_ON;
}
break;
// 如果检测到PA6被拉高(按键A按下)设定为取消警报模式
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_SET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
// 同时将标志位设置为OFF
mark = J_OFF;
}
break;
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
HAL_NVIC_SetPriority(SysTick_IRQn,0,0); //将 SysTick 定时器中断的抢占优先级和响应优先级都设置为 0(即最高优先级)
MX_GPIO_Init();
while (1)
{
}
}
我们把代码编译烧录进去就实现我们的整个项目。