|
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.
 |
Have a female voice
announce the time & temperature (and pretty much anything else) every hour
|
 |
Trigger alarms based on
the time or temperature
|
 |
Display OUCH as an alarm
message on the SLED4C display
|
 |
Learn how to use the
DS1620 with CCS C
|
 |
Learn to use the DS1307
with CCS C
|
 |
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
|