PicBasic Pro, PicBasic, BASIC Stamp, Microchip PIC, 8051, and Remote Control Projects


How To Build a
Talking Time & Temperature Display With The CCS C Compiler

I had an "incredibly" fun time putting this particular project together, and I hope you do too. This project shows how to build a talking clock with time & temperature display + it includes several handy C functions you may find useful for other embedded projects down the road.

 I think you're only limited by your imagination with this one, but here are just a few of the fun things you can do with this project.

bullet

Have a female voice announce the time & temperature (and pretty much anything else) every hour

bullet

Trigger alarms based on the time or temperature

bullet

Display OUCH as an alarm message on the SLED4C display

bullet

Learn how to use the DS1620 with CCS C

bullet

Learn to use the DS1307 with CCS C

bullet

Learn how to use the EMIC text-to-speech module with CCS C

If you're a beginner with the CCS C compiler, take your time to go over the various functions. C isn't much harder to learn than BASIC, but it does take more time to get familiar with.

Although I used the PIC16F876A for this project, this code can easily be ported to another PIC, and it's quite small when compiled as-is.  For the PIC16F876A this compiles to approximately 1.36K using the CCS PCM compiler version 3.206.

If you've ever used a serial LCD, then you know how convenient they are. LCDs are great, but it's almost impossible to see an LCD display from a few feet away. If the LCD is exposed to direct sunlight or bright ambient light, forget it. This is when a 7-segment LED display really shines. The SLED4 offers the convenience of serial control with a bright and easy to see display in any lighting condition.


Figure#1: SLED4 Pin Diagram

The 20K potentiometer shown above in figure #1 is optional, and may be replaced with a CDS photocell, a fixed resistor, or eliminated altogether. A photocell will react to changes in ambient lighting, and automatically dim the display in darkness, or brighten it in daylight.

If you don't need automatic display brightness control, simply leave pin #1 un-connected or use the equation below to calculate a fixed resistor value for your preferred brightness level.

The SLED4C has an onboard 10K 1% resistor to set the default display brightness to an acceptable level under most normal indoor lighting conditions. To change this, place a resistor from ground to pin #1 on the SLED4C. This will place the external resistor in parallel to the onboard 10K resistor.

The SLED4C uses a 4-digit common cathode Super RED 7-segment display module. Care should be taken to not exceed 25mA per LED segment to avoid damaging the LED display. We recommend the use of a potentiometer or CDS photocell, but the equation below will aid in selecting a fixed value resistor if required.

Example:

To set the SLED4C peak LED segment current value to ~18mA, use a 2K 1% resistor connected between ground and pin #1 for an Rt (resistance total) value of ~1.6K.

Note: When placing an external display intensity control resistor between the display pin #1 and ground, the external resistor will be in parallel with the onboard SLED4C 10K 1% Rx resistor. The following equation may be used to determine the total parallel resistance.

Rt = Parallel resistance total
R1 = SLED4C onboard 10K 1% Rx resistor
R2 = External resistor connected between the SLED4C Rx input pin #1 and ground

Rt = R1 x R2 / R1 + R2. To set the approximate maximum current at 18mA per segment, use an external resistor value of 2K.
Rt = 10K x 2K / 10K + 2K = 20,000,000 / 12,000 = 1.6K. As shown in figure 2, 1.6K results in ~18mA peak segment drive current.


Figure#2: RX Resistor VS LED Peak Current Levels

This project shows how to use the EMIC text-to-speech module, DS1620 temperature sensor & DS1307 RTC for a digital talking time & temperature display.


Figure#3: EMIC Text-To-Speech Module Pin-Out

Wire the PIC16F876A pin #11 labeled RSIN to the EMIC pin labeled SIN. Wire the PIC pin #12 labeled RESET to the EMIC pin labeled /RESET. We're not using any of the other EMIC pins for this project. The remainder of the circuit is shown below in Figure #4.


Figure#4: SLED4 Time & Date Controller Schematic

How This Stuff Works:

