librtc - Manipuler le temps, pas l'espace
Posté le 24/05/2016 13:39
Ce projet est sur la forge de PC - Version actuelle : 1.0
Introduction
Comme j'étais dans un effort de simplifier encore le développement sous Linux avec des libs statiques et que je bidouillais le C-Engine, j'ai remarqué qu'il avait des fonctions que je trouvais pas très propres pour bidouiller la RTC : c'est pourquoi j'ai décidé d'en faire une lib, pour centraliser les efforts.
La RTC, késako ?
La Real-Time Clock est un simple compteur incrémenté régulièrement (64 fois par seconde) par un oscillateur. C'est un module qui tourne indépendemment du processeur.
Histoire de le préciser, cet oscillateur n'est pas magique, et a besoin de batterie pour continuer de tourner. Si vous enlevez les piles ou si elles sont mortes, il arrêtera de tourner, et peut-être que le compteur se réinitialisera : dans tous les cas, celle-ci peut se dérégler.
De plus, il n'y a pas de gestion native du fuseau horaire, ni même au niveau du système, donc l'heure est en GMT par défaut. Donc gardez à l'esprit, si votre addin utilise cette lib, qu'il y a des chances que la RTC contienne n'importe quoi à son lancement. Demandez à l'utilisateur de confirmer l'heure au lancement si vous souhaitez ne pas avoir de surprises.
Interface
Pour utiliser la librtc, pensez à inclure son header avec
#include <rtc.h> et à l'ajouter au linker avec
-lrtc (une fois installée).
Avant toute utilisation de la RTC, il faut l'initialiser :
rtc_init();
Cette initialisation ne doit être faite qu'
une seule fois pendant tout le programme. Elle retourne si tout va bien, i.e. si le microprocesseur est reconnu (la librtc se servant directement du matériel, sans appels système).
Ensuite, pour obtenir la date, prenez déjà connaissance de la structure suivante :
typedef struct {
int hz64;
int sec;
int min;
int hour;
int wday;
int day;
int month;
int year;
} rtc_date_t;
Ensuite, pour obtenir la date actuelle de la RTC, il vous suffit de la récupérer via :
rtc_date_t date = rtc_getdate();
Pour obtenir le timestamp (i.e. le nombre de secondes passées depuis
le 1er janvier 1970 à minuit), il vous suffit de faire :
unsigned int time = rtc_gettime();
Pour obtenir le nombre approximatif de millisecondes passées depuis le démarrage du programme (la fonction qui vous intéressera sans doute), vous pouvez ainsi faire :
unsigned int ticks = rtc_getticks();
Maintenant, si ce que vous voulez faire, c'est régler l'heure, deux fonctions risquent de vous intéresser :
rtc_settime(time); // régler le temps à l'aide d'un timestamp UNIX
rtc_setdate(date); // régler le temps à l'aide d'une date
Je vous conseille d'utiliser les fonctions getdate et setdate que celles utilisant des timestamps, étant donné que le compteur de la RTC est sous forme de date -- cela étant, vous avez le choix.
Aussi, si vous modifiez la date, vous risquez fort de vous en foutre du jour de la semaine (week day) alors qu'il peut être intéressant de le garder correct. Du coup, n'hésitez pas à le passer à la fonction
rtc_adjustwday qui fait ça pour vous :
rtc_date_t date = rtc_getdate();
// on modifie
rtc_adjustwday(&date);
rtc_setdate(date);
Quelques fonctions de conversion sont également disponibles :
rtc_date_t date2 = rtc_dateoftime(time);
unsigned int time2 = rtc_timeofdate(date);
Pour le moment, c'est tout.
A priori, rien à venir. Des propositions pas hors-sujet ?
Citer : Posté le 24/05/2016 17:49 | #
On peut obtenir la date de manière automatique ?
Hum va falloir réfléchir à des jeux de gestion .
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
Citer : Posté le 24/05/2016 17:59 | #
En fait, les compteurs du module RTC (Real-Time Clock) sont, tant en SH3 qu'en SH4, organisés en date (64-Hz/sec/min/heures/semaine/jour/jour de la semaine/mois/année) [sous format BCD, mais la lib fait les conversions qu'il faut]. Les ticks et timestamps s'obtiennent avec des calculs (adaptés de la musl libc, ma référence pour pas mal de choses).
(donc mieux vaut se baser sur la date que sur les timestamps/ticks)
(j'aime donner des détails)
Ajouté le 24/05/2016 à 18:58 :
La version 0.2.1 est là !
C'est surtout une version correctrice, qui :
- corrige la gestion du jour de la semaine ;
- corrige timeofdate (pour renvoyer un timestamp Unix qui fonctionne) ;
- corrige getdate (qui ne renvoyait pas le bon compteur 64-Hz).
Les prochaines versions devraient gérer l'édition de la date.
Mon blog ⋅ Mes autres projets
Citer : Posté le 24/05/2016 19:00 | #
cette lib est très intéressante mais la fonction :
se réfère elle forcement au 1er janvier 1970 à minuit ?
ne me charriez pas si ma question est idiote
Citer : Posté le 24/05/2016 19:03 | #
Elle renvoie un timestamp Unix, qui représente le nombre de secondes passées depuis le 1er janvier 1970 à minuit, donc oui
Mon blog ⋅ Mes autres projets
Citer : Posté le 24/05/2016 19:53 | #
Et on peut l'avoir sur sa calculatrice
Citer : Posté le 24/05/2016 20:10 | #
Hackcell : bien sûr. Le RTC (Real Time Clock, je crois), et une sorte de petite montre qui fonctionne tant qu'elle a du courant, et qui sert entre autre, à réguler la vitesse du processeur, synchroniser les bus… enfin bref. Il y en a une dans tout les PC, et il y en a une dans les calculatrices Casio. Et cette horloge permet aussi d'avoir l'heure (c'est le but d'une horloge, en soit). Et comme on peut y accéder depuis l'OS et ainsi avoir une horloge qui se dérègle pas et qui reste à l'heure même si la calculatrice est éteinte. Mais pas si on enlève les piles. J'ai dit qu'il fallait du courant.
Je me suis rendu compte que ce pavé risque d'embrouiller plus qu'autre chose, mais je le poste quand même. Pas envie d'avoir écrit tout ça pour rien.
Et je passe la main pour expliquer plus simplement.
Ajouté le 24/05/2016 à 20:12 :
D’ailleurs, il se peut que je me sois fourvoyé et que j'ai dit des âneries.
Citer : Posté le 24/05/2016 20:18 | #
En fait pas exactement Breizh
On a effectivement quelques oscillateurs qui font tout tourner, y compris la RTC. La RTC, dans l'histoire, c'est simplement un gros compteur évolué, avec un petit système d'alarme (inutilisable sans un interrupt handler custom, malheureusement).
Ajouté le 25/05/2016 à 17:40 :
Tralala, c'est la version 0.3 !
Au programme :
- des corrections, encore, toujours. (mais cette fois, c'est la bonne !)
- et la possibilité d'éditer la date dans la RTC ! (on l'attendait celle-là)
Les nouvelles fonctions sont décrites dans le post principal.
Gardez à l'esprit que l'interaction avec la RTC n'est pas encore testée (donc qu'il y a une chance que ça rate (mais théoriquement, ça devrait pas)) quand vous l'utilisez. (cependant, je serais ravi d'avoir des tests )
Mon blog ⋅ Mes autres projets
Citer : Posté le 26/05/2016 08:11 | #
(https://fr.m.wikipedia.org/wiki/Circuit_synchrone) qui est beaucoup plus précise que la rtc
Je me demande bien pourquoi casio a inclus ce rtc... Ils s'en servent dans l'os ?
Coïncidence ? Je ne pense pas.
Citer : Posté le 26/05/2016 08:26 | #
Beh à vrai dire, les MPUs possèdent effectivement des circuits comme celui que tu évoques. La RTC est juste un compteur indépendant au processeur, qui fonctionne justement grâce à l'un de ces circuits (elle s'incrémente à chaque fois qu'il la poke), du coup c'est pratique, il suffit de lire où le compteur en est.
Et c'est possiblement utilisé dans l'OS, mais pas pour donner l'heure (ou pas à un endroit commun en tout cas), plus pour donner des ticks, ou pour régler une alarme quand le compteur atteint une certaine date. Enfin, on peut se demander comment l'OS de base la gère (je n'ai pas encore vérifié ça).
Mon blog ⋅ Mes autres projets
Citer : Posté le 27/05/2016 22:00 | #
et qui sert entre autre, à réguler la vitesse du processeur, synchroniser les bus… enfin bref.
Euh, tu confonds avec le Clock Pulse Generator là. La RTC en est dépendante est c'est lui qui fournit les signaux du processeur, des bus, de la mémoire et de l'horloge périphérique.
Comme j'étais dans un effort de simplifier encore le développement sous Linux avec des libs statiques et que je bidouillais le C-Engine, j'ai remarqué qu'il avait des fonctions que je trouvais pas très propres pour bidouiller la RTC : c'est pourquoi j'ai décidé d'en faire une lib, pour centraliser les efforts.
Heureux de savoir qu'il y en a qui s'intéressent au développement sous Linux !
J'ai regardé rapidement les sources... si je peux me permettre un commentaire, c'est pas trop mal. Y'a trop de multiplications à mon goût... après c'est une manie à moi :3 Dans include/rtc/internals.h, inclus stdint.h au lieu de redéfinir des types de base. Par contre le code dans les headers de internals, c'est impardonnable. Statique ou pas, tu multiplies du code, c'est une violation élémentaire du principe DRY ! (Je suis très chiant mais ça m'amuse.)
Je trouve juste ça un peu dommage pour le dessin et la RTC. À part que gint est (significativement) plus rapide que ML, il faut que j'y intègre l'API de gestion (entre autres pour les interruptions), du coup ça fait redondant par rapport à ta lib...
Je pense que tu vois ce que je veux dire. Je suis sincèrement content de voir cet effort vers le développement Linux, maintenant si tu le veux bien, ce serait cool qu'on puisse s'arranger pour se compléter !
Citer : Posté le 28/05/2016 02:43 | #
Pour le stdint.h, malheureusement, chez moi... x)
In file included from ./include/rtc/internals.h:27:0,
from src/rtc_init.c:24:
/opt/sh3eb-elf/lib/gcc/sh3eb-elf/6.1.0/include/stdint.h:9:26: fatal error: stdint.h: No such file or directory
# include_next <stdint.h>
^
(et les headers standards ont toujours un peu foiré avec gcc 4.9.1 ou 6.1.0, donc bon, j'évite)
Les fonctions des headers de internals est en always_inline, c'est une idée que j'ai reprise de la musl libc (mon point de départ pour pas mal de trucs) et que je trouvais plus propre.
Et du coup, même si cette lib est utilisable directement, elle est surtout vouée à finalement être intégrée dans des libs plus grosses, comme gint ou caphics (qui, comme elle suit des principes différents niveau graphismes, a une raison d'être). Et pour l'instant, c'est du WIP, c'est pour tester, et je trouvais ça plus propre d'avoir une lib pour ça, comme ça d'autres personnes peuvent tester et contribuer facilement.
Mon blog ⋅ Mes autres projets
Citer : Posté le 28/05/2016 14:07 | #
Les fonctions des headers de internals est en always_inline
Oups, au temps pour moi
Je sais pas si j'arrive à trouver ça propre par contre ! Je pense que c'est juste le fait de mettre du code dans un header : lorsque tu cherches le code d'une fonction, en général tu veux qu'il soit dans les sources... pourquoi ne pas juste mettre ça dans un fichier source ? Ce serait pareil, non (ou alors j'ai raté quelque chose) ?
J'ai l'impression d'être plus chiant qu'autre chose, alors je vais te laisser tranquille. Au passage on aura besoin d'implémenter la lib standard sous Linux : est-ce que ça te dirait de former le code pour ça, en utilisant les types et les noms de fonctions standard ?
Citer : Posté le 28/05/2016 14:20 | #
En fait, faut voir ces fonctions-là comme des grosses macros, plus élégamment écrites.
Et je préfère ne pas modifier le code de cette lib pour intégrer des noms standards, faudra modifier ça quand on reprendra le code (histoire de ne pas rentrer en conflit, puisque cette lib n'est pas standard)
Ajouté le 19/07/2016 à 10:08 :
Version 1.0 publiée ! C'est bon, a priori, les fonctions marchent correctement, et une fonction bonus (rtc_adjustwday) a été ajoutée. L'addin de test est arrivé par la même occasion.
Deux informations importantes :
- cette lib n'est utilisable que par les gens sous Gnunux (qui me laisse plus libre de mes choix que le SDK de Windows) ;
- une documentation sous forme de manpages est également disponibles, les instructions pour les installer sont dans le README.
Mon blog ⋅ Mes autres projets
Citer : Posté le 07/06/2017 17:25 | #
Bonjour,
J'aimerais utiliser cette lib seulement je n'arrive pas à créer de compte sur GitLab car je ne reçois aucune confirmation par Email (j'ai réessayer plusieurs fois). Est-il possible de me la donner en fichier joint(=zip) ?
Merci d'avance
Citer : Posté le 07/06/2017 17:28 | #
Tu peux trouver ça dans la source d'Omgclock. Cette lib n'est plus entretenue, au passage.
Mon blog ⋅ Mes autres projets
Citer : Posté le 24/08/2018 13:49 | #
D'après un coup rapide sur les sources, tu ne tiens pas compte du carry dans la lecture :
snap.year = _rtc_cnt->year;
snap.month = _rtc_cnt->month;
snap.wday = _rtc_cnt->wday;
snap.day = _rtc_cnt->day;
snap.hour = _rtc_cnt->hour;
snap.min = _rtc_cnt->min;
snap.sec = _rtc_cnt->sec;
snap.hz64 = _rtc_cnt->hz64;
Dommage !
Citer : Posté le 01/09/2018 01:23 | #
Pouh, c'est vieux cette lib maintenant. Si tu as la foi de corriger, je t'en prie
Mon blog ⋅ Mes autres projets
Citer : Posté le 01/09/2018 08:58 | #
Je le ferais bien par principe, mais je ne trouve pas de dépôt Git avec le code !
Citer : Posté le 01/09/2018 12:11 | # | Fichier joint
Excuse-moi, c'est vrai que j'ai changé tous les dépôts plusieurs fois sur cette forge
Voici, en pièce jointe, tout ce que j'ai en local concernant la librtc
Mon blog ⋅ Mes autres projets
Citer : Posté le 01/09/2018 12:32 | #
Parfait, voilà un diff
+++ librtc.new/public/src/getdate.c 2018-09-01 12:30:07.544791093 +0200
@@ -35,14 +35,19 @@
rtc_date_t date;
// copy as quickly as possible
- snap.year = _rtc_cnt->year;
- snap.month = _rtc_cnt->month;
- snap.wday = _rtc_cnt->wday;
- snap.day = _rtc_cnt->day;
- snap.hour = _rtc_cnt->hour;
- snap.min = _rtc_cnt->min;
- snap.sec = _rtc_cnt->sec;
- snap.hz64 = _rtc_cnt->hz64;
+ do {
+ _rtc_cr1->c.CF = 0;
+
+ snap.year = _rtc_cnt->year;
+ snap.month = _rtc_cnt->month;
+ snap.wday = _rtc_cnt->wday;
+ snap.day = _rtc_cnt->day;
+ snap.hour = _rtc_cnt->hour;
+ snap.min = _rtc_cnt->min;
+ snap.sec = _rtc_cnt->sec;
+ snap.hz64 = _rtc_cnt->hz64;
+ }
+ while(_rtc_cr1->c.CF != 0);
// get from snap
date.year = intofbcd16(snap.year); // 0000 to 9999
diff -ru librtc.old/public/src/setdate.c librtc/public/src/setdate.c
--- librtc.old/public/src/setdate.c 2018-09-01 12:25:45.867481880 +0200
+++ librtc.new/public/src/setdate.c 2018-09-01 12:30:17.510847530 +0200
@@ -31,19 +31,18 @@
void rtc_setdate(rtc_date_t date)
{
- // halt RTC
- _rtc_cr2->c.START = 0;
-
// set RTC fields
- _rtc_cnt->hz64 = (uint8bswp(date.hz64) >> 1);
- _rtc_cnt->sec = bcd8ofint(date.sec);
- _rtc_cnt->min = bcd8ofint(date.min);
- _rtc_cnt->hour = bcd8ofint(date.hour);
- _rtc_cnt->wday = bcd8ofint(date.wday);
- _rtc_cnt->day = bcd8ofint(date.day);
- _rtc_cnt->month = bcd8ofint(date.month);
- _rtc_cnt->year = bcd16ofint(date.year);
+ do {
+ _rtc_cr1->c.CF = 0;
- // restart RTC
- _rtc_cr2->c.START = 1;
+ _rtc_cnt->hz64 = (uint8bswp(date.hz64) >> 1);
+ _rtc_cnt->sec = bcd8ofint(date.sec);
+ _rtc_cnt->min = bcd8ofint(date.min);
+ _rtc_cnt->hour = bcd8ofint(date.hour);
+ _rtc_cnt->wday = bcd8ofint(date.wday);
+ _rtc_cnt->day = bcd8ofint(date.day);
+ _rtc_cnt->month = bcd8ofint(date.month);
+ _rtc_cnt->year = bcd16ofint(date.year);
+ }
+ while(_rtc_cr1->c.CF != 0);
}