| |
PROYECTOS
: AUX RTC
|
|
| |
Real Time Clock con un DS1307 |
|
 |
|
Circuito auxiliar de Reloj en Tiempo Real mediante
un DS1307
|
| |
Descripción:
|
-
Nuestros PIC's no saben
en qué día y hora viven. Perdidos en el no-tiempo, la realidad analógica
o digital es un continuo donde las fracciones de tiempo superior a
algunos milisegundos son entes abstractos e inasibles.
Anclados en patrones de frecuencia decimales que no son divisibles por
sus primos binarios, esclavos del redondeo, siempre les sobran o les
faltan unos microsegundos para dar el segundo perfecto.
Como Reloj en Tiempo Real un PIC abandonado a su cuarzo y a sus
divisores, preescalers y postescalers, simplemente no da la talla.
|
-
Y ahí es donde entran
los amables señores de
Dallas Maxim y su
cucaracha octópoda clockeadora: el DS1307, intitulada por
ellos mismos como un "64 x 8, Serial, I2C Real-Time Clock" O
sea un corazón de reloj con alguna RAM adicional (no volátil ya que es
asistida por una batería de Litio que la mantiene a flote mientras no le
falte el fuelle de los voltios).
|
-
Este proyecto consiste
esencialmente en poner en santa compaña a nuestro PIC con su Reloj
colgando del BUS RRBOARD2
(compatible), aprovechando que ambos hablan en I2C, por lo
que es de esperar que no tengamos grandes problemas para llevarlo a buen
puerto.
|
|
|
-
Entre esta información
y mi propio jugo de cerebro, destilado en estos últimos días, tengo el
gusto de presentaros completo este pequeño ejercicio de integración
temporal de un PIC y sus circunstancias.
|
| |
Implementación:
|
-
La implementación
consiste en posibilitar la comunicación I2C entre la
RRBOARD2 y el DS1307 y
explicar lo fundamental de este chip, mostrándoos a continuación qué
podemos hacer con él y qué no (visto desde el punto de vista de dentro
de un PIC claro está, ya que es de cajón y no pienso comentar por
ejemplo que tragarse un DS1307 sin quitarle las puntiagudas patillas
puede ser incompatible con la vida, tal y como la conocemos).
|
- No pienso repetir palabra por palabra,
como un vulgar loro de bar portuario, lo que ya dice de forma
meridianamente clara el Datasheet del DS1307, así que tras poneros el
Diagrama de Bloques del mismo (extraído de dicho datasheet) paso a
comentar algunas circunstancias interesantes a tener en cuenta.
|
|

|
|
|
-
2º.- La alimentación es
doble. Por un lado el VCC de nuestro circuito normal, el del PIC, y por
otro una Batería de Litio que va a permitir que el reloj siga su normal
funcionamiento aún cuando apaguemos el PIC. (Esta batería sirve también
para mantener viva la NVRAM adicional de que disponemos) El mismo DS1307
se encarga de realizar la conmutación entre una y otra por lo que no
tenemos que tener en cuenta esta circunstancia y podemos olvidarnos de
ella (salvo la de cambiar la pila cuando se agote)
|
|

|
- 3º.- El DS1307 tiene un pin de salida
que debidamente habilitado nos ofrece una onda cuadrada con las
frecuencias que puedes ver en la tabla superior. Esta salida es a
colector abierto por lo que es necesario, si la queremos utilizar para
inyectarla en cualquier otro circuito, colocarle una resistencia pull-up
de unos 10 Kohm a VCC.
Ten en cuenta que si nuestro DS1307 va a pasar grandes periodos de
tiempo alimentándose solo de la batería el tener esta opción de salida
habilitada consume cientos de veces mas intensidad que sin ella por lo
que podemos dejar la batería tiesa en muy poco tiempo. Si no es
necesario es preferible deshabilitar esta opción (mas adelante veremos
cómo hacerlo).
|