I recommend you download the SLED4C display datasheet located  HERE and EMIC text-to-speech module docs HERE to follow along with the explanations & C source code below. The datasheets cover a lot of additional details  not shown on this project page.


Figure#5: SLED4  LED Display Connections

As shown above in figure #5, the 4-digit common cathode LED clock display module is wired to the display controller a, b, c, d, e, f, g and dp cathode drivers. The leftmost LED digit is labeled #5. The rightmost LED digit is labeled #2. LED digits 5 through 2 are controlled by the anode "bank" drivers. The bank 1 anode driver controls the clock display colon (labeled L1 and L2), and the third upper right-hand decimal point LED labeled L3.

Writing data to the leftmost LED digit (Bank 5) requires sending code to the display register for the Bank 5 digit position. Writing data for display on the rightmost LED digit or Bank 2, we send data for Bank 2. The colon LEDs and upper right hand decimal point LED labeled L3 are turned ON or OFF by writing data to the Bank 1 register. Here's how it works.

The Configuration Register

The configuration register requires a single byte value to configure the decode mode, and control the power-down/normal power modes. The decode mode for each bank driver determines how data will be displayed on each digit of the LED display.

Example: If the display driver IC receives a hex value of $A for display on the LED digit connected to Bank 5, and Bank 5 is in HEX decode mode, then the LED connected to Bank 5 will display the capital letter A. If the display driver receives a hex value of $A for Bank 5, and Bank 5 is configured for "special decode" mode, then the LED digit connected to Bank 5 will display the special character which is the capital letter U. The chart in the datasheet HERE shows the characters for all three decode modes.

This byte sized configuration value has to be sent to the SLED4C before sending data to the display register. This configuration byte consists of 7 bits of decode mode data + 1 bit to control the display power-down or normal power mode. Refer to figure #6 below for details. Refer to the datasheet for the different decode modes, and characters associated with each mode.


Figure#6: Display Driver Configuration Register

You will notice how the configuration byte is loaded with the correct configuration bits in the samples below. Depending on the data we want to display, we need to first configure the display to decode data for display on each bank before sending the actual data to be displayed. Look at the short example below while referring to figure #5 above.

Note: Something else you may find interesting about the power-down mode is that whatever data was displayed on the LEDs at the time you entered power-down mode, will be restored when you return to normal power mode. This is a really neat feature of the SLED4C display. It maintains the contents of the Display Register and restores the display to the same state on return to normal power mode.

The Main Code:

/*

File name: SLED4C_1620a.c

Talking time & temp with display of time & temperature on the
SLED4C serial LED display module. Uses the DS1620 temp sensor,
DS1307 RTC, and EMIC text-to-speech module

When the temperature >= the high alarm setpoint, display the OUCH
message. Below setpoint, display the temp normally with voice
updates of time & temp on every hour.

MAX temperature range is from 0 to 99

*/

#include <SLED4C_1620a.h>          // header file & function prototypes

void Main(void)
{
   int announce=0;                 // holds # of time/temp announcements

   init();                         // initialize I/O & peripherals

   while(1)                        // loop forever
   {
     read_time();                  // read & display DS1307 time
     read_temp();                  // read & display DS1620 temp

     // announce time/temp on roll-over from 59 to 00 minutes
     if(ds1307_regs[MINUTES_REG]==0x00)
     {
       if(!announce)               // say time & temp only if announce=0
       {
         say_time();               // say the current time
         delay_ms(3000);           // delay 3 seconds
         say_temp();               // say the current temperature
         announce +=1;             // increment announcement counts
       }
     }

     if(!ds1307_regs[MINUTES_REG]==0x00)
        announce=0;                // clear announcements until it's time
   }
}

// initialize hardware
void init(void)
{
   port_b_pullups(FALSE);          // portb internal pull-ups off
   setup_comparator(NC_NC_NC_NC);  // all comparators = off
   disable();                      // SLED4C enable pin must idle high
   output_bit(CLK,0);              // SLED4C clock pin must idle low
   disable_interrupts(GLOBAL);     // global interrupts off
   setup_EMIC(2,1);                // set EMIC volume 0=low 7=max & pitch 0=low to 6=high
   set_time();                     // set ds1307 time on power-up
   init_1620();                    // inititialize DS1620 temp sensor
   setpoint=0x85;                  // set high temp alarm setpoint here
}

