0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何用Arduino UNO实现DTMF解码器

454398 来源:网络整理 作者:网络整理 2019-11-25 15:47 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

步骤1:了解算法

如何用Arduino UNO实现DTMF解码器

在DTMF中,每个符号根据图片上的表格使用两个频率进行编码。

该设备捕获麦克风的输入并计算八个频率的幅度。具有最大幅度的两个频率给出了编码符号的一行和一列。

数据采集

为了执行频谱分析,应以某个可预测的频率捕获样本。为了达到这个目的,我使用了具有最大精度的自由运行ADC模式(预分频器128),它提供了9615Hz的采样率。下面的代码显示了如何配置ArduinoADC。

void initADC() {

// Init ADC; f = ( 16MHz/prescaler ) / 13 cycles/conversion

ADMUX = 0; // Channel sel, right-adj, use AREF pin

ADCSRA = _BV(ADEN) | // ADC enable

_BV(ADSC) | // ADC start

_BV(ADATE) | // Auto trigger

_BV(ADIE) | // Interrupt enable

_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz

ADCSRB = 0; // Free-run mode

DIDR0 = _BV(0); // Turn off digital input for ADC pin

TIMSK0 = 0; // Timer0 off

}

And the interrupt handler looks like this

ISR(ADC_vect) {

uint16_t sample = ADC;samples[samplePos++] = sample - 400;

if(samplePos 》= N) {

ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off

}

}

频谱分析

收集样本后,我计算出8个频率的幅度,这些频率编码符号。我不需要为此运行完整的FFT,因此我使用了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 c = pgm_read_float(&(cos_t[k]));

float s = pgm_read_float(&(sin_t[k]));

float a = 2. * c;

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 = c * v_2 - v_1;

im = s * v_2;

amp = sqrt(re * re + im * im);

spectrum[k] = amp;

}

}

步骤2:代码

上图显示了数字3的编码示例,其中最大幅度对应于697Hz和1477Hz频率。

完整的草图如下

/**

* Connections:

* [ Mic to Arduino ]

* - Out -》 A0

* - Vcc -》 3.3V

* - Gnd -》 Gnd

* - Arduino: AREF -》 3.3V

* [ Display to Arduino ]

* - Vcc -》 5V

* - Gnd -》 Gnd

* - DIN -》 D11

* - CLK -》 D13

* - CS -》 D9

*/

#include

#include

#include

#define CS_PIN 9

#define N 256

#define IX_LEN 8

#define THRESHOLD 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t samples[N];

volatile uint16_t samplePos = 0;

float spectrum[IX_LEN];

// Frequences [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Calculated for 9615Hz 256 samples

const float cos_t[IX_LEN] PROGMEM = {

0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449,

0.6895405447370669, 0.6343932841636456, 0.5555702330196023, 0.4713967368259978

};

const float sin_t[IX_LEN] PROGMEM = {

0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334,

0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.8819212643483549

};

typedef struct {

char digit;

uint8_t index;

} digit_t;

digit_t detected_digit;

