这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let'sdo2026年第3期】任务1:串口信息打印、LED控制和PWM播放音

共1条 1/1 1 跳转至页

【let'sdo2026年第3期】任务1:串口信息打印、LED控制和PWM播放音乐

高工
2026-11-11 21:12:20     打赏

Let's do第三期活动使用的开发板是ST的NUCLEO-C562RE开发板,MCU的型号为STM32C562RET6,内核是Cortex-M33,主频144MHz,片上Flash为512KB,RAM为128KB,属于入门级的MCU。本次任务实现开发板灯光控制、PWM控制蜂鸣器以及串口信息输出。

1、软件开发板环境

任务中使用到的软件如下:

  • STM32CubeMX2用于配置开发板外设

  • STM32CubeIDE for VSCode用于代码编译和调试

2、硬件组成

实现任务目标使用到以下硬件

NUCELO-C562RE开发板

  • PWM蜂鸣器

  • Arduino Grove 转接板

硬件的连接图如下

Buzzer_Hardware.png

MCU和ST-Link的连接原理图如下

debug_port.png

MCU用于串口通讯的引脚

串口功能MCU引脚

VCP_TXPA2
VCP_RXPA3

Grove Buzzer和Arduino接口的接线对应关系

ArduinoGrove-Buzzer

5VRed
GNDBlack
Not ConenctedWhite
D2Yellow

通过Arduino Grover转接板连接D2(PA10)到蜂鸣器的控制端

Buzzer_Ctrl.png

Buzzer_Ctrl2.png







 

Buzzer.png

板载的LED的硬件原理图

LED.png

3、CubeMX2配置外设

CubeMX2中根据开发板型号创建NUCELO-C562RE开发板的工程

Create_Project_With_Board.png

开发板中LED、调试串口的引脚都已分配但是没有设置相应的功能,在PinOut界面设置相应引脚的功能,点击引脚界面的配置按钮跳转到引脚的设置界面。

LED_Pin_config1.png

引脚的设置为输出模式,初始电平为低电平

LED_Pin_config2.png

由于工程是基于NUCLEO-C562RE开发板创建的,工程的Parts界面选中LD1部件,激活该组件,并为其分配相应的引脚PA5。

LED_Part_Active.png

同样地,Pinout界面配置串口使用到的引脚为串口

USART2_Config1.png

串口的配置页面中设置串口的工作模式为异步通讯并设置相应的通讯参数

USART2_Config2.png

使能串口的中断,设置相应的串口中断优先级

USART2_Config3.png

CubeMX2中的组件中有用于调试输出的basic_stdio组件,激活该组件并设置组件的输出接口为调试串口

basic_stdio_config.png

设置工程的类型为CMake,工具链设置为GCC,并生成工程

Setting_Gen_Project_Type.png

4、软件实现

工程生成main.c引入生成LED外设(mx_led.h)、Basic_Stdio组件(mx_basic_stdio_app.h)头文件用于使用其中的函数

#include "main.h"

#include <stdio.h>
#include <string.h>
#include "generated/utilities/mx_basic_stdio_app.h"
#include "led.h"
#include "stm32c5xx_hal.h"

#include "mx_led.h"
#include "mx_basic_stdio_app.h"

定义用于接收串口数据的宏定义和缓存区

#define RX_BUF_SIZE 64
uint8_t rx_buf[RX_BUF_SIZE];
volatile uint8_t rx_data_ready = 0;   // 指令就绪标志
volatile uint16_t rx_data_len = 0;    // 实际接收长度

串口的中断回调函数中处理数据接收标志位并记录接收的数据长度

void HAL_UART_RxCpltCallback(hal_uart_handle_t *huart, uint32_t size_byte, hal_uart_rx_event_types_t rx_event)
{
    if (huart->instance== HAL_UART2) {
        rx_data_len = size_byte;
        rx_data_ready = 1;            // 通知主循环处理

    }
}

main.c中初始化 basic_stdio组件,调用HAL_UART_ReceiveToIdle_IT函数接收数据长度最大为RX_BUF_SIZE的数据,并在串口空闲时触发中断。

    mx_basic_stdio_init();
    printf("EEPW Let do 3\r\n");

    HAL_UART_ReceiveToIdle_IT(mx_usart2_uart_gethandle(), rx_buf, RX_BUF_SIZE);