// shift out 8-bits in DAT MSB first
void send_byte(int DAT)
{
   int n;
   for(n=0; n<8; n++)              // loop for 8-bits total
   {
     output_bit(DOUT,DAT & 0x80);  // output MSB of 8-bit value
     output_bit(CLK, 1);           // clock pin = 1
     output_bit(CLK, 0);           // clock pin = 0
     DAT = DAT << 1;               // shift lower bits left into MSB of byte
   }
}

// shift out low nibble in NIB MSB first
void send_nib(int NIB)
{
   int n;
   for(n=0; n<4; n++)              // loop for 4-bits total
   {
     output_bit(DOUT,NIB & 0x08);  // output MSB of 4-bit value
     output_bit(CLK, 1);           // clock pin = 1
     output_bit(CLK, 0);           // clock pin = 0
     NIB = NIB << 1;               // shift lower bits left into MSB of nibble
   }
}

// take SLED4C enable pin low
void enable(void)
{
   output_bit(EN,0);               // this enables data entry into SLED4C
}                                  // onboard config & data display registers

// take SLED4C enable pin high
void disable(void)
{
   output_bit(EN,1);               // this transfers data to the SLED4C internal
}                                  // registers then disables further data entry

// write a byte to the DS1620
void write_1620(byte cmd)
{
   byte i;

   for(i=0; i<8; i++)
   {
     output_low(CLKD);
     output_bit(DQ, shift_right(&cmd,1,0));
     output_high(CLKD);
   }
}

// initialize the DS1620
void init_1620(void)
{
   // set CLK and RST to correct states
   output_high(CLKD);
   delay_us(2);
   output_low(RST);

   // set DS1620 config to continuous convert
   // and CPU communications mode
   output_high(RST);
   write_1620(0x0C);           // send Write Config command
   write_1620(0x02);           // configure for CPU mode, continuous convert
   output_low(RST);

   // start conversion
   output_high(RST);
   write_1620(0xEE);           // send Start Convert command
   output_low(RST);
}

// read & display temperature from DS1620.
void read_temp(void)
{
   byte i;

   output_high(RST);
   write_1620(0xAA);          // send Read Temp command

   temp_data = 0;             // clear temp_data on entry

   for(i=0; i<9; i++)         // loop for 9-bits
   {
     output_low(CLKD);
     temp_data = temp_data >> 1;
     if (input(DQ))
     {
        temp_data = temp_data | 0x100;
     }
     output_high(CLKD);
   }

   temp_data = temp_data/2;      // convert temp_data to whole deg C

   output_low(RST);

   convert();                    // convert C to F then to BCD

   if (temp_r >= setpoint)       // if temp >= setpoint display OUCH
   {
      ouch();                    // call OUCH function if temp >= high setpt
      return;                    // do not update temp display when temp >= setpt
   }

   enable();                     // enable SLED4C data entry
   send_byte(0b11001011);        // digits 5,4,2 HEX decode. Digits 3,1 special
   disable();                    // write config value into config register

   delay_ms(1);                  // short pause between config & display reg write

   enable();                     // enable SLED4C data entry
   send_nib(0x00);               // DIM display + DP's off
   send_byte(temp_r);            // display temp on digits 5/4
   send_byte(0xFF);              // display Deg F on digits 3/2
   send_nib(0x00);               // turn colon off
   disable();                    // write data into display registers
   delay_ms(2000);               // wait 2 seonds between temp & time updates
}

// convert temp from deg C to deg F, then pass
// to BCD conversion conversion function
void convert(void)
{
   temp_data = (temp_data * 9/5)+32; // convert C to F
   temp_r = toBCD(temp_data);        // convert to BCD for display
}

