//***************************************************************************
//* A P P L I C A T I O N N O T E F O R T H E A V R F A M I L Y
//*
//* File Name : "Klingel_DTMF.c"
//* Title : Klingel2FritzBox
//* Version : 0.3
//* Target MCU : Any AVR with SRAM, 8 I/O pins and PWM (hier Tiny 25)
//*
//* DESCRIPTION
//* Programm zur Klingelsignalisierungserkennung um daraufhin mittels
//* PWM Wahl einen internen Sammelruf an alle an die Fritzbox angeschlossenen
//* Telefone zu initiieren
//* Klingelsignalerkennung mittels Interrupt (Zählen von Halbwellen)
//* PWM Implementierung auf Basis des Vorschlags AVM314
//*
//***************************************************************************
#include <stdio.h>
#define __IAR_SYSTEMS_ASM__
#include <avr/io.h>
#include "compat/ina90.h"
#include "avr/interrupt.h"
#include <inttypes.h>
#include <util/delay.h>
//#define Xtal 8000000 // system clock frequency
//#define F_CPU 8000000L
//#define Fck Xtal/prescaler // Timer1 working frequency
#define prescaler 1 // timer1 prescaler
#define N_samples 128 // Number of samples in lookup table
#define Fck F_CPU/prescaler // Timer1 working frequency
#define delaycyc 10 // port B setup delay cycles
#define DTMF_FLOW_STEPS 3 // Number of DTMF Tones to generate
#define nop() __asm volatile ("nop")
//************************** SIN TABLE *************************************
// Samples table : one period sampled on 128 samples and
// quantized on 7 bit
//**************************************************************************
PROGMEM unsigned char auc_SinParam [128] = {
64,67,
70,73,
76,79,
82,85,
88,91,
94,96,
99,102,
104,106,
109,111,
113,115,
117,118,
120,121,
123,124,
125,126,
126,127,
127,127,
127,127,
127,127,
126,126,
125,124,
123,121,
120,118,
117,115,
113,111,
109,106,
104,102,
99,96,
94,91,
88,85,
82,79,
76,73,
70,67,
64,60,
57,54,
51,48,
45,42,
39,36,
33,31,
28,25,
23,21,
18,16,
14,12,
10,9,
7,6,
4,3,
2,1,
1,0,
0,0,
0,0,
0,0,
1,1,
2,3,
4,6,
7,9,
10,12,
14,16,
18,21,
23,25,
28,31,
33,36,
39,42,
45,48,
51,54,
57,60};
//*************************** x_SW ***************************************
//Table of x_SW (excess 8): x_SW = ROUND(8*N_samples*f*510/Fck)
//**************************************************************************
//high frequency (coloun)
//1209hz ---> x_SW = 79
//1336hz ---> x_SW = 87
//1477hz ---> x_SW = 96
//1633hz ---> x_SW = 107
const unsigned char auc_frequencyH [4] = { 107,96,87,79 };
//low frequency (row)
//697hz ---> x_SW = 46
//770hz ---> x_SW = 50
//852hz ---> x_SW = 56
//941hz ---> x_SW = 61
const unsigned char auc_frequencyL [4] = { 61,56,50,46 };
/*
const unsigned char DTMF_flow[DTMF_FLOW_STEPS][2] = { // hi lo -> x_SW
{ 79, 46}, // '1'
{ 87, 46}, // '2'
{ 96, 46}, // '3'
{ 79, 50}, // '4'
{ 87, 50}, // '5'
{ 96, 50}, // '6'
{ 79, 56}, // '7'
{ 87, 56}, // '8'
{ 96, 56}, // '9'
{ 79, 61}, // '*'
{ 87, 61}, // '0'
{ 96, 61} // '#'
};
*/
// modified because of Fck deviation
const unsigned char DTMF_flow[DTMF_FLOW_STEPS][2] = { // hi lo -> x_SW
{ 76, 59}, // '*'
{ 76, 59}, // '*'
{ 93, 54} // '9'
};
//************************** global variables ****************************
volatile unsigned char x_SWa = 0x00; // step width of high frequency
volatile unsigned char x_SWb = 0x00; // step width of low frequency
volatile unsigned int i_CurSinValA = 0; // position freq. A in LUT (extended format)
volatile unsigned int i_CurSinValB = 0; // position freq. B in LUT (extended format)
volatile unsigned int i_TmpSinValA; // position freq. A in LUT (actual position)
volatile unsigned int i_TmpSinValB; // position freq. B in LUT (actual position)
volatile unsigned char x_RingDetectionCounter = 0x00;
//**************************************************************************
// Time delay to ensure a correct setting of the pins of Port B
//**************************************************************************
void long_delay(uint16_t ms) {
for(; ms>0; ms--) _delay_ms(1);
}
//**************************************************************************
// Timer overflow interrupt service routine
//**************************************************************************
ISR(TIM0_OVF_vect)
{
// move Pointer about step width aheaed
i_CurSinValA += x_SWa;
i_CurSinValB += x_SWb;
// normalize Temp-Pointer
i_TmpSinValA = (char)(((i_CurSinValA+4) >> 3)&(0x007F));
i_TmpSinValB = (char)(((i_CurSinValB+4) >> 3)&(0x007F));
// calculate PWM value: high frequency value + 3/4 low frequency value
if (x_SWa == 0)
{
OCR0A = 0;
}
/*
else if ( x_SWb == 0 )
{
OCR0A = pgm_read_byte(&auc_SinParam[i_TmpSinValA]);
}
*/
else
{
OCR0A = (pgm_read_byte(&auc_SinParam[i_TmpSinValA]) + (pgm_read_byte(&auc_SinParam[i_TmpSinValB])-(pgm_read_byte(&auc_SinParam[i_TmpSinValB])>>2)));
}
}
ISR(ANA_COMP_vect)
{
x_RingDetectionCounter++;
}
//**************************************************************************
// Initialization
//**************************************************************************
void init (void)
{
DDRB = (1<<DDB4) + (1<<DDB5); // PB4 as output
PORTB = (1<<PB4) + (1<<PB5); // set PB4 to hi
DDRB = 0; // all as input, now pull up on PB4 + PB5 should be enabled
DDRB = (1<<DDB0) + (1<<DDB2); // PB0 (OC0A) as output, PB2 as Output (Hook Relais)
TCCR0A = (1<<COM0A1)+(1<<WGM00); // Clear on Compare match, PWM Phase Correct
TCCR0B = (1<<CS00); // Prescaler clk/1
TIMSK = (1<<TOIE0); // Int Timer0 Overflow enabled
ACSR = (1<<ACIS1) + (1<<ACIE) + (1<<ACBG); // IR on falling edge, IR enabled, Bandgap Volatge
x_SWa=0;
x_SWb=0;
OCR0A = 0;
long_delay(10);
sei(); // Interrupts enabled
long_delay(10);
}
void ringin ()
{
unsigned char uc_flow = 0;
PORTB |= ( 1 << PB2 ); // abheben
long_delay(200); // 500msec warten
// send DTMF (**9)
for ( uc_flow = 0; uc_flow < DTMF_FLOW_STEPS; uc_flow++)
{
long_delay(40); // 40msec Pause
x_SWa = DTMF_flow[uc_flow][0];
x_SWb = DTMF_flow[uc_flow][1];
long_delay(50); // Signal 50 msec senden
// reset DTMF for pause
x_SWa=0;
x_SWb=0;
}
long_delay(16000); // 16sec Klingeln lassen
PORTB &= ~( 1 << PB2 ); //auflegen
}
//**************************************************************************
// MAIN
//**************************************************************************
int main (void)
{
init();
for(;;)
{
// reset DTMF
x_SWa=0;
x_SWb=0;
long_delay(80); // count pulses for 80msec (or 100?!?) to detect ringin
// Test call initiation
if ( !(PINB & (1<<PINB4)) || (x_RingDetectionCounter <= 6) )
{
ringin();
}
x_RingDetectionCounter = 0;
/*
if ( !(PINB & (1<<PINB4)) )
{
PORTB |= ( 1 << PB2 ); // abheben
x_SWa = 76;
x_SWb = 0;
long_delay(25000); // 5sec Klingeln lassen
}
*/
// PORTB &= ~(1<<PB2);
// PORTB ^= ( 1 << PB2 );
}
return (0);
}