Club robotique de Sophia-Antipolis

Accueil > POBOTpedia > Capteurs > Autres capteurs > Horloge RTC et calendrier I2C

Horloge RTC et calendrier I2C

pour un robot toujours à l’heure

samedi 2 décembre 2006, par Julien H.

Avoir un robot à l’heure n’est pas toujours une priorité, mais arriver à contrôler une puce en I2C est un exercice intéressant.

En particulier, la puce RTC (Real Time Clock) permet d’avoir à disposition de notre programme une horloge autonome, ce qui peut être utile pour synchroniser des étapes du programme sans reposer sur les compteurs internes qui sont souvent surchargés.

C’est également l’occasion de décortiquer une datasheet de A à Z et enfin c’est le premier composant que j’avais à disposition pour tester la communication I2C 😉

Edition 2010 : ce composant est désormais disponible pour une dizaine d’euros avec toute son électronique, sous forme d’une extension pour Arduino prête à l’emploi.

Description du composant

On va parler ici du Dallas DS1307, une horloge RTC archi-connue disponible pour quelques euros. C’est une puce (en boitier DIL sur la photo) à 8 pattes.

Pin Nom Rôle
1 X1 cristal
2 X2 cristal
3 Vbat tension batterie
4 GND masse
5 SDA ligne des données (data) I²C
6 SCL ligne d’horloge (clock) I²C
7 SQW/out sortie signal carré
8 Vcc tension logique 5V

Le cristal quartz utilisé pour une horloge de ce type n’est pas comparable à ceux qu’on utilise pour les microcontrôleurs : on utilise une valeur particulière, par exemple 32,763 kHz, qui est plus adaptée pour compter des millisecondes. C’est un composant cylindrique très fin qu’on trouve dans les montres.

Description des pattes

Datasheet

La datasheet nous apprend qu’il s’agit en fait d’une mémoire (d’une taille 64 octets de 8 bits) avec un programme capable de tenir à jour un calendrier grâce à une pile de 3V et d’un oscillateur.

Datasheet DS1307

On parle avec ce composant grâce à une liaison série à deux fils (c’est de l’I2C qui ne veut pas dire son nom) et le DS1307 est considéré comme esclave d’adresse 1101000 (0x68 pour les intimes). C’est bien sûr le microcontrôleur qui sera le maitre et notre programme interrogera la puce qui lui répondra l’heure qu’il est.

En résumé, il faut connecter à ce composant :

 un cristal (semblable à ceux qu’on trouve dans les montres à quartz, et pour cause !)
 une batterie 3V (on utilise une pile lithium CR2032)
 une tension d’alimentation 5V (celle du µC)
 la connexion au bus I2C
 deux résistances de pull-up sur le bus I2C

Exemple de circuit

Exemples de programme

Comme dit plus haut, il s’agit d’une mémoire à registres. La lecture se fait donc en deux temps :
 écrire le registre auquel on souhaite accéder
 demander la lecture de ce registre

Il y a 8 registres consacrés à l’horloge temps réel (Real Time Clock) dans ce composant (dans cet ordre) :
 les secondes (de 0 à 59)
 les minutes (de 0 à 59)
 les heures (de 0 à 23)
 le jour de la semaine (de 1 à 7)
 la date du jour (dans le mois, de 1 à 31)
 le mois (de 1 à 12)
 l’année (à partir de 0, il suffit de lui ajouter 2000)
 la configuration

Certains des octets comportent des bits de configuration : par exemple pour l’heure, les bits qui ne servent pas à coder l’heure sont utilisés pour changer le mode 12 heures ou 24 heures et le cas échéant pour stocker si c’est le matin (AM) ou l’après-midi (PM).

Attention, les valeurs des octets que vous allez écrire ou récupérer sont codés en BCD ou binary coded decimal, il faut donc utiliser des routines de conversion, assez simples (division ou multiplication par 10, reste de la division entière, et des décalages de 4 bits pour charger ou récupérer la partie haute de l’octet).


