vendredi 29 juin 2012

Carte SD et Arduino


Cet article a été mis à jour le 17 décembre 2020.

Voici quelques recommandations pour brancher un lecteur de carte SD à une carte Arduino Uno  (il s'agit du modèle fabriqué par LC Studio qu'on peut facilement trouver sur eBay pour une bouchée de pain). 

Ces petits modules comportent 16 connecteurs, mais la plupart d'entre eux sont redondants et dans les faits 6 connexions sont nécessaires pour les faire fonctionner: "+3.3" et "GND" pour l'alimentation, "SCK", "MOSI", "MISO" et "CS" pour la communication SPI.

Bien qu'ils soit possible de les alimenter avec une tension de 5 V, ces modules sont conçus pour communiquer à un niveau logique de 3,3 V.  Certains modules de cartes SD (plus élaborés, et donc plus coûteux) comportent déjà un circuit qui abaisse à 3,3 V les tensions d'entrée, mais ce n'est pas le cas du module fabriqué par LC Studio.  L'entrée "+5" est la seule qui puisse être branchée à 5 volts, parce qu'elle est munie d'un régulateur de tension. 


Une façon possible d'abaisser à 3,3 volts le signal de 5 volts émis par l'Arduino consisterait à utiliser un simple diviseur de tensions (deux résistances en série).  Cette pratique est toutefois déconseillée par l'auteur de la bibliothèque SD, car le signal de 3,3 V risque d'être déformé par des transitions exponentielles qui perturbent l'interprétation correcte des signaux. 

Il faut plutôt abaisser le signal au moyen d'un circuit intégré spécialisé, capable de réagir suffisamment rapidement, comme le 4050 (74HC4050 ou CD4050), qui permet d'abaisser jusqu'à 6 signaux logiques simultanément.

Cette transformation doit être effectuée pour les signaux SPI qui vont de l'Arduino vers le lecteur de cartes SD:  CS (chip enable), SCK (serial clock) et MOSI (master out, slave in).  Toutefois, il ne serait pas approprié d'altérer le signal MISO (master in, slave out) qui est émis à 3,3 V par le lecteur SD (l'Arduino interprète un signal de 3,3 V comme une valeur logique haute).

Notez également qu'il faut éviter que les fils utilisés pour les branchements soient trop longs.

Les connexions

Trois connecteurs du module SD sont branchés directement à l'Arduino:
  • GND du module SD --->  GND de l'Arduino
  • +5 du module SD ----> 5 V de l'Arduino  (OU +3.3 du module SD ---> 3V3 de l'Arduino)
  • MISO du module SD ----> 12 de l'Arduino Uno (ou 50 si c'est un Mega)
Toutefois, les broches SCK, MOSI et CS du lecteur de carte SD sont branchées à des sorties du 4050. De cette façon, les signaux de 5 V générés par les broches 13, 11 et 10 de l'Arduino seront transformés en signaux de 3,3 V:
  • Broche CS du module SD: broche 2 du 4050
  • Broche 10 de l'Arduino Uno: broche 3 du 4050
  • Broche MOSI du module SD: broche 4 du 4050
  • Broche 11 de l'Arduino Uno: broche 5 du 4050
  • Broche SCK du module SD: broche 6 du 4050
  • Broche 13 de l'Arduino Uno: broche 7 du 4050
De plus, il faut correctement alimenter le circuit intégré:
  • Broche 1 du 4050: sortie 3,3 V de l'Arduino Uno
  • Broche 8 du 4050: broche GND de l'Arduino Uno
Il est également conseillé de relier à la masse (GND) toutes les sorties inutilisées, soit les broches 9, 11 et 14 du 4050.

Dans le passé, j'ai aussi utilisé avec succès le 74HC125 plutôt qu'un 4050. Les connexions sont indiquées sur le schéma ci-dessous.

Pour tester le fonctionnement correct de votre carte SD, vous pouvez exécuter l'exemple "CardInfo" distribué avec l'IDE Arduino.

J'ai aussi fait ce petit sketch en pigeant dans les exemples officiels:  après avoir vérifié la présence de la carte SD, je vérifie son contenu, puis je créé un nouveau fichier pour y écrire une quelconque banalité. 