|
| |
-
4º.- En la tabla
superior podéis ver la estructura de la NVRAM, donde se mezclan tanto
los registros de configuración, como los de salvaguarda de la fecha y
hora del dispositivo, como asimismo los bancos de RAM de libre
disposición para el usuario.
En esta tabla tener en cuenta que el Bit 7 de la dirección 0x00 hay que
colocarla a 0 para que todo funcione. Es el Enable general del
dispositivo que pone en marcha el oscilador interno.
|
|

|
- 5º.- El byte alojado en la dirección
0x07 es el Control Register que nos permite configurar la función
del pin de salida según los siguientes condicionantes:
El bit 4, SQWE, habilita o deshabilita la función de salida
externa del pin Out.
El bit 7, OUT, establece el estado del pin de salida cuando
SQWE está deshabilitado. Si OUT es 1 y SQWE es 0
entonces el pin de salida está en alto indefinidamente, si OUT es
0 y SQWE es 0 entonces el pin de salida está por el contrario en
bajo indefinidamente.
Los bits 0 y 1 sirven para seleccionar la frecuencia de salida cuando
SQWE está en alto según la tabla expuesta en 2º.
|
| |
Esquema:
|
- Este el circuito propuesto por el
fabricante como típico para su buen funcionamiento. Es el que vamos a
implementar exactamente hasta el último detalle.
|
|

|
- Del pinout nada que añadir:
|
|

|
- Y este es el esquema definitivo que
vamos a construir, en el que podemos destacar algunos detalles:
|
- 1º.- La comunicación con la
RRBOARD2 la realizamos mediante
nuestro buen amigo el conector CON-ML10 para cable plano de 10
hilos (alimentación y un puerto completo de 8 bits)
|
- 2º.- JP1-PB y JP3-PC
permiten seleccionar la conexión de los SDA y SCL del I2C
a los pines 0..1 ó 3..4 del puerto al que estén conectados (recordad de
la RRBOARD2 está diseñada para las familias 18F4550 y 16F877 y el I2C
lo implementa la primera en los pines RB0 y RB1 y la segunda en los RC3
y RC4)
|
- 3º.- JP2-OUT Permite conectar o
desconectar el pin Out del DS1307 al pin Rx3 del puerto de
la RRBOARD2 (Muy útil para usarlo con la Interrupción Externa 2 del
18F4550)
|
- 4º.- Dependiendo de dónde conectemos
nuestro circuito en la RRBOARD2 podemos necesitar o no las resistencias
Pull-Up imprescindibles para el bus I2C. Si lo conectamos al
PORTB tenemos disponibles la internas del PIC, en cualquier otro caso
podemos hacer uso del jumper JP3-PU para conectar dichas
resistencias Pull-Up a VCC.
|
- 5º.- Añadimos ademas el jumper OUT
para poder utilizar externamente la señal de onda cuadrada del pin Out
del DS1307.
|
| |
| |
|

|
Circuito impreso:
|
|

|
Fotografías:
|
|
Y aquí el muchacho en plenitud de
facultades: |
| |
|

|
|
|

|
Funcionando con el software de Test:
|
| |
|
 |