const char table[4][4] PROGMEM = {

{‘1’, ‘2’, ‘3’, ‘A’},

{‘4’, ‘5’, ‘6’, ‘B’},

{‘7’, ‘8’, ‘9’, ‘C’},

{‘*’, ‘0’, ‘#’, ‘D’}

};

const uint8_t char_indexes[4][4] PROGMEM = {

{1, 2, 3, 10},

{4, 5, 6, 11},

{7, 8, 9, 12},

{15, 0, 14, 13}

};

byte font[16][8] = {

{0x00,0x38,0x44,0x4c,0x54,0x64,0x44,0x38}, // 0

{0x04,0x0c,0x14,0x24,0x04,0x04,0x04,0x04}, // 1

{0x00,0x30,0x48,0x04,0x04,0x38,0x40,0x7c}, // 2

{0x00,0x38,0x04,0x04,0x18,0x04,0x44,0x38}, // 3

{0x00,0x04,0x0c,0x14,0x24,0x7e,0x04,0x04}, // 4

{0x00,0x7c,0x40,0x40,0x78,0x04,0x04,0x38}, // 5

{0x00,0x38,0x40,0x40,0x78,0x44,0x44,0x38}, // 6

{0x00,0x7c,0x04,0x04,0x08,0x08,0x10,0x10}, // 7

{0x00,0x3c,0x44,0x44,0x38,0x44,0x44,0x78}, // 8

{0x00,0x38,0x44,0x44,0x3c,0x04,0x04,0x78}, // 9

{0x00,0x1c,0x22,0x42,0x42,0x7e,0x42,0x42}, // A

{0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x7c}, // B

{0x00,0x3c,0x44,0x40,0x40,0x40,0x44,0x7c}, // C

{0x00,0x7c,0x42,0x42,0x42,0x42,0x44,0x78}, // D

{0x00,0x0a,0x7f,0x14,0x28,0xfe,0x50,0x00}, // #

{0x00,0x10,0x54,0x38,0x10,0x38,0x54,0x10} // *

};

void initADC() {

// Init ADC; f = ( 16MHz/prescaler ) / 13 cycles/conversion

ADMUX = 0; // Channel sel, right-adj, use AREF pin

ADCSRA = _BV(ADEN) | // ADC enable

_BV(ADSC) | // ADC start

_BV(ADATE) | // Auto trigger

_BV(ADIE) | // Interrupt enable

_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz

ADCSRB = 0; // Free-run mode

DIDR0 = _BV(0); // Turn off digital input for ADC pin

TIMSK0 = 0; // Timer0 off

}

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 c = pgm_read_float(&(cos_t[k]));

float s = pgm_read_float(&(sin_t[k]));

float a = 2. * c;

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 = c * v_2 - v_1;

im = s * v_2;

amp = sqrt(re * re + im * im);

spectrum[k] = amp;

}

}

float avg(float *a, uint16_t len) {

float result = .0;

for (uint16_t i = 0; i 《 len; i++) {

result += a[i];

}

return result / len;

}

int8_t get_single_index_above_threshold(float *a, uint16_t len, float threshold) {

if (threshold 《 THRESHOLD) {

return -1;

}

int8_t ix = -1;

for (uint16_t i = 0; i 《 len; i++) {

if (a[i] 》 threshold) {

if (ix == -1) {

ix = i;

} else {

return -1;

}

}

}

return ix;

}

void detect_digit(float *spectrum) {

float avg_row = avg(spectrum, 4);

float avg_col = avg(&spectrum[4], 4);

int8_t row = get_single_index_above_threshold(spectrum, 4, avg_row);

int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col);

if (row != -1 && col != -1 && avg_col 》 200) {

detected_digit.digit = pgm_read_byte(&(table[row][col]));

detected_digit.index = pgm_read_byte(&(char_indexes[row][col]));

} else {

detected_digit.digit = 0;

}

}

void drawSprite(byte* sprite) {

// The mask is used to get the column bit from the sprite row

byte mask = B10000000;

for(int iy = 0; iy 《 8; iy++ ) {

for(int ix = 0; ix 《 8; ix++ ) {

lmd.setPixel(7 - iy, ix, (bool)(sprite[iy] & mask ));

// shift the mask by one pixel to the right

mask = mask 》》 1;

}

// reset column mask

mask = B10000000;

}

}

void setup() {

cli();

initADC();

sei();

Serial.begin(115200);

lmd.setEnabled(true);

lmd.setIntensity(2);

lmd.clear();

lmd.display();

detected_digit.digit = 0;

}

unsigned long z = 0;

void loop() {

while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish

goertzel(samples, spectrum);

detect_digit(spectrum);

if (detected_digit.digit != 0) {

drawSprite(font[detected_digit.index]);

lmd.display();

}

if (z % 5 == 0) {

for (int i = 0; i 《 IX_LEN; i++) {

Serial.print(spectrum[i]);

Serial.print(“ ”);

}

Serial.println();

Serial.println((int)detected_digit.digit);

}

z++;

samplePos = 0;

ADCSRA |= _BV(ADIE); // Resume sampling interrupt

}

