ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32多功能电子琴项目实战:从原理图到PWM发声与调试

STM32多功能电子琴项目实战:从原理图到PWM发声与调试 简介本资源是一套面向嵌入式初学者与课程设计者的STM32多功能电子琴完整开发方案聚焦单片机外设驱动与音频控制实践解决从硬件搭建到软件逻辑实现的一体化学习需求。资源包含169个文件涵盖Keil工程uvprojx、ioc、hex等、原理图schdoc、prjpcb、核心代码47个.h头文件、20个.c源文件、编译中间文件o、d、crf及说明文档pdf、md、txt总大小10.6MB结构清晰便于理解工程组织与模块划分。已有1368人学习下载体现了其在教学实践与毕业设计中的广泛适用性。用户可直接导入Keil MDK环境运行完整掌握基于STM32定时器PWM驱动无源蜂鸣器发声、矩阵按键扫描识别、音乐播放状态机设计播放/暂停/切歌/弹奏等关键技术点并通过原理图快速复现硬件电路是嵌入式系统课程实训与电子类创新项目落地的高实用性参考范例。 每次看到网上那些用STM32做的小玩意我都有点感慨。大学那会儿我第一个正经八百的完整项目就是基于STM32的多功能电子琴带原理图、带源代码虽然现在看来很多写法笨拙得不行但正是那个项目把定时器、中断、GPIO、状态机这些概念彻底焊死在了我的脑子里。这篇文章就把当初那些设计思路、核心代码怎么写、硬件上为什么要这么搭、以及调试时踩过的一堆坑全部拿出来说一说。无论你是刚学完单片机基础想做点硬核玩意儿的在校生还是工作后想捡起一块开发板找找感觉的嵌入式工程师这个项目都很适合拿来练手。这东西能把“点灯”和“真正做产品”之间的那段距离填上因为电子琴虽然结构不复杂但它包含了人机交互按键、实时响应中断、信号输出PWM发音、多任务调度扫描、显示、发声几乎把单片机最核心的知识点全覆盖了。而“多功能”三个字也不是噱头通常指的是在标准琴键之外加入自动播放、录音回放、音色切换等功能。这篇文章会从整体设计讲到每一行关键代码最后聊透我踩过的那些坑。1. 项目整体设计与思路拆解1.1 为什么选STM32做电子琴而不选51或Arduino我见过很多人的第一个疑问是电子琴而已拿51单片机或一块Arduino Nano不照样能做吗确实能做进阶一点的甚至连一台AVR的ATmega328都能出声但STM32在这个项目上优势非常明显——不是“能不能做”的问题而是“能做到什么程度”的问题。首先51单片机的定时器资源太少一般就2到3个定时器。电子琴需要至少一个定时器用来生成音符频率如果需要PWM输出还得占用额外的定时器通道如果要实现录音回放又得有定时器做采样定时还要有定时器做播放节拍。三个定时器资源在51上已经捉襟见肘而在STM32F103C8T6这种入门级芯片上Time1、Time2、Time3、Time4四个定时器加上每个定时器4个独立通道分配起来轻松很多。其次是主频和指令周期。STM32F103的72MHz主频运行在8MHz的51单片机面前就像一个小跑车和一个扭扭车的区别。声音合成、按键消抖、状态扫描这些任务放在一起对单片机来说其实是个多任务系统高主频带来的余量让我在写代码的时候根本不需要抠周期逻辑能写得非常清晰。再说开发环境。我当年刚接触STM32的时候用的是标准外设库后来转成了HAL库。前者适合学原理后者适合做项目。如果你还在纠结用什么我的建议很直接如果从零开始直接用HAL库加CubeMX配工程代码少写一半但如果你打算以后做深度嵌入式开发我反而推荐把标准库的关键代码读一遍感受寄存器和底层操作是怎么回事再回到HAL库写应用会通透很多。1.2 多功能电子琴的系统架构与功能划分在开始画原理图之前我习惯先画一张功能框图把自己脑子里的模块关系理清楚然后再动手。这个项目的功能可以分为三层输入层、控制层、输出层。输入层负责的是按键电路包括琴键按键和功能按键。控制层是STM32核心负责按键扫描、音符映射、发声控制、录音存储和回放调度。输出层包括音频输出通过PWM直接驱动无源蜂鸣器或经功放驱动扬声器和状态指示OLED或LED。我给自己设定的功能目标是这样的功能模块具体能力琴键输入支持至少1个八度8个白键5个黑键我实际做的是2个八度共17键音调输出通过PWM输出标准十二平均律音阶音准偏差小于1Hz音色切换利用不同的波形占空比和脉冲节奏模拟2~3种音色自动播放内置2~3首示例曲目支持中断切换录音回放支持一段旋律的录制、存储和回放掉电通过EEPROM或Flash保存显示功能使用OLED显示当前音调、音色和模式状态这六个功能听起来多但实际上系统耦合度并没有那么高。关键在于把每个功能做成独立的模块功能之间只通过约定的接口通信这样写代码的时候思路清晰调试的时候也能快速定位问题。1.3 方案选型蜂鸣器还是扬声器无源还是有源这个点在选型的时候犹豫了很久。市面上常见的电子琴DIY套件多数用蜂鸣器但蜂鸣器有两种有源蜂鸣器内部自带振荡源拆开里面有个线圈和振荡电路通电就响频率固定只能发“嘀嘀嘀”的声音这种是拿来当报警器用的做不了电子琴。而电子琴必须用无源蜂鸣器或无源扬声器因为需要外部输入不同频率的方波信号通过控制频率来控制音高。原理上就是单片机输出PWM波形方波的基频就是声音的频率比如A4音符是440Hz那定时器就生成440Hz的方波蜂鸣器的振膜就会以440Hz振动发出A4的声音。至于蜂鸣器和扬声器的选择我个人的建议是这样练手的初期阶段用无源蜂鸣器就够了便宜一个几毛钱到一两块钱驱动简单一个GPIO输出PWM就能驱动而且声音足够大不需要功放。但蜂鸣器的声音偏向“滴滴”声音色比较干瘪因为它的振膜是一个压电片或微型电磁振膜频响范围有限。如果想要更接近真实电子琴的效果后期可以把输出端换成小功率扬声器加一个简单的功放电路比如LM386或PAM8403模块。此时原来的PWM方波不能直接驱动扬声器了需要经过一级功放电路整形和放大。这个方案我会在后面原理图解读部分详细说。2. 硬件原理图设计与核心电路分析2.1 MCU最小系统电路设计任何STC32或是STM32项目的硬件基础都是最小系统这部分如果画错后面所有代码都白写。STM32F103C8T6的最小系统包括电源电路、复位电路、时钟电路、启动模式配置和去耦电容几个部分。电源电路部分板载的AMS1117-3.3芯片把USB输入的5V转换为3.3V输入和输出端分别加10uF和100nF的电容滤波。这里我要特别强调很多初学者画完电源电路后忘记在MCU的每个电源引脚VDD、VDDA旁边放置100nF去耦电容这是非常容易忽视的问题。STM32在切换IO状态或定时器高频翻转时瞬间电流变化很大如果电源引脚不远处没有储能电容电压会产生毛刺导致系统偶尔死机或蜂鸣器发音时有杂音。时钟电路标准的8MHz无源晶振接在OSC_IN和OSC_OUT两个引脚上两侧并联22pF负载电容。这个22pF的取值是晶振数据手册规定的负载电容值不是随便选的。如果选错晶振的起振时间会变长甚至不起振这也是一个经典坑。复位电路就是NRST引脚接一个10kΩ上拉电阻和一个100nF的电容到地并通过按键拉到地。启动模式配置则要将BOOT0引脚通过一个10kΩ下拉电阻接GND让芯片从用户Flash启动这个引脚如果悬空或接线错误程序烧录进去也运行不了。2.2 按键矩阵电路的设计与优化按键部分是电子琴最核心的输入界面设计上我采用的是行列扫描矩阵结构。如果有17个琴键和4个功能键一共21个按键如果每个按键接一个GPIO那就需要21个引脚而STM32F103C8T6虽然有45个IO口但一个电子琴就占掉一半引脚完全不合理。所以我把按键排成了4行×5列的矩阵只需要9个GPIO即可驱动21个按键。矩阵扫描的原理很简单把4行设置为输入模式并启用下拉电阻5列设置为输出模式。扫描时让第1列输出低电平其余列输出高电平然后依次读取4行状态如果哪一行读到低电平就说明该行与第1列交叉处的按键已被按下。然后切换第2列为低电平重复检测如此循环总共扫描5×420个交叉点。我第21个按键是独立的单独接了一个GPIO用于特殊功能音色切换。这里要注意一个问题在扫描过程中行和列的角色不能搞反。实际操作中我发现如果行设置为输出、列设置为输入也可以工作但扫描代码要对应修改。设计原理图时就在网络标号上标注清楚“ROW_x”和“COL_x”写代码时就不会糊涂。还有一个细节矩阵扫描的二极管隔离问题。在比较复杂的矩阵键盘中为了防止多个按键同时按下时产生电流回流路径导致误判一般会在每个按键上串联一个1N4148二极管。电子琴这种应用场景通常是单音模式几乎不会遇到多个琴键同时按下的极端情况所以可以省略二极管简化电路。但如果后期要做和弦那就必须加上二极管了。2.3 音频输出电路PWM直驱与功放扩展音频输出电路是这个项目里最有意思的部分同时也是最需要实测验证的部分。最简单的方案是直接把STM32的定时器PWM通道引脚接到无源蜂鸣器一端蜂鸣器另一端接地。中间串联一个100Ω左右的限流电阻防止音量过大的同时拉高GPIO负载。实测下来这种方法音色清脆虽然有点干涩但作为入门版本的方案完全够用。想要音色更好就要加功放电路。我做过一个扩展方案用STM32引脚输出PWM信号经过一个RC低通滤波器把方波变成接近正弦的模拟信号再送入LM386功放模块最后驱动一个8Ω的小扬声器。RC滤波的截止频率要设定在5kHz以上我取的R1kΩ、C100nF算出来的截止频率大约是1.6kHz刚好覆盖人耳最敏感的中音区。这里顺带说一说为什么是低通滤波器方波在频域上是由基频加上无数奇次谐波组成的高次谐波越多声音听起来越“方”也就是越刺耳。低通滤波器把高次谐波滤掉声音自然就会从“滴滴”声变成柔和的“咚”声这就是很多人说的“音色甜美”的实际来源。不过要注意滤波后的信号电压衰减得厉害必须靠功放才能驱动扬声器。2.4 电源方案与低噪声设计电源设计直接影响声音输出的干净程度这是我在示波器上看了无数噪波之后得出的深刻结论。USB输入的5V经过AMS1117变为3.3V给单片机供电但蜂鸣器或功放供电和MCU的3.3V供电之间要做好隔离。简单做法是在模拟地AGND和数字地DGND之间做一个0Ω电阻或磁珠单点连接防止模拟电路的大电流脉动通过地线干扰数字电路。如果不用功放蜂鸣器直接从同一个3.3V电源取电噪声问题其实不严重。但如果用了功放芯片扬声器的瞬态电流可以达到几百毫安甚至安培级别这个电流会在地线上产生噪声压降导致MCU偶尔复位或PWM波形抖动。所以功放电源建议直接从5V或独立的3.3V供电取不要和MCU电源混在一起。还有一个小细节USART通信和SWD下载接口的排针我也在原理图上预留了。USB的D和D-通过1.5kΩ上拉电阻接3.3V这个上拉电阻对于USB枚举是必须的但如果你用的是CH340这种USB转串口芯片方案则可以省略。这个在设计时要提前想清楚否则打板出来USB无法识别。3. 软件实现与核心代码解析3.1 音符频率表的原理与计算方法电子琴软件部分的核心是一张音符频率对照表。标准十二平均律中每相邻两个半音之间的频率比是2的1/12次方即约1.059463倍。国际上通用的标准音A4频率为440Hz那么其上方第n个半音的频率计算公式为f 440 × 2^(n/12)其中n为与A4的间隔半音数向上为正、向下为负。例如A4下方一个半音即升G4或降A4符号为G#4/Ab4的频率为440 × 2^(-1/12) ≈ 415.30Hz而A4上方一个半音A#4的频率为440 × 2^(1/12) ≈ 466.16Hz。表格可以做出来C4到B5这2个八度总共24个音符的频率值。下面我给出部分关键音符的频率方便你做验证音符频率Hz备注C4261.63中央CD4293.66E4329.63F4349.23G4392.00A4440.00标准音B4493.88C5523.25A5880.00在STM32中我们不需要直接输出这些频率的模拟波形而是通过PWM输出方波。方波的基频就是PWM的频率由定时器的预分频值PSC和自动重载值ARR共同决定。公式为PWM频率 定时器时钟频率 / ((PSC1) × (ARR1))系统时钟是72MHz如果想生成440Hz的PWM可以设置PSC71即72MHz/721MHz然后ARR需要满足1MHz/440 ≈ 2272.7。所以ARR取整为2273此时实际输出频率为1MHz/2273 ≈ 439.9Hz误差很小人耳完全分辨不出来。每个音符合法法推算出一个ARR值写入一个const数组里。下面是核心代码片段// 音符频率 - 定时器自动重载值假设PSC71定时器时钟1MHz // ARR 1000000 / 频率 const uint16_t note_arr[] { 2273, // C4 261.63Hz 2025, // D4 293.66Hz 1804, // E4 329.63Hz 1703, // F4 349.23Hz 1517, // G4 392.00Hz 1351, // A4 440.00Hz 1204, // B4 493.88Hz 1136, // C5 523.25Hz // ... 其他音符 };3.2 定时器PWM生成音符信号我用的是STM32的TIM2 CH1通道作为PWM输出引脚对应的引脚是PA0。初始化代码如下使用HAL库void MX_TIM2_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; // 72MHz / 72 1MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 2273; // 初始为C4 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim2); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 1136; // 50%占空比 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); }这里占空比设为50%产生的是等宽方波。如果想切换音色核心思路就是改变占空比。实测下来占空比10%~50%之间变化时声音的“硬度”会明显不同。低占空比声音柔和高占空比声音更“亮”于是我可以做三种音色钢琴占空比40%、笛子占空比20%、脉冲波占空比50%等。不过要提醒的是单纯改变占空比对音色的区分度有限因为蜂鸣器本身的频响特性只有特定频段比较灵敏。想进一步模拟音色还可以加入ADSR包络线也就是声音从开始到结束的音量变化手指按下时声音有一个短暂的升频过程然后保持松开时快速衰减。ADSR的实现靠的是软件定时器中断每1ms检查一次当前按键状态并动态调整占空比这里占空比充当了音量控制的功能。3.3 按键扫描与消抖状态机按键扫描是电子琴软件中最核心的公共基础设施它不光是读取电平还要兼顾消抖、连按检测、释放检测等。传统做法是延时消抖也就是检测到按键变化后delay 10ms再看一次但在电子琴这种有严格实时性要求的项目里main函数里任何阻塞性质的delay都会影响声音输出所以必须用状态机配合定时器扫描。我的做法是TIM4配置为1ms中断在中断服务函数里调用按键扫描函数。这个扫描函数维护一个“当前状态”和“上一次状态”的变量核心是一个防抖用的计数数组#define KEY_ROWS 4 #define KEY_COLS 5 uint8_t key_pressed[KEY_ROWS][KEY_COLS]; uint8_t key_debounce_cnt[KEY_ROWS][KEY_COLS]; void KeyScan_Handler(void) { static uint8_t col 0; // 将当前列设置为低电平其他列高电平后读取行状态 HAL_GPIO_WritePin(GPIOB, COL0_PIN | COL1_PIN | COL2_PIN | COL3_PIN | COL4_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, col_pin[col], GPIO_PIN_RESET); for (uint8_t row 0; row KEY_ROWS; row) { uint8_t pinVal HAL_GPIO_ReadPin(GPIOC, row_pin[row]); if (pinVal GPIO_PIN_RESET) { if (key_debounce_cnt[row][col] 10) { key_debounce_cnt[row][col]; } if (key_debounce_cnt[row][col] 10) { if (!key_pressed[row][col]) { key_pressed[row][col] 1; OnKeyPress(row, col); // 按下事件触发发声 } } } else { if (key_debounce_cnt[row][col] 0) { key_debounce_cnt[row][col]--; } if (key_debounce_cnt[row][col] 0 key_pressed[row][col]) { key_pressed[row][col] 0; OnKeyRelease(row, col); // 释放事件停止发声 } } } col (col 1) % KEY_COLS; }这种做法的好处是按键事件被分成了“按下”和“释放”两种而且消抖是在时间维度上累计的不是简单地延时等待。判断连续10个1ms周期都检测到低电平才确认按下而释放的判断则是连续检测到高电平10次。这样既不会漏掉快速弹奏中的按键又不会产生机械抖动导致的误触。3.4 发声控制与实时响应设计按键扫描确认到一个键被按下后接下来要做的就是查找音符表设置TIM2的ARR值然后开启PWM输出。钢琴这类乐器的发声逻辑是按下时叫“起音”持续按住时声音保持松开时声音快速衰减到零。在简单的蜂鸣器系统里起音就是立刻输出满占空比方波保持就是持续输出释放就是停止PWM。我采用的设计是OnKeyPress事件里先记录当前按下的是哪个音符然后立刻切换PWM频率。但如果同一时间按下了多个键这是弹琴时很常见的情况在单声部模式下必须决定谁先出声音。最简单有效的策略是“后来者优先”void OnKeyPress(uint8_t row, uint8_t col) { uint8_t note_idx keymap_matrix[row][col]; if (note_idx NOTE_COUNT) { ring_buffer[head] note_idx; head (head 1) % BUFFER_SIZE; ActiveNote_Change(note_idx); // 立即切换PWM频率 } }实际代码里我甚至加了3层优先级如果某键被按住不放再按第二个键第二个键的声音优先松开第二个键后如果第一个键还在按住则自动恢复第一个键的声音。这个功能需要用“按键压栈”的方式记录所有当前被按下的键弹起时从栈顶移出对应音高。虽然这个实现并不复杂但加上之后整个电子琴的体验感会直线上升不像只有简单“一次只发一个音”的玩具。3.5 自动播放与录音回放自动播放模块的核心是一个曲谱数据结构。我定义了两个数组一个存音符编号一个存每个音符的时值typedef struct { uint8_t note; // 音符编号或休止符 uint8_t duration; // 时值单位100ms } MusicScore; const MusicScore song_example[] { {NOTE_C4, 4}, {NOTE_D4, 4}, {NOTE_E4, 4}, {NOTE_F4, 4}, {NOTE_E4, 2}, {NOTE_D4, 2}, {NOTE_C4, 4}, {NOTE_REST, 2}, // ... };播放时系统使用TIM3的2ms定时器作为节拍基准通过一个状态机来控制每个音符的播放时长。每到一个音符就把该音符的频率写入PWM定时器然后启动发声等待duration个节拍单位后切换到下一个音符。如果用户按下了任意琴键自动播放立刻停止系统恢复到手动演奏模式。这个“抢占优先级”很重要实际使用中用户会本能地乱按如果程序不响应用户会觉得系统死机了。录音回放就更直白了定义一个大数组每当OnKeyPress或OnKeyRelease事件发生时把当前的时间戳和事件类型记录下来。回放时就是把这些记录按顺序重新回放一遍。时间戳我用的TIM4的计数器值精度为1ms这样在弹奏时按键的间隔节奏也能被真实还原。4. 调试过程与常见问题排查实录4.1 音调不准的经典原因与修复我调试时遇到的第一个大问题就是明明是C4音示波器上看输出的频率竟然是240Hz左右比标准261.63Hz低了将近20Hz听起来就是“跑调”。第一个排查方向是系统时钟是否正确的72MHz。用示波器观察PA8引脚的MCO信号主时钟输出发现实际频率是72MHz没错那问题就出在定时器分频上。仔细一查初始化的分频写的是PSC71计数器时钟为1MHz按理说ARR2273就能得到440Hz。但算回来261Hz却变成240Hz说明实际定时器时钟慢了约8%。排查下来罪魁祸首是CubeMX工程里没有开启外部高速晶振HSE系统用内部8MHz RC振荡器经PLL倍频到64MHz而不是72MHz。8MHz RC和外部晶振精度差异导致频率偏差约8%。这个问题提醒我在CubeMX中一定要确认Clock Configuration页面里显示的HCLK是72MHz同时在系统时钟初始化时选择HSE作为PLL时钟来源。4.2 按键扫描失灵或误触发按键扫描的问题主要集中在两个地方列的GPIO拉低顺序不对以及行输入GPIO的上下拉配置错误。有个典型症状是按下第一个键时发声正常按第二个键时第一个键的声音还在响。正常情况下应该只有当前键发声。排查后发现是列扫描顺序问题——我扫描的是COL_1、COL_2、COL_3但原理图里接线的顺序是COL_3、COL_2、COL_1。写代码的时候没对应上导致按键索引映射错位相当于按下B键时被映射成了A而A键还留在按下状态里没被处理。另一个更隐蔽的问题是输入GPIO没有配置下拉电阻。矩阵扫描时未按下的行处于高阻状态如果IO内部没有下拉环境中的电磁干扰就会让行电平随机跳动触发虚假按键。解决办法是在CubeMX的GPIO配置里把行引脚设置为输入模式并把GPIO_Pull设置为GPIO_PULLDOWN同时列引脚配置为输出推挽模式。4.3 蜂鸣器有杂音或声音发闷杂音问题分两种情况物理层面的共地问题和代码层面的占空比突变问题。物理层面上如果蜂鸣器工作电流较大而供电线过细会在电流脉冲时产生电压跌落导致蜂鸣器的音量忽大忽小。解决方法是在蜂鸣器供电引脚旁边并联一个100uF的电解电容和一个100nF的陶瓷电容做成一个简单的电源滤波。代码层面上我在快速弹奏时发现从C4瞬切到C5的瞬间会有一声“啪”的爆音。原因是ARR值从2273骤降到1136占空比没变但频率的突变让振膜的位移发生了阶跃。解决思路是做一个简单的渐变过渡也就是在4ms内把ARR值从旧值逐步调整到新值每次只步进一小步。这样声音过渡顺滑更接近真实钢琴的滑音效果。4.4 下载调试失败的常见坑程序写好后第一次下载调试经常遇到SWD接口连接不上的问题特别是如果你在初始化代码里不小心把PA13或PA14重映射成了普通IO就会把下载口给干掉。这是STM32下载失败的最经典原因之一。万一真把SWD口占了也不必拆芯片。解决办法是用ST-Link Utility或CubeProgrammer的“Connect under reset”模式先按住开发板的复位键软件点击连接后立刻松开复位键这时候MCU会短暂停留在复位状态调试器就能抢在用户程序运行之前建立连接。之后擦除Flash问题就解决了。还有一个习惯我非常推荐在工程的初始化代码中加一个1秒的“窗口期”这个窗口期内不允许把SWD引脚初始化为普通IO只有过了窗口期且检测到没有调试器连接时才可以把SWD引脚切换到其他功能。这样即使写错了代码也能正常下载新固件避免反复折腾硬件。5. 从原理图到实物的制作心得与扩展建议原理图绘制我用的是立创EDA嘉立创EDA专业版它有开源广场里面有大量现成的STM32最小系统封装和元件库制图效率非常高。整个项目的原理图大概分成三块STM32最小系统、按键矩阵、音频输出。布局时注意把电源部分和数字部分分开核心板的地线铺铜要完整按键部分要留出足够大的物理空间方便安装按键。PCB打样的话我建议最少做10*10cm以内的板子这样可以享受优惠价。如果不想打PCB也可以直接在面包板上搭电路。不过我得说电子琴按键区域在面包板上搭起来会很混乱飞线多到怀疑人生建议有条件的还是打一次板哪怕做出来不美观起码能在实际焊接中积累经验。关于固件烧录我后来把程序移植到了HAL库版本并接入了STM32CubeProgrammer的烧录流程整个下载过程从以前的“连不上”变成了点一下鼠标的事。编译用ARM GCC Makefile也可以用Keil或者STM32CubeIDE三者选一个趁手的就行不必纠结。最后再分享一个扩展方向如果你觉得蜂鸣器音色太单薄可以试试用STM32内置的DAC模块做波形合成输出正弦波或三角波再配合外置功放音质会有质变。DAC的采样率建议至少32kHz把每个音符的波形样本存在Flash里播放时通过DMA循环搬运到DAC这样音色可以做得非常像真实的钢琴或长笛。这个方向也是我从电子琴到音频合成的入门台阶做出来之后的成就感是完全不一样的。做一个基于STM32的多功能电子琴最大的意义不在于你弹出了《小星星》而在于你完整走了一遍从需求分析、原理图设计、PCB布局、代码编写到联调排错的整个流程。我自己做完这个项目以后再看那些复杂的电机控制板、飞控系统心里都不再发怵因为底层的那套方法论已经通了。希望这篇文章也能帮你把这条路走顺。本文还有配套的精品资源点击获取
返回列表