// display OUCH when temp >= high alarm setpoint
void ouch(void)
{
   int y = 8, n;

   enable();                     // enable data entry
   send_byte(0b11010111);        // digits 5,3 hex decode. 4,2,1 special
   disable();                    // write config data into config register

   for(n=0; n<8; n++)            // execute ouch display function 8 times
   {
      y ^= 8;                    // toggle y.bit.3
      enable();                  // enable SLED4C data entry
      send_nib(y);               // toggle DIM & BRIGHT, no DP's
      send_byte(0x0A);           // display OU on digits 5/4
      send_byte(0xC2);           // display CH on digits 3/2

      send_nib(0x00);            // turn colon off
      disable();                 // write display data into display registers
      delay_ms(250);             // set display "blink loop" delay time in mS
   }
}

// this function converts an 8 bit binary value
// to an 8 bit BCD value. Input range from 0 to 99.

char toBCD(char bin_val)
{
   char temp;
   char retval;

   temp = bin_val;
   retval = 0;

   while(1)
   {
   // get tens digit by multiple subtraction
   // of 10 from bin_val
      if(temp >= 10)
      {
         temp -= 10;
         retval += 0x10; // increment tens digit
      }
         else            // get ones digit by adding remainder
      {
         retval += temp; // adjusted result
         break;
      }
   }
         return(retval);
}

// set time & date on power-up
void set_time(void)
{
   char i;

   // put initial date/time into array
   ds1307_regs[SECONDS_REG]     = 55;// seconds
   ds1307_regs[MINUTES_REG]     = 59;// minutes
   ds1307_regs[HOURS_REG]       = 21;// hour
   ds1307_regs[DAY_OF_WEEK_REG] = 0; // not used
   ds1307_regs[DATE_REG]        = 14;// date
   ds1307_regs[MONTH_REG]       = 07;// month
   ds1307_regs[YEAR_REG]        = 04;// year

   // convert to BCD

   for(i=0; i<7; i++)
   {
      ds1307_regs[i] = toBCD(ds1307_regs[i]);
   }

   ds1307_regs[SECONDS_REG] &= 0x7f;
   ds1307_regs[HOURS_REG] &= 0x3f;

   // write 7 bytes of BCD data to ds1307
   i2c_start();
   i2c_write(WRITE_CNTRL);

   // start at seconds register
   i2c_write(SECONDS_REG);

   // write 7 bytes to registers 0 to 6
   for(i=0; i<7; i++)
   {
      i2c_write(ds1307_regs[i]);
   }
   i2c_write(CONTROL_INIT_VAL);
   i2c_stop();
}

// read time & date from DS1307
void read_time(void)
{
   i2c_start();
   i2c_write(WRITE_CNTRL);

   // start i2c read at seconds register
   i2c_write(SECONDS_REG);

   i2c_start();
   i2c_write(READ_CNTRL);

   // read the 7 bytes from the ds1307. Mask off the unused bits
   ds1307_regs[SECONDS_REG]     = i2c_read() & 0x7f;
   ds1307_regs[MINUTES_REG]     = i2c_read() & 0x7f;
   ds1307_regs[HOURS_REG]       = i2c_read() & 0x3f;
   ds1307_regs[DAY_OF_WEEK_REG] = i2c_read() & 0x07;
   ds1307_regs[DATE_REG]        = i2c_read() & 0x3f;
   ds1307_regs[MONTH_REG]       = i2c_read() & 0x1f;
   ds1307_regs[YEAR_REG]        = i2c_read(0);

   i2c_stop();

   enable();                     // enable SLED4C data entry
   send_byte(0b11000001);        // all banks HEX decode
   disable();                    // write config value into config register

   enable();                     // enable SLED4C data entry
   send_nib(0x00);               // DIM display + DP's off
   send_byte(ds1307_regs[HOURS_REG]);   // display hour on banks 5/4
   send_byte(ds1307_regs[MINUTES_REG]); // display minutes on banks 3/2

   send_nib(0x02);               // turn colon on
   disable();                    // write data into display registers
   delay_ms(2000);               // wait 2 seconds between temp & time updates
}