/*
Experimentations avec le lecteur de carte SD.
Branchements:
CS -> pin 10 (53 sur mega)
MOSI -> pin 11 (50 sur mega)
MISO -> pin 12 (51 sur mega)
SCK -> pin 13 (52 sur mega)
Basé sur les exemples pris sur arduino.cc
https://electroniqueamateur.blogspot.com/2012/06/carte-sd-et-arduino.html
*/
#include <SD.h>
// déclaration de quelques variables
const int chipSelect = 10; //pin de l'Arduino reliee au CS du module SD
Sd2Card card;
SdVolume volume;
SdFile root;
File myFile;
void setup()
{
// Ouverture du port serie
Serial.begin(9600);
while (!Serial) {
;
}
Serial.print("\nRecherche de la carte SD...");
pinMode(10, OUTPUT); // 53 si ArduinoMega. Peu importe la valeur de chipSelect.
// On vérifie si une carte est présente
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("pas de carte?");
return;
}
else {
Serial.println("la carte est bien la.");
}
// On trouve le volume
if (!volume.init(card)) {
Serial.println("La carte n'est pas formatee?");
return;
}
// on sort la liste des fichiers déjà présents
Serial.println("\nFichiers trouves sur la carte (nom, date et taille): ");
root.openRoot(volume);
root.ls(LS_R | LS_DATE | LS_SIZE);
// On se prepare a jouer dans les fichiers
if (!SD.begin(chipSelect)) {
Serial.println("Echec de l'initialisation");
return;
}
Serial.println("Initialisation OK.");
// ouverture du fichier pour ecriture (il sera créé s'il n'existait pas déjà)
myFile = SD.open("SECRET.txt", FILE_WRITE);
// Si l'ouverture du fichier a fonctionné, on écrit quelque chose à l'intérieur
if (myFile) {
Serial.print("Ecriture dans le fichier...");
myFile.println("Arduino est venu ici!");
// fermeture du fichier
myFile.close();
Serial.println("c'est fait.");
}
else {
// si l'ouverture du fichier a échoué, on l'indique
Serial.println("impossible d'ouvrir le fichier pour l'ecriture");
}
// on ouvre le fichier, et on vérifie son contenu
myFile = SD.open("SECRET.txt");
if (myFile) {
Serial.println("Contenu du fichier:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// Fermeture du fichier
myFile.close();
}
else {
// si l'ouverture du fichier a échoué, on l'indique
Serial.println("impossible d'ouvrir le fichier pour la lecture");
}
}
void loop(void) {
}
view raw carte_SD.ino hosted with ❤ by GitHub

Yves Pelletier (Twitter: @ElectroAmateur)


mardi 26 juin 2012

Arduino, clavier numérique et afficheur LCD

Je me suis procuré un petit clavier numérique à 16 touches:  il s'agit du modèle qui ne coûte presque rien en frais postaux puisqu'il est aussi mince et flexible qu'un petit carré de carton.  J'ai l'intention d'ajouter ce clavier numérique à mon clavier MIDI, car j'ai constaté que naviguer à travers des dizaines de banques de sons en ne disposant que d'un bouton "up" et d'un bouton "down", ce n'est vraiment pas pratique.  Grâce à ce "keypad", je pourrai accéder directement à la banque de son désirée, à la condition de connaître son numéro.

Mais avant tout, j'ai fait quelques tests afin de me familiariser avec mon nouveau gadget.

Il s'agit donc d'un petit clavier comportant 16 touches:  en plus des chiffres, j'ai les 4 premières lettres de l'alphabet, ainsi que les symboles "*" et "#".  8 connecteurs permettent de le brancher à l'Arduino.  Au moyen d'un multimètre, j'ai rapidement constaté que les 4 premiers connecteurs sont reliés aux lignes alors que les 4 derniers sont reliés au colonnes.  Par exemple, si j'appuie sur la touche "6" (deuxième ligne, troisième colonne), la résistance devient nulle entre les connecteurs 2 et 7.

Après avoir installé la bibliothèque "keypad", j'ai expérimenté le sketch suivant, pour vérifier que tout fonctionnait convenablement:  chaque fois qu'une touche est enfoncée, le nom de la touche est affiché dans le moniteur série.  La seule modification notable par rapport au sketch fourni avec la librairie, c'est que j'ai dû régler le "debounce time" à une valeur assez élevée, car les touches avaient tendance à réagir deux fois quand mon intention était de ne les enfoncer qu'une seule fois.