/**
* Transform a binary value into BCD coded value
*
*/
u08 transformToBcd(u08 val)

// 4 higher bits are the integer result of division by 10
// 4 lower bits are the remaining part
return ((val/10) << 4) + val % 10 ;

/**
* Decode a BCD coded value into binary value
*
*/
u08 transformFromBcd(u08 bcd)

return 10*((bcd & 0xF0)>>4) + (bcd & 0x0F) ;

Les appels aux fonctions I2C dépendent des librairies que vous utilisez. Voici ce que ça donne avec AVRlib :


#define CLOCK_ADDRESS 0xD0

// useful constants for easy reminding of RTC bytes’ order
#define DS1307_SEC 0
#define DS1307_MIN 1
#define DS1307_HR 2
#define DS1307_DOW 3
#define DS1307_DATE 4
#define DS1307_MTH 5
#define DS1307_YR 6

// bytes array for the sending to the I2C device
u08 cmd[] = "012345678" ;

// how many bytes are to be sent (max index of previous array)
u08 cmdSize ;

// buffer to store the received bytes
u08 buffer[] = "0123456789" ;

// specific array for the RTC values (hour, minutes, month, ..) decoded
u08 rtc[7] ;

int main(void)

// initialization of libraries (AVRlib)
timerInit() ;
uartInit() ;
uartSetBaudRate(9600) ;
rprintfInit(uartSendByte) ;
vt100Init() ;
vt100ClearScreen() ;
i2cInit() ;
// infinite loop that reads and display current date & time
while (1)

// First, announce that you’re requestion the RTC values
cmd[0] = 0x00 ;
cmdSize = 1 ;
i2cMasterSend(CLOCK_ADDRESS,cmdSize,cmd) ;

// Second, receive the 7 bcd bytes from RTC
i2cMasterReceive(CLOCK_ADDRESS,7,buffer) ;

// decode buffer into rtc array
rtc[DS1307_SEC] = transformFromBcd(buffer[DS1307_SEC]) ;
rtc[DS1307_MIN] = transformFromBcd(buffer[DS1307_MIN]) ;
rtc[DS1307_HR] = transformFromBcd(buffer[DS1307_HR]) ;
rtc[DS1307_DOW] = buffer[DS1307_DOW] ;
rtc[DS1307_DATE] = transformFromBcd(buffer[DS1307_DATE]) ;
rtc[DS1307_MTH] = transformFromBcd(buffer[DS1307_MTH]) ;
rtc[DS1307_YR] = transformFromBcd(buffer[DS1307_YR]) ;

// At last, display it in the serial console
rprintf("%d/%d/%d @ %d :%d :%d",rtc[DS1307_DATE],rtc[DS1307_MTH],2000+rtc[DS1307_YR],rtc[DS1307_HR],rtc[DS1307_MIN],rtc[DS1307_SEC]) ;

rprintfCRLF() ;
delay_ms(1000) ;

return 0 ;

Il est important de noter qu’ici, l’adresse I2C (sur 7 bits) a été convertie en 8 bits (ajout d’un zéro à la fin) et devient D0. La librairie I2C gère la différence entre une écriture et une lecture. C’est le point le plus "pénible" de la communication I2C, car on hésite toujours sur le format de l’adresse selon le matériel ou le logiciel qu’on utilise. Pour l’instant, nous avons sous la main un analyseur logique qui nous permet de vérifier :

<docb1832|center>

Pour la première utilisation d’une horloge (ou lors du remplacement de la pile), il est nécessaire d’enregistrer l’heure courante. Voici le code correspondant, qui utilise la fonction de codage BCD présentée ci-dessus et qui va tout simplement écrire les 7 octets à la suite du registre 0 qui reste toujours nécessaire pour garder la logique "mémoire" du composant DS1307.


/*
*
*/
void initClock(void)