// say the time
void say_time(void)
{
   printf("say=the time iz %X  %X;",ds1307_regs[HOURS_REG],ds1307_regs[MINUTES_REG]);
   delay_ms(2000);          // allow time for message to play
}                           // adjust as required for message length

// say the temperature
void say_temp(void)
{
   printf("say=the temperature iz %Ld degrees faren hyte;",temp_data);
   delay_ms(2000);          // allow time for message to play
}                           // adjust as required for message length

// initialize EMIC module & set volume + pitch
void setup_EMIC(int vol, int pitch)
{
   output_bit(EM_Rst,0);     // hard reset the EMIC module
   delay_us(200);            // wait a minimum of 100uS
   output_float(EM_Rst);     // float reset output to release
   delay_ms(1000);           // wait for EMIC OK response before proceeding
   printf("volume=%d;",vol); // send volume setting
   delay_ms(1000);           // wait for EMIC OK response before proceeding
   printf("pitch=%d;",pitch);
   delay_ms(1000);
}


The Header File:
/*
File name: SLED4C_1620a.h
Header file for SLED4C_1620a.c

*/

#include <16f876A.h>
#use delay(clock=20000000)
#use rs232(baud=2400, xmit=PIN_C0)
#use i2c(Master, SDA=PIN_B0, SCL=PIN_B1)


// Define DS1620 interface pins
#define DQ PIN_B2     // Wire rb2 to pin #1 on DS1620
#define CLKD PIN_B3   // Wire rb3 to pin #2 on DS1620
#define RST PIN_B4    // Wire rb4 to pin #3 on DS1620

// Define SLED4C interface pins
#define EN PIN_B5     // Wire rb5 to enable pin #5 on SLED4C
#define CLK PIN_B6    // Wire rb6 to clock pin #4 on SLED4C
#define DOUT PIN_B7   // Wire rb7 to data pin #3 on SLED4C

// DS1307 control bytes
#define WRITE_CNTRL 0xd0
#define READ_CNTRL 0xd1

// DS1307 register offsets
#define SECONDS_REG 0
#define MINUTES_REG 1
#define HOURS_REG 2
#define DAY_OF_WEEK_REG 3
#define DATE_REG 4
#define MONTH_REG 5
#define YEAR_REG 6
#define CONTROL_REG 7
#define DATE_TIME_BYTE_COUNT 7
#define DS1307_NVRAM_START_ADDR 8
#define CONTROL_INIT_VAL 0x80

// EMIC text to speech constants
#define EM_Rst PIN_C1       // EMIC voice module reset pin #14


// Global vars
signed long temp_data;      // raw temp
int8 temp_r;                // final BCD temp result
int setpoint;               // high temp setpoint

// PIC I/O & peripheral init function prototype
void init(void);            // PIC port & peripheral initialization

// SLED4C serial display function Prototypes
void send_byte(int DAT);    // send byte value to SLED4C
void send_nib(int NIB);     // send nibble value to SLED4C
void enable(void);          // enable data entry into SLED4C registers
void disable(void);         // write data into SLED4C registers & disable

// DS1620 & temp related function prototypes
void write_1620(byte cmd);  // write to DS1620
void init_1620(void);       // initialize DS1620
void read_temp(void);       // read temp from DS1620
void convert(void);         // convert C to F then to BCD for display
void ouch(void);            // display OUCH when >= high temp setpoint
char toBCD(char bin_val);   // conversion to BCD

// DS1307 function prototypes
void set_time(void);
void read_time(void);
char read_byte(char addr);
void write_byte(char addr, char value);

//EMIC text-to-speech module function prototypes
void say_time(void);
void say_temp(void);
void setup_EMIC(int vol, int pitch);

int ds1307_regs[DATE_TIME_BYTE_COUNT];

dimensions in mm

You can purchase the new SLED4C serial seven-segment display modules HERE
Purchase the EMIC text-to-speech module HERE

Until the next project - have fun - and don't blow anything up...;o]

Regards,

-Bruce


Back to the PicBasic Projects Section

Copyright © 1999-2008 Reynolds Electronics

| Contact Information |