Pour ce sketch, j'ai branché le connecteur 1 du clavier (voir schéma) à la pin 9 de l'Arduino, le connecteur 2 du clavier à la pin 8 de l'Arduino, et ainsi de suite jusqu'au connecteur 8 du clavier qui est branché à la pin 2 de l'Arduino.

/*
Pour tester un keypad branché à l'Arduino: la touche enfoncée s'affiche dans le moniteur série.
http://electroniqueamateur.blogspot.com/2012/06/arduino-clavier-numerique-et-afficheur.html
*/
#include <Keypad.h>
const byte ROWS = 4; //nombre de lignes
const byte COLS = 4; //nombre de colonnes
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //entrées numériques où sont branchées les lignes
byte colPins[COLS] = {5, 4, 3, 2}; //entrées numériques où sont branchées les colonnes
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
void setup(){
keypad.setDebounceTime(500);
Serial.begin(9600);
}
void loop(){
char key = keypad.getKey();
if (key != NO_KEY){
Serial.println(key);
}
}

Ensuite, j'ai ajouté un afficheur à cristaux liquides afin que le clavier numérique contrôle ce qui était affiché à l'écran (une sorte de mini machine à écrire, si on veut).  Dans ce sketch, la touche "*" permet d'effacer le dernier caractère affiché.  

Ce fut l'occasion de faire l'essai de mon nouveau Arduino Mega car mon afficheur LCD a besoin de 6 ports numériques, alors que le clavier en nécessite 8!  

Cette fois, le clavier est branché de la façon suivante:  

pin 1 du clavier à la pin 39 du Mega
pin 2 du clavier à la pin 41 du Mega
pin 3 du clavier à la pin 43 du Mega
pin 4 du clavier à la pin 45 du Mega
pin 5 du clavier à la pin 47 du Mega
pin 6 du clavier à la pin 49 du Mega
pin 7 du clavier à la pin 51 du Mega
pin 8 du clavier à la pin 53 du Mega

(Voir cet article pour les branchements de mon afficheur LCD.)

/*********************************************************
ArduinoTypewriter: ce qu'on écrit sur le clavier numérique
s'affiche sur le LCD
Matériel: Arduino Mega, clavier numérique et LCD.
26 juin 2012.
http://electroniqueamateur.blogspot.com/2012/06/arduino-clavier-numerique-et-afficheur.html
**********************************************************/
// include the library code:
#include <LiquidCrystal.h>
#include <Keypad.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const byte ROWS = 4; //nombre de lignes
const byte COLS = 4; //nombre de colonnes
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {39, 41, 43, 45};
//entrées numériques où sont branchées les lignes
byte colPins[COLS] = {47, 49, 51, 53};
//entrées numériques où sont branchées les colonnes
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
int ligne = 0;
int colonne = 0;
void setup() {
// nombre de colonnes et de lignes du LCD:
lcd.begin(16, 2);
keypad.setDebounceTime(300);
}
void loop() {
char key = keypad.getKey();
if (key != NO_KEY){
if (key == '*'){//on efface le dernier chiffre
if (colonne > 0){
colonne = colonne - 1;
}
else {
ligne = !(ligne);
colonne = 15;
}
lcd.setCursor(colonne, ligne);
lcd.print(' ');
}
else{ // alors on a tapé un chiffre
lcd.setCursor(colonne, ligne);
lcd.print(key);
colonne=colonne+1;
if (colonne > 15){
if (ligne == 0){// alors on saute à la ligne suivante
colonne = 0;
ligne = 1;
}
else{// alors on retourne au début
colonne = 0;
ligne = 0;
}
}
}
}
}

Finalement, j'ai conçu un petit "Quiz Mathématique" permettant de pratiquer mon calcul mental:  si on presse la touche "A", l'afficheur nous propose une addition.  On tape la réponse, on appuie sur la touche "#" et l'afficheur nous indique si notre réponse est correcte ou non.  Pour se faire proposer une autre addition, on appuie à nouveau sur la touche "A"... ou sur la touche "B" si on préfère une soustraction (la touche "C" propose une multiplication, et la touche "D" propose une division).  C'est un peu comme une calculatrice, mais à l'envers puisque c'est vous qui devez trouver la réponse.  Encore une fois, la touche "*" permet d'effacer le dernier caractère affiché, afin de corriger une erreur d'entrée.

