This firmware implements exactly the algorithm described in this article — a 1-bit A/D DTMF decoder
running on a PIC18F2550 microcontroller. The device receives audio from a handheld
radio (HT), decodes DTMF tones using only a comparator and dot-product arithmetic, and activates a
load when a 2-digit password is received over the radio.
/*
* ============================================================================
* RADIO_DTMF_CONT.c
* DTMF Tone Decoder via 1-bit A/D — Radio HT Controller
* ============================================================================
*
* OVERVIEW:
* This firmware runs on a PIC18F2550 microcontroller and decodes DTMF
* (Dual-Tone Multi-Frequency) tones received from a handheld radio (HT).
* The audio output of the radio is digitized using only a comparator
* (1-bit A/D converter), implementing the algorithm described by
* Scott Dattalo in "DTMF Decoding with a 1-bit A/D Converter".
*
* When the correct 2-digit password is received (currently "27"),
* the firmware activates a load (output pin) for a defined period.
*
* HARDWARE:
* - MCU : PIC18F2550 @ 48 MHz (PLL from 20 MHz crystal)
* - PIN_C1 : Comparator input — digitized audio from radio speaker output
* - PIN_A1 : Green LED — idle / ready state
* - PIN_A2 : Red LED — digit received / load active
* - PIN_C0 : PTT output — reserved for future transmission use
*
* SIGNAL CHAIN:
* Radio audio → comparator → 1-bit stream → Timer1 ISR samples at 16 kHz
* → 250-sample circular buffer
* → Goertzel-style dot products
* → DTMF digit decode
* → password check → load control
*
* ALGORITHM (Dattalo 1-norm method):
* Each DTMF frequency (row: 697, 770, 852, 941 Hz;
* col: 1209, 1336, 1477, 1633 Hz) is represented
* by two pre-computed square-wave reference tables:
* - In-phase (I): tab1 = positive half-cycle, tab2 = negative half-cycle
* - Quadrature (Q): same frequency, 90-degree shifted
*
* For each sample window, the firmware computes:
* IQ_n++ if sample matches reference; IQ_n-- otherwise (1-norm dot product)
* Then sums: SOMA = IQ_I1 + IQ_Q1 + IQ_I2 + IQ_Q2 for each frequency pair.
* A SOMA > 100 indicates that frequency is present.
*
* DTMF FREQUENCY MAP:
* 1209 Hz 1336 Hz 1477 Hz 1633 Hz
* 697 Hz : 1 2 3 A
* 770 Hz : 4 5 6 B
* 852 Hz : 7 8 9 C
* 941 Hz : * 0 # D
*
* PASSWORD:
* Currently set to 2-digit code "27" (configurable via the 'numero==27' check).
* Extend dig[] array and ndig threshold for longer passwords.
*
* COMPILER: CCS PCH (PCWH) C Compiler for PIC18
* TARGET : PIC18F2550
* CLOCK : 48 MHz (HSPLL, PLL5, CPUDIV1)
* ============================================================================
*/
#pragma zero_ram // Zero all RAM before startup (clean state)
#pragma case // Case-sensitive symbol matching
#include <18F2550.h>
/* --- Oscillator / Fuse Configuration ---
* HSPLL : High-Speed + PLL enabled
* NOWDT : Watchdog Timer disabled
* NOPROTECT: Code read-protection off
* NOLVP : Low-voltage programming disabled (requires MCLR)
* NODEBUG : ICD debug disabled
* USBDIV : USB clock = OSC/2 (not used here, but required by 18F2550)
* PLL5 : PLL prescaler ÷5 → 20 MHz / 5 = 4 MHz → PLL → 96 MHz / 2 = 48 MHz
* CPUDIV1 : CPU clock = 48 MHz / 1 = 48 MHz
* VREGEN : USB voltage regulator enabled (required by 18F2550 even if USB unused)
*/
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)
/* --- I/O Pin Definitions --- */
#define LEDRED PIN_A1 // Red LED: digit received or load active
#define LEDGREEN PIN_A2 // Green LED: idle / ready
#define PTT PIN_C0 // PTT (Push-To-Talk) — reserved for radio TX
/* ==========================================================================
* DTMF REFERENCE TABLES — Square-Wave Dot-Product Kernels
* ==========================================================================
*
* Each DTMF frequency is represented by 4 binary (0/1) reference arrays:
* - tab1 : in-phase reference, positive half-period
* - tab2 : in-phase reference, negative half-period (inverted)
* - I prefix: in-phase component (cosine equivalent)
* - Q prefix: quadrature component (sine equivalent, 90° shifted)
*
* Table length = number of samples per period at 16 kHz:
* Period samples = round(16000 / frequency)
* 697 Hz → 23 samples 770 Hz → 21 samples
* 852 Hz → 19 samples 941 Hz → 17 samples
* 1209 Hz → 13 samples 1336 Hz → 12 samples
* 1477 Hz → 11 samples 1633 Hz → 10 samples
*
* The dot product is computed by comparing the digitized sample against
* these tables using the 1-norm method (increment on match, decrement on mismatch).
* This avoids multiplication entirely — ideal for 8-bit PICs.
* ==========================================================================
*/
/* --- Row Frequency: 697 Hz (23 samples/period) --- */
static char Q697tab1[23]={1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}; // Q in-phase pos
static char Q697tab2[23]={0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1}; // Q in-phase neg
static char I697tab1[23]={1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1}; // I quadrature pos
static char I697tab2[23]={0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; // I quadrature neg
/* --- Row Frequency: 770 Hz (21 samples/period) --- */
static char Q770tab1[21]={1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0};
static char Q770tab2[21]={0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1};
static char I770tab1[21]={1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1};
static char I770tab2[21]={0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
/* --- Row Frequency: 852 Hz (19 samples/period) --- */
static char Q852tab1[19]={1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1};
static char Q852tab2[19]={0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
static char I852tab1[19]={1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0};
static char I852tab2[19]={0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1};
/* --- Row Frequency: 941 Hz (17 samples/period) --- */
static char Q941tab1[17]={0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
static char Q941tab2[17]={1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1};
static char I941tab1[17]={1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0};
static char I941tab2[17]={0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1};
/* --- Column Frequency: 1336 Hz (12 samples/period) --- */
static char Q1336tab1[12]={1,1,1,1,1,1,0,0,0,0,0,0};
static char Q1336tab2[12]={0,0,0,0,0,0,1,1,1,1,1,1};
static char I1336tab1[12]={0,0,0,1,1,1,1,1,1,0,0,0};
static char I1336tab2[12]={1,1,1,0,0,0,0,0,0,1,1,1};
/* --- Column Frequency: 1209 Hz (13 samples/period) --- */
static char Q1209tab1[13]={1,1,1,1,1,1,1,0,0,0,0,0,0};
static char Q1209tab2[13]={0,0,0,0,0,0,0,1,1,1,1,1,1};
static char I1209tab1[13]={1,1,1,0,0,0,0,0,0,0,1,1,1};
static char I1209tab2[13]={0,0,0,1,1,1,1,1,1,1,0,0,0};
/* --- Column Frequency: 1477 Hz (11 samples/period) --- */
static char Q1477tab1[11]={1,1,1,1,1,1,0,0,0,0,0};
static char Q1477tab2[11]={0,0,0,0,0,0,1,1,1,1,1};
static char I1477tab1[11]={1,1,0,0,0,0,0,0,1,1,1};
static char I1477tab2[11]={0,0,1,1,1,1,1,1,0,0,0};
/* --- Column Frequency: 1633 Hz (10 samples/period) --- */
static char Q1633tab1[10]={1,1,1,1,1,0,0,0,0,0};
static char Q1633tab2[10]={0,0,0,0,0,1,1,1,1,1};
static char I1633tab1[10]={1,1,0,0,0,0,0,1,1,1};
static char I1633tab2[10]={0,0,1,1,1,1,1,0,0,0};
/* ==========================================================================
* SAMPLE BUFFER
* ==========================================================================
* 250 binary samples captured at 16 kHz = ~15.6 ms window.
* This covers at least 10 full cycles of the lowest DTMF frequency (697 Hz),
* providing sufficient resolution for reliable detection.
*/
static char tabela[250]; // Circular sample buffer (0 or 1 per sample)
/* ==========================================================================
* GLOBAL STATE VARIABLES
* ==========================================================================
*/
int ii = 0; // Sample buffer write index (used in ISR)
int k1,k2,k3,k4,k5,k6,k7,k8; // Modulo indices — position within each period table
int OK = 0; // Flag: sample buffer is full and ready for processing
int SERVO= 0; // Load control state (0=off, 1=on)
int DTMF = 0; // Flag: DTMF detection mode active
int ALRED= 0; // Alert/alarm state flag
int ix = 0; // Auxiliary index (used for 1633 Hz table, see k8)
int cont = 0; // Silence/mismatch counter (debounce between digits)
/* --- Dot-product accumulators: 4 per frequency × 8 frequencies = 32 total ---
* IQ1..IQ4 : 697 Hz (I+, I−, Q+, Q−)
* IQ5..IQ8 : 770 Hz
* IQ9..IQ12 : 852 Hz
* IQ13..IQ16 : 941 Hz
* IQ17..IQ20 : 1209 Hz
* IQ21..IQ24 : 1336 Hz
* IQ25..IQ28 : 1477 Hz
* IQ29..IQ32 : 1633 Hz
*/
int IQ1=0,IQ2=0,IQ3=0,IQ4=0,IQ5=0,IQ6=0,IQ7=0,IQ8=0,IQ9=0,IQ10=0,IQ11=0;
int IQ12=0,IQ13=0,IQ14=0,IQ15=0,IQ16=0,IQ17=0,IQ18=0,IQ19=0,IQ20=0,IQ21=0;
int IQ22=0,IQ23=0,IQ24=0,IQ25=0,IQ26=0,IQ27=0,IQ28=0,IQ29=0,IQ30=0,IQ31=0;
int IQ32=0;
/* --- Frequency presence scores (1-norm sums) ---
* SOMA1 = 697 Hz row score SOMA5 = 1209 Hz column score
* SOMA2 = 770 Hz row score SOMA6 = 1336 Hz column score
* SOMA3 = 852 Hz row score SOMA7 = 1477 Hz column score
* SOMA4 = 941 Hz row score SOMA8 = 1633 Hz column score
*/
int SOMA1=0,SOMA2=0,SOMA3=0,SOMA4=0,SOMA5=0,SOMA6=0,SOMA7=0,SOMA8=0;
static int16 Amostra = 0; // Raw ADC value (not used in 1-bit mode)
static char S4KHZ = 0; // 4 kHz sub-tick counter (unused in final version)
static char bitn = 0; // Bit counter (auxiliary)
static char bb = 0; // Byte assembly register (auxiliary)
static char SA; // Sample accumulation register (auxiliary)
/* ============================================================================
* MAIN FUNCTION
* ============================================================================
*/
void main(void) {
/* --- Local variables --- */
int16 ATRASO, numero; // ATRASO: timing delay; numero: decoded 2-digit number
int i, c; // Loop counter; general purpose
int ENT, ENTOLD; // Current and previous comparator input
int N0, N1; // Decoded row index (N0) and column index (N1)
int dig[4]; // Received digit buffer (up to 4 digits)
int ndig = 0; // Number of digits received so far
int dold = 0; // Debounce flag (prevents double-counting same digit)
int cont2 = 0; // Secondary counter (reserved)
/* --- Timer Configuration ---
* Timer1: Generates the 16 kHz sampling interrupt
* At 48 MHz: instruction cycle = 83.33 ns
* Timer1 preload = 64836 → overflow every 700 cycles → 16 kHz
* Timer2: Disabled (not used)
* Timer3: Internal, prescaler ÷8 (reserved for future use)
*/
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED, 127, 1);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
enable_interrupts(GLOBAL); // Enable global interrupt flag
set_timer1(64836); // Preload Timer1 for 16 kHz overflow
enable_interrupts(INT_TIMER1); // Enable Timer1 overflow interrupt
/* --- Startup Sequence ---
* Green LED ON = system ready
* Briefly activate load (SERVO=2) then release after 1 second
* This provides a power-on confirmation pulse on the output
*/
output_high(PIN_A1); // Green LED ON — system initializing
output_low(PIN_A2); // Red LED OFF
output_low(PIN_C0); // PTT OFF
ALRED = 0;
SERVO = 2; // Momentary load activation (startup pulse)
delay_ms(1000);
SERVO = 0; // Release load
/* --- Enable DTMF detection --- */
DTMF = 1;
ndig = 0;
/* ========================================================================
* MAIN LOOP — DTMF Detection and Password Validation
* ========================================================================
*/
while(1) {
/* --- Wait until sample buffer is full (OK flag set by Timer1 ISR) --- */
if (DTMF && OK) {
disable_interrupts(INT_TIMER1); // Pause sampling during processing
/* --- Reset all accumulators for new detection cycle --- */
OK = 0;
SOMA1=0; SOMA2=0; SOMA3=0; SOMA4=0;
SOMA5=0; SOMA6=0; SOMA7=0; SOMA8=0;
IQ1=0; IQ2=0; IQ3=0; IQ4=0;
IQ5=0; IQ6=0; IQ7=0; IQ8=0;
IQ9=0; IQ10=0; IQ11=0; IQ12=0;
IQ13=0; IQ14=0; IQ15=0; IQ16=0;
IQ17=0; IQ18=0; IQ19=0; IQ20=0;
IQ21=0; IQ22=0; IQ23=0; IQ24=0;
IQ25=0; IQ26=0; IQ27=0; IQ28=0;
IQ29=0; IQ30=0; IQ31=0; IQ32=0;
/* ----------------------------------------------------------------
* DOT-PRODUCT LOOP — 250 samples × 8 frequencies × 4 components
* ----------------------------------------------------------------
* For each sample i, compute the table index as i mod period_length.
* Compare sample against each reference table:
* match → accumulator++
* mismatch → accumulator-- (floor at 0 to avoid negative values)
*
* This is the 1-norm Goertzel-style dot product running on
* binary (0/1) samples — no multiplication required.
* ----------------------------------------------------------------
*/
for (i = 0; i < 250; i++) {
/* Compute modulo index for each frequency's table period */
k1 = i % 23; // 697 Hz index
k2 = i % 21; // 770 Hz index
k3 = i % 19; // 852 Hz index
k4 = i % 17; // 941 Hz index
k5 = i % 13; // 1209 Hz index
k6 = i % 12; // 1336 Hz index
k7 = i % 11; // 1477 Hz index
k8 = ix % 10; // 1633 Hz index (ix is global; mirrors i in ISR)
/* --- 697 Hz row: in-phase (I) and quadrature (Q) --- */
if (tabela[i] == I697tab1[k1]) IQ1++; else if(IQ1>0) IQ1--;
if (tabela[i] == Q697tab1[k1]) IQ2++; else if(IQ2>0) IQ2--;
if (tabela[i] == I697tab2[k1]) IQ3++; else if(IQ3>0) IQ3--;
if (tabela[i] == Q697tab2[k1]) IQ4++; else if(IQ4>0) IQ4--;
/* --- 770 Hz row --- */
if (tabela[i] == I770tab1[k2]) IQ5++; else if(IQ5>0) IQ5--;
if (tabela[ix]== Q770tab1[k2]) IQ6++; else if(IQ6>0) IQ6--; // Note: ix index
if (tabela[i] == I770tab2[k2]) IQ7++; else if(IQ7>0) IQ7--;
if (tabela[i] == Q770tab2[k2]) IQ8++; else if(IQ8>0) IQ8--;
/* --- 852 Hz row --- */
if (tabela[i] == I852tab1[k3]) IQ9++; else if(IQ9>0) IQ9--;
if (tabela[i] == Q852tab1[k3]) IQ10++; else if(IQ10>0) IQ10--;
if (tabela[i] == I852tab2[k3]) IQ11++; else if(IQ11>0) IQ11--;
if (tabela[i] == Q852tab2[k3]) IQ12++; else if(IQ12>0) IQ12--;
/* --- 941 Hz row --- */
if (tabela[i] == I941tab1[k4]) IQ13++; else if(IQ13>0) IQ13--;
if (tabela[i] == Q941tab1[k4]) IQ14++; else if(IQ14>0) IQ14--;
if (tabela[i] == I941tab2[k4]) IQ15++; else if(IQ15>0) IQ15--;
if (tabela[i] == Q941tab2[k4]) IQ16++; else if(IQ16>0) IQ16--;
/* --- 1209 Hz column --- */
if (tabela[i] == I1209tab1[k5]) IQ17++; else if(IQ17>0) IQ17--;
if (tabela[i] == Q1209tab1[k5]) IQ18++; else if(IQ18>0) IQ18--;
if (tabela[i] == I1209tab2[k5]) IQ19++; else if(IQ19>0) IQ19--;
if (tabela[i] == Q1209tab2[k5]) IQ20++; else if(IQ20>0) IQ20--;
/* --- 1336 Hz column --- */
if (tabela[i] == I1336tab1[k6]) IQ21++; else if(IQ21>0) IQ21--;
if (tabela[i] == Q1336tab1[k6]) IQ22++; else if(IQ22>0) IQ22--;
if (tabela[i] == I1336tab2[k6]) IQ23++; else if(IQ23>0) IQ23--;
if (tabela[i] == Q1336tab2[k6]) IQ24++; else if(IQ24>0) IQ24--;
/* --- 1477 Hz column --- */
if (tabela[i] == I1477tab1[k7]) IQ25++; else if(IQ25>0) IQ25--;
if (tabela[i] == Q1477tab1[k7]) IQ26++; else if(IQ26>0) IQ26--;
if (tabela[i] == I1477tab2[k7]) IQ27++; else if(IQ27>0) IQ27--;
if (tabela[i] == Q1477tab2[k7]) IQ28++; else if(IQ28>0) IQ28--;
/* --- 1633 Hz column --- */
if (tabela[i] == I1633tab1[k8]) IQ29++; else if(IQ29>0) IQ29--;
if (tabela[i] == Q1633tab1[k8]) IQ30++; else if(IQ30>0) IQ30--;
if (tabela[i] == I1633tab2[k8]) IQ31++; else if(IQ31>0) IQ31--;
if (tabela[i] == Q1633tab2[k8]) IQ32++; else if(IQ32>0) IQ32--;
} /* end sample loop */
/* ----------------------------------------------------------------
* FREQUENCY PRESENCE SCORES (1-norm sums)
* Each SOMA = sum of 4 IQ accumulators for one frequency.
* Max theoretical value = 250 × 4 = 1000 (all samples match).
* Threshold of 100 (~10% of max) distinguishes present from absent.
* ----------------------------------------------------------------
*/
SOMA1 = IQ1 + IQ2 + IQ3 + IQ4; // 697 Hz row score
SOMA2 = IQ5 + IQ6 + IQ7 + IQ8; // 770 Hz row score
SOMA3 = IQ9 + IQ10 + IQ11 + IQ12; // 852 Hz row score
SOMA4 = IQ13 + IQ14 + IQ15 + IQ16; // 941 Hz row score
SOMA5 = IQ17 + IQ18 + IQ19 + IQ20; // 1209 Hz column score
SOMA6 = IQ21 + IQ22 + IQ23 + IQ24; // 1336 Hz column score
SOMA7 = IQ25 + IQ26 + IQ27 + IQ28; // 1477 Hz column score
SOMA8 = IQ29 + IQ30 + IQ31 + IQ32; // 1633 Hz column score
/* ----------------------------------------------------------------
* ROW / COLUMN IDENTIFICATION
* N0: detected row (1=697Hz 2=770Hz 3=852Hz 4=941Hz)
* N1: detected column (1=1209Hz 2=1336Hz 3=1477Hz 4=1633Hz)
* ----------------------------------------------------------------
*/
if (SOMA1 > 100) N0 = 1; // 697 Hz row
else if (SOMA2 > 100) N0 = 2; // 770 Hz row
else if (SOMA3 > 100) N0 = 3; // 852 Hz row
else if (SOMA4 > 100) N0 = 4; // 941 Hz row
if (SOMA5 > 100) N1 = 1; // 1209 Hz column
else if (SOMA6 > 100) N1 = 2; // 1336 Hz column
else if (SOMA7 > 100) N1 = 3; // 1477 Hz column
else if (SOMA8 > 100) N1 = 4; // 1633 Hz column
/* ----------------------------------------------------------------
* DIGIT DECODING — DTMF Matrix to Digit
* Standard DTMF keypad mapping:
* N0\N1 | 1(1209) | 2(1336) | 3(1477)
* ------+---------+---------+---------
* 1(697)| 1 | 2 | 3
* 2(770)| 4 | 5 | 6
* 3(852)| 7 | 8 | 9
* 4(941)| * | 0 | #
*
* 'dold' debounce flag prevents the same key from being
* counted twice during the hold-time of a pressed tone.
* ----------------------------------------------------------------
*/
if (N0==1 && N1==1 && !dold) { dig[ndig]=1; ndig++; dold=1; }
else if (N0==1 && N1==2 && !dold) { dig[ndig]=2; ndig++; dold=5; }
else if (N0==1 && N1==3 && !dold) { dig[ndig]=3; ndig++; dold=1; }
else if (N0==2 && N1==1 && !dold) { dig[ndig]=4; ndig++; dold=1; }
else if (N0==2 && N1==2 && !dold) { dig[ndig]=5; ndig++; dold=1; }
else if (N0==2 && N1==3 && !dold) { dig[ndig]=6; ndig++; dold=1; }
else if (N0==3 && N1==1 && !dold) { dig[ndig]=7; ndig++; dold=1; }
else if (N0==3 && N1==2 && !dold) { dig[ndig]=8; ndig++; dold=1; }
else if (N0==3 && N1==3 && !dold) { dig[ndig]=9; ndig++; dold=1; }
else if (N0==4 && N1==2 && !dold) { dig[ndig]=0; ndig++; dold=1; }
else {
/* No DTMF tone detected in this window — increment silence counter */
cont++;
if (cont > 10) {
/* After ~10 consecutive silent windows, reset debounce.
* This allows the next key press to be registered. */
cont = 0;
dold = 0;
output_high(PIN_A1); // Green LED ON — back to idle
output_low(PIN_A2); // Red LED OFF
}
}
/* Indicate digit received — Red LED ON, Green LED OFF */
if (dold) {
output_high(PIN_A2); // Red LED ON
output_low(PIN_A1); // Green LED OFF
}
/* ----------------------------------------------------------------
* PASSWORD VALIDATION
* After 2 digits are received, decode the 2-digit number and
* compare against the expected password (currently 27).
*
* To change the password: modify the 'numero == 27' condition.
* To use a longer password: increase dig[] size and ndig threshold.
* ----------------------------------------------------------------
*/
if (ndig > 1) {
ndig = 0; // Reset digit counter
numero = dig[0] * 10 + dig[1]; // Combine digits into a number
if (numero == 27) {
/* --- Correct password received — Activate load --- */
output_high(PIN_A2); // Red LED ON (load active indicator)
output_low(PIN_A1); // Green LED OFF
delay_ms(3000); // Load ON for 3 seconds
output_low(PIN_A2); // Red LED OFF (load releasing)
delay_ms(3000); // Brief pause before returning to ready
output_high(PIN_A1); // Green LED ON — back to idle
} else {
/* --- Wrong password — flash both LEDs as rejection signal --- */
output_high(PIN_A2); // Both LEDs ON briefly
output_high(PIN_A1);
delay_ms(2000); // 2 second rejection flash
}
}
/* --- Reset detection state for next window --- */
N0 = 0; N1 = 0;
OK = 0; ii = 0;
enable_interrupts(INT_TIMER1); // Resume sampling
}
} /* end while(1) */
} /* end main */
/* ============================================================================
* TIMER1 INTERRUPT SERVICE ROUTINE — 16 kHz Sampler
* ============================================================================
*
* Fires at 16,000 times per second (every ~62.5 µs).
* Each invocation reads one bit from the comparator on PIN_C1 and
* stores it in the circular sample buffer 'tabela[]'.
*
* When 250 samples have been collected (one detection window ≈ 15.6 ms),
* the OK flag is set to signal the main loop that a window is ready.
*
* Timer1 preload 64848 → overflow after (65536 − 64848) = 688 cycles
* At 48 MHz (instruction cycle 83.33 ns): 688 × 83.33 ns ≈ 57.3 µs ≈ 16 kHz
* ============================================================================
*/
#int_TIMER1
void TIMER1_isr(void) {
static int16 s = 0; // Reserved (unused in current version)
static int16 contador= 0; // Reserved (unused in current version)
char InS, OutpS; // Input sample; output sample (reserved)
set_timer1(64848); // Reload timer for next 16 kHz tick
if (DTMF) {
if (ii < 250) {
/* --- Capture one 1-bit sample from comparator --- */
tabela[ii] = input(PIN_C1); // 0 or 1 (comparator output)
ii++;
OK = 0; // Buffer not yet complete
} else {
/* --- Buffer full: signal main loop to process --- */
ii = 0;
OK = 1;
}
}
}