// init clock ("real" values)
rtc[DS1307_SEC] = 0 ;
rtc[DS1307_MIN] = 48 ;
rtc[DS1307_HR] = 22 ;
rtc[DS1307_DOW] = 5 ;
rtc[DS1307_DATE] = 26 ;
rtc[DS1307_MTH] = 12 ;
rtc[DS1307_YR] = 8 ;

// transform into BCD ready for sending (using the buffer)
cmd[DS1307_SEC+1] = transformToBcd(rtc[DS1307_SEC]) ;
cmd[DS1307_MIN+1] = transformToBcd(rtc[DS1307_MIN]) ;
cmd[DS1307_HR+1] = transformToBcd(rtc[DS1307_HR]) ;
cmd[DS1307_DOW+1] = rtc[DS1307_DOW] ;
cmd[DS1307_DATE+1] = transformToBcd(rtc[DS1307_DATE]) ;
cmd[DS1307_MTH+1] = transformToBcd(rtc[DS1307_MTH]) ;
cmd[DS1307_YR+1] = transformToBcd(rtc[DS1307_YR]) ;

// prepare sending by setting register 0 in first byte + 7 other bytes
cmd[0] = 0 ;
cmdSize = 8 ;

// send to the clock
i2cMasterSend(CLOCK_ADDRESS,cmdSize,cmd) ;

On teste, ça fonctionne !

Bon changement de minute

Au final, c’est un code assez simple. Nous n’avons présenté que l’utilisation de base, avec un code sans protection (vérification des valeurs minimums et maximums lors de l’écriture, etc...) et sans gérer l’arrêt et le démarrage de l’horloge (bit de poids le plus fort à 0 pour start ou 1 pour stop sur l’octet des secondes).

Il existe des bibliothèques de code pour différents microcontrôleurs et différents langages. Ainsi pour les cartes et l’environnement Arduino, je vous conseille la discussion très intéressante sur leur forum (dont je me suis inspiré pour coder mes propres fonctions) : http://www.arduino.cc/cgi-bin/yabb2...

Portfolio

Vos commentaires

  • Le 7 décembre 2006 à 00:17, par Eric P. En réponse à : Horloge et calendrier I2C

    A noter que les 2 résistances de pull-up des lignes du bus I2C ne sont à placer qu’une seule fois sur le bus, et non pas au niveau de chaque périphérique. En général on les met côté micro-contrôleurs, et ainsi il n’y a plus à s’en soucier au niveau des circuits périphériques.

    • Le 26 décembre 2008 à 20:12, par ? En réponse à : Horloge et calendrier I2C

      Le DS1307 existera encore lorsque vous nous proposerez des exemples de programmes ?

    • Le 26 décembre 2008 à 23:34, par Julien H. En réponse à : Horloge et calendrier I2C

      Il existe toujours, et les exemples de programme ont été enfin publiés (finalement, personne ne s’était penché dessus, faute de temps et de moyens). D’autres horloges I2C existent, qui proposent des fonctions d’alarme, ou utilisent des tensions inférieures à 5v, de plus en plus courantes.

    • Le 11 janvier 2009 à 02:32, par ericc46 En réponse à : Horloge et calendrier I2C

      bonjour a tous chez achetté un robot vacuum m288 la base elle marche pas tres bien niveau de la programmation l’horloge je pense deconne est ce que quelqu’un a une idée pour la changer cet horloge a cristaux liquides qui ne marche pas tres bien elle sefface,ou le robot ne part pas merci de vous penchersurmon pro bleme salutations a tout le mondes

    • Le 11 janvier 2009 à 08:59, par Julien H. En réponse à : Horloge et calendrier I2C

      Bonjour et merci pour ton message. Mais il ne s’agit pas du même type d’horloge : ici il s’agit d’un composant et pas d’un afficheur avec écran. Peut-être un visiteur ayant le même robot que toi pourra t’aider.

    Répondre à ce message

Un message, un commentaire ?

modération a priori

Attention, votre message n’apparaîtra qu’après avoir été relu et approuvé.

Qui êtes-vous ?

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.