/**********************************************************************
On tente de résoudre un problème mathématique posé par Arduino.
Matériel: Arduino Mega, Afficheur LCD, Keypad numérique 4X4.
26 juin 2012
http://electroniqueamateur.blogspot.com/2012/06/arduino-clavier-numerique-et-afficheur.html
**********************************************************************/
// deux bibiliothèques sont utilisées:
#include <LiquidCrystal.h>
#include <Keypad.h>
// les broches associéees à l'afficheur
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// initialisation du keypad
const byte ROWS = 4; //nombre de lignes
const byte COLS = 4; //nombre de colonnes
char keys[ROWS][COLS] = {
{
'1','2','3','A' },
{
'4','5','6','B' },
{
'7','8','9','C' },
{
'*','0','#','D' }
};
byte rowPins[ROWS] = {
39, 41, 43, 45}; //entrées numériques où sont branchées les lignes
byte colPins[COLS] = {
47, 49, 51, 53}; //entrées numériques où sont branchées les colonnes
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
int colonne = 0;
char derniereTouche;
int mode = 0; // 0: addition, 1: soustraction, 2: multiplication 3: division
long premierNombre = 0;
long deuxiemeNombre = 0;
long reponseSoumise = 0;
void setup() {
// nombre de colonnes et de lignes de l'afficheur LCD
lcd.begin(16, 2);
keypad.setDebounceTime(300); //fonctionne bien pour mon modèle
randomSeed(analogRead(0));
AfficheQuestion();
}
void loop() {
char key = keypad.getKey();
if (key != NO_KEY){
if (key == 'A'){
mode = 0; // addition
AfficheQuestion();
}
if (key == 'B'){
mode = 1; // addition
AfficheQuestion();
}
if (key == 'C'){
mode = 2; // addition
AfficheQuestion();
}
if (key == 'D'){
mode = 3; // addition
AfficheQuestion();
}
if (key == '*'){//on efface le dernier chiffre
if (colonne > 0){
colonne = colonne - 1;
lcd.setCursor(colonne, 1);
lcd.print(' ');
reponseSoumise = (reponseSoumise-(int(derniereTouche)-48))/10;
}
}
if ((key == '0') || (key == '1') || (key == '2')|| (key == '3')
|| (key == '4')|| (key == '5')|| (key == '6')|| (key == '7')
|| (key == '8')|| (key == '9')){ // alors on a tapé un chiffre
if (colonne < 10){
lcd.setCursor(colonne, 1);
lcd.print(key);
colonne=colonne+1;
reponseSoumise = reponseSoumise*10+(int(key)-48);
derniereTouche=key;
}
}
if (key == '#'){//on vérifie la réponse
lcd.clear();
lcd.setCursor(0, 0);
colonne = 0;
if (mode == 0){
if (reponseSoumise == (premierNombre+deuxiemeNombre)){
lcd.print("Bravo:");
lcd.setCursor(0, 1);
lcd.print("Bonne reponse!");
}
else{
lcd.print("NON! Reponse:");
lcd.setCursor(0, 1);
lcd.print(premierNombre+deuxiemeNombre);
}
}
if (mode == 1){
if (reponseSoumise == (premierNombre-deuxiemeNombre)){
lcd.print("Bravo:");
lcd.setCursor(0, 1);
lcd.print("Bonne reponse!");
}
else{
lcd.print("NON! Reponse:");
lcd.setCursor(0, 1);
lcd.print(premierNombre-deuxiemeNombre);
}
}
if (mode == 2){
if (reponseSoumise == (premierNombre*deuxiemeNombre)){
lcd.print("Bravo:");
lcd.setCursor(0, 1);
lcd.print("Bonne reponse!");
}
else{
lcd.print("NON! Reponse:");
lcd.setCursor(0, 1);
lcd.print(premierNombre*deuxiemeNombre);
}
}
if (mode == 3){
if (reponseSoumise == (premierNombre/deuxiemeNombre)){
lcd.print("Bravo:");
lcd.setCursor(0, 1);
lcd.print("Bonne reponse!");
}
else{
lcd.print("NON! Reponse:");
lcd.setCursor(0, 1);
lcd.print(premierNombre/deuxiemeNombre);
}
}
}
}
}
void AfficheQuestion ()
{
long temp;
if (mode == 0){ // addition: deux nombres entre 0 et 99
premierNombre = random(100);
deuxiemeNombre = random(100);
}
if (mode == 1){ // soustraction
premierNombre = random(100);
deuxiemeNombre = random(100);
if (deuxiemeNombre > premierNombre){
temp = premierNombre;
premierNombre = deuxiemeNombre;
deuxiemeNombre = temp;
}
}
if (mode == 2){ //multiplication
premierNombre = random(11);
deuxiemeNombre = random(11);
}
if (mode == 3){ //division
deuxiemeNombre = random(1,10);
premierNombre = random(11)*deuxiemeNombre;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(premierNombre);
if (mode == 0){
lcd.setCursor(3, 0);
lcd.print('+');
lcd.setCursor(5, 0);
}
if (mode == 1){
lcd.setCursor(3, 0);
lcd.print('-');
lcd.setCursor(5, 0);
}
if (mode == 2){
lcd.setCursor(3, 0);
lcd.print('*');
lcd.setCursor(5, 0);
}
if (mode == 3){
lcd.setCursor(3, 0);
lcd.print('/');
lcd.setCursor(5, 0);
}
lcd.print(deuxiemeNombre);
reponseSoumise = 0;
colonne = 0;
}
view raw MathQuiz.ino hosted with ❤ by GitHub

Yves Pelletier (Twitter: @ElectroAmateur)

vendredi 15 juin 2012

MIT 6.002x: un bilan

Le cours "Electronics and Circuits" offert par MITx est terminé depuis quelques jours déjà.  MITx (maintenant rebaptisé edX suite à une fusion avec Harvard), est l'initiative de cours en ligne gratuits offerte par le célèbre Massachusetts Institute of Technology (j'en avais brièvement parlé ici, lors de mon inscription au cours).

