📄 goertzel.h
字号:
#ifndef ___GOERTZEL_H
#define ___GOERTZEL_H
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "gcos.h" // for the GCOSXXXX constants
#include "commands.h"
#define MAXGOERTZELITEMS (sizeof(gcos)/sizeof(gcos[0]))
#define GOERTZEL_BLOCK_SIZE (AUDIO_BLOCK_LENGTH)
prog_int16_t gcos[] = {
GCOS0440, // 440Hz - Local Dial, remote ringing and busy tone
GCOS0697, // 697Hz - DTMF keypad row 1 (digits 1, 2, 3 and A)
GCOS0770, // 770Hz - DTMF keypad row 2 (digits 4, 5, 6 and B)
GCOS0852, // 852Hz - DTMF keypad row 3 (digits 7, 8, 9 and C)
GCOS0941, // 941Hz - DTMF keypad row 4 (digits *, 0, # and D)
GCOS1209, // 1209Hz - DTMF keypad col 1 (digits 1, 4, 7 and *)
GCOS1336, // 1336Hz - DTMF keypad col 2 (digits 2, 5, 8 and 0)
GCOS1477, // 1477Hz - DTMF keypad col 3 (digits 3, 6, 9 and #)
GCOS1633 // 1633Hz - DTMF keypad col 4 (digits A, B, C and D)
// (This last one is almost never present in standard
// telephone keypads but it is used in other kinds of
// singalling; for instance, a Brazilian Called ID
// message is typically "A1<caller number with>C",
// where caller number is the 2-digit area code
// followed by the 7 or 8 digit phone number itself
};
volatile short q1[2*MAXGOERTZELITEMS],q2[2*MAXGOERTZELITEMS];
volatile unsigned char gcount;
volatile short *d,*q;
volatile unsigned char nn,cc,at_least_one,accum_flags;
volatile unsigned short accum[MAXGOERTZELITEMS-1];
volatile unsigned short mag;
volatile short coef;
prog_int8_t dtmf_digits[] = "123A456B789C*0#D";
volatile unsigned char dtmf_frames;
volatile unsigned char ring_frames_on,ring_frames_off;
// This macro implements the inner goertzel round:
//
// short *d;
// short y=((d[0]*gcos)>>8)-d[1]+c; d[1]=d[0]; d[0]=y; d+=2;
//
// It uses a 16-bit x 16-bit multiplication, yielding a 32 bit result. The
// shift operation is implemented by simply discarding the last 8 bits.
// Since y is a short, the upper 8 bits are also discarded.
//
// I had to code this in assembler because trying to do this with 16-bit
// arithmetic in C killed precision, and making 'gcos' a signed 32-bit forces
// the entire multiplication to be 32-bit * 32-bit -- which GCC generates in
// a subroutine call with *ten* multiplications, dozens of additions and lots
// of overhead cycles in setting up things; this resulted in more than 100
// cycles per sample, which is clearly unnaceptable.
//
// With this routine, the same task is accomplished in only four multiplies
// and 42 cycles.
//
// Notice that it clobbers r0, r1. GCC uses r1 for other purposes, so
// it must be saved beforehand and restored afterwards.
#define goertzel_inner_16(gcos) \
asm( \
"ld r21,Y \n\t" \
"ldd r22,Y+1 \n\t" \
"ldi r23,lo8(%0) \n\t" \
"ldi r16,hi8(%0) \n\t" \
"muls r16,r22 \n\t" \
"mov r19,r0 \n\t" \
"mul r23,r21 \n\t" \
"mov r18,r1 \n\t" \
"mulsu r16,r21 \n\t" \
"add r18,r0 \n\t" \
"adc r19,r1 \n\t" \
"mulsu r22,r23 \n\t" \
"add r18,r0 \n\t" \
"adc r19,r1 \n\t" \
\
"ldd r24,Y+2 \n\t" \
"ldd r25,Y+3 \n\t" \
"sub r18,r24 \n\t" \
"sbc r19,r25 \n\t" \
"mov r24,r30 \n\t" \
"mov r25,r31 \n\t" \
"add r24,r18 \n\t" \
"adc r25,r19 \n\t" \
"ld r18,Y \n\t" \
"ldd r19,Y+1 \n\t" \
\
"std Y+2,r18 \n\t" \
"std Y+3,r19 \n\t" \
"st Y,r24 \n\t" \
"std Y+1,r25 \n\t" \
\
"subi r28,252 \n\t" \
\
: : "i" ((gcos)) : "r29","r28","r21","r22", \
"r23", "r24", "r25", "r16", "r18", "r19", \
"r0","r1","r2","memory" );
// This macro implements the inner goertzel round:
//
// short *d;
// short y=((d[0]*gcos)>>8)-d[1]+c; d[1]=d[0]; d[0]=y; d+=2;
//
// Since some values of gcos are less thatn 256, this version of the routine
// uses a 16-bit x 8-bit multiplication, yielding a 24 bit result. The
// shift operation is implemented by simply discarding the last 8 bits.
// Since y is a short, the upper 8 bits are also discarded. Overall, these
// facts allows us to use only two multiplications and just 36 cycles, six
// less than the previous one.
//
// Notice that it clobbers r0, r1 and r2. GCC uses r1 for other purposes, so
// it must be saved beforehand and restored afterwards.
#define goertzel_inner_08(gcos) \
asm( \
"ld r21,Y \n\t" \
"ldd r22,Y+1 \n\t" \
"ldi r23,lo8(%0) \n\t" \
"mul r23,r21 \n\t" \
"mov r18,r1 \n\t" \
"eor r19,r19 \n\t" \
"mulsu r22,r23 \n\t" \
"add r18,r0 \n\t" \
"adc r19,r1 \n\t" \
\
"ldd r24,Y+2 \n\t" \
"ldd r25,Y+3 \n\t" \
"sub r18,r24 \n\t" \
"sbc r19,r25 \n\t" \
"mov r24,r30 \n\t" \
"mov r25,r31 \n\t" \
"add r24,r18 \n\t" \
"adc r25,r19 \n\t" \
"ld r18,Y \n\t" \
"ldd r19,Y+1 \n\t" \
\
"std Y+2,r18 \n\t" \
"std Y+3,r19 \n\t" \
"st Y,r24 \n\t" \
"std Y+1,r25 \n\t" \
\
"subi r28,252 \n\t" \
\
: : "i" ((gcos)) : "r29","r28","r21","r22", \
"r23", "r24", "r25", "r16", "r18", "r19", \
"r0","r1","r2","memory" );
// This macro implements the inner goertzel round:
//
// short *d;
// short y=((d[0]*gcos)>>8)-d[1]+c; d[1]=d[0]; d[0]=y; d+=2;
//
// However, one of the gcos coefficients is 1, so the multiplication can
// be skipped altogether. This allows this step to be computed in just 27
// cycles.
#define goertzel_inner_00(gcos) \
asm( \
"ld r18,Y \n\t" \
"ldd r19,Y+1 \n\t" \
\
"ldd r24,Y+2 \n\t" \
"ldd r25,Y+3 \n\t" \
"sub r18,r24 \n\t" \
"sbc r19,r25 \n\t" \
"mov r24,r30 \n\t" \
"mov r25,r31 \n\t" \
"add r24,r18 \n\t" \
"adc r25,r19 \n\t" \
"ld r18,Y \n\t" \
"ldd r19,Y+1 \n\t" \
\
"std Y+2,r18 \n\t" \
"std Y+3,r19 \n\t" \
"st Y,r24 \n\t" \
"std Y+1,r25 \n\t" \
\
"subi r28,252 \n\t" \
\
: : "i" ((gcos)) : "r29","r28","r21","r22", \
"r23", "r24", "r25", "r16", "r18", "r19", \
"r0","r1","r2","memory" );
#endif // __GOERTZEL_H
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -