拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RIOT 操作系统 DAC DDS 音频测试应用实战:用 DAC 播放正弦波、方波与语音

RIOT 操作系统 DAC DDS 音频测试应用实战用 DAC 播放正弦波、方波与语音【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT导读本篇文章围绕 RIOT 操作系统中的tests/drivers/dac_dds测试/演示应用展开完整讲解如何使用 DAC数模转换器配合定时器以直接数字合成DDS的方式输出音频波形。读完本文你将掌握dac_dds驱动模块的初始化、双缓冲播放、命令式控制等核心用法学会在本机构建并在开发板上通过 shell 命令行播放正弦波、方波、锯齿波以及一段预先录制的 8 kHz 语音。DAC DDS 是什么在 RIOT 中dac_ddsDAC Direct Digital Synthesis是一个位于 drivers/dac_dds 的驱动模块其头文件 drivers/include/dac_dds.h 中对它的定位描述是Use a DAC to play a buffer of samples即以指定采样率通过 DAC 播放一段音频样本缓冲区。它的工作方式非常直观应用把一段样本数据例如 8 位或 16 位采样交给驱动驱动借助一个周期性定时器按sample_rate的节奏把缓冲区中的每个样本逐一写入 DAC为了让音频连续播放驱动内部采用**双缓冲double buffering**机制缓冲区 A 在播放时应用可以预先填充缓冲区 BA 播完后自动切换为 B从而实现不间断的数据供给。tests/drivers/dac_dds正是这套驱动的手动测试与演示程序manual test, and demo application它默认初始化单个 DAC DDS 通道并根据命令行请求在 DAC 输出引脚上播放正弦波、方波、锯齿波或一段问候语音。构建与烧录测试应用位于 tests/drivers/dac_dds其 Makefile 默认目标板为mcb2388BOARD ? mcb2388构建时需启用dac_dds与shell两个模块USEMODULE dac_dds USEMODULE shell同时音频数据通过 RIOT 的 BLOB 机制嵌入固件BLOBS hello.rawBLOB 机制由 makefiles/blob.inc.mk 实现——它会把 BLOBS 列表中的任意文件转换成可通过#include blob/hello.raw.h直接引用的 C 头文件从而将原始音频数据静态地链接进固件。编译并烧录以native或真实开发板为例make BOARDmcb2388 -C tests/drivers/dac_dds flash term注意测试应用依赖目标板的 DAC 外设。请把耳机或扬声器连接到开发板的 DAC 输出引脚main.c的文件头注释中明确提示了这一操作要求之后即可听到生成的音频。在 shell 中使用四个核心命令应用启动后进入 RIOT shell注册了如下命令见 main.c 中的shell_commands表命令作用hello播放内置问候语音init freq [bit]以指定采样率和位深重新初始化 DAC DDSsaw freq secs播放指定频率、时长的锯齿波sine freq secs播放指定频率、时长的正弦波square freq secs播放指定频率、时长的方波README 中给出的典型用法为 hello sine 440 2第一条命令播放内置的问候语音第二条命令在默认 8000 Hz 采样率下播放 440 Hz、持续 2 秒的标准音 A4 正弦波。hello播放问候语音hello命令cmd_greeting直接把嵌入固件的hello.raw音频一次性交给 DAC DDS 播放dac_dds_set_cb(DAC_DDS_CHAN, NULL, NULL); dac_dds_play(DAC_DDS_CHAN, hello_raw, hello_raw_len);由于这段音频本身录制为 8 位/8000 Hz若当前配置的采样率或位深与其不符程序会打印警告Warning: audio clip was recoded with 8bit/8000 Hz。默认情况下该命令在编译时启用ENABLE_GREETING默认 1。init调整采样率与位深init freq bit允许动态改变播放参数dac_dds_init(DAC_DDS_CHAN, sample_rate, res_16b ? DAC_FLAG_16BIT : DAC_FLAG_8BIT, NULL, NULL);freq采样率单位 Hzbit只接受8或16分别对应DAC_FLAG_8BIT与DAC_FLAG_16BIT传入其他值会提示Only 8 and 16 bit samples supported.。sine/saw/square三种波形生成三个命令的参数格式统一为freq secs内部通过play_function(period, samples, sample_gen_t fun)完成播放周期计算为(sample_rate freq/2) / freq带四舍五入采样总数计算为secs * sample_rate16 位模式下采样数翻倍每样本占 2 字节。波形由三个独立的样本生成函数填充见 main.c正弦波_fill_sine_samples_8/_fill_sine_samples_16使用imath.h提供的fast_sini()快速查表算法按SINI_PERIOD / period的步进在一个周期内取样随后用memcpy把单个周期复制到整个缓冲区锯齿波_fill_saw_samples_8/_fill_saw_samples_16用(i * 0xFFUL) / period8 位或(i * 0xFFFFUL) / period16 位线性递增填充方波_fill_square_samples以半个周期为单位交替填0xFF与0x00。播放流程与双缓冲原理play_function展示了dac_dds驱动的推荐使用方式详见 drivers/dac_dds/dac_dds.cdac_dds_set_cb(DAC_DDS_CHAN, _unlock, lock); while (samples) { size_t len min(samples, len_aligned); samples - len; underruns !dac_dds_play(DAC_DDS_CHAN, buf, len); /* wait for buffer flip */ mutex_lock(lock); }其核心流程是先用dac_dds_set_cb()注册一个回调——这里注册的是_unlock它会在当前缓冲区播放完毕、可以提交下一块缓冲区时解锁一个互斥锁调用dac_dds_play()提交缓冲区。若此时驱动尚未开始播放返回false说明发生了一次缓冲不足underrun或刚刚启动播放若返回true表示缓冲区已排入队列、等当前块播完后自动接续随后线程阻塞在mutex_lock(lock)上直到回调解锁再提交下一块数据。驱动内部的双缓冲状态机驱动以struct dac_ctx维护每个通道的状态dac_dds.cstatic struct dac_ctx { const uint8_t *buffers[2]; /* The two sample buffers */ size_t buffer_len[2]; /* Size of the sample buffers */ size_t idx; /* Current position in the current buffer */ dac_dds_cb_t cb; /* Called when the current buffer is done */ void *cb_arg; /* Callback argument */ uint16_t sample_ticks; /* Timer ticks per sample */ uint8_t cur; /* Active sample buffer */ uint8_t playing; /* DAC is playing */ uint8_t is_16bit; /* Sample size is 16 instead of 8 bit */ } _ctx[DAC_DDS_NUMOF];初始化时驱动根据timer_hz / sample_rate计算出每个样本对应的定时器 tick 数sample_ticks并把定时器中断绑定到内部回调_timer_cb。每次定时器触发8 位模式下把buf[idx] 8写入 DAC高位对齐到 16 位 DAC 寄存器16 位模式下把低、高两个字节拼成(h 8) | l后写入当前缓冲读完后若下一块缓冲已就绪则自动切换否则停止定时器并置playing 0。dac_dds_play()在调用时只切换cur指针指向的缓冲区索引并在驱动空闲时启动timer_set_periodic()。整个设计使得数据供给与应用逻辑解耦唯一需要注意的是回调运行在定时器中断上下文头文件明确建议不要在回调里做实际的缓冲区填充或重 I/O而应仅用它唤醒一个线程由线程负责填充样本并再次调用dac_dds_play()——这正是play_function中 mutex 回调组合的用意。关于 underrun缓冲不足play_function把初始的 underrun 计数置为-1注释说明 One underrun indication is expected (for the first sample)——即首次启动播放时dac_dds_play返回false属于正常现象不计入故障。后续若实际发生缓冲供给不及时程序会打印During playback, %d underruns occurred.提示音频出现断流。默认参数与板级配置驱动的默认参数定义在 drivers/dac_dds/include/dac_dds_params.h#ifndef DAC_DDS_PARAM_DAC #define DAC_DDS_PARAM_DAC DAC_LINE(0) #endif #ifndef DAC_DDS_PARAM_TIMER #define DAC_DDS_PARAM_TIMER (TIMER_NUMOF - 1) #endif #ifndef DAC_DDS_PARAM_TIMER_HZ #define DAC_DDS_PARAM_TIMER_HZ MHZ(1) #endif即默认使用第一条 DAC 线、最后一个定时器定时器频率为 1 MHz。头文件同时给出约束定时器频率至少应为采样率的 2 倍must be at least 2x sample rate以保证定时精度。测试应用侧的可配置宏main.c包括DAC_DDS_CHAN默认0DAC DDS 通道号DAC_DDS_DAC默认DAC_DDS_PARAM_DAC使用的 DAC 外设DAC_BUF_SIZE默认2048样本缓冲区字节数ENABLE_GREETING默认1是否编译hello命令。在低内存目标板上Makefile 中指定LOW_MEMORY_BOARDS : samd10-xmini构建系统会自动把ENABLE_GREETING置为 0、将DAC_BUF_SIZE缩小到 1024以避免内存不足或烧录过慢ifneq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS))) ENABLE_GREETING ? 0 CFLAGS -DDAC_BUF_SIZE1024 else ENABLE_GREETING ? 1 endif此外play_function会校验周期不能超过缓冲区大小超限时打印Period duration exceeds sample buffer size.测试缓冲的填充也做了整周期对齐处理len_aligned DAC_BUF_SIZE - DAC_BUF_SIZE % period保证每次提交的数据块都能完整播放波形周期。音频源hello.raw 的来历与许可证hello.raw是一段由 GeoTrinity 录制的 Ahoy! 问候语音原始素材来自维基百科上的GT_Ahoi_Deutsch_GT.ogg标注采用CC-BY-SA 3.0 International许可证见 tests/drivers/dac_dds/README.md。它通过如下两条命令转换并裁剪得到先使用ffmpeg将 Ogg 音频转为单声道、8000 Hz、无符号 8 位 PCM 裸流pcm_u8再使用 Python 去除首尾的静音去除前导/尾随的0x80中值字节并补回一个尾随字节避免文件以中值静音开头$ ffmpeg -i GT_Ahoi_Deutsch_GT.ogg -ac 1 -ar 8000 -f u8 -c:a pcm_u8 hello.raw $ python3 -c s open(hello.raw, rb).read(); s s.strip(b\x80) b\x80; open(hello.raw, wb).write(s)这段转换流程本身也是一份实用的参考任何 8 位 8000 Hz 的单声道 PCM 裸数据u8/pcm_u8格式都可以通过 BLOB 机制嵌入固件作为dac_dds的播放素材。若使用其他采样率或位深的素材需结合hello命令的采样率警告机制先通过init命令将 DDS 配置到与素材一致的参数。小结tests/drivers/dac_dds虽是一个手动测试应用却完整示范了 RIOT 中 DAC 音频输出的标准做法BLOB 嵌入音频数据、dac_dds_init初始化、dac_dds_play 回调驱动的双缓冲连续播放以及基于fast_sini的嵌入式波形合成。无论你是在调试某块开发板的 DAC 音频通路还是想在自己的 RIOT 应用中实现提示音或简单音频播放都可以直接复用本测试的 shell 命令设计与play_function播放框架再结合 drivers/dac_dds/dac_dds.c 与 drivers/include/dac_dds.h 深入理解其底层机制。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门