| |
| |
Recursos:
|
- Datasheet del
DS1307 (PDF 875 Kb)
|
|
|
|
|
|
|
| |
Software: |
| |
- Primero y antes que nada la librería
Driver CCS C para el DS1307. Como comenté mas arriba esta
librería es un gazpacho entre las varias que he encontrado por esos
mundos de Dios fundamentalmente las expuestas en la primera sección de
esta página.
|
- Las cosas que yo personalmente he
introducido en este driver son:
- Parámetros pasados a
ds1307_init() para configurar en el inicio la función OUT
del DS1307.
- La funcionalidad
disable_interrupts(global) / enable_interrupts(global) en
cada una de las funciones definidas dependiendo del #define
use_interrups en el programa principal.
- Función ds1307_get_day_of_week()
que me devuelve el string con el nombre del día de la
semana en la fecha actual. (totalmente nueva y que no he encontrado
por ahí).
- He añadido las funciones necesarias
para escribir y leer todos los registros del DS1307 ds1307_read_nvram_byte()
y ds1307_write_nvram_byte()
|
| |
| |
Librería _ds1307.c |
|
| |
///////////////////////////////////////////////////////////////////////////////////////
/// DS1307.C
///
/// Driver for Real Time Clock
///
/// modified by Redpic 08/2006
///
///
http://picmania.garcia-cuervo.net
///
///
///
/// void ds1307_init(val)
///
/// - Enable oscillator without clearing the seconds register
///
/// used when PIC loses power and DS1307 run from 3V
BAT
///
/// - Config Control Register with next parameters:
///
///
DS1307_ALL_DISABLED All disabled
///
///
DS1307_OUT_ON_DISABLED_HIHG Out to Hight on Disable Out
///
///
DS1307_OUT_ENABLED Out Enabled
///
///
DS1307_OUT_1_HZ Freq. Out to 1 Hz
///
///
DS1307_OUT_4_KHZ Freq. Out to 4.096 Khz
///
///
DS1307_OUT_8_KHZ Freq. Out to 8.192 Khz
///
///
DS1307_OUT_32_KHZ Freq. Out to 32.768 Khz
///
///
///
///
Example init:
///
///
ds1307_init(DS1307_ALL_DISABLED);
///
///
ds1307_init(DS1307_OUT_ENABLED | DS1307_OUT_1_HZ);
///
///
///
/// void ds1307_set_date_time(day,mth,year,dow,hour,min,sec) - Set the
date/time ///
///
///
/// void ds1307_get_date(day,mth,year,dow) - Get the date
///
///
///
/// void ds1307_get_time(hr,min,sec) - Get the time
///
///
///
/// char ds1307_read_nvram_byte(char addr) - Read byte in address
///
///
///
/// void ds1307_write_nvram_byte(char addr, char value) - Write byte in
address ///
///
///
/// void ds1307_get_day_of_week(char* ptr) - Get string Day Of Week
///
///
///
/// If defined USE_INTERRUPTS all functions disable Global Interrupts on
starts and ///
/// enable Global on ends else usar can do it hiself
///
///
///
///////////////////////////////////////////////////////////////////////////////////////
#ifndef RTC_SDA
#define RTC_SDA PIN_B0
#define RTC_SCL PIN_B1
#endif
#use i2c(master, sda=RTC_SDA, scl=RTC_SCL)
#define DS1307_ALL_DISABLED 0b00000000 // All disabled
#define DS1307_OUT_ON_DISABLED_HIHG 0b10000000 // Out to Hight on Disable
Out
#define DS1307_OUT_ENABLED 0b00010000 // Out Enabled
#define DS1307_OUT_1_HZ 0b00000000 // Freq. Out to 1 Hz
#define DS1307_OUT_4_KHZ 0b00000001 // Freq. Out to 4.096 Khz
#define DS1307_OUT_8_KHZ 0b00000010 // Freq. Out to 8.192 Khz
#define DS1307_OUT_32_KHZ 0b00000011 // Freq. Out to 32.768 Khz
#define Start_user_address_nvram 0x08
#define End_user_address_nvram 0x3f
char days_of_week[7][11]={"Lunes\0","Martes\0","Miércoles\0","Jueves\0","Viernes\0","Sábado\0","Domingo\0"};
byte ds1307_bin2bcd(byte binary_value);
byte ds1307_bcd2bin(byte bcd_value);
void ds1307_init(int val){
byte seconds = 0;
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
i2c_start();
i2c_write(0xD0);
i2c_write(0x00);
i2c_start();
i2c_write(0xD1);
seconds = ds1307_bcd2bin(i2c_read(0));
i2c_stop();
seconds &= 0x7F;
delay_us(3);
i2c_start();
i2c_write(0xD0);
i2c_write(0x00);
i2c_write(ds1307_bin2bcd(seconds));
i2c_start();
i2c_write(0xD0);
i2c_write(0x07);
i2c_write(val);
i2c_stop();
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
void ds1307_set_date_time(byte day, byte mth, byte year, byte dow, byte hr,
byte min, byte sec){
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
sec &= 0x7F;
hr &= 0x3F;
i2c_start();
i2c_write(0xD0);
i2c_write(0x00);
i2c_write(ds1307_bin2bcd(sec));
i2c_write(ds1307_bin2bcd(min));
i2c_write(ds1307_bin2bcd(hr));
i2c_write(ds1307_bin2bcd(dow));
i2c_write(ds1307_bin2bcd(day));
i2c_write(ds1307_bin2bcd(mth));
i2c_write(ds1307_bin2bcd(year));
i2c_stop();
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
void ds1307_get_date(byte &day, byte &mth, byte &year, byte &dow){
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
i2c_start();
i2c_write(0xD0);
i2c_write(0x03);
i2c_start();
i2c_write(0xD1);
dow = ds1307_bcd2bin(i2c_read() & 0x7f);
day = ds1307_bcd2bin(i2c_read() & 0x3f);
mth = ds1307_bcd2bin(i2c_read() & 0x1f);
year = ds1307_bcd2bin(i2c_read(0));
i2c_stop();
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
void ds1307_get_time(byte &hr, byte &min, byte &sec){
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
i2c_start();
i2c_write(0xD0);
i2c_write(0x00);
i2c_start();
i2c_write(0xD1);
sec = ds1307_bcd2bin(i2c_read() & 0x7f);
min = ds1307_bcd2bin(i2c_read() & 0x7f);
hr = ds1307_bcd2bin(i2c_read(0) & 0x3f);
i2c_stop();
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
char ds1307_read_nvram_byte(char addr){
char retval;
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
i2c_start();
i2c_write(0xD0);
i2c_write(addr);
i2c_start();
i2c_write(0xD1);
retval = i2c_read(0);
i2c_stop();
return(retval);
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
void ds1307_write_nvram_byte(char addr, char value){
#ifndef USE_INTERRUPTS
disable_interrupts(global);
#endif
i2c_start();
i2c_write(0xD0);
i2c_write(addr);
i2c_write(value);
i2c_stop();
#ifndef USE_INTERRUPTS
enable_interrupts(global);
#endif
}
void ds1307_get_day_of_week(char* ptr){
byte lday;
byte lmonth;
byte lyr;
byte ldow;
ds1307_get_date(lday,lmonth,lyr,ldow);
sprintf(ptr,"%s",days_of_week[ldow]);
}
///////////////////////////////////////////////////////////////////////////////
byte ds1307_bin2bcd(byte binary_value){
byte temp;
byte retval;
temp = binary_value;
retval = 0;
while(1){
if(temp >= 10){
temp -= 10;
retval += 0x10;
}else{
retval += temp;
break;
}
}
return(retval);
}
byte ds1307_bcd2bin(byte bcd_value){
byte temp;
temp = bcd_value;
temp >>= 1;
temp &= 0x78;
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
///////////////////////////////////////////////////////////////////////////////
|
|
| |
|
|
Y por
último el programa de Test de todo esto.
|
- Al inicio y marcado en negrita
podéis ver la inclusión y definición necesaria para el uso de la
librería _ds1307.c.
|
- He implementado la Interrupción
Externa 2 del 18F4550 de la RRBOARD2 para que baile al son del
OUT del DS1307, detectando los flancos alternativos de la onda
cuadrada que me emite y encendiendo y apagando el LED conectado a
PIN_RE0.
|
- Antes de entrar en el bucle infinito
While(true) del main() realiza un completo chequeo y muestreo de
TODAS las funciones disponibles en el Driver.
|
- He añadido también un mini-menú
que permite activar o desactivar la monitorización de la fecha y
la hora mediante el canal RS-232.
|
- En el fuente siguiente hay una función
inicial de puesta en hora del DS1307 que posteriormente he
comentado, solo la uso una vez y después con el DS1307 perfectamente
sincronizado no es necesario utilizarla más.
|
| |
Software de Test rtc.c |
|
| |
// Real Time Clock
& NVRAM
// Hardware DS1307 of Dallas Maxim
// With interface I2C
#include <18f4550.h>
#fuses
HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
// Libreria DS1307.c
//////////////////////////////////////////////////////////
#define RTC_SDA PIN_B0
#define RTC_SCL PIN_B1
#define USE_INTERRUPTS 1
#include <_ds1307.c>
///////////////////////////////////////////////////////////////////////////////
const char Version[]="1.0.D\0";
int1 flanco=0;
int1 dump,kdump;
int n,i=0x00;
char c=0x00;
char rec=0x00;
byte sec;
byte min;
byte hrs;
byte day;
byte month;
byte yr;
byte dow;
char sdow[11];
// INTERRUPCION por RECEPCION SERIE
-------------------------------------------
#int_rda
void serial_isr() {
rec=0x00;
if(kbhit()){
rec=getc();
if(rec=='o'){ dump=1; }
if(rec=='f'){ dump=0; }
rec=0x00;
}
}
// INTERRUPCION por EXT2 Clock Out
--------------------------------------------
#int_ext2
ext2_handler() {
if(flanco==1){
ext_int_edge(2,H_TO_L);
output_high(PIN_E0);
}else{
ext_int_edge(2,L_TO_H);
output_low(PIN_E0);
}
++flanco;
}
//-----------------------------------------------------------------------------
void flash_porte(void){
for(i=0;i<3;i++){
output_e(0x07);
delay_ms(75);
output_e(0x00);
delay_ms(75);
}
}
void lee_y_transmite_date_and_time(void){
ds1307_get_day_of_week((char*) sdow);
ds1307_get_date(day,month,yr,dow);
ds1307_get_time(hrs,min,sec);
printf("\f\%s \%02d/\%02d/\%02d ",sdow,day,month,yr);
printf("\%02d:\%02d:\%02d\r\n", hrs,min,sec);
}
void main() {
disable_interrupts(global);
disable_interrupts(int_timer1);
disable_interrupts(int_rda);
disable_interrupts(int_ext);
disable_interrupts(int_ext1);
disable_interrupts(int_ext2);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_psp(PSP_DISABLED);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(FALSE);
set_tris_b(0b00000111);
set_tris_e(0b00010000);
output_e(0x00);
set_tris_c(0b10000000);
delay_ms(500);
printf("\r\n");
printf("[RRBOARD2] Real Time Clock version %s\r\n",version);
printf("based on DS1307 Dallas Maxim Hardware\r\n\n");
printf("Available RS232 commands:\r\n");
printf(" [o] Dump Date & Time ON\r\n");
printf(" [f] Dump Date & Time OFF\r\n\n");
flash_porte();
ext_int_edge(2,H_TO_L);
flanco=0;
enable_interrupts(int_ext2);
enable_interrupts(int_rda);
enable_interrupts(global);
// Inicializa DS1307
printf("Inicializando DS1307 ...\r\n\n");
ds1307_init(DS1307_OUT_ON_DISABLED_HIHG | DS1307_OUT_ENABLED | DS1307_OUT_1_HZ);
// Set date for -> 5 Agosto 2006 Viernes
// Set time for -> 02:50:00
// printf("Set Date & Time to ...\r\n");
// ds1307_set_date_time(5,8,6,5,2,50,00);
// Lee Fecha y Hora actual y transmite
printf("Fecha y Hora actual en el DS1307\r\n\n");
lee_y_transmite_date_and_time();
printf("\r\n\n");
// Test de RAM -> primero Write, segundo Read
printf("Test de la NVRAM interna (Write and read) ...\r\n\n");
for(i=Start_user_address_nvram;i<end_user_address_nvram+1;i++){
ds1307_write_nvram_byte(i,i);
}
n=0;
for(i=Start_user_address_nvram;i<end_user_address_nvram+1;i++){
c=ds1307_read_nvram_byte(i);
printf("%X ",c);
if(++n==0x0f){
n=0;
printf("\r\n");
}
}
printf("\r\n");
dump = 0;
kdump= 1;
do{
if(dump==1){
if((dump!=kdump)&&dump==1){
printf("Set Dump ON\r\n\n");
kdump=dump;
}
delay_ms(1000);
lee_y_transmite_date_and_time();
}else{
if((dump!=kdump)&&dump==0){
printf("\r\nSet Dump OFF\r\n\n");
kdump=dump;
}
}
} while (TRUE);
}
|
|
| |
|
|

Esta página se modificó el
27/12/2008
|