Simple serial and timer ISR state machine
This is a replacement firmware for a K164. The K164 Kit is a simple DTMF decoder board still available or can be built with available schematics. This example is a good reference for the AT89C2051 or 8051 on how to setup interrupts and create a simple real time state machine.
/*
k164_js.c
Purpose: New firmware for the k164 dtmf decoder board and
the AT89C2051-24PC The source code was compiled with sdcc.
URLs:
http://www.digikey.com/product-detail/en/AT89C2051-24PU/AT89C2051-24PU-ND/1118880
http://www.electronics123.com/kits-and-modules/Telephone-Call-Logger-Kit-16k.html
http://www.kitsrus.com/pdf/k164.pdf
Compile: sdcc k164_js.c ; packihx k164_js.ihx > k164_js.hex
Simulate: s51 k164_js.hex
Copyright (C) 2009 Nu Tech Software Solutions, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
AUTHOR: Sean Mathews <coder at f34r.com> 1/27/2009
*/
#include <at89x051.h>
#define STATUS_LED P3_5
#define HOOK_LED P3_4
#define LOOP_STATUS P3_3
#define STD_STATUS P3_7
#define MODE_SWITCH P1_5
/* UART parameters */
#define CpuClk 20275200 // 20.2752 MHz clock chip on the k164 board
#define Baudrate 9600 // UART - 9600,N,8,1 baud used by current firmware
#define Timer1ReloadValue (256-(2*CpuClk/32/12/Baudrate))
#define F34R_MODE 0
char szVERSION[] = "V1.0";
/*
To determine the value that must be placed in TH1 to generate a given baud rate, we may use the following equation (assuming PCON.7 is clear).
TH1 = 256 - ((Crystal / 384) / Baud)
If PCON.7 is set then the baud rate is effectively doubled, thus the equation becomes:
TH1 = 256 - ((Crystal / 192) / Baud)
make this next macro work and we wont hvae to hard code the values ...
*/
#define InterruptRate 10000 // how oftin to hit our interrupt per second
#define Timer0H 0xBE //(char)((0xFF00 & (65536 - (InterruptRate / 12 * 1000))) >> 8)
#define Timer0L 0x00 //(char)(0x00FF & (65536 - (InterruptRate / 12 * 1000)))
/* prototypes */
void hw_init();
char getchar( void );
void myputchar( char c );
void doevents();
void myputs(char *);
void itoa(int value, char* string, int radix);
void uitoa(unsigned int value, char* string, int radix);
void send_version(void);
void send_hello(void);
void send_help(void);
#define UNKNOWN 0x01
#define OFFHOOK 0x02
#define ONHOOK 0x03
#define VERSION 0x04
#define EGGS 0x05
#define RESET 0x06
#define SEND_HELP 0x07
char hook_state;
char input_state;
int notdone=1;
#define ON 0x02
#define OFF 0x03
char std_state;
static char state_machine_active=0;
/* plug all of the other interrupt vectors */
#ifdef SDCC
void mydummyISR (void) interrupt 12 _naked {
}
#endif
/* Serial interrupt to track incoming key strokes */
void serial_isr(void) interrupt 4 {
if (RI != 0)
{
RI = 0;
if(SBUF == '?')
hook_state = UNKNOWN;
if(SBUF == 'V' || SBUF == 'v')
input_state = VERSION;
if(SBUF == 'R' || SBUF == 'r')
input_state = RESET;
if(SBUF == '!')
input_state = EGGS;
if(SBUF == 'H' || SBUF == 'h')
input_state = SEND_HELP;
}
return;
}
/*-------------------------------------------------------------------------
integer to string conversion
Written by: Bela Torok, 1999 in the public domain
bela.torok@kssg.ch
usage:
uitoa(unsigned int value, char* string, int radix)
itoa(int value, char* string, int radix)
value -> Number to be converted
string -> Result
radix -> Base of value (e.g.: 2 for binary, 10 for decimal, 16 for hex)
---------------------------------------------------------------------------*/
#define NUMBER_OF_DIGITS 16 /* space for NUMBER_OF_DIGITS + '\0' */
void uitoa(unsigned int value, char* string, int radix)
{
unsigned char index, i;
index = NUMBER_OF_DIGITS;
i = 0;
do {
string[--index] = '0' + (value % radix);
if ( string[index] > '9') string[index] += 'A' - ':'; /* continue with A, B,.. */
value /= radix;
} while (value != 0);
do {
string[i++] = string[index++];
} while ( index < NUMBER_OF_DIGITS );
string[i] = 0; /* string terminator */
}
void itoa(int value, char* string, int radix)
{
if (value < 0 && radix == 10) {
*string++ = '-';
uitoa(-value, string, radix);
}
else {
uitoa(value, string, radix);
}
}
/* setup UART */
void hw_init() {
LOOP_STATUS = 1; //set our loop status pin to an input
STD_STATUS = 1; //set our std status pin to an input
MODE_SWITCH = 1; //set the "ECHO" switch input on the K164 board to input
EA = 0; // disable all interrupts
PCON |= 0x80; // SMOD = 1 double speed clock for our baud rate interrupt
TH1 = TL1 = Timer1ReloadValue; // timer 1 mode 1 reload value 9600 baud as calculated in our macro
TMOD &= 0x0f; /* Set timer 1 */
TMOD |= 0x20; /* Set timer 1 as Gate=0 Timer, mode 2 */
TR1 = 1; // turn on serial timer Timer 1
SCON = 0x40; // init port as 8-bit UART with variable baudrate
SCON |= 0x10; // Enabling serial reception
// SCON |= 0x02; // Setting TI bit
ES = 1; // Enable Serial Interrupt */
/* Timer 0 setup */
TMOD &= 0xf0; /* Set timer 0 */
TMOD |= 0x01; /* Set timer 0 16 bit timer */
/* configure generic timer 0 reset value */
TH0 = Timer0H;
TL0 = Timer0L; // reload with 35711 for 1Hz
TR0 = 1; // turn on timer 0
ET0 = 1; // Enable timer 0 interrupt
RI = 0;
TI = 1;
EA = 1; // enable all interrupts
}
/* setup FIRMWARE */
void fw_init() {
/* initialize our state machine to ON HOOK */
hook_state = UNKNOWN;
input_state = UNKNOWN;
std_state = UNKNOWN;
/* Turn off our LED's we just started */
HOOK_LED = 0;
STATUS_LED = 0;
}
/* read a character from UART */
char getchar( void ) {
while(!RI);
RI = 0;
return(SBUF);
}
/* send a character to UART port */
void myputchar( char c ) {
while(!TI);
TI =0;
SBUF = c;
}
void myputs(char *sz) {
while(*sz) myputchar(*sz++);
}
/* Timer 0 interrupt the state machines main interrupt */
void timer_isr(void) interrupt 1 {
static int suppressfirst=1;
static int x=0;
static int counter=0;
char buffer[17];
/* configure generic timer 0 reset value */
TH0 = Timer0H;
TL0 = Timer0L;
/* every 1 second do our event routine */
if(x++>50) {
x=0;
doevents();
}
/* we need to control this or we will be trying to send out serial data from two threads */
if(state_machine_active) {
if( input_state == VERSION ) {
send_version();
input_state = UNKNOWN;
}
if( input_state == SEND_HELP ) {
send_help();
input_state = UNKNOWN;
}
if( input_state == EGGS ) {
myputs("! Jack Edin 1961-2012 rip - Logic Unlimited !\r\n");
myputs("! Sean Mathews - NuTech.com !\r\n");
input_state = UNKNOWN;
}
if( input_state == RESET ) {
notdone=0;
input_state = UNKNOWN;
}
/* check state of the hook line it seems to be inverted */
if(!LOOP_STATUS) {
HOOK_LED = 1; /* ON NPN Transistor base*/
if( hook_state != OFFHOOK ) {
counter++;
if(counter>10) { // 100ms
hook_state = OFFHOOK;
if(!suppressfirst) {
myputs("OFFHOOK\r\n");
} else {
suppressfirst=0;
}
}
}
} else {
HOOK_LED = 0; /* OFF NPN Transistor base*/
counter=0;
if( hook_state != ONHOOK ) {
hook_state = ONHOOK;
if(!suppressfirst) {
myputs("ONHOOK\r\n");
} else {
suppressfirst=0;
}
}
}
/* check state of the STD pin on the MT8870CE chip */
if(STD_STATUS) {
if( std_state != ON ) {
std_state = ON;
if(MODE_SWITCH==F34R_MODE) {
myputs("TONE ");
}
switch(P1 & 0x0f) {
case 10:
buffer[0]='0';
buffer[1]=0;
break;
case 11:
buffer[0]='*';
buffer[1]=0;
break;
case 12:
buffer[0]='#';
buffer[1]=0;
break;
default:
itoa(P1 & 0x0f,buffer,10);
break;
}
myputs(buffer);
if(MODE_SWITCH==F34R_MODE) {
myputs("\r\n");
}
}
} else {
if( std_state != OFF ) {
std_state = OFF;
}
}
}
}
/* Event routine for periodic processing */
void doevents() {
static char flipflop=0;
/* one second event handler. Future use...*/
/* flash the status led every 1 second */
if(MODE_SWITCH!=F34R_MODE) {
STATUS_LED = !STATUS_LED;
} else {
flipflop = !flipflop;
if(flipflop)
STATUS_LED = !STATUS_LED;
}
}
/* MAIN */
void main(void) {
notdone=1;
/* first setup our states and any other startup code so
when our hardware calls our routines they are ready */
fw_init();
/* ok now setup our hardware and start the interrupts */
hw_init();
/* tell the world we are up and running */
send_hello();
/* let the state machine go */
state_machine_active=1;
/* ... */
while (notdone) { }
// disable all interrupts
EA = 0;
// jump to 0
((void (code *)(void)) 0) ();
}
void send_hello() {
myputs("\r\n! K164mh Telephone DTMF Decoder ");
myputs(szVERSION);
myputs(" written for my good friend Jack Edin 1961-2012 rip!\r\n");
}
void send_version() {
myputs(szVERSION);
myputs("\r\n");
}
void send_help() {
myputs("\r\n! Every line that starts with a ! is considered informational\r\n!and is not part of any call logging.\r\n");
myputs("! The state messages are ONHOOK [%1], OFFHOOK, TONE %1\r\n");
myputs("! The tones can also be on the ONHOOK line if the device is in inbound calls mode\r\n");
myputs("! K164mh commands: \r\n! ? = Information\r\n! V = Version\r\n! R = Reset\r\n! H = This info\r\n");
}