ISR(ADC_vect) {

uint16_t sample = ADC;

samples[samplePos++] = sample - 400;

if(samplePos 》= N) {

ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off

}

}

步骤3:原理图

应进行以下连接:

麦克风与Arduino

Out -》 A0

Vcc -》 3.3V

Gnd -》 Gnd

将AREF连接到3.3V很重要。

显示到Arduino

Vcc -》 5V

Gnd -》 Gnd

DIN -》 D11

CLK -》 D13

CS -》 D9

步骤4:结论

这里可以改进什么?我以9615Hz的速率使用N = 256个样本,该速率有一些频谱泄漏,如果N = 205且速率为8000Hz,则所需频率与离散化网格重合。对于该ADC,应在定时器溢出模式下使用。
责任编辑:wv

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 解码器
    +关注

    关注

    9

    文章

    1204

    浏览量

    42896
  • DTMF
    +关注

    关注

    1

    文章

    74

    浏览量

    48049
  • Arduino
    +关注

    关注

    190

    文章

    6516

    浏览量

    196054
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    UNO Q:开启Arduino的全新未来

    计算与实时响应能力。这种处理能力的结合弥合了数字世界与物理世界的鸿沟,借助强大的开发工具套件,将用户的创意变为现实。 [UNO Q]将Arduino推动技术普及的理念与Qualcomm的微处理专业
    的头像 发表于 12-15 11:46 207次阅读

    贸泽电子开售全新Arduino UNO Q单板计算机

    贸泽电子开售全新Arduino UNO Q单板计算机。Arduino UNO Q单板计算机(SBC)将高性能计算与实时控制结合,提供理想的创新平台。
    的头像 发表于 11-08 09:50 1008次阅读

    Arduino UNO Q 登陆 DigiKey,现已开放预订

    融合高性能微处理与专用微控制Arduino UNO Q加强创新开发能力 美国, 明尼苏达, 锡夫里弗福尔斯市 - 2025 年 10 月 07 日 全球领先的电子元器件与自动化产
    的头像 发表于 10-13 14:55 321次阅读
    <b class='flag-5'>Arduino</b> <b class='flag-5'>UNO</b> Q 登陆 DigiKey,现已开放预订

    增强T-BOX设计,新唐编解码器的重要作用

    (工作电压范围:2.5V-5.5V),最大输出功率可达1W。此外,通过软件控制的内部寄存可以实现灵活的省电模式。 主要优势:T-BOX设计架构中的汽车级编解码器 音频编解码器
    发表于 09-05 06:26

    何用Arduino Nano/UNO R3开发板给另一个Arduino IDE不能下载的Arduino Nano/UNO R3开发板重新烧录引导程序bootlaoder

    本文介绍了如何用能够Arduino IDE下载的Arduino Nano/UNO R3开发板给另一个Arduino IDE不能下载的
    的头像 发表于 08-08 20:16 3142次阅读
    如<b class='flag-5'>何用</b><b class='flag-5'>Arduino</b> Nano/<b class='flag-5'>UNO</b> R3开发板给另一个<b class='flag-5'>Arduino</b> IDE不能下载的<b class='flag-5'>Arduino</b> Nano/<b class='flag-5'>UNO</b> R3开发板重新烧录引导程序bootlaoder

    解码器的 0.02-4.0 GHz 高隔离 SP4T 吸收开关 skyworksinc

    电子发烧友网为你提供()带解码器的 0.02-4.0 GHz 高隔离 SP4T 吸收开关相关产品参数、数据手册,更有带解码器的 0.02-4.0 GHz 高隔离 SP4T 吸收开关的引脚图、接线图
    发表于 08-08 18:33
    带<b class='flag-5'>解码器</b>的 0.02-4.0 GHz 高隔离 SP4T 吸收开关 skyworksinc

    0.1-2.7 GHz SP4T 开关,带集成逻辑解码器 skyworksinc

    电子发烧友网为你提供()0.1-2.7 GHz SP4T 开关,带集成逻辑解码器相关产品参数、数据手册,更有0.1-2.7 GHz SP4T 开关,带集成逻辑解码器的引脚图、接线图、封装手册、中文
    发表于 08-08 18:32
    0.1-2.7 GHz SP4T 开关,带集成逻辑<b class='flag-5'>解码器</b> skyworksinc

    解码器的 20 MHz-3.0 GHz 高功率 SP4T 开关 skyworksinc

    电子发烧友网为你提供()带解码器的 20 MHz-3.0 GHz 高功率 SP4T 开关相关产品参数、数据手册,更有带解码器的 20 MHz-3.0 GHz 高功率 SP4T 开关的引脚图、接线图
    发表于 08-08 18:32
    带<b class='flag-5'>解码器</b>的 20 MHz-3.0 GHz 高功率 SP4T 开关 skyworksinc

    0.25 - 2.15 GHz 4x2 开关矩阵,带音调/电压解码器 skyworksinc

    电子发烧友网为你提供()0.25 - 2.15 GHz 4x2 开关矩阵,带音调/电压解码器相关产品参数、数据手册,更有0.25 - 2.15 GHz 4x2 开关矩阵,带音调/电压解码器的引脚图
    发表于 08-07 18:35
    0.25 - 2.15 GHz 4x2 开关矩阵,带音调/电压<b class='flag-5'>解码器</b> skyworksinc

    4 x 2 开关矩阵,带音调/电压解码器 250 MHz–2.15 GHz skyworksinc

    电子发烧友网为你提供()4 x 2 开关矩阵,带音调/电压解码器 250 MHz–2.15 GHz相关产品参数、数据手册,更有4 x 2 开关矩阵,带音调/电压解码器 250 MHz–2.15
    发表于 08-06 18:30
    4 x 2 开关矩阵,带音调/电压<b class='flag-5'>解码器</b> 250 MHz–2.15 GHz skyworksinc

    OT82111_VC1:USB OTG音频解码器固件技术解析

    引言随着移动设备对高品质音频输出的需求不断提升,支持USBOTG和I²S输出的音频解码器成为便携音频领域的重要解决方案。本文将介绍一款专为移动设备设计、具备ASRC采样率转换功能的USBOTG音频
    的头像 发表于 07-25 15:23 585次阅读
    OT82111_VC1:USB OTG音频<b class='flag-5'>解码器</b>固件技术解析

    Transformer架构中解码器的工作流程

    解码器的作用主要是制作文本序列。与编码类似,解码器也配备了一组类似的子层。它具有两个Multi-Head attention层,一个点前馈层,并且在每个子层之后都包含剩余连接和层归一化。
    的头像 发表于 06-10 14:32 931次阅读
    Transformer架构中<b class='flag-5'>解码器</b>的工作流程

    DM5885视频解码器英文手册

    电子发烧友网站提供《DM5885视频解码器英文手册.pdf》资料免费下载
    发表于 04-02 14:42 1次下载

    监控网络高清视频解码器,开启安防监控新时代

    监控网络高清视频解码器,开启安防监控新时代 在安防监控领域,您是否还在为设备兼容性差、操作复杂、画面显示效果不佳而烦恼?现在,一款全新的监控网络高清视频解码器震撼登场,为您彻底解决这些难题
    的头像 发表于 02-20 14:59 1530次阅读
    监控网络高清视频<b class='flag-5'>解码器</b>,开启安防监控新时代

    深入了解山景蓝牙音频解码器的工作原理以及应用领域

    蓝牙音频解码器是一种用于将数字音乐解码成模拟声音信号的装置。蓝牙解码器现在被广泛应用于智能手机、平板电脑和电脑等数字音乐播放设备,能够一定程度上提高音质,让音乐更加高保真。
    的头像 发表于 01-08 09:39 2252次阅读
    深入了解山景蓝牙音频<b class='flag-5'>解码器</b>的工作原理以及应用领域