Le moins qu'on puisse dire, c'est que ce cours a suscité de l'intérêt:  plus de 150 000 personnes d'origines diverses y ont ouvert un compte.  Par contre, seulement 69 000 d'entre eux ont réellement démarré le cours en s'attaquant aux exercices proposés lors de la première semaine.  Environ 10 000 étudiants étaient encore actifs au milieu du semestre, et 7 157 d'entre nous avons réussi le cours.

Le faible taux de persévérance et de réussite n'a rien de surprenant:  le cours était assez difficile et demandait une appréciable quantité de travail hebdomadaire.  De nombreux étudiants ont réalisé en cours de route que leurs connaissances en mathématiques étaient trop chancelantes pour leur permettre d'effectuer les tâches demandées (équations différentielles, nombres complexes...).

Le Dr. Anant Agarwal a quand même de quoi être fier:  combien de professeurs peuvent se vanter d'avoir offert une formation universitaire de qualité à plus de 7000 personnes en 14 semaines seulement?

J'ai personnellement apprécié l'expérience.  J'ai appris beaucoup de choses sur un sujet qui me passionne (les mosfets, les amplificateurs opérationnels, etc).  Les explications sur vidéo données par le professeur Agarwal étaient d'une impressionnante limpidité (malgré leur longueur parfois excessive causée par des répétitions à mon avis inutiles).  Les devoirs constituaient toujours un défi, mais le dynamique groupe de discussion était d'une aide précieuse pour obtenir quelques indices.  Et le simulateur de circuits permettait de combler en partie la principale lacune d'un cours en ligne:  l'impossibilité d'avoir accès à du matériel de laboratoire...

Pour ceux qui l'ont manqué, le cours 6.002x sera redémarré au cours des prochains mois; edX offrira dès l'automne de nouveaux cours qui n'ont malheureusement pas été identifiés.

Je participerai certainement à d'autres cours offerts par edX (ou par d'autres services du même genre, comme Coursera ou Udacity)...mais j'aimerais bien qu'une université francophone suive cet exemple et commence aussi à offrir des cours gratuits en ligne!

Yves Pelletier (Twitter: @ElectroAmateur)