STM32G030C8T6 串口外部中断读取ADC值打印五向按键方向

五向按键方向检测实验文档

  1. 需求分析和原理

1.1 需求分析

  • **材料、基于STM32G030C8T6 单片机开发板 **:

image.png

  • 底板原理图和核心板原理图

image.pngimage.png

  • 目标:通过串口助手打印五向按键的方向。
  • 芯片型号:STM32G030C8T6。
  • 按键和ADC配置
    • ADC输入:PA1
    • 五向按键输入:PA8(命名为key_five),设置为外部中断,配置为上升沿触发,下拉输入。

1.2 原理

通过配置PA8为外部中断引脚,当五向按键被按下时,PA8会产生上升沿触发中断。在中断处理函数中,读取PA1的ADC值,根据不同的ADC值判断按键的方向,并通过串口将方向信息打印出来。

  1. 实验过程

2.1 实验步骤

  1. 配置STM32CubeMX生成初始化代码。
  2. 编写关键代码实现按键方向检测和串口打印。
  3. 编译并下载程序到STM32G030C8T6芯片。
  4. 使用串口助手观察并验证按键方向检测效果。

2.2 STM32CubeMX配置流程

  1. 打开STM32CubeMX,选择STM32G030C8T6芯片。

image.png

  1. GPIO/ADC 配置PA1为ADC1_IN1。

image.png

  1. GPIO/GPIO配置PA8为GPIO_EXTI8,选择上升沿触发模式,内部下拉。

image.png
image.png

  1. 勾选USART1 并配置mode为 asynchronous (异步)

image.png

  1. 在NVIC 下勾选 EXTI line 4 to 15 interrupts 和 USART1 global interrupt/USART1 wake-up through EXTI line 25

image.png

  1. Project Manager下配置项目名、路径、工具链 MDK-ARM V5,Code Generator勾选 Generate peripheral initialization as a pair of’c/.h fles per peripheral

image.png
image.png

  1. 生成初始化代码,使用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 */

image.png
image.png
image.png

实验现象

image.png


当按下五向按键的不同方向时,通过串口助手可以观察到相应的方向打印:

  • 按下左键时,串口助手显示"LEFT"。
  • 按下右键时,串口助手显示"RIGHT"。
  • 按下上键时,串口助手显示"UP"。
  • 按下下键时,串口助手显示"DOWN"。
  • 按下中间键时,串口助手显示"CENTER"。
  1. 结论

本实验通过配置STM32G030C8T6的PA8引脚为外部中断,并在中断处理函数中读取ADC值,成功实现了五向按键的方向检测,并通过串口助手打印方向信息。实验现象与预期一致,验证了实验的可行性和正确性。
串口助手打印五向按键方向.MP4
实验视频链接: https://www.bilibili.com/video/BV12o3be2EB6?t=12.3

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用CubeMX读取PA5电池电量的数据并显示到串口的步骤: 1. 打开CubeMX,选择相应的芯片型号(stm32g030c8t6),创建一个新的工程。 2. 在Pinout标签页中,将PA5引脚配置为模拟输入模式(Analog)。 3. 在Configuration标签页中,选择ADC1,然后将ADC1的采样时间设置为合适的(比如6个时钟周期)。 4. 在Code Generation标签页中,选择使用HAL库,并勾选USART2串口的使能选项。 5. 在main函数中,使用HAL库的函数初始化ADC串口,并设置ADC的通道为PA5。 6. 在while循环中,使用HAL库的函数启动ADC转换,并等待转换完成。 7. 在转换完成后,使用HAL库的函数将读取到的电量转换为电压,并将电压以字符串的形式发送到串口上。 下面是示例代码: ```c #include "main.h" #include "stdio.h" #include "string.h" ADC_HandleTypeDef hadc1; UART_HandleTypeDef huart2; float battery_voltage = 0.0; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC1_Init(void); static void MX_USART2_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_USART2_UART_Init(); while (1) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 100); uint16_t adc_value = HAL_ADC_GetValue(&hadc1); battery_voltage = (float)adc_value / 4096 * 3.3; char buffer[32]; sprintf(buffer, "Battery voltage: %.2fV\r\n", battery_voltage); HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), 100); HAL_Delay(1000); } } 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_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 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_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc1.Init.LowPowerAutoWait = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.NbrOfConversion = 1; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; hadc1.Init.OversamplingMode = DISABLE; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_5; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } } static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void Error_Handler(void) { __disable_irq(); while (1) { } } ``` 这个例子会每秒钟读取一次PA5引脚的电量,并将电量转换为电压(假设PA5引脚连接的是一个0-3.3V的电压信号),然后通过USART2串口发送到PC端。你可以在串口调试软件中查看电压的变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值