这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 过程帖|LED、按键、蜂鸣器和串口

共1条 1/1 1 跳转至页

过程帖|LED、按键、蜂鸣器和串口

菜鸟
2026-11-08 15:49:29     打赏

任务一的目标是把开发板最基本的三样东西跑通,LED 能闪、按键有反应、串口能打印。工程用 STM32CubeIDE 建,下载器用板载 ST-Link,串口走 ST-Link 的虚拟串口,波特率 115200,八位数据一位停止无校验。这一步通了,说明编译、下载、串口三条链路都正常。

ed17a90fee5155697d2af6bc9f31d84b_720.png

先配灯。板载 LD2 在 PA5 上,也就是 Arduino 排针的 D13,物理位置是 CN5 第 6 脚。配成推挽输出,初始输出低电平。这颗灯是高电平点亮。

GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();

GPIO_InitStruct.Pin   = GPIO_PIN_5;              /* PA5 = D13 = LD2 */
GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull  = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

再配按键。板载蓝色 B1 在 PC13 上。按照 UM3121 手册第 11.2 节,这颗按键接的是 VDD,按下时引脚为高电平,松开为低电平,所以引脚要开内部下拉,中断触发选上升沿。中断线 13 对应 EXTI13,中断号是 24。

GPIO_InitStruct.Pin  = GPIO_PIN_13;              /* PC13 = B1 */
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

HAL_NVIC_SetPriority(EXTI13_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI13_IRQn);

中断服务函数只清标志并转发,业务放在回调里,同时用一个 30 毫秒的时间窗做消抖,一次按压只记一次。这里用无符号相减而不是大小比较,是为了兼容毫秒计数回绕。

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
  static uint32_t last_tick = 0;

  if (GPIO_Pin != GPIO_PIN_13) return;
  if ((g_ms_tick - last_tick) < 30u) return;     /* 30ms 消抖窗口 */
  last_tick   = g_ms_tick;
  g_key_event = 1u;                              /* 交给主循环处理 */
}

蜂鸣器用的是 Grove 有源蜂鸣器模块,四线接口,按颜色分是黑色接地、红色接电源、黄色是信号,第四根是空脚。信号线接 Arduino 排针的 A3,对应的 MCU 引脚是 PB0,物理位置在 CN8 第 4 脚。电源接 3V3,也就是 CN6 第 4 脚,地接 CN6 第 6 或第 7 脚。控制方式是写高电平发声、写低电平停止,普通推挽输出即可。

GPIO_InitStruct.Pin  = GPIO_PIN_0;               /* PB0 = A3 */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

void Buzzer_Beep(uint16_t ms)                    /* 响 ms 毫秒,不阻塞 */
{
  s_buzz_on = 1u;
  s_buzz_ms = ms;
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
}

void Buzzer_Service1ms(void)                     /* 由 1ms 时基调用 */
{
  if (s_buzz_on == 0u) return;
  if (--s_buzz_ms == 0u)
  {
    s_buzz_on = 0u;
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  }
}

闪灯和蜂鸣器的计时没有用延时函数,而是单独做了一个 1 毫秒的时基。芯片主频 250 兆,TIM6 预分频 249 得到 1 兆计数频率,再数 1000 次就是一个毫秒,中断优先级设为 3。

htim6.Instance               = TIM6;
htim6.Init.Prescaler         = 250 - 1;          /* 250MHz / 250 = 1MHz */
htim6.Init.Period            = 1000 - 1;         /* 1MHz / 1000 = 1kHz  */
htim6.Init.CounterMode       = TIM_COUNTERMODE_UP;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim6);
HAL_TIM_Base_Start_IT(&htim6);

时基回调里做三件事:毫秒计数、蜂鸣器倒计时、LED 翻转。后面任务里的电机换相也会挂在这里,主循环即使被采集任务占住,这些动作也不会停。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance != TIM6) return;

  g_ms_tick++;
  Buzzer_Service1ms();

  if (s_led_blink != 0u && ++s_led_acc >= 500u)
  {
    s_led_acc = 0u;
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);         /* 500ms 翻转一次 */
  }
}

串口用 USART2,也就是板载虚拟串口,PA2 发送、PA3 接收,复用功能 AF7,PC 端打开对应 COM 口即可。打印用阻塞发送,接收用中断,每进一次中断收一个字节,攒进环形缓冲,主循环再按行解析。要让接收中断工作,除了配置引脚复用,还要在底层初始化里使能 USART2 的 NVIC 中断。

HAL_UART_Receive_IT(&huart2, (uint8_t *)&s_rx_byte, 1);   /* 启动接收 */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  uint8_t nh = (uint8_t)((s_rx_head + 1u) % sizeof(s_rx_q));

  if (nh != s_rx_tail)                            /* 缓冲满了就丢新字节 */
  {
    s_rx_q[s_rx_head] = s_rx_byte;
    s_rx_head = nh;
  }
  HAL_UART_Receive_IT(&huart2, (uint8_t *)&s_rx_byte, 1); /* 重新武装 */
}

烧进去以后按一下 B1,绿灯开始每 500 毫秒翻转一次,蜂鸣器响 60 毫秒,串口同时打印一行日志:

image.png

任务一到这里就完成了。下一步接 LIS3DH,先用 I²C 把三轴数据读出来。



共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]