=====Présentation Arduino et cartes d'extension (shield)=====
=== Objectifs ===
* Prise en main de l'environnement Arduino
* Analyse du schéma électrique de la carte arduino et de la connectivité avec une carte d'extension
* Syntaxe Arduino
* Utilisation de la maquette Arduino
* Utilisation haut niveau d'une communication série asynchrone
**Le TP se déroule sous Linux mais vous pouvez tout à fait utiliser la version Windows ou OS/X d'Arduino**
==== Carte Arduino pour le TD/TP ====
Arduino est une plateforme de développement open-source qui se compose d'une carte microcontrôleur et d'un environnement de développement associé. La carte Arduino UNO se compose de :
* un microcontrôleur 8-bit Atmega328p cadencé à 16Mhz (exécutant une instruction par cycle d'horloge) https://www.google.fr/url?q=https://docs.google.com/viewer%3Fa%3Dv%26pid%3Dsites%26srcid%3DZGVwaW5mb25hbmN5Lm5ldHxtaW5lc3xneDo0NjFmYzI1NTdkZDU2YmE2&sa=U&ved=0ahUKEwjolNmb5MTKAhXECBoKHVDtCD4QFggWMAA&usg=AFQjCNGxQ8y7E0qcjTBHVI0f65so7he5jg
* un convertisseur USB/UART TTL
* une régulation de tension 5v
* un connecteur au pas de 2.54mm au standard Arduino pour la connection d'une ou plusieurs carte(s) d'extension
Le schéma de cette carte est visible sur: http://arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf
Pour plus d'informations, vous reporter à http://arduino.cc/
Il existe des cartes compatibles arduino que vous pouvez acheter pour 4 euros par exemple sur: http://www.ebay.com/itm/NEW-UNO-R3-ATmega328P-CH340-Mini-USB-Board-Compatible-Arduino-/311155383820?pt=LH_DefaultDomain_0&hash=item48724e5e0c
Il existe aussi des kits tels que: https://www.dx.com/fr/p/funduino-uno-r3-lcd-1602-keypad-shield-v2-0-lcd1602-expansion-board-building-blocks-deep-blue-315276?tc=EUR&gclid=CJ_Hvd645b4CFaXHtAod-1MA4A#.VMRAaTVrpwH
Il est également possible d'utiliser un outils en ligne pour développer sur arduino sans rien installer sur votre machine: https://codebender.cc/
=====Prise en main de l'Arduino=====
==== Branchement de la carte Arduino ====
La carte Arduino se branche au PC via un des ports USB disponibles en façade. Le connecteur côté Arduino est de type USB-A mâle et le connecteur côté PC est de type USB-B femelle. Avant de passer à la suite, connectez la carte au PC à l'aide du câble fourni.
**La carte Arduino n'étant pas protégée, il est préférable de vérifier que la table de TP est libre de tout objet métallique afin d'éviter les court-circuits !**
==== Prise de main de l'outil Arduino IDE ====
Le système d'exploitation utilisé lors des TP de bus de communication est Debian (basé Linux). Sous Linux le lancement des utilitaires peut se faire de deux manières:
* Utilisation d'un raccourci sur le bureau
* Lancement au travers de la ligne de commande
Pour lancer l'application Arduino au travers de la ligne de commande, suivez la suite d'instructions suivante :
- Taper ALT+F2 puis saisir lxterm et touche entrée
- Dans le terminal, taper le nom de l'application (arduino) suffixé par &
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_launch.png}}
- Attendre le chargement de l'application, une fois l'application chargée, vous devriez avoir la vue suivante
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_launched.png}}
==== Configuration de l'IDE ====
Dans l'onglet "Outils", accédez au menu "Port Série" et cliquez sur la valeur /dev/ttyACM0 ou /dev/ttyUSB0. Accédez aussi eu menu "Carte" et vérifiez que la valeur sélectionnée est "Arduino Uno".
==== Votre premier sketch ====
Maintenant que l'éditeur est lancé, nous pouvons entrer dans la fenêtre le programme suivant
int led = A2; //si le shield RS232 est présent
//int led = 13; //si le shield RS232 n'est pas présent, pour utiliser la led de la carte UNO
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
==== Compilation du sketch ====
Copier coller le contenu dans votre fichier et cliquez sur le bouton "Vérifier" pour le compiler.
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_blink_verify.png}}
Les erreurs de compilation apparaissent en rouge (et en anglais) dans la fenêtre de compilation en bas de l'IDE.
==== Chargement sur la platine Arduino ====
Une fois que la compilation ne génère plus d'erreurs, chargez le fichier sur la carte Arduino (préalablement connectée sur le port USB) en cliquant sur le bouton "Téléverser"
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_blink_load.png}}
A partir de ce point, si votre code est correct, la led numérotée 13 sur la carte arduino (ou RTS de la carte d'extension RS232) doit clignoter.
==== Précautions à prendre avec la plateforme et l'IDE ====
Sauvegarder vos fichiers en LOCAL sur la machine dans le dossier /home/IUT/"login"/Arduino/ et copier ces fichiers vers le disque réseau /mnt/ponsan..../ A CHAQUE FIN DE SEANCE!!!
* Sauvegarder vos programmes sous des noms différents pour chaque exercice
* Lors de la sauvegarde, une fenêtre apparaît pour demander quels noms donner au programme. Si vous ne faites pas attention, cette fenêtre peut passer en arrière plan et l'IDE Arduino ne répondra plus à aucune sollicitation tant que vous n'aurez pas ré-ouvert cette fenêtre.
* Pensez à sauvegarder votre code fréquement à l'aide la combinaisn ctrl+s
* La carte Arduino étant "nue", faîtes attention à ne pas provoquer de court-circuit en manipulant des objets métalliques
* Le carte étant posé sur la table et le cable étant branché sous la table, faites attention à ne pas accidentellement tirer sur le câble.
=====Comment gérer l'absence de débogueur=====
Nous allons utiliser la librairie Serial pour échanger des caractères ASCII entre le PC et la carte Arduino de manière bidirectionnelle. Vous pouvez accéder au descriptif de cette librairie en sélectionnant le menu "Aide" puis le menu "Référence".
Le debogage de votre application pourra se faire à l'aide d'affichage vers la console MAIS il faut tenir compte du fait que le code ainsi modifié ne sera pas exactement le même code que celui sans les appels d'affichage.
Egalement, vous pourrez utiliser des broches du microcontroleur pour indiquer l'entrée ou la sortie d'une portion de code, afin de tester précisement le timing.
===== Exercice 1 : Utilisation de la librairie Serial =====
**Repérer sur le schéma électrique de la carte les broches réservées pour la communication série. Ces brôches ne devront pas être utilisées pour une autre fonction!**
L'application à programmer implémente le comportement suivant :
- Configurer l'UART pour une communication 8N1 à 9600Bauds (Serial.begin)
- Attendre que un caractère soit disponible sur le périphérique UART (Serial.available)
- Lire le caractère (Serial.read)
- Si le caractère est une minuscule, le convertir en majuscule (?)
- Envoyer le caractère sur la liaison série (Serial.write)
Pour des formats de trames autres que 8N1, vous trouverez les valeurs de paramètres adaptés à la méthode Serial.begin à l'adresse: http://arduino.cc/en/Serial/Begin
void setup()
{
Serial.begin(9600);
Serial.println("Hello, world?"); // Envoi de la chaîne terminée par un saut de ligne
}
void loop()
{
char car ;
if (Serial.available()>0){
car = Serial.read();
if ( (car>='a') && (car<='z') )
car=car+'A'-'a';
Serial.write(car);
}
}
{{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Compiler et charger ce programme sur la carte.
==== Utilisation de la console série sur le PC ====
L'IDE arduino dispose d'une console série permettant de visualiser les caractères envoyés par l'Arduino sur son port périphérique matériel. Pour accéder à la console série, il suffit de cliquer sur la loupe en haut à droite de l'éditeur de code.
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_terminal.png}}
Une console série apparaîtra alors sous la forme suivante.
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_terminal2.png}}
Il suffit alors de configurer le baudrate de la communication à l'aide la liste déroulante en bas à gauche.
Pour envoyer des caractères à l'application, il suffit de les rentrer dans la zone de texte en haut de de la fenêtre, puis de cliquer sur "Envoyer".
{{https://bvdp.inetdoc.net/files/iut/td1_capt/arduino_terminal_send.png}}
Les caractères émis par l'UART HARD du Arduino doivent ensuite s'afficher dans la partie basse de la fenêtre.
{{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Vérifier la communication avec la carte Arduino en utilisant le programme chargé précédemment.
===== Exercice 2 : Accès bas niveau aux broches du microcontroleur=====
Les fonctions Arduino pinMode(...) et digitalWrite(...) sont compatibles avec différentes cartes équipées de différents microcontroleurs. Ceci permet d'écrire des programmes Arduino compatibles avec différentes cartes mais représente un coût non négligeable en terme de vitesse d’exécution, ce qui peut conduire à de la gigue (Jitter: the deviation from true periodicity of a presumably periodic signal) lorsque l'on souhaite construire un signal à l'aide du microcontroleur.
Analyser les fichiers suivants pour repérer comment les fonctions d'accés aux broches sont implémentées pour le ATMEGA328P:
/usr/share/arduino/hardware/arduino/cores/arduino/wiring_digital.c
/usr/share/arduino/hardware/arduino/cores/arduino/Arduino.h
/usr/share/arduino/hardware/arduino/variants/standard/pins_arduino.h
En vous aidant de la doc de l'ATMEGA328P et de ces fichiers, repérer les registres du microcontroleur permettant l’accès direct aux broches. Attention, les registres de ce microcontroleur ne sont pas adressables bit par bit, il faut donc effectuer des opérations logiques (& | ^ ...) et des masques pour intervenir individuellement sur une broche.
{{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Ecrire un programme le plus simple possible permettant de générer un signal carré de rapport cyclique 50% et de période la plus courte possible. On autorisera ici à écrire une boucle while(1) dans la fonction loop(). Afin d'obtenir un rapport cyclique de 50%, vous pourrez faire appel à l'instruction suivante qui génère un délai de 62.5ns (soit 1 cycle à 16Mhz):
__asm__("nop\n\t");
===Solution:===
Avec DigitalWrite, impossible d'obtenir des périodes inférieures à 10us. Avec accès via registres, le signal généré est de période 0.5us soit de fréquence 2Mhz:
void setup()
{
pinMode(8, OUTPUT);
}
void loop()
{
while(1) {
PORTB &= ~0x01 ;
__asm__("nop\n\t");
__asm__("nop\n\t");
PORTB |= 0x01 ;
}
}
===== Exercice 3 : Gestion du temps sur le microcontroleur=====
La gestion du temps peut se faire généralement de deux manières:
- La plus simple consiste à faire appel à des boucles d'attente durant lesquelles le processeur perd du temps à ne (quasiment) rien faire. La plus petite durée d'attente possible est obtenue en exécutant l'instruction assembleur NOP. Les fonctions Arduino delay(...) et delayMicroseconds(...) utilisent ce principe pour des durées plus longues exprimées en millisecondes et en microsecondes. Le problème est que les temps d'attente s'ajoutent à la durée d'exécution des instructions (de calcul etc...) ce qui conduit à une mauvaise maîtrise du temps.
- La seconde méthode consiste à faire appel à un périphérique intégré au microcontroleur: le TIMER, qui va (dé)compter des périodes d'un signal de référence, généralement l'horloge système du microcontroleur (ou un signal générée à partir du signal de référence mais dont la fréquence aura pu être prédivisée par un facteur entier réglable). Pour générer un signal périodique, le timer pourra être configuré en mode rechargement automatique. Afin de générer des périodes plus longue que ce qu'il est possible d'obtenir juste avec le prédiviseur et le timer, il est possible de mettre en cascade un facteur de prédivision, un facteur de timer matériel suivi d'un facteur de compteur logiciel, ce dernier étant réalisé à l'aide d'une variable et d'opérations de calcul.
N.B. Pour des facteurs de division non entiers, il est possible de rattraper l'erreur en ajoutant ou retranchant de temps en temps une valeur comme on le fait avec le 29 février pour les années bissextiles.
Dans le cas de l'utilisation d'un timer servant à déclencher l'exécution d'une tâche périodique, il est possible de mettre en oeuvre deux approches:
- La **scrutation** consiste à venir tester l'état d'un indicateur (bit d'un registre) régulièrement afin de détecter le débordement du timer par exemple. Cette approche permet d'intégrer le code de la tâche périodique à l'intérieur du programme principale mais ne permet pas une très bonne maîtrise du temps, ce qui peut conduire à de la gigue.
- L'**interruption** consiste à interrompre l'exécution de la tâche en cours (généralement le programme principal) pour aller exécuter un programme spécifique à l'événement qui l'a déclenchée. L'exécution du programme qui a été interrompu reprend généralement à la fin de l'exécution du programme d'interruption. Cette approche permet généralement une meilleure maîtrise du temps séparant l'occurence de l'événement et l'exécution du programme associé mais nécessite des précautions ainsi qu'une configuration adéquate. Afin de vérifier le bon réglage du timer ainsi que le temps passé dans le programme d'interruption (qui doit être inférieur à la période entre deux interruptions), nous conseillons d'utiliser une broche du microcontroleur qui sera par exemple mise à 1 à l'entrée dans le programme d'interruption puis remise à 0 au moment de la sortie.
{{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Dans un premier temps, modifier votre programme précédent pour générer un signal de rapport cyclique 50% et de période 624us à l'aide de la fonction delayMicroseconds(...). Vérifier à l'oscilloscope le signal obtenu et adapter votre programme.
===Solution:===
void setup()
{
pinMode(8, OUTPUT);
}
void loop()
{
while(1) {
PORTB &= ~0x01 ;
__asm__("nop\n\t");
__asm__("nop\n\t");
delayMicroseconds(311);
PORTB |= 0x01 ;
delayMicroseconds(311);
}
}
{{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Ensuite, générer le même signal en faisant appel à l'interruption TIMER. Conseil: utiliser le timer 2 et lire la documentation de l'ATMEGA328P pour sa configuration.
===Solution:===
//624us de période=312us de demi période (entre chaque interruption)
// =156*32*62.5ns
void setupTimer2(){
TCCR2A = 0;// set entire TCCR2A register to 0
TCNT2 = 0;//initialize counter value to 0
OCR2A = 156-1;// (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
//prescaler= 1/32 , see p162 https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVwaW5mb25hbmN5Lm5ldHxtaW5lc3xneDo0NjFmYzI1NTdkZDU2YmE2
TCCR2B = (1 << CS21)|(1 << CS20);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
}
// TIMER ISR VECTOR and configuration
ISR(TIMER2_COMPA_vect){
PORTB ^= 0x01 ;
}
void setup()
{
pinMode(8, OUTPUT);
cli();//stop interrupts
setupTimer2(); // setup interrupt with prescaler (see datasheet for prescaler value, and reload value)
sei();//allow interrupts
}
void loop()
{
}
===== Exercice 4 : Gestion des bits sur le microcontroleur=====
Le signal numérique a générer sur la broche est généralement issu d'une valeur sur plusieurs bits qui est **sérialisée**. Ceci revient, à chaque période bit, à extraire la valeur du bit (depuis une variable par exemple) et a appliquer un signal dépendant de ce bit sur la broche.
=====Programme d'exemple=====
//macro à activer pour afficher des infos de debug dans le programme, la commenter sinon
//#define DEBUG
//int led = A2; //si le shield RS232 est présent
int led = 3; //si le shield RS232 n'est pas présent, pour utiliser la led de la carte UNO
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
Serial.begin(9600);
Serial.println("Hello, world?"); // Envoi de la chaîne terminée par un saut de ligne
}
unsigned int compteur=0;
// the loop routine runs over and over again forever:
void loop() {
/*
compteur=compteur+1;
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(3);
Serial.println("+"); // Envoi de la chaîne terminée par un saut de ligne
//if (false)// wait for a second
{
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(200); // wait for a second
Serial.println("."); // Envoi de la chaîne terminée par un saut de ligne
}
Serial.println(compteur);
*/
#ifdef DEBUG
Serial.println("je debuggue");
#endif
/*
char car;
if (Serial.available()>0){
car = Serial.read();
if ((car%2)==0) //est ce que le code ascii est pair?
digitalWrite(led, HIGH);
else
digitalWrite(led, LOW);
}
*/
/* génère un signal de fréquence 102kHz)
digitalWrite(led, HIGH);
digitalWrite(led, LOW);
*/
//la broche 3 de l'arduino est connectée au bit 3 du port D
/* génère un signal de fréquence 841.5kHz)
PORTD|=0x8; // met à 1 le bit 3 du port D
PORTD&= ~0x8; // met à 0 le bit 3 du port D
*/
/* génère un signal de fréquence 2,664MHz)
while(1){
PORTD|=0x8; // met à 1 le bit 3 du port D
PORTD&= ~0x8; // met à 0 le bit 3 du port D
}
*/
// génère un signal de rapport cyclique 50% de fréquence 2MHz)
while(1){
PORTD|=0x8; // met à 1 le bit 3 du port D
__asm__("nop\n\t");
__asm__("nop\n\t");
PORTD&= ~0x8; // met à 0 le bit 3 du port D
}
}