L’objectif est de mettre en oeuvre la carte STM32 NUCLEO (F401RE) et l’environnement de développement mbed.
La carte STM32 NUCLEO-F401RE est une carte de développement fabriquée par STMicroelectronics autour d’un micro-contrôleur ARM Cortex 32 bits STM32. Toutes les cartes Nucleo de STMicroelectronics sont prises en charge par la plateforme de développement mbed. Elle est commercialisée pour un prix d’environ 10 euros !
Quelques caractéristiques :
Et aussi :
La carte Nucleo propose de 2 types d’extensions :
Site officiel : www.st.com
Documentation : nucleo-f401re.pdf
mbed est un IDE (Integrated Development Environment) ou EDI (Environnement de Développement Intégré) en ligne. Il nécessite donc une connexion Internet et un navigateur web.
Site : https://developer.mbed.org/
Rapide tour d’horizon :
La première étape pour pouvoir utiliser mbed consiste à s’enregistrer (Log In/Signup).
Pour développer avec la carte Nucleo, il y a 2 façons de procéder :
mbed.htm
dans un navigateur.Objectifs :
Étapes par étapes :
Créer une nouvelle application en cliquant sur New puis choisir comme modèle (template) “Blinky LED …” et donner un nom à votre projet :
Vous pouvez modifier le code dans l’éditeur :
Pour tester, il suffit de compiler … d’enregistrer le binaire fourni (extension .bin
) et de le copier dans le dossier NUCLEO.
Bonus : Essayez maintenant de créer un programme pour tester le bouton USER (bleu sur la carte) à partir du modèle “Read the board user button state”.
Objectifs :
Étapes par étapes :
Créer une nouvelle application en cliquant sur New puis choisir comme modèle (template) “Empty Program” et donner un nom à votre projet :
Ajouter un nouveau fichier avec New File :
Puis éditez le programme suivant :
On obtient une erreur à la compilation (ce qui est normal car la bibliothèque mbed n’a pas été intégrée au projet et donc le fichier mbed.h
est introuvable) :
On peut la corriger en cliquant sur “Fix it!” ce qui va nous permettre d’importer la bibliothèque mbed :
Maintenant, on peut compiler avec succès.
On va maintenant importer une nouvelle bibliothèque à notre projet en cliquant sur Import, puis on recherche “morse” :
On séléctionne la bibliothèque MorseGenerator
, on clique sur Import! et on importe :
La classe MorseGenerator
a été ajoutée au projet et on peut accéder directement à sa documentation :
En utilisant la documentation (on peut utiliser le copier/coller), on édite le programme suivant :
#include "mbed.h"
#include "morsegenerator.h"
DigitalOut myled(LED1);
void morsecallback(int val)
{
myled = val;
}
int main() {
MorseGenerator morse = MorseGenerator(morsecallback);
while(1)
{
morse.transmit("SOS BTS SN");
wait(5);
}
}
On compile et on teste sur la carte !
La transmission série est un mode de transmission de données dans lequel les éléments d’information se succèdent les uns après les autres sur une seule voie entre deux points. Elle s’oppose à la transmission parallèle, qui transmet simultanément les éléments d’information sur plusieurs voies.
La transmission série domine dès que les composants ou périphériques à relier sont à quelque distance. L’ensemble des télécommunications s’établit sur des liaisons série.
La transmission série est très présente dans le monde industriel :
pour relier des capteurs/actionneurs (sensor bus) ou des composants de bas niveau, on utilise des technologies comme le bus TWI/I2C, 1-Wire, AS-i, …
pour relier des périphériques (appareils divers, système de commande, …), on utilise des bus de terrain (field bus) comme le bus CAN, DMX, les liaisons RS232 ou RS485, …
La transmission série se fait généralement par trames et nécessite la mise en oeuvre de protocole.
Remarque : Communiquer consiste à transmettre des informations, mais tant que les interlocuteurs ne lui ont pas attribué un sens, il ne s’agit que de données et pas d’information. Les interlocuteurs doivent donc non seulement parler un langage commun mais aussi maîtriser des règles minimales d’émission et de réception des données. C’est le rôle d’un protocole de s’assurer de tout cela.
La carte Nucleo dispose de 3 ports série UART (Universal Asynchronous Receiver Transmitter) : Serial1, Serial2 et Serial 6. Le port Serial2 est déjà utilisé par ST-LINK. On va donc utiliser le port Serial1 :
On va utiliser un adaptateur USB/RS232 (TTL) pour relier la carte Nucleo et le PC. La prise USB contient un convertisseur USB <-> Série (pl2303) avec un cable terminé par 4 fils :
On va donc réaliser le cablage suivant :
Remarque : détection sous Linux.
Côté PC, on utilisera un émulateur de terminal comme cutecomm ou minicom sous Linux ou HyperTerminal ou Teraterm sous Windows.
Pour accèder au port série, on utilise la classe Serial
à laquelle on précise en paramètre les broches utilisées pour les lignes TX et RX :
Il est possible de paramètrer le port série :
Pour émettre des données, il existe plusieurs méthodes : putc()
, puts()
, printf()
, write()
.
Pour recevoir des données, il existe aussi plusieurs méthodes : getc()
, read()
. On vérifiera préalablement la présence d’octets à lire avec readable()
.
if(port.readable())
{
char c;
c = port.getc();
switch(c)
{
case 'A' :
case 'a' :
// @todo : allumer la led
break;
case 'E' :
case 'e' :
// @todo : éteindre la led
break;
default :
port.printf("%c\n", c); // echo
break;
}
}
Vous devez écrire un programme qui communique avec votre PC par une liaison USB/RS232. Le programme doit :
Remarque : il vous est possible d’exporter votre projet ou tout simplement un fichier (main.cpp
par exemple) en cliquant avec le bouton droit :
Liens tutoriels :
La carte STM32 NUCLEO (F401RE) est supportée par PlatformIO avec le modèle STM32F401RE (96k RAM. 512k Flash). Différents frameworks sont disponibles dont Arduino.
Le fichier platformio.ini
sera alors :
; PlatformIO Project Configuration File
; https://docs.platformio.org/en/latest/boards/ststm32/genericSTM32F401RE.html
[env:genericSTM32F401RE]
platform = ststm32
board = genericSTM32F401RE
framework = arduino
upload_protocol = stlink
La carte étant branchée sur le port USB ST-LINK, on utilisera le protocole stlink
et PlatformIO sélectionnera alors OpenOCD :
Processing genericSTM32F401RE (platform: ststm32; board: genericSTM32F401RE; framework: arduino)
------------------------------------------------------------------------------------------------
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/genericSTM32F401RE.html
PLATFORM: ST STM32 (15.1.0) > STM32F401RE (96k RAM. 512k Flash)
HARDWARE: STM32F401RET6 84MHz, 96KB RAM, 512KB Flash
...
Building in release mode
Compiling .pio/build/genericSTM32F401RE/src/main.cpp.o
Linking .pio/build/genericSTM32F401RE/firmware.elf
Checking size .pio/build/genericSTM32F401RE/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM: [ ] 0.9% (used 856 bytes from 98304 bytes)
Flash: [ ] 2.6% (used 13736 bytes from 524288 bytes)
Configuring upload protocol...
AVAILABLE: blackmagic, dfu, jlink, serial, stlink
CURRENT: upload_protocol = stlink
Uploading .pio/build/genericSTM32F401RE/firmware.elf
xPack OpenOCD x86_64 Open On-Chip Debugger 0.11.0+dev (2021-10-16-21:15)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
debug_level: 1
hla_swd
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000060 msp: 0x20018000
** Programming Started **
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
shutdown command invoked
==== [SUCCESS] Took 2.64 seconds ===
Le programme peut aussi être fabriqué en ligne de commande (CLI) à partir du terminal :
$ pio run -v
Processing genericSTM32F401RE (platform: ststm32; board: genericSTM32F401RE; framework: arduino; upload_protocol: stlink; monitor_speed: 115200)
...
$ arm-none-eabi-objcopy -O binary .pio/build/genericSTM32F401RE/firmware.elf .pio/build/genericSTM32F401RE/firmware.bin
On obtient alors :
$ file .pio/build/genericSTM32F401RE/firmware.elf
.pio/build/genericSTM32F401RE/firmware.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
$ ls -hl .pio/build/genericSTM32F401RE/firmware.bin
-rwxrwxr-x 1 tv tv 14K déc. 6 10:00 .pio/build/genericSTM32F401RE/firmware.bin
Le firmware obtenu peut être directement copié dans le dossier NUCLEO
(l’espace flash est monté comme une clé USB) :
$ ls -l /media/$USERNAME/NUCLEO/
$ cp .pio/build/genericSTM32F401RE/firmware.bin /media/$USERNAME/NUCLEO/
Remarque : sous GNU/Linux, il est probablement nécessaire de configurer udev pour les droits d’accès au périphérique (99-platformio-udev.rules).
Exemple mettant en oeuvre la LED et le bouton embarqués sur la carte STM32 NUCLEO :
#include <Arduino.h>
#define TEMPO 300
uint32_t tempo = TEMPO;
uint32_t choix = 1;
void setup()
{
Serial.begin(115200);
pinMode(PA5, OUTPUT);
pinMode(PC13, INPUT);
Serial.println("NUCLEO Ok");
}
void loop()
{
digitalWrite(PA5, HIGH);
delay(tempo);
digitalWrite(PA5, LOW);
delay(tempo);
int etatEntree = digitalRead(PC13);
if(etatEntree == 0)
{
choix = (++choix % 3) + 1;
tempo = choix * TEMPO;
Serial.println(tempo);
}
}
Dans PlatformIO, il existe aussi la carte ST Nucleo F401RE qui supporte en plus le framework Mbed.
Le fichier platformio.ini
sera alors :
; PlatformIO Project Configuration File
; https://docs.platformio.org/en/latest/boards/ststm32/genericSTM32F401RE.html
[env:nucleo_f401re]
platform = ststm32
board = nucleo_f401re
framework = mbed
upload_protocol = stlink
monitor_speed = 9600
Exemples avec mbed
:
#include "mbed.h"
DigitalOut myled(LED1);
int main()
{
while(1)
{
myled = !myled;
ThisThread::sleep_for(1s);
/*
myled = 1;
ThisThread::sleep_for(1s);
myled = 0;
ThisThread::sleep_for(1s);
*/
}
}
#include "mbed.h"
DigitalIn mybutton(USER_BUTTON);
DigitalOut myled(LED1);
int main()
{
while(1)
{
if(mybutton == 0)
{
myled = !myled;
printf("Appui bouton - Led %d\n", myled.read());
ThisThread::sleep_for(200ms); // 200 ms
}
}
}
#include "mbed.h"
InterruptIn mybutton(USER_BUTTON);
DigitalOut myled(LED1);
typedef std::chrono::duration<int,std::milli> millisecondes;
//std::chrono::duration<uint32_t, std::milli> tempo(200);
millisecondes tempo(200);
void pressed()
{
if(tempo == std::chrono::milliseconds(200))
{
tempo = std::chrono::milliseconds(1000); // 1 sec
}
else
{
tempo = std::chrono::milliseconds(200); // 200 ms
}
}
int main()
{
mybutton.fall(&pressed);
while (1)
{
myled = !myled;
ThisThread::sleep_for(tempo);
}
}
Timer
, Timeout
et Ticker
#include "mbed.h"
using namespace std::chrono;
InterruptIn mybutton(USER_BUTTON);
DigitalOut myled(LED1);
Timer timer;
Timeout timeout;
Ticker ticker;
std::chrono::microseconds t(3000000);
typedef std::chrono::duration<int,std::milli> millisecondes;
//std::chrono::duration<uint32_t, std::milli> tempo(200);
millisecondes tempo(200);
// Pour Ticker
void periode()
{
tempo = std::chrono::milliseconds(500);
}
// Pour Timeout
void change()
{
tempo = std::chrono::milliseconds(1000); // 1 sec
}
// Bouton
void pressed()
{
if(tempo == std::chrono::milliseconds(200))
{
tempo = std::chrono::milliseconds(1000); // 1 sec
}
else
{
tempo = std::chrono::milliseconds(200); // 200 ms
}
}
int main()
{
mybutton.fall(&pressed);
timeout.attach(&change, 3s); // au bout de 3s, passe en clignotement lent
ticker.attach(&periode, 5s); // toutes les 5s, cf. detach()
timer.start();
ThisThread::sleep_for(100ms);
timer.stop();
printf("timer = %llu secondes\n", duration_cast<seconds>(timer.elapsed_time()).count());
printf("timer = %llu millisecondes\n", duration_cast<milliseconds>(timer.elapsed_time()).count());
//printf("timer = %d millisecondes\n\r", timer.read_ms()); // obsolete
printf("timer = %llu microsecondes\n", duration_cast<microseconds>(timer.elapsed_time()).count());
//printf("timer = %d microsecondes\n\r", timer.read_us()); // obsolete
timer.start();
ThisThread::sleep_for(50ms);
timer.stop();
printf("timer = %llu millisecondes\n", duration_cast<milliseconds>(timer.elapsed_time()).count());
timer.reset();
timer.start();
ThisThread::sleep_for(100ms);
timer.stop();
printf("timer = %llu millisecondes\n", duration_cast<milliseconds>(timer.elapsed_time()).count());
while (1)
{
myled = !myled;
ThisThread::sleep_for(tempo);
}
}
$ dmesg
...
[85318.556891] usb 3-14: new full-speed USB device number 21 using xhci_hcd
[85318.573600] usb 3-14: New USB device found, idVendor=067b, idProduct=2303
[85318.573610] usb 3-14: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[85318.573615] usb 3-14: Product: USB-Serial Controller
[85318.573619] usb 3-14: Manufacturer: Prolific Technology Inc.
[85318.574320] pl2303 3-14:1.0: pl2303 converter detected
[85318.575054] usb 3-14: pl2303 converter now attached to ttyUSB0
$ lsusb
...
Bus 003 Device 021: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
$ ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 mai 27 10:01 /dev/ttyUSB0
$ sudo chmod 666 /dev/ttyUSB0
$ sudo vim /etc/udev/rules.d/51-usb.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE:="0666"