#include //CMX469A preamble & syncword info #define PREAMBLE 0xAA #define UPPERSYNC 0xCB #define LOWERSYNC 0x23 //abbreviations for data types #define uchar unsigned char #define uint unsigned int void config_8051(void); //8051 initialization routine //TRANSMIT FUNCTIONS void tx(void); //main transmit routine void transmit469A(uchar y); //CMX469A transmit routine, y=data void ds1620write(uchar a); //writes bits to temperature sensor uint ds1620read(void); //reads bits from temperature sensor //RECEIVE FUNCTIONS void rx(void); //main receive routine void config_display(void); //configure display driver IC void display_digit(uchar x); //passes individual digit to display driver void start_transfer(void); //initiates display driver communications void end_transfer(void); //terminates display driver communications void send_clk(void); //display driver clock toggling routine void parse_temperature(uchar x); //formats raw temperature data for display driver void display_info(uchar h, uchar t, uchar o, uchar d);//passes entire 24-bit frame to display driver bit checkfor(uchar x); //looks for value passed to it (preamble or syncword) uchar get_bits(uchar x); //extracts raw received bits from CMX469A //TX BIT DEFINITIONS sbit TXEN = P1^3; //uC output to 469A Tx EnableN, sbit TXDATA = P1^4; //uC output to 469A Tx Data sbit DS1620RST = P3^5; //uC output to DS1620 reset pin sbit DS1620CLK = P3^4; //uC output to DS1620 clock pin sbit DS1620DQ = P3^7; //uC input/output to/from DS1620 data pin //RX BIT DEFINITIONS sbit DISPLAYENABLE = P1^7; //uC output to display driver "enable" sbit DISPLAYDATA = P1^6; //uC output to display driver "data" sbit DISPLAYCLOCK = P1^5; //uC output to display driver "clock" sbit CARRIERDETECT = P1^1; //uC input from 469A Carrier Detect O/P sbit RXDATA = P1^2; //uC input from 469A Clocked Data O/P //TX GLOBAL VARIABLES bit txflag=0; uchar timercounter=0, temperature=0x00; //RX GLOBAL VARIABLES bit rxflag=0, rxdelay=0; uchar hundreds, tens, ones, decimal; //************************************************************************ FUNCTION: void config_ds1620(void) PURPOSE: Configuration of DS1620 temperature sensor. DESCRIPTION: This function repeatedly calls a "write" routine to pass values to the DS1620 temperature sensor. These values configure the device and begin temperature conversion. The function neither takes in nor returns a variable. *************************************************************************/ void config_ds1620(void) { ds1620write(0x0C); //send "write config" command ds1620write(0x0A); //send configuration data ds1620write(0xEE); //send "start convert" command } /************************************************************************ FUNCTION: void ds1620write(uchar a) PURPOSE: Write values to the DS1620 temperature sensor. DESCRIPTION: This function receives a Byte from the calling routine that is to be written to the DS1620 temperature sensor. The function does not return a variable. The DS1620 communications are least significant bit first. *************************************************************************/ void ds1620write(uchar a) { uchar i, leastbit; DS1620CLK=0; //set low before transaction DS1620RST=1; //initiates DS1620 transaction for(i=0; i<8; ++i) { leastbit = a & 0x01; //get leastbit if(leastbit !=0) DS1620DQ=1; else DS1620DQ=0; DS1620CLK=1; DS1620CLK=0; a=a>>1; } DS1620RST=0; //terminates DS1620 transaction } /************************************************************************ FUNCTION: uint ds1620read(void) PURPOSE: Read temperature data from the DS1620 temperature sensor. DESCRIPTION: This function begins by writing the "read temperature" command to the DS1620. Please note that this could not be performed using the "ds1620write(uchar a)" function because that function ends by taking the DS1620 /RST line low. When the /RST line is pulled low, the DS1620 communication is terminated and the expected 9 bits of temperature data are not provided at the DS1620 DQ pin. Therefore, the "read temperature" command must be written and the temperature data extracted before the /RST line can be pulled low. After the "read temperature" command is written to the DS1620, 9 bits of temperature data are then read from the device. This data is contained within a two-Byte integer variable that is passed to the calling routine. The DS1620 communications are least significant bit first. *************************************************************************/ uint ds1620read(void) { bit q; uchar a=0xAA, i, leastbit; uint x=0x0000, ds1620data=0x0000; DS1620CLK=0; //set low before transaction DS1620RST=1; //initiates DS1620 transaction //send "read temperature" command first for(i=0; i<8; ++i) { leastbit = a & 0x01; //get leastbit if(leastbit !=0) DS1620DQ=1; else DS1620DQ=0; DS1620CLK=1; DS1620CLK=0; a=a>>1; } //read out data for(i=0; i<9; ++i) { q=DS1620DQ; if(q!=0) x=0x100; else x=0x000; DS1620CLK=1; DS1620CLK=0; ds1620data = ds1620data >> 1; //right shift LSB towards LSB position ds1620data = ds1620data | x; //append new bit to existing data } DS1620RST=0; //end transaction return(ds1620data); } /************************************************************************ FUNCTION: void config_8051() PURPOSE: Configure the AT89C2051 microcontroller. DESCRIPTION: The function writes ones to P1.1 and P3.7 to configure those pins as inputs; ones are also written to P3.2 & P3.3 to configure those pins for external interrupt use. IT0 and IT1 are set to configure the external interrupts as falling?edge triggered interrupts; this is done so that interrupts will be issued on CMX469A "Tx Sync O/P" and "Rx Sync O/P" signal falling edges. Timer0 & Timer1 are both configured for 16-bit timer mode. A 50ms delay is desired from Timer0, and the 11.0592MHz crystal translates to 1.09us per "count". This means that 46080 counts will create a 50ms overflow from Timer0, therefore Timer0 is loaded with 0x4C00 (65536-46080=19456d=0x4C00) to create a 50ms overflow. Interrupts are enabled for Timer1, External Interrupt 1, Timer0, and External Interrupt 0. Timer0 is turned on to begin the first one-second delay before transmission begins, and control then reverts to the calling routine. This routine neither takes in nor returns a value to the calling function. *************************************************************************/ void config_8051() { P1_1=1; //configure "CARRIERDETECT" pin as input P3 |= 0x8C; //write 1s to P3.2 & P3.3 for external interrupt use, P3.7 as input TCON |= 0x05; //set IT0 & IT1 for falling edge-triggered TMOD=0x11; //Timer1 & Timer0=Mode 1 (16-bit timer) TH0=0x4C; //load register with 0x4C=19456 (46080 counts=50ms until overflow) TL0=0x00; IE=0x8F; //enabled interrupts; global IRQ, Timer1, EX1, Timer0, EX0 TR0=1; //turn on Timer 0 } /************************************************************************ FUNCTION: void extinterrupt0(void) interrupt 0 PURPOSE: Interrupt service routine (ISR) for External Interrupt 0. DESCRIPTION: When the CMX469A "Tx Sync O/P" signal transitions from high to low, the falling edge is detected by External Interrupt 0. This causes an interrupt to occur and the program automatically vectors to this ISR. The global variable "txflag" is set to one by this ISR, and control then reverts to the previous routine. This routine neither takes in nor returns a variable. *************************************************************************/ void extinterrupt0(void) interrupt 0 //"Tx Sync" clock from 469A { txflag=1; } /************************************************************************ FUNCTION: void timer0(void) interrupt 1 PURPOSE: Interrupt service routine (ISR) for Timer0. DESCRIPTION: Timer0 has been configured to overflow once every 50ms. The Timer0 overflow causes an interrupt to occur and the program automatically vectors to this ISR. Timer0 is reloaded with 0x4C00 to allow the next 50ms delay to begin, and the global "timercounter" variable is incremented. Control then reverts to the previous routine. This routine neither takes in nor returns a variable. *************************************************************************/ void timer0(void) interrupt 1 //Timer IRQ for delay between Tx events { TH0=0x4C; //reload timer for next 50ms count TL0=0x00; ++timercounter; } /************************************************************************ FUNCTION: void extinterrupt1 (void) interrupt 2 PURPOSE: Interrupt service routine (ISR) for External Interrupt 1. DESCRIPTION: When the CMX469A "Rx Sync O/P" signal transitions from high to low, the falling edge is detected by External Interrupt 1. This causes an interrupt to occur and the program automatically vectors to this function. The global variable "rxflag" is set to one by this ISR, and control then reverts to the previous routine. This routine neither takes in nor returns a variable. *************************************************************************/ void extinterrupt1 (void) interrupt 2 //"Rx Sync" clock from 469A { rxflag=1; } /************************************************************************ FUNCTION: void timer1(void) interrupt 3 PURPOSE: Interrupt service routine (ISR) for Timer1. DESCRIPTION: Timer1 is used to insert a delay at the end of the receive ("rx") function. The reason for doing this is to ensure that the CMX469A "Carrier Detect O/P" has a chance to deassert before the "statecheck" function is next executed. Timer1 has been configured to overflow after 5ms. The Timer1 overflow causes an interrupt to occur and the program automatically vectors to this ISR. The global "rxdelay" variable is set to one, and control then reverts to the previous routine. This routine neither takes in nor returns a variable. *************************************************************************/ void timer1(void) interrupt 3 //Rx delay to prevent false entry into "rx" function { rxdelay=1; } /************************************************************************ FUNCTION: void config_display(void) PURPOSE: Configure the MC14489B display driver IC. DESCRIPTION: The MC14489B display driver IC has a one-Byte configuration register that must be properly setup before the IC can pass information to the 4-digit LED display. Bits 7 & 6 of this register determine whether the LEDs will be driven with "hex decode" mode or "special decode" mode; hex decode mode was used for this project. Bits 5..1 of this configuration register determine how each individual "bank" of LEDs will be driven by the display driver IC. Bit 0 selects either low-power or normal-power mode for all LEDs. The information is passed to the MC14489B most significant bit first. This function does not return a value to the calling function. *************************************************************************/ void config_display(void) //Config Reg 0xE1=Bank 1-4 active { uchar i, top_bit, x=0xE1; start_transfer(); for(i=0; i<8; ++i) { top_bit = x & 0x80; if(top_bit !=0) DISPLAYDATA=1; else DISPLAYDATA=0; send_clk(); x=x<<1; } end_transfer(); } /************************************************************************ FUNCTION: void parse_temperature(uchar x) PURPOSE: Prepare the received temperature data for the 4-digit LED display. DESCRIPTION: This function takes in a one-Byte variable from the calling routine that contains 8 bits of received temperature data. Operations are then performed on this data to determine the appropriate values for the global variables "hundreds", "tens", "ones", and "decimal" (e.g. *.0 or *.5). Control is then passed back to the calling routine. Note: The 9th received bit corresponds to the sign of the temperature. Since all temperatures are positive for this application, the 9th bit will always be 0 and is therefore discarded. This function does not return a value to the calling function. *************************************************************************/ void parse_temperature(uchar x) { uchar s, LSB; //check decimal digit LSB = x & 0x01; if(LSB != 0) decimal=5; else decimal=0; x=x>>1; //rightshift by 1=divide by 2 to get Celsius temp from raw data hundreds=x/100; s=x-100*hundreds; tens=x/10; ones=s-tens*10; } /************************************************************************ FUNCTION: void display_info(uchar h, uchar t, uchar o, uchar d) PURPOSE: Pass properly formatted temperature data to the MC14489B display driver IC. DESCRIPTION: This function receives variables corresponding to the hundreds ("h"), tens ("t"), ones ("o"), and decimal ("d") digits of the received temperature data. The MC14489B display driver has two internal registers; a configuration register (one Byte wide) and a display register (three Bytes wide). No special addressing is required for these two registers; the length of the data written to the device determines which register is accessed. The three-Byte write to the display register begins with a write of 0x80, which illuminates all LEDs, activates the decimal point for "bank 2", and passes zeros to "bank 5" (not used in this project). The function then passes the individual digits to the display driver via the "display_digit" function. Control is then passed back to the calling routine. The information is passed to the MC14489B most significant bit first. This function does not return a value to the calling function. *************************************************************************/ void display_info(uchar h, uchar t, uchar o, uchar d) { unsigned char z=0xA0, msb=0x00, i; start_transfer(); for(i=0; i<8; ++i) { msb = z & 0x80; //Get MSB if(msb !=0) DISPLAYDATA=1; else DISPLAYDATA=0; send_clk(); z=z<<1; } //Send each digit to display display_digit(h); //bank 4, hundreds digit display_digit(t); //bank 3, tens digit display_digit(o); //bank 2, ones digit display_digit(d); //bank 1, decimal (0 or 5) digit end_transfer(); } /************************************************************************ FUNCTION: void display_digit(uchar x) PURPOSE: Write specific temperature digit (hundreds, tens, ones, or decimal) to the MC14489B display driver IC. DESCRIPTION: This function receives a one-Byte variable from the calling routine. The received variable corresponds to either the hundreds, tens, ones, or decimal digit of the received temperature data. The data is written to the display driver IC, and control is then passed back to the calling routine. This function does not return a value to the calling function. *************************************************************************/ void display_digit(uchar x) { uchar top_bit=0x00, i; for(i=0; i<4; ++i) { top_bit = x & 0x08; if(top_bit !=0) DISPLAYDATA=1; else DISPLAYDATA=0; send_clk(); x=x<<1; } } /************************************************************************ FUNCTION: void start_transfer() PURPOSE: Configure the MC14489B display driver IC for a write cycle. DESCRIPTION: This function sets low both the clock and enable lines of the MC14489B, and control is then passed back to the calling routine. This function neither takes in nor returns a value to the calling function. *************************************************************************/ void start_transfer() { DISPLAYCLOCK=0; DISPLAYENABLE=0; } /************************************************************************ FUNCTION: void end_transfer() PURPOSE: End a write cycle to the MC14489B display driver IC. DESCRIPTION: This function sets the MC14489B clock line low and enable line high to terminate the act of writing to the device. Control is then passed back to the calling routine. This function neither takes in nor returns a value to the calling function. *************************************************************************/ void end_transfer() { DISPLAYCLOCK=0; DISPLAYENABLE=1; } /************************************************************************ FUNCTION: void send_clk() PURPOSE: Toggle the clock of the MC14489B display driver IC. DESCRIPTION: This function toggles the MC14489B clock in order to latch in the data bit present on its "Data In" pin. Control is then passed back to the calling routine. This function neither takes in nor returns a value to the calling function. *************************************************************************/ void send_clk() { DISPLAYCLOCK=1; DISPLAYCLOCK=0; } /************************************************************************ FUNCTION: void transmit469A(uchar y) PURPOSE: Write data to the CMX469A for transmission. DESCRIPTION: This function takes in a one-Byte variable that represents the data to be transmitted.. Once the CMX469A "Tx Sync O/P" transitions from high to low, the most significant data bit is extracted and written to the CMX469A "Tx Data I/P" pin. Control is then passed back to the calling routine after all bits have been passed to the CMX469A for transmission. This function does not return a value to the calling function. *************************************************************************/ void transmit469A(uchar y) //y=Tx data { uchar i, top_bit=0x00, mask=0x80; TXDATA=0; txflag=0; TXEN=0; //enable 469A transmitter for(i=0;i<8;++i) { top_bit = y & mask; //get MSB if(top_bit !=0) TXDATA=1; else TXDATA=0; while(txflag!=1); //wait for Tx Clock falling edge txflag=0; y<<=1; } TXEN=1; //disable CMX469A transmitter TXDATA=0; } /************************************************************************ FUNCTION: bit checkfor(uchar x) PURPOSE: Extract and check received bits for user-defined patterns. DESCRIPTION: This function receives a one-Byte variable that corresponds to the pattern to be searched for in the received data stream. Eight bits are obtained from the CMX469A and stored in "temp_byte". A preliminary check is made to determine if "temp_byte" matches the desired bit pattern. If a match is made, the "checkfor_flag" is set to one and a value of zero for "rxerror" is passed to the calling routine. In the case where "temp_byte" does not match the desired pattern, a new bit is obtained and appended to a bit-shifted version of "temp_byte". A counter is incremented and checked to ensure that an excessive number of bit shifts have not been performed. An excessive number of bit shifts (i.e. matching attempts) will cause the function to end and an error condition ("rxerror" = 1) will be flagged to the calling routine. The function continues checking for the desired bit pattern until (1) the pattern is detected or (2) an excessive number of matching attempts have been performed. This function returns a bit variable, indicative of the matching process success/failure, to the calling function. *************************************************************************/ bit checkfor(uchar x) //Checks for preamble or syncword { uchar temp_byte=0x00, new_bit=0x00, z=0x00; bit checkfor_flag=0, rxerror=0; temp_byte=get_bits(8); //grab bits from 469A while(checkfor_flag!=1) { if(temp_byte!=x) //if expected value not detected... { //get new bit and then check again new_bit=get_bits(1); temp_byte <<= 1; temp_byte |= new_bit; } else //if expected value is detected, get out of loop { checkfor_flag=1; rxerror=0; //no error detected } ++z; if(z>8) //error condition { checkfor_flag=1; rxerror=1; } } return(rxerror); } /************************************************************************ FUNCTION: uchar get_bits(uchar x) PURPOSE: Extract received bits from the CMX469A. DESCRIPTION: This function receives a one-Byte variable that corresponds to the number of bits to be extracted from the CMX469A. This function returns the extracted bits to the calling function. *************************************************************************/ uchar get_bits(uchar x) //Actual data reception, x is # of bits needed { uchar i=0x00, temp=0x00, rxbits=0x00; for(i=0; i19) //is Tx needed? b=2; else b=3; return(b); } /************************************************************************ FUNCTION: void rx(void) PURPOSE: Receive data from CMX469A and pass data to microcontroller for processing. DESCRIPTION: The function begins by checking the received data bits for the user-defined values of preamble, uppersync, and lowersync. If any of these values are not found in the received data stream, no data is extracted and the function ends without updating the display. If the preamble, uppersync and lowersync are all found in the received bits, the actual temperature data is received and used to update the LED display. A 5ms delay is introduced after the display update to ensure the CMX469A "Carrier Detect O/P" has deasserted before control is passed back to the main routine and the "statecheck" function is again called. This function neither takes in a value from, or passes a value to, the calling function. *************************************************************************/ void rx(void) { bit error; ET0=0; //stop Tx 1sec IRQ until Rx completes error=checkfor(PREAMBLE); if(error!=1) error=checkfor(UPPERSYNC); if(error!=1) error=checkfor(LOWERSYNC); if(error!=1) { temperature=get_bits(8); //changed from 9 to 8 bits parse_temperature(temperature); display_info(hundreds, tens, ones, decimal); rxdelay=0; TH1=0xEE; //5ms delay to ensure carrier detect deasserts before next statecheck TL1=0x00; TR1=1; while(rxdelay!=1); rxdelay=0; TR1=0; //turn off timer to prevent subsequent IRQs ET0=1; //re-enable Tx routine interrupt } else ; } /************************************************************************ FUNCTION: void tx(void) PURPOSE: Transmit data using the CMX469A. DESCRIPTION: The function begins by resetting the "timercounter" variable that is used for the one-second delay between transmissions. The temperature data is read from the temperature sensor. The CMX469A transmission begins with 16 bits of preamble (0xAAAA), 16 bits of syncword (0xCB23), and the 8 bits of temperature data. The CMX469A is capable of transmitting large data frames, but for simplicity, only 8 bits of temperature data are used in this application. This function neither takes in a value from, or passes a value to, the calling function. *************************************************************************/ void tx(void) { timercounter=0; //reset interrupt timer flag temperature=ds1620read(); //read 9 bits of temperature data transmit469A(PREAMBLE); //16 bits of preamble transmit469A(PREAMBLE); transmit469A(UPPERSYNC); //syncword transmit469A(LOWERSYNC); transmit469A(temperature); //temperature data } void main() { uchar state=0; config_display(); config_ds1620(); config_8051(); //configure processor TXEN=1; //initialize with Tx disabled while(1) { state = statecheck(); switch (state) { case 1: //Rx Routine rx(); break; case 2: //Tx Routine tx(); break; default: //do nothing break; } } }