五向按键方向检测实验文档
- 需求分析和原理
1.1 需求分析
- **材料、基于STM32G030C8T6 单片机开发板 **:
- 底板原理图和核心板原理图:
- 目标:通过串口助手打印五向按键的方向。
- 芯片型号:STM32G030C8T6。
- 按键和ADC配置:
- ADC输入:PA1
- 五向按键输入:PA8(命名为key_five),设置为外部中断,配置为上升沿触发,下拉输入。
1.2 原理
通过配置PA8为外部中断引脚,当五向按键被按下时,PA8会产生上升沿触发中断。在中断处理函数中,读取PA1的ADC值,根据不同的ADC值判断按键的方向,并通过串口将方向信息打印出来。
- 实验过程
2.1 实验步骤
- 配置STM32CubeMX生成初始化代码。
- 编写关键代码实现按键方向检测和串口打印。
- 编译并下载程序到STM32G030C8T6芯片。
- 使用串口助手观察并验证按键方向检测效果。
2.2 STM32CubeMX配置流程
- 打开STM32CubeMX,选择STM32G030C8T6芯片。
- GPIO/ADC 配置PA1为ADC1_IN1。
- GPIO/GPIO配置PA8为GPIO_EXTI8,选择上升沿触发模式,内部下拉。
- 勾选USART1 并配置mode为 asynchronous (异步)
- 在NVIC 下勾选 EXTI line 4 to 15 interrupts 和 USART1 global interrupt/USART1 wake-up through EXTI line 25
Project Manager
下配置项目名、路径、工具链 MDK-ARM V5,Code Generator勾选 Generate peripheral initialization as a pair of’c/.h fles per peripheral
- 生成初始化代码,使用Keil 5进行项目开发。
2.3 Keil 5 代码
main.c 关键函数
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "stdio.h"
#include "string.h"
__ASM (".global __use_no_semihosting");
struct FILE {int handle; };
FILE __stdout;
void _sys_exit(int x) { x = x; }
void _ttywrch(int ch){ ch = ch;}
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch};
HAL_UART_Transmit(&huart1, temp, 1, 2);
return ch;
}
/* USER CODE END 0 */
/**
* @brief 应用程序入口点。
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* 复位所有外设,初始化Flash接口和Systick。 */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* 配置系统时钟 */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* 初始化所有配置的外设 */
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* 无限循环 */
/* USER CODE BEGIN WHILE */
printf("ADC Direction Detection Demo\r\n");
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief 系统时钟配置
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** 配置主内部调节器输出电压
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** 初始化RCC振荡器,按RCC_OscInitTypeDef结构中指定的参数配置
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** 初始化CPU,AHB和APB总线时钟
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief 发生错误时执行此函数。
* @retval 无
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* 用户可以在此添加自己的错误处理代码 */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief 报告源文件名和错误行号
* @param file: 指向源文件名的指针
* @param line: 断言错误行号
* @retval 无
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* 用户可以在此添加自己的错误报告代码 */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
stm32g0xx_it.c 中 关键函数
#include "main.h"
#include "stm32g0xx_it.h"
#include "stdio.h"
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
extern ADC_HandleTypeDef hadc1;
#define ADC_UP_MIN 2100
#define ADC_UP_MAX 2199
#define ADC_RIGHT_MIN 3000
#define ADC_RIGHT_MAX 3099
#define ADC_DOWN_MIN 550
#define ADC_DOWN_MAX 750
#define ADC_LEFT_MIN 1550
#define ADC_LEFT_MAX 1750
#define ADC_CENTER_MIN 2500
#define ADC_CENTER_MAX 2850
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M0+ 处理器中断处理器 */
/******************************************************************************/
/**
* @brief 处理不可屏蔽中断。
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief 处理硬故障中断。
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief 处理系统服务调用通过SWI指令。
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVC_IRQn 0 */
/* USER CODE END SVC_IRQn 0 */
/* USER CODE BEGIN SVC_IRQn 1 */
/* USER CODE END SVC_IRQn 1 */
}
/**
* @brief 处理挂起请求的系统服务。
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief 处理系统滴答定时器。
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32G0xx 外设中断处理器 */
/* 在这里添加使用的外设的中断处理器。 */
/* 对于可用的外设中断处理器名称,请参阅启动文件(startup_stm32g0xx.s)。 */
/******************************************************************************/
/**
* @brief 处理EXTI线4到15的中断。
*/
void EXTI4_15_IRQHandler(void)
{
/* USER CODE BEGIN EXTI4_15_IRQn 0 */
/* USER CODE END EXTI4_15_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(key_five_Pin);
/* USER CODE BEGIN EXTI4_15_IRQn 1 */
/* USER CODE END EXTI4_15_IRQn 1 */
}
/**
* @brief 处理USART1全局中断 / USART1通过EXTI线25唤醒中断。
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/**
* @brief 处理GPIO外部中断上升沿回调函数。
* @param GPIO_Pin: GPIO引脚
* @retval 无
*/
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == key_five_Pin)
{
/* ADC读取和方向处理 */
uint32_t adc_value = 0;
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 50) == HAL_OK)
{
adc_value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
/* 根据ADC值确定方向 */
if (adc_value >= ADC_UP_MIN && adc_value <= ADC_UP_MAX)
{
printf("Direction: UP\r\n");
}
else if (adc_value >= ADC_RIGHT_MIN && adc_value <= ADC_RIGHT_MAX)
{
printf("Direction: RIGHT\r\n");
}
else if (adc_value >= ADC_DOWN_MIN && adc_value <= ADC_DOWN_MAX)
{
printf("Direction: DOWN\r\n");
}
else if (adc_value >= ADC_LEFT_MIN && adc_value <= ADC_LEFT_MAX)
{
printf("Direction: LEFT\r\n");
}
else if (adc_value >= ADC_CENTER_MIN && adc_value <= ADC_CENTER_MAX)
{
printf("Direction: CENTER\r\n");
}
else
{
printf("ADC Value: %d \r\n", adc_value);
}
}
}
}
/* USER CODE END 1 */
实验现象
当按下五向按键的不同方向时,通过串口助手可以观察到相应的方向打印:
- 按下左键时,串口助手显示"LEFT"。
- 按下右键时,串口助手显示"RIGHT"。
- 按下上键时,串口助手显示"UP"。
- 按下下键时,串口助手显示"DOWN"。
- 按下中间键时,串口助手显示"CENTER"。
- 结论
本实验通过配置STM32G030C8T6的PA8引脚为外部中断,并在中断处理函数中读取ADC值,成功实现了五向按键的方向检测,并通过串口助手打印方向信息。实验现象与预期一致,验证了实验的可行性和正确性。
串口助手打印五向按键方向.MP4
实验视频链接: https://www.bilibili.com/video/BV12o3be2EB6?t=12.3