LOCODUINO
Discussions Générales => Présentez vous ! => Discussion démarrée par: Julaye le octobre 30, 2021, 11:50:29 pm
-
Bonjour à tous,
Débutante en ferromodélisme depuis une année mais tombée dans l'informatique embarquée petite, avec de l'assembleur et un KIM-1 ...
Je m'intéresse à l'Arduino Nano pour réaliser des automatismes sympa sur mon réseau en construction.
Voilà :)
-
Bonjour Julaye,
voila une présentation courte et précise comme un sketch Arduino, c'est un bon début....
Bienvenue sur notre forum préféré et bonne découverte avec ce superbe "jouet" qu'est le NANO, par contre oublie l'assembleur (comme j'ai été obligé de le faire) et vive le monde des "objets": https://www.locoduino.org/spip.php?article85
Bien cordialement
Antoine
PS: C'est quoi le "KIM-1" ?
-
Bienvenue Julaye,
Nul doute que tu vas nous montrer des choses intéressantes en environnement embarqué dans notre domaine chéri ;D
Bienvenue ;)
-
A mon tour de te souhaiter la bienvenue.
Le monde des objets est effectivement très riche, mais personne n'est obligé d'arrêter l'assembleur. En tout cas, une série d'articles y est consacré.
Sur LOCODUINO, il y en a pour tous les goûts et je suis certain que tu sauras lui apporter ta touche au gré de tes envies. On en rediscute bientôt. ;)
Christian
-
Bonjour et bienvenue sur le site de Locoduino :)
Je pense que tu trouveras ici une aide précieuse pour les animations de ton réseau, que ce soit dans les (excellents) articles du site,
ou par les réponses des membres de Locoduino, toujours très disponible :D
A+ Rémi 8)
-
mais personne n'est obligé d'arrêter l'assembleur.
Christian
Je parlais juste pour l'Arduino bien sûr :-[
-
Merci à tous pour l'accueil 8)
@Tony04 J'ai arrêté l'assembleur depuis l'époque où j'avais développé un petit assembleur pour le 68HC11 ... 1998 si je me souviens bien ...
@Remi oui et je m'en suis déjà inspirée, notamment le poste de soudure que j'ai revisité un peu. Cf dans l'imprimerie sur cette vidéo :
https://www.youtube.com/watch?v=GU4KVuNW2DI
@chris_bzg je choisi toujours l'outil et l'architecture la plus adaptée à ce que je souhaite programmer. Les objets sont un outil possible mais pas toujours le plus efficient. Pour l'embarqué sur du matériel comme le Nano, j'aime bien le C (de Ritchie), les machines à état fini et les co-routines.
@Dominique oui j'ai fait un petit projet, parce que en fait je suis paresseuse et je n'aime pas passer mon temps à coder, j'aime bien décrire (meta programmation, même en embarqué c'est possible, low code/no code pour reprendre le jargon à la mode) : https://github.com/Julaye/Lumieres
@Tony04 KIM-1 : le truc avec lequel j'ai commencé, quelques mois avant l'Apple II ... https://fr.wikipedia.org/wiki/KIM-1. Désolée je n'ai aucune photo de l'époque, je me souviens juste que vu mon âge (12 ans je crois), je ne parlais pas encore anglais et que je déchiffrais les docs avec le dictionnaire de mon père.
Bonne semaine,
-
Cf dans l'imprimerie sur cette vidéo
Jolie petite imprimerie !
-
mais personne n'est obligé d'arrêter l'assembleur.
Christian
Je parlais juste pour l'Arduino bien sûr :-[
Bonjour Tony,
Moi aussi je parlais d'Arduino. :)
Si tu as lu le premier des articles assembleur, il démontre certains problèmes engendrés par l'IDE avec le C.
Mais j'ai aussi dit que l'assembleur, ça peut être très ch... et que ce langage ne se justifie que dans des cas bien particuliers (quasiment jamais en modélisme ferroviaire).
En fait, je rebondissais juste sur ta remarque quand tu disais avoir été "obligé" ; ça m'a amusé. En tout cas, je n'oblige à rien : chacun fait ce qu'il lui plaît (plaît, plaît...). ;)
-
Merci à tous pour l'accueil 8)
@chris_bzg je choisi toujours l'outil et l'architecture la plus adaptée à ce que je souhaite programmer. Les objets sont un outil possible mais pas toujours le plus efficient. Pour l'embarqué sur du matériel comme le Nano, j'aime bien le C (de Ritchie), les machines à état fini et les co-routines.
Bonne semaine,
Bien que la POO soit un outil extrêmement puissant, je l'utilise assez peu, mais il est vrai aussi que la majorité des montages que je publie s'adresse à des gens qui débutent. Les montages trop compliqués ne sont jamais réalisés par nos lecteurs. Donc le C me suffit dans bien des cas et j'utilise les classes pour les bibliothèques (LightEffect par exemple). Dans l'équipe, Jean-Luc et Thierry maitrisent la POO (entre autres) : si un jour tu veux t'y intéresser, lis leurs articles.
Et pour le reste, c'est sans doute toi qui va nous apprendre des choses intéressantes. ;)
-
Bonjour Julaye,
À mon tour je te souhaite la bienvenue parmi nous.
@chris_bzg je choisi toujours l'outil et l'architecture la plus adaptée à ce que je souhaite programmer. Les objets sont un outil possible mais pas toujours le plus efficient. Pour l'embarqué sur du matériel comme le Nano, j'aime bien le C (de Ritchie), les machines à état fini et les co-routines.
Les machines à états finis sont très présentes dans les systèmes embarqués. Je les ai utilisées dans la plupart de mes développements pour le modélisme ferroviaire. Au début j'ai réalisé des projets directement en C sur des Attiny2313 et Atmega8, plus récemment je suis passé à l'Arduino pour plus de puissance et utiliser C++. Mon réseau hivernal est contrôlé par plusieurs classes C++ implémentant chacune une machine à états finis. Voir https://forum.locoduino.org/index.php?topic=763.msg13315#new (https://forum.locoduino.org/index.php?topic=763.msg13315#new).
Mais les coroutines sont intéressantes lorsque le traitement est majoritairement séquentiel. Une implantation très pratique des coroutines sont les protothreads dont la documentation se trouve ici: http://dunkels.com/adam/pt/ (http://dunkels.com/adam/pt/). C'est simple à mettre en oeuvre et ne requiert que quelques includes, car implanté sous forme de macros du préprocesseur.
L'exemple ci-après est la gestion en C sur Attiny2313 d'un PN dont les barrières sont actionnées par des servo moteurs. Il y a 3 protothreads: 2 pour faire clignoter les signaux sur les 2 PN, 1 pour actionner les barrières de l'un des PN, l'autre n'en ayant pas.
Au plaisir d'échanger sur ces sujets.
Meilleures salutations.
//
// PassageNiveau.c
//
// Programme de contrôle du passage à niveau.
//
// 2010.06.12 MHP Création.
// 2011.06.14 MHP Premiers tests concluants de commande du servo-moteur.
// 2011.06.20 MHP Utilisation des protothreads PC (Adam Dunkels, adam@sics.se).
// 2011.06.22 MHP Premier essai des prothothreads AVR.
// 2011.06.24 MHP Correction des timings ville et prise en compte du transistor de sortie (inverser PWM).
// 2011.07.06 MHP Configuration des entrées/sorties définitive!
// 2011.07.07 MHP Réglage des délais pour la ville.
// 2011.09.10 MHP Raccourcissement de la période et attente avant de redécter le train au PN ville.
// 2011.12.20 MHP Suppression de l'anti-rebond pour la détection des trains et ralentissement des mouvements
// des barrières.
// 2012.04.01 MHP Suppression du délai entre 2 détections PN ville car nouveaux circuits de détection unidirectionnels.
//
// Directives de compilation: PC ou AVR.
//
// Includes standard: types, IO.
#ifdef PC
#include <stdint.h>
#include <stdio.h>
#endif
#ifdef AVR
#include <inttypes.h>
#include <avr/io.h>
#endif
// Protothreads.
#include "pt.h"
static struct pt ptGererSignalVille, ptGererBarrieresVille, ptGererSignalGare;
// Timers.
typedef uint16_t tTimer;
static tTimer tmrBaisserBarrieresVille, tmrBarrieresVille, tmrClignSignalVille, tmrMvtBarrieresVille;
static tTimer tmrGareA, tmrGareB, tmrClignSignalGare;
// Tous les délais sont exprimés nombres de périodes de PERIODE millisecondes.
// Période en ms.
#define PERIODE 10
// La durée durant laquelle le signal fonctionne est exprimée en nombre d'itérations
// plutôt qu'à l'aide d'un timer.
#define DELAI_CLIGN_VILLE (300/PERIODE)
#define DUREE_SIGNAL_VILLE (14000/PERIODE)
#define NB_ITER_SIGNAL_VILLE (DUREE_SIGNAL_VILLE/(2*DELAI_CLIGN_VILLE)+1)
#define DELAI_BAISSER_BARRIERES_VILLE (400/PERIODE)
// La durée durant laquelle le signal fonctionne est exprimée en nombre d'itérations
// plutôt qu'à l'aide d'un timer.
#define DELAI_CLIGN_GARE (300/PERIODE)
#define DUREE_SIGNAL_GARE (8000/PERIODE)
#define NB_ITER_SIGNAL_GARE (DUREE_SIGNAL_GARE/(2*DELAI_CLIGN_GARE))
// Le délai lorsque les barrières sont à nouveau levées est plus grand que le délai entre
// le début du signal de ville et la descente des barrières.
#define DELAI_BARRIERES_VILLE (DUREE_SIGNAL_VILLE - 5*DELAI_BAISSER_BARRIERES_VILLE)
#define DELAI_SERVO (60/PERIODE)
// Constantes communes PC et AVR
// Spécifique AVR.
#ifdef AVR
#define F_CPU 1000000UL
#include <util/delay.h>
// Configuration des ports
#define INPUT_VILLE PD1
#define INPUT_GARE_A PD2
#define INPUT_GARE_B PD3
#define INPUT_ATTENTE_A PD4
#define INPUT_ATTENTE_B PD5
#define OUTPUT_SERVO PB3
#define OUTPUT_SIGNAL_VILLE PB1
#define OUTPUT_SIGNAL_GARE PB0
#define CONFIG_DDRB (_BV(OUTPUT_SERVO)|_BV(OUTPUT_SIGNAL_VILLE)|_BV(OUTPUT_SIGNAL_GARE))
#define PASSAGE_VILLE (!(PIND & _BV(INPUT_VILLE)))
#define PASSAGE_GARE_A (!(PIND & _BV(INPUT_GARE_A)))
#define PASSAGE_GARE_B (!(PIND & _BV(INPUT_GARE_B)))
#define CONFIG_PULLUPD (_BV(INPUT_VILLE)|_BV(INPUT_GARE_A)|_BV(INPUT_GARE_B)|_BV(INPUT_ATTENTE_A)|_BV(INPUT_ATTENTE_B))
#define CONFIG_DDRD ~CONFIG_PULLUPD
#define ATTENTE_A (!(PIND & _BV(INPUT_ATTENTE_A)))
#define ATTENTE_B (!(PIND & _BV(INPUT_ATTENTE_B)))
#define SetClignVille (PORTB |= _BV(OUTPUT_SIGNAL_VILLE))
#define ClrClignVille (PORTB &= ~_BV(OUTPUT_SIGNAL_VILLE))
#define SetClignGare (PORTB |= _BV(OUTPUT_SIGNAL_GARE))
#define ClrClignGare (PORTB &= ~_BV(OUTPUT_SIGNAL_GARE))
#endif
#ifdef PC
// Pour simuler le passage des trains.
static uint8_t PASSAGE_VILLE, PASSAGE_GARE_A, PASSAGE_GARE_B, ATTENTE_A, ATTENTE_B;
#endif
#define MIN_SERVO 1000
#define MAX_SERVO 1900
#define PAS_SERVO 20
#ifdef AVR
#define CONFIG_TCCR1A (_BV(COM1A1)|_BV(WGM11))
// Pour le test, prescaler sur 1024.
#if 0
#define CONFIG_TCCR1B (_BV(CS12)|_BV(CS10)|_BV(WGM12)|_BV(WGM13))
#endif
// Fonctionnement normal, prescaler sur 1.
#define CONFIG_TCCR1B (_BV(CS10)|_BV(WGM12)|_BV(WGM13))
#define PERIODE_SERVO 20000
#endif
static PT_THREAD(GererSignalGare(struct pt *pt))
{
// On doit pouvoir traiter ces événements quelque soit l'endroit où
// on se trouve dans le protothread.
if (ATTENTE_A && (tmrGareA > 0))
{
#ifdef PC
printf("Train en attente gare A\n");
#endif
tmrGareA = DUREE_SIGNAL_GARE;
} // if
if (ATTENTE_B && (tmrGareB > 0))
{
#ifdef PC
printf("Train en attente gare B\n");
#endif
tmrGareB = DUREE_SIGNAL_GARE;
} // if
PT_BEGIN(pt);
while (1)
{
PT_WAIT_UNTIL(pt, PASSAGE_GARE_A || PASSAGE_GARE_B);
if (PASSAGE_GARE_A && (tmrGareA == 0))
{
#ifdef PC
printf("Passage gare A\n");
#endif
tmrGareA = DUREE_SIGNAL_GARE;
} // if
if (PASSAGE_GARE_B && (tmrGareB == 0))
{
#ifdef PC
printf("Passage gare B\n");
#endif
tmrGareB = DUREE_SIGNAL_GARE;
} // if
while ((tmrGareA > 0) || (tmrGareB > 0))
{
#ifdef PC
printf("Signal gare ON\n");
#endif
#ifdef AVR
SetClignGare;
#endif
tmrClignSignalGare = DELAI_CLIGN_GARE;
PT_WAIT_UNTIL(pt, tmrClignSignalGare == 0);
#ifdef PC
printf("Signal gare OFF\n");
#endif
#ifdef AVR
ClrClignGare;
#endif
tmrClignSignalGare = DELAI_CLIGN_GARE;
PT_WAIT_UNTIL(pt, tmrClignSignalGare == 0);
} // while
} // while
PT_END(pt);
} // GererSignalGare
static PT_THREAD(GererSignalVille(struct pt *pt))
{
static uint16_t nIter;
PT_BEGIN(pt);
while (1)
{
PT_WAIT_UNTIL(pt, PASSAGE_VILLE);
for (nIter = 0; nIter < NB_ITER_SIGNAL_VILLE; nIter++)
{
#ifdef PC
printf("Signal ville ON\n");
#endif
#ifdef AVR
SetClignVille;
#endif
tmrClignSignalVille = DELAI_CLIGN_VILLE;
PT_WAIT_UNTIL(pt, tmrClignSignalVille == 0);
#ifdef PC
printf("Signal ville OFF\n");
#endif
#ifdef AVR
ClrClignVille;
#endif
tmrClignSignalVille = DELAI_CLIGN_VILLE;
PT_WAIT_UNTIL(pt, tmrClignSignalVille == 0);
} // for
#ifdef PC
printf("Fin signal ville\n");
#endif
} // while
PT_END(pt);
} // GererSignalVille
static PT_THREAD(GererBarrieresVille(struct pt *pt))
{
static uint16_t nLen; // Longueur de l'impulsion des servos.
PT_BEGIN(pt);
while (1)
{
PT_WAIT_UNTIL(pt, PASSAGE_VILLE);
#ifdef PC
printf("Attendre pour baisser les barrières\n");
#endif
tmrBaisserBarrieresVille = DELAI_BAISSER_BARRIERES_VILLE;
PT_WAIT_UNTIL(pt, tmrBaisserBarrieresVille == 0);
#ifdef PC
printf("Début baisser les barrières\n");
#endif
tmrBarrieresVille = DELAI_BARRIERES_VILLE;
nLen = MIN_SERVO;
while (nLen <= MAX_SERVO)
{
#ifdef PC
printf("Baisser barrières, servo: %0d\n", nLen);
#endif
#ifdef AVR
OCR1A = nLen;
#endif
nLen += PAS_SERVO;
tmrMvtBarrieresVille = DELAI_SERVO;
PT_WAIT_UNTIL(pt, tmrMvtBarrieresVille == 0);
} // while
#ifdef PC
printf("Barrières baissées\n");
#endif
PT_WAIT_UNTIL(pt, tmrBarrieresVille == 0);
nLen -= PAS_SERVO;
while (nLen >= MIN_SERVO)
{
#ifdef PC
printf("Lever barrières, servo: %0d\n", nLen);
#endif
#ifdef AVR
OCR1A = nLen;
#endif
nLen -= PAS_SERVO;
tmrMvtBarrieresVille = DELAI_SERVO;
PT_WAIT_UNTIL(pt, tmrMvtBarrieresVille == 0);
} // while
#ifdef PC
printf("Barrières levées\n");
#endif
} // while
PT_END(pt);
} // GererBarrieresVille
void main(void)
{
// Initialiser les protothreads.
PT_INIT(&ptGererSignalVille);
PT_INIT(&ptGererBarrieresVille);
PT_INIT(&ptGererSignalGare);
// Configuration spécifique AVR.
#ifdef AVR
DDRD = CONFIG_DDRD;
PORTD = CONFIG_PULLUPD;
DDRB = CONFIG_DDRB;
ICR1 = PERIODE_SERVO;
OCR1A = MIN_SERVO;
TCCR1A = CONFIG_TCCR1A;
TCCR1B = CONFIG_TCCR1B;
#endif
#ifdef PC
uint16_t nIter;
//PASSAGE_VILLE = 1;
PASSAGE_GARE_A = 1;
for (nIter = 0; nIter <= 1000; nIter++)
{
printf("Temps: %0000d\n", nIter * PERIODE);
if (nIter == 2)
PASSAGE_GARE_A = 0;
if (nIter == 40)
{
printf("ATTENTE_A = 1\n");
ATTENTE_A = 1;
}
if (nIter > 45)
ATTENTE_A = 0;
if (nIter == 50)
{
printf("PASSAGE_GARE = 1\n");
PASSAGE_GARE_B = 1;
}
if (nIter > 50)
PASSAGE_GARE_B = 0;
#endif
#ifdef AVR
while (1)
{
#endif
// Mettre à jour tous les timers.
if (tmrBaisserBarrieresVille > 0) tmrBaisserBarrieresVille--;
if (tmrBarrieresVille > 0) tmrBarrieresVille--;
if (tmrClignSignalVille > 0) tmrClignSignalVille--;
if (tmrMvtBarrieresVille > 0) tmrMvtBarrieresVille--;
if (tmrGareA > 0) tmrGareA--;
if (tmrGareB > 0) tmrGareB--;
if (tmrClignSignalGare > 0) tmrClignSignalGare--;
// Traiter les événements.
GererSignalVille(&ptGererSignalVille);
GererBarrieresVille(&ptGererBarrieresVille);
GererSignalGare(&ptGererSignalGare);
#ifdef AVR
_delay_ms(PERIODE);
#endif
} // while, for
} // main
-
Re,
@chris_bzg POO oulala ça me rappelle mes cours de PtitLoo à la fac ;) (cf http://www.lirmm.fr/~ferber/Ptitloo/ptitloo.htm)
Je ne vais pas vous apprendre grand chose, vous faites tous pleins de trucs sur les Arduino depuis bien longtemps.
@Marc-Henri Merci pour ces bouts de code très instructif :) Je vais les lire plus en détail, ça donne beaucoup d'idées pour mon automate.
J'avais vu les protothreads que tu mentionnes, c'est effectivement ce que j'envisage d'utiliser pour implémenter des automatismes en // dans mon automate. Beaucoup plus jeune, j'avais implanté des co-routines dans un système embarqué à base de 68HC11. J'allais pour en coder pour l'AVR et j'ai vu que c'était déjà fait lol.
Ton exemple de PN est intéressant, je vais voir si je peux l'implémenter avec le micro code que je propose. J'ai déjà codé un bout de PN dans la doc, mais ton PN est plus complexe.
@+
-
@Marc-Henri,
Ne connaissant pas les protothread, j'ai compilé ton programme PassageNiveau.c et j'ai été obligé de charger la bibliothèque Protothreads (normal), et de changer void main(void) en int main(void) pour qu'il compile sans erreur.
Pourquoi ?
Donc j'ai (toujours) des choses à apprendre...
Merci
Dominique
-
@Dominique,
Quelques recherches sur le web m'ont permis de voir que void main n'est pas standard, même si certains compilateurs l'acceptent. C'était le cas de la version d'avr-gcc utilisée lors du développement de ce projet, mais cela ne passe plus avec la version actuelle.
Il faut donc bien spécifier int comme type de retour. Je pense que c'est dû au fait qu'un programme en C écrit pour un ordinateur autre qu'un système embarqué doit retourner une valeur à l'issue de son exécution (0: tout s'est bien passé, autre valeur: erreur). Cela ne s'applique évidemment pas à nos programmes constitués d'une boucle principale ne se terminant pas, mais c'est exigé par la plupart des compilateurs.
Meilleures salutations.