在本文中,我将
继续在已经在腺体中进行的3号实验室工作中体现我的灵感。 我们正在谈论使用Goertzel算法在Arduino的音频拨号模式下通过声音检测数字。
为了实现这一点,我使用了Arduino UNO,一个驻极体麦克风(
adafruit )和一个带有MAX7219驱动器的8x8显示器。
行动计划
- 离散化足够多的样本(使用上一篇文章中的程序,我确信256个样本就足够了)。
- 找到与编码字符所需的频率相对应的频率响应幅度。
- 振幅的两个最大值将给出所需字符的行索引和列索引,例如,图3看起来像。

实作
在开始实施之前,我们先回答一个问题-Arduino UNO是否对我们有足够的性能?
时钟频率:16MHz
一个采样周期需要13个时钟周期
提供最高精度的预分频器值:128
结果表明16 MHz / 13/128〜9615 Hz-所需的采样频率,这意味着您可以使用最高4.8 kHz的频率。
ADC调整
ADC工作有多种模式,最有趣的模式如下(
数据表中ADCSRB关键字的完整列表)
- 单次读取-使用AnalogRead()方法,但这是一个阻塞调用,需要100µs,并且使用它不可能提供恒定的采样率
- 自由运行模式-在此模式下,下一个采样周期在上一个采样周期结束后立即开始,并提供最大采样频率
- 定时器溢出-采样从定时器溢出开始,这使您可以微调采样频率
ADC设置代码,为简单起见,我使用了自由运行模式。
ADMUX = 0;
信号处理
输入完整的样本数组后,我们将关闭ADC的中断,并使用Goertzel算法计算频谱的幅度。 我不会使用这种详尽的
资源来竞争算法的描述,但是我将给出实现:
void goertzel(uint8_t *samples, float *spectrum) { float v_0, v_1, v_2; float re, im, amp; for (uint8_t k = 0; k < IX_LEN; k++) { float cos = pgm_read_float(&(cos_t[k])); float sin = pgm_read_float(&(sin_t[k])); float a = 2. * cos; v_0 = v_1 = v_2 = 0; for (uint16_t i = 0; i < N; i++) { v_0 = v_1; v_1 = v_2; v_2 = (float)(samples[i]) + a * v_1 - v_0; } re = cos * v_2 - v_1; im = sin * v_2; amp = sqrt(re * re + im * im); spectrum[k] = amp; } }
预先为对应于所需频率的样本计算正弦和余弦。 代码的完整版本在
这里 。
结论
最重要的是,事实证明,Arduino UNO资源足以进行简单的声音处理。
我学到的主要课程是ADC是一件敏感的事情,在成功识别并将角色发送到控制台后,我花了一周的时间对其进行调试以使其与显示器配合使用,因为我将麦克风接地和max7219连接在一起,并且所有采样立即变成了噪声。
会更好吗? 是的,选择采样频率和采样数量以使所需频率与采样晶格重合会更正确,这样可以防止频谱扩展。 这样的参数已经是f = 8 kHz,N = 205,在这种情况下,您需要在自由运行模式下而不是自由模式下运行ADC,但需要定时器溢出,两者之间的差异将非常明显。
课程已接近尾声,但仍有很多想法。
谢谢您的关注。