主循环中实现对串口接收数据的指令解析和执行,控制LED的亮灭

    while (1) {
        if (rx_data_ready) {
            rx_buf[rx_data_len] = '\0';   // 添加字符串结束符

            if (strstr((char*)rx_buf, "LED ON") != NULL) {
                led_on(LED_0);
                printf("LED ON\r\n");
            } 
            else if (strstr((char*)rx_buf, "LED OFF") != NULL) {
                led_off(LED_0);
                printf("LED OFF\r\n");
            }
            else {
                printf("Unknown cmd\r\n");
            }

            rx_data_ready = 0;            // 清除标志
            // 立即重新开启接收,准备下一帧
            HAL_UART_ReceiveToIdle_IT(mx_usart2_uart_gethandle(), rx_buf, RX_BUF_SIZE);            
        }
        HAL_Delay(10);
    }

5、CubeMX配置PWM

控制外接无源蜂鸣器的引脚为PA10,可复用为TIM1_CH3,配置为PWM输出,产生PWM,控制蜂鸣器发出一段音乐。

CubeMX2中选择PA10引脚,设置其复用功能为TIM1_CH3,点击配置图标进入外设配置页面

TIM1_CH3_Config.png

使能TIM1模块,调整定时器的时钟分频系数和定时器计数周期,设置TIM1的频率为1KHz

TIM1_Prescaler_Couter_Paras.png

使能通道3,设置输出比较模式为PWM Mode1,使能CH3输出以及引脚映射

TIM1_CH3_Config2.png

设置TIM1的定时器更新中断以及优先级

TIM1_CH3_Config3.png

保存设置并生成代码

6、PWM驱动蜂鸣器发出声音

输出 PWM 驱动无源蜂鸣器播放音乐,关键在于用 PWM 频率决定音高,用持续时间决定节拍,PWM 频率公式:

PWM频率 = TIM1_CLK / ((PSC + 1) × (ARR + 1))

为方便计算,可固定 ARR(如 999),只调 PSC 来改变频率。占空比固定为 50%(CCR = ARR/2)。每个音符对应一个 PSC 值,切换音符就是切换 PSC,由此定义音符频率表如下:

// 音符频率表(C4~B4,单位 Hz)
const uint16_t note_freq[] = {
    262, 294, 330, 349, 392, 440, 494,  // C4~B4
};

改变TIM1_CH3的PWM输出频率的函数定义如下

// 设置 TIM1_CH3 的 PWM 频率
static void Buzzer_SetFreq(uint32_t freq_hz)
{
   hal_tim_handle_t *htim = mx_tim1_gethandle();  

   if (freq_hz == 0U)  
  {  
     HAL_TIM_OC_SetCompareUnitPulse(htim, HAL_TIM_OC_COMPARE_UNIT_3, 0U);  // 静音  
     return;  
  }  

   /* ARR = CNT_CLK / freq - 1 */  
   uint32_t arr = CNT_CLK / freq_hz;  
   if (arr < 2U) arr = 2U;  
   if (arr > 0x10000UL) arr = 0x10000UL;  

   HAL_TIM_SetPeriod(htim, arr - 1U);  // ARR → 频率  
   HAL_TIM_OC_SetCompareUnitPulse(htim, HAL_TIM_OC_COMPARE_UNIT_3, arr / 2U);  // 50% 占空比  
}

播放的音频有音符以及相应的节拍数构成,定义以下函数,输入参数为音符序列的数组以及相应的节拍数数组,通过修改频率和控制PWM的持续节拍,实现音乐的播放。

// Do Re Mi Fa Sol La Si(各一拍)
const uint8_t demo_notes[] = {0, 1, 2, 3, 4, 5, 6};
const uint8_t demo_beats[] = {1, 1, 1, 1, 1, 1, 2};


// 播放一段旋律:notes 数组为音符索引(0~6),beats 为节拍数
void Buzzer_PlayMelody(const uint8_t *notes, const uint8_t *beats, uint8_t len)
{
    for (uint8_t i = 0; i < len; i++) {
        if (notes[i] == 0xFF) {
            // 休止符
            Buzzer_SetFreq(0);
        } else {
            Buzzer_SetFreq(note_freq[notes[i]]);
        }
        HAL_Delay(beats[i] * 500);  // 1 拍 = 500 ms
    }
    Buzzer_SetFreq(0);  // 播放结束,静音
}

main.c中添加PWM Buzzer指令,用于播放音乐

            else if (strstr((char*)rx_buf, "PWM Buzzer") != NULL) {
                Buzzer_PlayMelody(demo_notes, demo_beats, 7);
                printf("PWM Buzzer\r\n");
            }

编译并烧录工程到开发板,工程的运行效果如视频所示





关键词: Let do 3         

共1条 1/1 1 跳转至页

回复

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