CH585 PCM 播放测试以及双 PWM 原理
简单聊一下双 PWM 音频播放原理及实现以及 PCM 播放测试 ...... 矜辰所致前言上一篇文章我们测试了一下 SBC 音频解码然后通过 双 PWM 播放音频的效果当时更多的是想看看效果只做了一些必要的解释说明。考虑到后面自己应用上需要有一些修改有些必要的原理还是要了解一下这样后期的做一些应用也知道去改什么地方。所以本文我们简单学习一下双 PWM 的原理 以及测试一下 PCM 音频播放。相关文章嵌入式语音开发应用基础说明CH585 SBC 音频解码播放测试说明.我是矜辰所致全网同名尽量用心写好每一系列文章不浮夸不将就认真对待学知识的我们矜辰所致金石为开目录前言一、 双 PWM 原理1.1 基础说明1.2 程序中双 PWM 的体现1.3 PWM 的频率1.4 音量调节二、 PCM 播放测试2.1 PCM 数据说明2.2 音频文件生成2.3 APP修改测试结语一、 双 PWM 原理现在有了 AI 很多通用技术的理论了解并不是什么难事我们简单说明一下主要还是结合一下程序中如何实现做一些分析。1.1 基础说明双 PWMDual PWM / Differential PWM音频驱动的本质是用两路互补的 PWM 信号来驱动扬声器通过差分放大还原出模拟音频波形。双 PWM 音频:两路互补的数字开关信号 → 差分驱动扬声器 → 利用感性负载滤除载波 → 还原模拟声音相对于单路 PWM 双路 PWM 方案有以下优点抗干扰强外界电磁干扰同时耦合到两根线差分相互抵消信噪比更高双路 PWM 喇叭线圈等效 LC 低通自动滤除高频载波 。输出功率翻倍差分峰峰值 2×VDD同等供电下音量更大无直流偏置静态时两路 50% 占空比扬声器两端电压差为 0音圈不会静态偏移不烧喇叭、低音更扎实外围电路无需输出电容单端需要隔直电容差分直接耦合常见波形与 PCM 映射逻辑如下音频采样值 → 调制为两路互补 PWMPWM_A 占空比 50% ΔPWM_B 占空比 50% - Δ举个例子 设 PWM 周期固定满幅占空比 0~100%.静音PCM0PWM 占空比 50%PWM - 占空比 50%两路电压平均值相等喇叭两端压差 0不发声。.正半周音频PCM0PWM 占空比升高PWM - 占空比同步降低喇叭左端平均电压 右端电流正向流过音圈。.负半周音频PCM0PWM 占空比降低PWM - 占空比同步升高喇叭右端平均电压 左端电流反向流过音圈。还有一种单极性交替 PWM (本文测试示例就使用的这种)pcm0PWM输出幅值PWM20pcm0PWM-输出幅值PWM10等价 H 桥单向导通正半周左 PWM 输出方波右侧恒低电流正向流过喇叭负半周右 PWM 输出方波左侧恒低电流反向流过喇叭过零点pcm0两路都是 0无静态压差1.2 程序中双 PWM 的体现在我们的测试程序中有这么一段数据的处理for(uint32_ti0;i(out_len1);i){if(pcm_buf[i]0){*p_datapcm_buf[i]/8;// 正值 → PWM1 有输出*p_data20;// PWM2 0}else{*p_data20-pcm_buf[i]/8;// 负值 → PWM2 有输出*p_data0;// PWM1 0}p_data;p_data2;}当音频信号为正0时p_dataPWM 通道 1输出对应的占空比p_data2PWM 通道 2静止在 0 电平。当音频信号为负0时p_data2PWM 通道 2输出对应的占空比取绝对值p_dataPWM 通道 1静止在 0 电平。代码中的数据事直接决定 PWM 的占空比的为什么上面的数据需要除 8 *p_data pcm_sample / 8;因为 这个数值是直接决定 PWM 的占空比的在程序中 PWM 的占空比 也和总周期计数有关这个值肯定不能大于总计数这个我们需要先往下看先了解一下 PWM 的频率有关的内容 。1.3 PWM 的频率PWM 音频播放频率要调整到音频的采样率 , 在使用 MCU 的时候一般的步骤如下确定/设置 定时器时钟来源确定/设置 有没有分频根据目标 PWM 载波频率推算自动重载值。对于本次的示例自动重载值变量为max_cnt// 根据采样率选择固定计数值if(rateCH5XX_AUDIO_SAMPLE_RATE_16K){max_cnt4875;}这个值是怎么算出来的16K 采样配套max_cnt 4875单次完整 PWM 周期计数总长 max_cnt 1 487616K 场景 PWM 载波频率F(pwm) 78000000 / 4876 ≈ 15996.72 Hz​8K 采样率是 16K 的一半所以 PWM 周期总计数必须翻倍总计数4876*2 9752max_cnt 9752-1 97518K 下载波频率F(pwm) 78000000 / 9752 ≈ 7998.36 Hz好了现在知道了在 16K 模式下面最大重装载值为 4875 也就可以解释为什么上面需要*p_data pcm_sample / 8;了示例中pcm_sample是一个int16_t的变量数值范围-32768 ~ 32767 。如果不缩放直接赋值大于 4875会溢出失效。除以 8最大输出 32767/8 ≈ 4095小于 4875留有安全余量这个值决定了占空比的大小示例中是为了把他设置到合理范围那么这里有一个比较重要的问题需要说明就是改变这个单个的占空比并不会改变声音内容它改变的是声音大小这里很多初学者会不太理解但是要记住 。❤️ 瞬时占空比数值只决定当前音量大小❤️❤️ 占空比随时间变化的节奏 / 快慢决定是什么声音、音调、语音内容。❤️1.4 音量调节上面我们已经说到了改变瞬时占空比就可以改变音量大小我们在示例基础上直接做了一个修改实际应用中就可以根据数值调整百分比直接上一下有关部分的代码#defineVOL_MAX256// 改变 audio_vol 的值改变声音大小 0 静音- 256 最大uint16_taudio_vol64;// 0静音256最大for(uint32_ti0;i(out_len1);i){int16_trawpcm_buf[i];// 音量缩放核心行int32_tvol_pcm(int32_t)raw*audio_vol/VOL_MAX;if(vol_pcm0){*p_datavol_pcm/8;*p_data20;}else{*p_data2(0-vol_pcm)/8;*p_data0;}p_data;p_data2;}二、 PCM 播放测试OK上面把一些需要了解的关键点理了一下我们本文还要测试一下 PCM 播放 实际应用中直接处理 PCM 的情况应该不会太多因为不压缩数据量实在太大了但是既然测试了那就记录一下而且可以方便的用来验证上面我们提到的原理部分的一些内容。2.1 PCM 数据说明我们知道 PCM 就是原始的音频数据我们嵌入式语音采样就是采样的原始的音频数据。 但是呢因为原始的音频数据很大一般应用中都不太可能本地存储下来除非是明确知道语音数据不大Flash 空间足够往往都是采集到了传输出去而且很多时候都会压缩传输。这是采集相关的话题后期我们应用可能需要分析说 。PCM 音频理论上来说的占用空间 16k1 秒32000 Byte ≈ 31.25 KB5 秒160000 Byte ≈ 156.25 KB10 秒320000 Byte ≈ 312.5 KB15 秒480000 Byte ≈ 468.75 KB8K 比 16K 小一半2.2 音频文件生成PCM 文件使用 ffmpeg 工具可以直接生成而且电脑端通过 ffplay 可以直接验证这个我们可以先准备好PC 端可以直接验证 。生成 PCM 指令:# 把mp3 转成 8K采样率 的 单声道 PCMffmpeg-i004.mp3-ar8000-ac1-fs16le 004p8k.pcm# 把 mp3 转成 16K采样率 的 单声道 PCMffmpeg-i004.mp3-ar16000-ac1-fs16le 004p16k.pcm# 把 mp3 的前 5S 转成 16K采样率 的 单声道 PCMffmpeg-t5-i004.mp3-ar16000-ac1-fs16le 004p16k5s.pcm查看 PCM 大小#windows 下dir004p8k.pcm示例如下C:\Users\OWNER\Desktop\Demo抽烟报警dir 004p8k.pcm 驱动器 C 中的卷是 系统 卷的序列号是 6E0E-5AEB C:\Users\OWNER\Desktop\Demo抽烟报警 的目录2026/06/2617:1381,502004p8k.pcm1个文件81,502字节0个目录50,758,230,016 可用字节 C:\Users\OWNER\Desktop\Demo抽烟报警dir 004p16k.pcm 驱动器 C 中的卷是 系统 卷的序列号是 6E0E-5AEB C:\Users\OWNER\Desktop\Demo抽烟报警 的目录2026/06/2617:13163,004004p16k.pcm1个文件163,004字节0个目录50,757,672,960 可用字节PCM 文件这里 dir 查出来的大小是真实的大小。使用如下指令可以在 PC 端直接播放 PCM 进行测试 #8K 单声道 s16le#-ch_layout mono单声道替代旧版 -ac 1ffplay-fs16le-ar8000-ch_layoutmono 004p8k.pcm#16K 单声道 s16leffplay-fs16le-ar16000-ch_layoutmono 004p16k.pcm最后把 PCM 转成 Hex 文件srec_cat.exe 004p8k.pcm-binary-offset32768-o004p8k_h.hex-intelsrec_cat.exe 004p16k.pcm-binary-offset32768-o004p16k_h.hex-intel或者 bin2hex.exe--offset32768004p8k.pcm 004p8k_h.hex2.3 APP修改测试现在存放的就是原始 PCM 数据解压代码也不需要从 Flash 读出来数据直接交给 PWM 输出就行了简单修改了一下直接上一下有关代码caseCH5XX_PWM_DMA_GET:{uint32_t*p_datap_event-p_data;uint32_t*p_data2p_event-p_data2;uint32_tlengthp_event-len;// 需要填充的采样点数switch(read_data_sta){case0:{// 计算还剩多少字节PCM数据uint32_tremain_bytessample.length-read_index;uint32_tneed_byteslength*2;// 每个采样16bit2字节if(remain_bytes0){read_data_sta1;break;}// 实际能读的字节数uint32_tcopy_bytes(remain_bytesneed_bytes)?remain_bytes:need_bytes;uint32_tcopy_samplescopy_bytes/2;// 直接从Flash读PCM数据// 关键修复Flash数据拷贝到RAM再读取// 使用__wrap_memcpy高速拷贝Flash数据到RAM缓存__wrap_memcpy(pcm_buf,(uint8_t*)(sample.start_addrread_index),copy_bytes);for(uint32_ti0;icopy_samples;i){int16_tpcm_samplepcm_buf[i];if(pcm_sample0){*p_datapcm_sample/8;*p_data20;}else{*p_data2(0-pcm_sample)/8;*p_data0;}p_data;p_data2;}read_indexcopy_bytes;// 如果数据不够补零静音for(uint32_ticopy_samples;ilength;i){*p_data0;*p_data20;}if(read_indexsample.length){PRINT(PCM play end\r\n);ch5xx_audio_stop();}}break;case1:ch5xx_audio_stop();break;default:break;}}break;因为音频效果文章不好放我就把我测试过程和结果描述一下。先测试 16K 播放就测试一个音频audio_record_tmusic_record[]{{0x000000,163004},// 大小按照原始大小};ch5xx_audio_init(CH5XX_AUDIO_SAMPLE_RATE_16K,audio_handler);可以正常播放只是声音感觉没有直接 SBC 解码好但是语音完全够用。再测试一下 8K audio_record_tmusic_record[]{{0x000000,163004},// 大小按照原始大小};ch5xx_audio_init(CH5XX_AUDIO_SAMPLE_RATE_8K,audio_handler);我开始以为会多给了后半段会破音但是测试下来后半段没有尖锐的声音 这个在代码中处理好了声音差了很多语音也能听清楚但是杂音比较明显。因为改到了 8K 我们的 PWM 最大重装载值翻倍了我们可以改成如下方式把声音放大if(pcm_sample0){*p_datapcm_sample/4;*p_data20;}else{*p_data2(0-pcm_sample)/4;*p_data0;}结语本文我们简单说明了一下双 PWM 音频播放的基本原理虽然并没有对着完全的源码分析但是对于我们应用需要修改到的地方都有提到不仅是在 CH585 上面所有的 MCU 使用此方式驱动音频对应部分都是类似的处理。之后我们还测试了 PCM 的播放总的来说作为语音播放来说 16K 完全可以满足需求8K 的声音就没有那么清晰会有杂音 (个人测试仅供参考与自己应用层代码处理也有关系) 。对于语音播放博主还计划要测试一下 ADPCM 解码播放说明毕竟 ADPCM 也是嵌入式常用编解码方式。好了本文就到这里。谢谢大家

相关新闻

Windows热键侦探:三分钟定位快捷键冲突的神器

Windows热键侦探:三分钟定位快捷键冲突的神器

Windows热键侦探:三分钟定位快捷键冲突的神器 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective 你是否曾经按下熟…

2026/10/11 14:45:15 阅读更多 →
动态规划如何驱动Cassie双足机器人实现稳定爬楼梯

动态规划如何驱动Cassie双足机器人实现稳定爬楼梯

1. 项目概述:当Cassie机器人遇上动态规划如果你关注过双足机器人领域,那么对Cassie这个名字一定不陌生。它那对标志性的、像鸵鸟腿一样的机械结构,以及在各种复杂地形上展现出的惊人稳定性,让它成为了机器人学研究的明星平台。但今…

2026/10/11 14:45:13 阅读更多 →
Web3 Token经济学的可持续性重构:从烧钱到按效付费

Web3 Token经济学的可持续性重构:从烧钱到按效付费

1. “烧不动Token”不是口号,是现金流警报器响了 最近在几个技术社区和行业闭门会上,听到最多的一句话不是“增长曲线又拉起来了”,而是:“这个季度的Token消耗预算,真烧不动了。”——不是不想烧,是账上数…

2026/9/26 5:02:35 阅读更多 →

最新新闻

Oracle 第二模型审查实战指南:用 agent-scripts 打包仓库上下文并交给 GPT-5.5 Pro 等外部模型

Oracle 第二模型审查实战指南:用 agent-scripts 打包仓库上下文并交给 GPT-5.5 Pro 等外部模型

【免费下载链接】agent-scripts Scripts for agents, shared between my repositories. 项目地址: https://gitcode.com/gh_mirrors/ag/agent-scripts 点击查看 免费下载 本文面向在 agent-scripts 仓库(AGENTS.MD)管理的多 Agent 工作流中运…

2026/10/11 15:02:52 阅读更多 →
汽车 EDR 都记录了什么?从触发条件到读取顺序

汽车 EDR 都记录了什么?从触发条件到读取顺序

EDR 记录的是碰撞前后几秒的运行数据 EDR 是 Event Data Recorder 的缩写,中文名称是汽车事件数据记录系统。它在车辆发生碰撞、或者车速在很短时间内出现剧烈变化时被唤醒,把碰撞前后几秒内的车速、制动状态、安全带状态、转向角度这一类运行参数写进存…

2026/10/11 15:02:52 阅读更多 →
磁导航机器人穿刺实验研究:从电磁定位到靶点误差控制

磁导航机器人穿刺实验研究:从电磁定位到靶点误差控制

简介:该PDF文献为磁导航系统引导下机器人穿刺在神经外科手术中准确性的实验研究,目标读者为神经外科医师、机器人手术科研人员及医疗AI开发者,聚焦机器人穿刺与人工穿刺的临床对比问题。资源共1个PDF文件,整体约1.07MB&#xff0c…

2026/10/11 15:02:52 阅读更多 →
JDBC从零到实战:连接MySQL实现增删改查与事务连接池

JDBC从零到实战:连接MySQL实现增删改查与事务连接池

接到这个标题的时候我就想起自己刚入行那会儿,第一次拿到“Java连接数据库实现增删改查”这个任务,心里是真的发怵。那时候我连JDBC和ODBC都分不清,更别说PreparedStatement为什么比Statement安全、连接串里的参数到底哪个漏不得。后来在项目…

2026/10/11 15:02:52 阅读更多 →
逆波兰表达式求值与栈的压入弹出序列:面试必刷的栈模拟经典

逆波兰表达式求值与栈的压入弹出序列:面试必刷的栈模拟经典

1. 逆波兰表达式求值:一道“送分题”怎么被写丢分 这是栈与队列高频算法题精讲的第二篇。上一篇我们打好了栈和队列的基础底子,这一篇把注意力集中在两道面试必刷原题上:逆波兰表达式求值、栈的压入弹出序列。这两道题在各类算法面试中出现频…

2026/10/11 15:02:52 阅读更多 →
ClaudePrism 内置编辑器使用指南:LaTeX/BibTeX 语法高亮、实时错误检查、正则搜索与 Vim 模式

ClaudePrism 内置编辑器使用指南:LaTeX/BibTeX 语法高亮、实时错误检查、正则搜索与 Vim 模式

【免费下载链接】claude-prism An offline-first scientific writing workspace powered by Claude. LaTeX Python 100 scientific skills all running locally. 项目地址: https://gitcode.com/gh_mirrors/cl/claude-prism 点击查看 免费下载 ClaudePrism 内置编…

2026/10/11 15:01:52 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →