/* ********************************************************************** ** Program: Rtcseg5e.c - Version : 1.4 - 02 October 2003 ** ** Compiler: uC/51 Ver. 1.10.10 ** ** Board: K51-AVR and 89c51 or compatible ** ** Firm: grifo(r) ITALIAN TECHNOLOGY ** ** Via Dell' Artigiano 8/6 40016 San Giorgio di Piano (BO) ** ** Tel.+39 051 892 052 Fax +39 051 893 661 ** ** http://www.grifo.com http://www.grifo.it ** ** sales@grifo.it tech@grifo.it grifo@grifo.it ** ** ** ** Written by: Graziano Gaiba ** ********************************************************************** This program allows to show the RTC or Real Time Clock on IC7 (PCF8583) to the four 7 segments displays: To set the RTC values keys T2 and T3 are used, in detail key T2 increments the hours and T3 increments the minutes. Whenever one of the two keys is pressed the seconds are reset. Key T1 switches between visualization of seconds and hours. Whenever a key is pressed, an acoustic signal is emitted. Date and eventual alarm are not managed. 05.06.2000 by Adriano Pedrielli (for K51 alone) (Original version in BASCOM 8051) 02.10.2003 by Graziano Gaiba Translation in uC/51. */ #include #include #include #define FALSE 0x00 // Boolean values #define TRUE 0xFF #define LF 0x0A #define CRET 0x0D /*************************** Global variables *****************************/ char unsigned bit d_dot; // Status of decimal point of DY2 near unsigned char secondi; // Last value of seconds char unsigned bit secondi_ora; // Show hour or seconds /***************** Addresses list for Saa1064 ***********************/ #define Saa1064 0x38 // Slave address SAA1064 #define Wsaa1064 0x70 // Slave address SAA1064 in Write #define Rsaa1064 0x71 // Slave address SAA1064 in Read #define Ctb 0x00 // Ind. Control byte #define Dig1 0x01 // Ind. Digit 1 #define Dig2 0x02 // Ind. Digit 2 #define Dig3 0x03 // Ind. Digit 3 #define Dig4 0x04 // Ind. Digit 4 /************************************************************************/ #define Rtc 0x50 // Slave address RTC PCF8583 #define Wrtc 0xA0 // Slave address RTC PCF8583 in Write #define Rrtc 0xA1 // Slave address RTC PCF8583 in Read // Inizialization od SAA1064 #define saa1064_init_length 0x06 // Inizialization sequence length const unsigned char saa1064_init[]={ Ctb, // Points control register 0x27, // = 0b00100111 // bit0 =1 dynamic mode // bit1 =1 digit 1+3 not blanked // bit2 =1 digit 2+4 not blanked // bit3 =0 no test segment // bit4 =0 no 3mA segment current // bit5 =1 6mA segment current // bit6 =0 no 12mA segment current // bit7 =0 indifferent 0, // writes DY1 off 0, // writes DY2 off 0, // writes DY3 off 0 // writes DY4 off }; // Codes to convert a figure from 0 to F in 7 segments coding const unsigned char cifra_7seg[]={0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F, 0x77,0x7C,0x39,0x5E,0x79,0x71}; // I2C BUS management globals bit unsigned char SDA @ 0xB6; // Pin SDA=P3.6 bit unsigned char SCL @ 0xB7; // Pin SCL=P3.7 // Management of keys and buzzer bit unsigned char T1 @ 0x90; // P1.0 bit unsigned char T2 @ 0x91; // P1.1 bit unsigned char T3 @ 0x92; // P1.2 bit unsigned char BUZ @ 0xB5; // P3.5 /********* General utilities and hardware section management ***********/ void iniser(unsigned long baud) /* Initialize serial line with: Bit x chr = 8 Stop bit = 1 Parity = None Baud rate = baud using timer 1 as baud rate generator. */ { SCON=0x052; // Mode 1, enable receiver TMOD&=0x00F; // Timer 1 in auto-reload TMOD|=0x020; TR1=0; // Stop TIMER 1 TH1=256-((2*11059200)/(384*baud)); // baud 14.7456 MHz PCON=PCON|0x080; // SetSMOD=1 for higher baud rates TR1=1; // Start TIMER 1 } void clrscr(void) /* Clear the screen of a generic console */ { unsigned char r; putc(CRET); for (r = 0 ; r < 25 ; r++) { putc(LF); // Sends 25 Line Feed } //endfor } void waitkey(void) /* Shows a message and waits for a key pressed */ { printf("hit a key to continue.."); getc(); puts(""); } void ritardo(unsigned int rit) /* Performs a software delay of rit milliseconds, calibrated on a CPU Clock 11 MHz. */ { unsigned int r,rit1ms; rit1ms=100; // Sperimental value for delay of 1 msec. with 80c32 do { for (r=0 ; r0); } void riti2c(void) /* Delay for I2CBUS synchronous communication. */ { #asm nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop #endasm } void starti2c(void) /* Start sequence for I2C BUS */ { SCL = 0; // Start sequence SDA = 1; riti2c(); SCL = 1; SDA = 0; riti2c(); SCL = 0; } void stopi2c(void) /* Stop sequence for I2C BUS */ { SCL = 0; // Stop sequence SDA = 0; riti2c(); SCL = 1; SDA = 1; riti2c(); SCL = 0; } void wri2c_bit(unsigned char i2cbit) /* Serializes bit D0 of i2cbit on I2C BUS */ { SCL = 0; // Set SDA and generates positive impulse on SCL SDA = i2cbit; riti2c(); SCL = 1; riti2c(); SCL = 0; } unsigned char rdi2c_bit(void) /* Deserializes a bit from I2C BUS and stores it in D0 of returned value*/ { unsigned char biti2c; SDA = 1; // Prevents conflicts on SDA acquisition SCL = 0; // Assures status of SCL riti2c(); SCL = 1; // Generates positive impulse on SCL and reads SDA biti2c = SDA; riti2c(); SCL = 0; return biti2c; } void wri2c_byte(unsigned char i2cbyte) /* Serializes byte i2cbyte to I2C BUS */ { unsigned char b; for (b = 1; b <= 8; b++) { if ((i2cbyte & 0x80) == 0) // Test and set bit b wri2c_bit(0); else wri2c_bit(1); i2cbyte = i2cbyte << 1; } } unsigned char rdi2c_byte(void) /* Deserializes byte from I2C BUS and stores it in returned value */ { unsigned char b,tmp; tmp = 0; for (b = 1; b <= 8; b++) { tmp = tmp << 1; tmp = tmp | rdi2c_bit(); // Reads and stores bit b } return tmp; } unsigned char wr_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char i2cdat) /* Writes byte i2cdat to address i2caddr of I2C BUS device with slave address in i2csla. Returns boolean flag indicating result of operation: 0=OK,1=error. */ { unsigned char i2cris; i2cris = 0; // Set OK as result starti2c(); // Start sequence wri2c_byte(i2csla); // Slave address+W i2cris = i2cris | rdi2c_bit(); // Check ACK on slave address+W wri2c_byte(i2cadd); // Address i2cris = i2cris | rdi2c_bit(); // Check ACK on address wri2c_byte(i2cdat); // Data i2cris = i2cris | rdi2c_bit(); // Check ACK on data stopi2c(); // Stop sequence return i2cris; // Result of operation } unsigned char rd_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char *i2cdat) /* Reads byte and returns it as result from address i2caddr of I2C BUS device with slave address in i2csla. Returns boolean flag indicating result of operation: 0=OK,1=error. */ { unsigned char i2cris; i2cris = 0; // Set OK as result starti2c(); // Start sequence wri2c_byte(i2csla); // Slave address+W i2cris = i2cris | rdi2c_bit(); // Check ACK on slave address+W wri2c_byte(i2cadd); // Address i2cris = i2cris | rdi2c_bit(); // Check ACK on address starti2c(); // Start sequence wri2c_byte(i2csla | 0x01); // Slave address+R i2cris = i2cris | rdi2c_bit(); // Check ACK on slave address+R *i2cdat = rdi2c_byte(); // Reads data stopi2c(); // Stop sequence return i2cris; // Result of operation } // Send to indicated slave address (as first parameter) number of bytes indicated // (as third parameter) stored from address indicated (as second parameter). // If number of bytes is 0, it does nothing. // Returns boolean flag indicating result of operation: // 0=OK,1=error. unsigned char i2c_invia(unsigned char slave, unsigned char *dati, unsigned char ndati) { unsigned char i2cris, i; if(! ndati) // If ndati is zero, exits return 0; i2cris=0; starti2c(); // Start sequence wri2c_byte(slave); // Slave address+W i2cris|=rdi2c_bit(); // Check ACK on slave address+W i=0; while(ndati--) // Send ndati bytes { wri2c_byte(dati[i++]); i2cris|=rdi2c_bit(); // Check ACK } stopi2c(); // Stop sequence return i2cris; // Return result } // Send to indicated slave address (as first parameter) number of bytes indicated // (as third parameter) stored from address indicated (as second parameter),then // expects to receive as answer indicated number of bytes (fourth parameter) and // stores them starting from address previously specified in second parameter. // Returns boolean flag indicating result of operation: // 0=OK,1=error. unsigned char i2c_ricevi(unsigned char slave, unsigned char *dati, unsigned char ndati_out, unsigned char ndati_in) { unsigned char i2cris, i; if((! ndati_in) && (! ndati_out)) // Se ndati_in e ndati_out sono zero,esce return 0; i2cris=0; // Invia i dati starti2c(); // Start sequencet wri2c_byte(slave); // Slave address+W i2cris|=rdi2c_bit(); // Check ACK on slave address+W i=0; while(ndati_out--) // Send ndati_out bytes { wri2c_byte(dati[i++]); // Send data i2cris|=rdi2c_bit(); // Check ACK } // Riceve i dati starti2c(); wri2c_byte(slave | 0x01); // Slave address+W i2cris|=rdi2c_bit(); // Check ACK on slave address+R i=0; while(ndati_in--) // Reads ndati_in bytes { dati[i++]=rdi2c_byte(); // Get data if(ndati_in) { wri2c_bit(0); // Send ACK } else { wri2c_bit(1); // Last data read, send NACK } } stopi2c(); // Stop sequence return i2cris; // Return the result } void demo_init(void) { unsigned char i; // Wait SAA1064 to turn on do { starti2c(); wri2c_byte(Rsaa1064); rdi2c_bit(); i = rdi2c_byte(); wri2c_byte(1); // Not Acknowledge stopi2c(); } while(i); // Send SAA1064 initialization sequence i2c_invia(Wsaa1064, saa1064_init, saa1064_init_length); } unsigned char bintobcd(unsigned char d) /* Procedure to transform a 'decimal' byte (0-99) in BCD format */ { d=((d/10)<<4)|(d%10); return d; } unsigned char bcdtobin(unsigned char d) /* Procedure to transform a BCD coded byte (0-99) in its decimal value */ { d=((d>>4)*10)+(d&0x0F); return d; } /******** Converts a figure in range from 0 to 9 in 7 segments ******** This procedure converts a figure in range from 0 to 9 in 7 segments format, if value is greater than 9 the display is off. Parameters: Input : value in the range from 0 to 9 Outout : value in 7 segments format. ************************************************************************/ unsigned char digit(unsigned char n) { if(n < 10) { return cifra_7seg[n]; // reads value from table } else { return 0; // If value greater than 9 turn off display } } /***************** Converts a BCD number in two figures *************** This procedure convers a BCD number fro 0 to (DEC 0-99) in two separate figures and returns its decimal value, to represent it. If number is above 153, the two figures are assigned value 10, which means display OFF. Parameters: Input: v, value from 0 to 153 BCD ( Decimal 0..99) Output: d, figure of decines u, figure of units v, decimal value of BCD ************************************************************************/ void cifre(unsigned char *v, unsigned char *d, unsigned char *u) { if(*v < 154) // if lower than 154 BCD( 100 in DEC) { *d = *v >> 4; // Get figure of decines *u = *v & 0x0F; // Get figure of units *v = *d *10 + *u; // Get decimal value } else { *d = 10; // Turn OFF decines *u = 10; // Turn OFF units *v = 0; // Reset decimal value } } /*********************** Visualize two BCD numbers *********************** This procedure allows to show two BCD numbers whose decimal representation is made of two figures. E.g.: BCD 153= Dec. 99 Decimal point on DY2 toggles every second. Parameters: Input: N1 as byte, first number N2 as byte, second number Output: nothing ************************************************************************/ void vis_2num(unsigned char n1, unsigned char n2) { unsigned char v, d, u, d1, u1; v=n1; cifre(&v, &d, &u); // Decomposition in two BCD figures if(! d) // Turn off figure if 0 { d=10; } v=n2; cifre(&v, &d1, &u1); starti2c(); // Start sequence wri2c_byte(Wsaa1064); // Slave address rdi2c_bit(); // Check ACK on slave address+R wri2c_byte(Dig1); // First digit rdi2c_bit(); wri2c_byte(digit(d)); // Writes in DY1 most significant nibble rdi2c_bit(); // Writes next nibble in DY2 and manages decimal point wri2c_byte(digit(u) | (d_dot ? 0x80 : 0x00)); rdi2c_bit(); // Decomposition in 2 nibbles of low part wri2c_byte(digit(d1)); // Write in DY3 most significant nibble rdi2c_bit(); wri2c_byte(digit(u1)); // Writes next nibble in DY4 rdi2c_bit(); stopi2c(); // Stop sequence } // Activate buzzer for specified time, in milliseconds void buzzer(unsigned int t) { BUZ=0; ritardo(t); BUZ=1; } // Performs debouncing on T1 and returns its status: // 1 if pressed // 0 if not pressed unsigned char debounceT1(void) { unsigned char status; unsigned int steps; if(T1) // If T1 not pressed... { return 0; // ...return it is not pressed } else { status=1; // Indicates that it is pressed steps=20; // Check for 20 times while(status) { if(T1) // If T1 not pressed... { status=0; // ...exit loop } if(! steps--) // If steps controls made... { break; // ...exit } ritardo(5); // Delay between controls } return status; // Returns status of T1 } } // Performs debouncing on T2 and returns its status: // 1 if pressed // 0 if not pressed unsigned char debounceT2(void) { unsigned char status; unsigned int steps; if(T2) // If T2 not pressed... { return 0; // ...return it is not pressed } else { status=1; // Indicates that it is pressed steps=20; // Check for 20 times while(status) { if(T2) // If T2 not pressed... { status=0; // ...exit loop } if(! steps--) // If steps controls made... { break; // ...exit } ritardo(5); // Delay between controls } return status; // Restituisce lo stato di T2 } } unsigned char debounceT3(void) { unsigned char status; unsigned int steps; if(T3) // If T3 not pressed... { return 0; // ...return it is not pressed } else { status=1; // Indicates that it is pressed steps=20; // Check for 20 times while(status) { if(T3) // If T3 not pressed... { status=0; // ...exit loop } if(! steps--) // If steps controls made... { break; // ...exit } ritardo(5); // Delay between controls } return status; // Restituisce lo stato di T3 } } void getrtc(unsigned char *ore,unsigned char *min,unsigned char *sec) /* Fetch date and time from RTC and returns them in function parameters */ { unsigned char dummy; rd_i2c(Wrtc,0x02,sec); // Read seconds rd_i2c(Wrtc,0x03,min); // Read minutes rd_i2c(Wrtc,0x04,ore); // Read hours rd_i2c(Wrtc,0x05,&dummy); // Read day, year rd_i2c(Wrtc,0x06,&dummy); // Read day of week and month } void setrtc(unsigned char ore,unsigned char min,unsigned char sec) /* Set RTC with data passed in parameters, in military format, and enables an interrupt each second. */ { wr_i2c(Wrtc,0x00,0x84); // Stop wr_i2c(Wrtc,0x01,0x00); // Set hundreds of seconds wr_i2c(Wrtc,0x02,bintobcd(sec)); // Set seconds wr_i2c(Wrtc,0x03,bintobcd(min)); // Set minutes wr_i2c(Wrtc,0x04,bintobcd(ore)); // Set hours, militarty format wr_i2c(Wrtc,0x05,(bintobcd(1)|(1<<6))); // Set day and year wr_i2c(Wrtc,0x06,(bintobcd(1)|(1<<5))); // Set month and week wr_i2c(Wrtc,0x07,0x00); // Set timer wr_i2c(Wrtc,0x00,0x00); // Start } void main(void) { unsigned char ore,min,sec; // Variables for RTC ritardo(2); iniser(19200); TI=0; RI=0; // Comment next lines if using serial line in polling (SIOTYPE=p or k) ES=1; // Enable interrupt serial port EA=1; // Enable all interrupts clrscr(); demo_init(); // Peripherals initialization secondi_ora=1; d_dot=0; ore=12; min=35; sec=0; secondi=0; // Inizializzazione RTC setrtc(ore,min,sec); while(1) // Infinite Loop { if(debounceT1()) // If T1 pressed { secondi_ora=!secondi_ora; // Switches from hour to seconds and viceversa } if(debounceT2()) // If T2 pressed { ore=bcdtobin(ore); min=bcdtobin(min); if(ore==23) { ore=0; } else { ore++; } setrtc(ore, min, 0); buzzer(50); // Buzzer beeps for 50 milliseconds sec=secondi=0; } if(debounceT3()) // If T2 pressed { ore=bcdtobin(ore); min=bcdtobin(min); if(min==59) { min=0; } else { min++; } setrtc(ore, min, 0); buzzer(50); // Il buzzer suona per 50 millisecondi sec=secondi=0; } getrtc(&ore,&min,&sec); // Fetch RTC values (in BCD) if(sec!=secondi) // If one second has passed { d_dot=!d_dot; // Complement decimal point secondi=sec; // Store the second } if(secondi_ora) { vis_2num(ore,min); // Visualizes hours and minutes } else { vis_2num(154, sec); // Turn OFF first two figures and // visualize seconds } } }