Vous êtes sur la page 1sur 23

Sommaire

1- Introduction générale
2-Assembleur ARM
3-Jeu d'instructions
4-Opérations binaires

A. Benali

1 2

STM32 MCUs c'est quoi ? Notion cortex

•La famille des processeurs STM32 est issue du fabriquant En lien avec les différentes applications embarquées ARM
STMicroelectronics développe des processeurs de profiles différents:
•Les chips STM32 sont groupés dans des séries fondées -Série Cortex-A : Application processor pour les OS complexes
autours du même processeur ou core ARM et applications avec l'utilisateur
•En interne chaque microcontrolleur est composé d'un core, -Série Cortex-R: Real-time systems
RAM statique, mémoire flash, interface de debogage et divers -Série Cortex-M: Microcontrolleurs optimisés pour les
périphériques, applications à faible coût de revient.
•Les core ARM sont classés par caractéristiques en Cortex-M7,
Cortex M4F,Cortex-M3, Cortex M0

Mini Quad Core ARM


Cortex-A7 RK3128
Android 4.4.4 TV Box
3 Player 4
Carte stm32F3Discovery Pourquoi stm32 ?

LED -Processeur STM32 est fourni avec de nombreux choix de


USB toolchain.
-En contrast Arduino ou PIC offrent des librairies simples et des
toolchains accessible à des programmeurs non expérimentés
Quartz -Arduino offre des solutions de prototypages simples et rapides.
-Les plateformes Arduino ne sont pas adaptées pour gérer des
activités concourantes en temps réel
-L'environnement Arduino ne prévoit pas en natif un système
STM32F303 d'exploitation RTOS temps réel.
LED

5 6

La famille des processeurs ARM Architecture des processeurs ARM


Cortex-A series (Application)  ARM architecture is a family of RISC-based processor
– High performance processors architectures
capable of full Operating System (OS) – Well-known for its power efficiency;
support;
Cortex-R series (Real-time)
– Hence widely used in mobile devices, such as smart-phones
– High performance for real-time and tablets
applications; – Designed and licensed to a wide eco-system by ARM
– High reliability
Cortex-M series (Microcontroller)  ARM Holdings
– Cost-sensitive solutions for deterministic
microcontroller
– The company designs ARM-based processors;
applications; – Does not manufacture, but licenses designs to semiconductor
– Applications include microcontrollers, partners who add their own Intellectual Property (IP)
mixed signal on top of ARM’s IP, fabricate and sell to customers;
devices, smart sensors, automotive body – Also offer other IP apart from processors, such as physical
electronics and
airbags;
IPs, interconnect IPs, graphics cores, and development
tools
7 8
Design an ARM-based SoC STM32 Cortex-M
Select a set of Intellectual Property (IP) cores from ARM and/or
other third-party IP vendors
 Integrate IP cores into a single chip design
 Give design to semiconductor foundries for chip fabrication

Licensable IPs SoC Design

9 10

STM32 Cortex-M STM32 :l'Offre complète

11 12
STM32 : Cortex M lequel choisir STM32 : Cortex M lequel choisir

• M0 --- Low end, Low power 32-bit. 8-bit/16-bit


The Cortex M4 and M7 are
replacement Binary Compatible
• M3 --- Main stream 32-bit
Integer SIMD Instructions – 8
• M4 --- M3 + DSP  STM32f3 à étudier & 16-bit data – Results
• M7 --- Plus de DSP grâce à une meilleure architecture packed into 32-bit
pipeline et plus de mémoire
MAC instructions are single
cycle but remember this is
RISC!

www.arm.com

Multiplier Accumulator (MAC) Jeu d'instructions Cortex-M


In computing, especially digital signal processing, the multiply–
accumulate operation is a common step that computes the
product of two numbers and adds that product to an
accumulator. The hardware unit that performs the operation is
known as a multiplier–accumulator (MAC, or MAC unit); the
operation itself is a ← a + ( b × c )

16
Cortex-M3 Cortex-M4

ARMv7M ARMv7E-M

17 18

Instructions du Cortex M Processor's technical reference manual

Les Cortex M4 et
M7 ont des binaires
compatibles.

Integer SIMD
Instructions -
Données 8 et 16 bits
- Résultats
compressés en 32
bits

Les instructions
MAC sont à cycle
unique

19 20
Architecture STM32f303VCT6 Architecture STM32f303VCT6

4 sources Les mains systèmes STM32F303xB/C/D/E, STM32F358xC et STM32F398xE


horloges consistent:
• Cinq masters:
72Mhz
–Cortex M4 core I-bus
–Cortex M4 core D-bus
–Cortex M4 core S-bus
– GP-DMA1 and GP-DMA2 (general-purpose DMAs)
• Sept slaves:
– Internal Flash memory on the DCode
72Mhz – Internal Flash memory on ICode
– Up to Internal 40 Kbyte SRAM
36Mhz – Internal 8 Kbyte CCM SRAM
72Mhz – AHB to APBx (APB1 or APB2), which connect all the APB peripherals
– AHB dedicated to GPIO ports
– ADCs 1, 2, 3 and 4.

source : www.st.com/resource/en/reference_manual/dm00043574.pdf " 21 22

Architecture STM32f303VCT6 Organisation de la mémoire


La mémoire de programme, la mémoire de données, les registres et les ports
S0: I-bus d'E / S sont organisés dans le même espace d'adressage linéaire de 4 Go.
Ce bus relie le bus d'instructions du coeur M4 au BusMatrix. Il est utilisé pour
accéder aux instructions dans internal Flash memory, SRAM et CCM SRAM. Les octets sont codés en mémoire au format Little Endian. Le LSB d'un mot est
stocké en premier et l'octet le plus significatif (MSB) à l'adresse la plus élevée.
S1: D-bus
Ce bus relie le bus de données du coeur M4 au BusMatrix. Il est utilisé pour L'espace mémoire adressable est divisé en 8 blocs principaux de 512 Mo
cibler la internal Flash memory, la SRAM et la CCM SRAM. chacun.

S2: S-bus
Ce bus relie le bus système de données du coeur M4 au BusMatrix. Ce bus
cible les périphériques ou SRAM. Ses cibles sont SRAM, le bus AHB vers les
bridge APB1/APB2 le AHB IO port ainsi que le périphérique ADC.

S3, S4: DMA-bus


Ce bus connecte l'interface master AHB de la DMA au BusMatrix. Celui-ci
gère les accès DMA à la flash, SRAM et périphériques.

23 24
Organisation de la mémoire … APB1 Organisation de la mémoire … APB2

25 26

Architecture simplifiée bus périphériques Les registres GPIO

0x4800 0400
Pour accéder à un périphérique GPIO

0x4800 0008
GPIOB 0x4800 0400
les seuls gpio qui
GPIOA 0x4800 0000
réagissent aux adresses 27 28
Outils et environnement développement Outils et environnement développement

• Freescale FRDM (€12-€50)


• NXP LPCXpresso (€20- €35)
• Atmel Xplained (€ 30- € 40)
• ST Discovery (€ 15 - € 50)
• TI Launch Pad (€ 15- € 30)

STM32303C-EVAL Evaluation board for


STM32F3Discovery STM32 F3 series - with STM32F303 MCU

29

STM32 vs Arduino (Radiospares) Outils développement et toolchains

propriétaire, gratuit pour STM32L0 et F0

Arduino UNO(19.28€)
Arduino MEGA (43.51€)

STM32F4 Produit libres à


discovery base de gcc
(18.72€)

STM32F4 Nucleo (11€) 31


Modèle du programmeur
Sommaire
Adresses Mémoire
Processeur

1- Introduction générale
Données
2-Assembleur ARM Registres
3-Jeu d'instructions
4-Opérations binaires  Mémoire extérieure au processeur.
 Des registres internes au processeur.

 Instructions de transferts entre les registres


 Opérations sur le contenu des registres.
 Instructions de transferts Registres-Mémoire.

 L’espace mémoire est communs aux instructions et aux


données.
33
 Les périphériques occupent une partie de l’espace mémoire.

Caractéristiques des processeurs ARM Caractéristiques des processeurs ARM : Cortex M

 Processeur RISC 32 bits :


 Les registres internes font 32 bits
 Les données manipulées sont de type
 word (32 bits),
 half–word (16 bits)
 byte (8 bits).

Historiquement les instructions faisaient toutes 32 bits.


Pour être plus compacts, réduction des coûts alors ajout jeux
d’instructions simplifiés :
Les instructions font :
• 32 bits (mode ARM)
• 16 bits (mode THUMB)
• mixte 32/16 bits (mode THUMB 2) 36
Modèle programmeur : les registres Modèle programmeur : Registres d'état xPSR
The PSRs are subdivided into three status registers:
16 registres généraux
• Application Program Status register (APSR)
3 registres avec des rôles particuliers :
• Interrupt Program Status register (IPSR)
• r15 (PC) : Compteur programme
• Execution Program Status register (EPSR)
• r14 (LR) : Link register (adresse de retour)
• r13 (SP) : Stack pointer (pointeur de pile)

• N : négatif  Interruptions :
• Z : zéro • F : désactiver les FIQ
• C : retenue • I : désactiver les IRQ
• V : dépassement  T : Thumb
 Mode de fonctionnement

Modèle programmeur : Registres d'état xPSR


Sommaire
MRS r0, APSR ; Read Flag state into R0

MRS r0, IPSR ; Read Exception/Interrupt state 1- Introduction générale


MRS r0, EPSR ; Read Execution state 2-Assembleur ARM
3-Jeu d'instructions
MSR APSR, r0 ; Write Flag state Manipulation des données
when accessing xPSR (all three PSRs as one), the symbol PSR
Transfert des données
is used: Branchement
Exécution conditionnelle
MRS r0, PSR ; Read the combined program status word 4-Opérations binaires
MSR PSR, r0 ; Write combined program state word
40
Jeu d'instructions : spécificité ARM Jeu d'instructions : Classification

Instructions ARM/Thumb On peut classer les instructions en trois grandes catégories :

 À l’origine les syntaxes des instructions thumb et arm étaient 1. Traitement et manipulation des données :
différentes. • Arithmétiques et logiques
 Une syntaxe unifiée (unified) est supportée par les versions • Tests et comparaisons
récentes des outils de développement. 2. Transfert de données depuis et vers la mémoire
 Du fait de la taille des instructions thumb (16-bits) certaines 3. Contrôle de flot
restrictions d’usage existent. • Branchements

Dans ce cours on s'intéresse à la syntaxe unifiée

Jeu d'instructions : Opérations arithmétique/logique


Sommaire Forme générale : Opération sur trois registres

OPE R_Dest, R_Src1, R_Src2


1- Introduction générale
2-Assembleur ARM
3-Jeu d'instructions
Exemples
Manipulation des données
Transfert des données AND r0,r1,r2 pour (r0 = r1&r2)
Branchement ADD r5,r1,r5 pour (r5 = r1 + r5)
Exécution conditionnelle
4-Opérations binaires

43
Jeu d'instructions : Opérations arithmétique/logique Transfert Données entre registres

ADD r0,r1,r2 r0=r1+r2 Addition


Opération sur 2 registres
ADC r0,r1,r2 r0=r1+r2+C Addition avec retenue
OPE R_Dest, R_Src
SUB r0,r1,r2 r0=r1-r2 Soustraction
SBC r0,r1,r2 r0=r1-r2-C+1 Soustraction avec retenue
Exemples
RSB r0,r1,r2 r0=r2-r1 Soustraction inversée
MOV r0,r1 pour (r0 = r1)
RSC r0,r1,r2 r0=r2-r1-C+1 Soustraction inversée avec retenue
MOV pc,lr pour (pc = lr )
AND r0,r1,r2 r0=r1&r2 Et binaire
MVN r0,r1 pour (r0 = ~r1)
ORR r0,r1,r2 r0=r1|r2 Ou binaire
EOR r0,r1,r2 r0=r1^r2 Ou exclusif binaire
BIC r0,r1,r2 r0=r1&~r2 Met à 0 les bits de r1 indiqués par r2

Opérations de décalage de registres Opérations de décalage de registres

Opération sur 3 registres


LSL : Logical Left Shift
OPE R_Dest, R_Src, R_Dec ASR: Arithmetic Right Shift

CF Destination 0 Destination CF
Exemples
Multiplication by a power of 2 Division by a power of 2,
LSL r0,r1,r2 pour (r0 = r1 << r2[7:0]) preserving the sign bit
ASR r3,r4,r5 pour (r3 = r4 >> r5[7:0])

Seul l’octet de poids faible de R_Dec est utilisé.


LSR : Logical Shift Right ROR: Rotate Right
Les instructions
LSL Décalage logique vers la gauche ...0 Destination CF Destination CF

LSR Décalage logique vers la droite Division by a power of 2 Bit rotate with wrap around
ASL Décalage arithmétique vers la gauche from LSB to MSB
ASR Décalage arithmétique vers la droite
ROR Décalage circulaire vers la droite
Opérations de décalage de registres et PSR Opérations de comparaison

Par défaut, les opérations arithmétiques et logiques ne modifient Les instructions


pas les indicateurs (N,Z,C,V) du PSR.
Il faut ajouter le suffixe “S” au mnémonique de l’instruction

Exemples CMP r0,r1 psr r0-r1 Comparer


ADDS r0,r1,r2 CMN r0,r1 psr r0+r1 Comparer à l’inverse
ANDS r0,r1,r2 TST r0,r1 psr r0&r1 Testerlesbitsindiquésparr1
MOVS r0,r1 TEQ r0,r1 psr r0^r1 Tester l’égalité bit à bit

Ces instructions ne modifient que les bits (N,Z,C,V) du PSR, le


résultat n’est pas gardé.

Opérandes immédiats Les multiplications


 Les opérandes ne peuvent être que des registres.
 Un des opérandes source peut être un immédiat.
 En fonction des versions de l’architecture :
• Un immédiat est une valeur constante qui est encodée
• Toutes les instructions ne sont pas disponibles sur toutes
dans une partie de l’instruction.
les architectures.
 On doit les précéder du symbole ‘#’.
• La retenue ”C” et le dépassement “V” n’ont pas toujours le
 Il peut être décimal, hexadécimal (0x) ou octal (0).
même comportement.
Exemples
MOV r0,#0x20 Exemples
CMP r0,#32
ADD r0,r1,#1 MUL r0,r1,r2 r0=r1*r2 multiplication
Two 32-bit instructions (movw, movt) are used to load the MLA r0,r1,r2,r3 r0=r1+r2*r3 mult. et accumulation
lower/upper halves of the address of counter (see mov32) MLS r0,r1,r2,r3 r0=r1-r2*r3 mult. soustraction
Exemples UMULL r0,r1,r2,r3 {r1,r0}=r2*r3 mult. 64bits non signée
MOVW r0,#0x2000 SMULL r0,r1,r2,r3 {r1,r0}=r2*r3 mult. 64bits signée
MOVT r0,#0x1111 UMLAL r0,r1,r2,r3 {r1,r0}+=r2*r3 MAC 64bits non signée
SMLAL r0,r1,r2,r3 {r1,r0}+=r2*r3 MAC 64bits signée
Transfert de données (1/2)
Sommaire Deux instructions de transfert de données entre la mémoire et
les registres.
 LDR : charger un registre avec une donnée en mémoire
1- Introduction générale  STR : enregistrer la valeur du registre en mémoire
2-Assembleur ARM
3-Jeu d'instructions Exemples
Manipulation des données LDR r0,[r1] (r0 [r1])
STR r0,[r1] (r0 [r1] ) Attention au sens de transfert de données
Transfert des données
Branchement
Exécution conditionnelle
4-Opérations binaires

53

Transfert de données (2/2) taille des données Modes d'adressages : indirect

LDR/STR : mots de 32 bits (words) Adressage indirect


LDRH/STRH : mots de 16 bits (half words) LDR r0,[r1] (r0 [r1])
LDRB/STRB : mots de 8 bits (byte)
Adressage indirect avec déplacement (offset)
Généralement, les adresses doivent être alignées : LDR r0,[r1,#8] (r0 [r1 + 8])
LDR r0,[r1,r2] (r0  [r1 + r2])
LDR/STR :4
LDRH/STRH :2 Adressage indirect avec déplacement et pré-incrémentation
LDRB/STRB : quelconque LDR r0,[r1,#8]! (r1 = r1 + 8 puis r0  [r1])

Adressage indirect avec déplacement et post-incrémentation


LDR r0,[r1],#8 (r0  [r1] puis r1 = r1 + 8)
Transferts multiples (1/2) Transferts multiples (2/2)

En plus des instructions LDR et STR le jeu d’instruction ARM Il existe 4 suffixes possibles pour les instructions de transferts
propose les instructions LDM et STM pour les transferts multiples. multiples :
 IA pour la post-incrémentation (Increment After )
Exemples  IB pour la pré-incrémentation (Increment Before)
LDMIA r0,{r1,r2,r3}  DA pour la post-décrémentation (Decrement After)
(r1  [r0])  DB pour la pré-décrémentation (Decrement Before)
(r2 [r0 + 4])
(r3 [r0 + 8]) Pour que la valeur du registre d’adresse soit modifiée il faut
STMIA r0,{r1-r3} ou STMIA r0,{r1,r2,r3} ajouter (!)
(Mem[r0] = r1) LDMIA r0!,{r1-r3}
(Mem[r0 + 4] = r2)
(Mem[r0 + 8] = r3)

Branchements (1/2)
Sommaire Il existe deux instructions de branchement :

B adresse Aller à l’adresse


1- Introduction générale BX registre Aller à l’adresse pointée par le registre
2-Assembleur ARM et éventuellement changer de mode
(ARM/THUMB interworking)
3-Jeu d'instructions
Manipulation des données Ces instructions modifient le compteur programme "pc" (r15)
Transfert des données BL(X) Pour sauvegarder l’adresse de retour dans "lr" (r14)
Branchement L’adresse de retour est celle de l’instruction suivant le BL.
Exécution conditionnelle
4-Opérations binaires

59
Branchements (1/2) Branchements (2/2)
 B <label>  Pour les instructions B et BL l’adresse est stockée comme
 PC relative. ±32 Mbyte range. un immédiat qui représente un offset par rapport à la
 BL <subroutine> position actuelle :
 Stores return address in LR • l’offset est forcement limité
 Returning implemented by restoring the PC from LR  Pour l’instruction BX le mode (ARM/Thumb) est déterminé
 For non-leaf functions, LR will have to be stacked en fonction du bit de poids faible de l’adresse normalement
func1 func2 non utilisé (voir interworking)

STMFD :
: sp!,{regs,lr}
:
: :
:
BL func1 BL func2
:
: :
:
: LDMFD
sp!,{regs,pc} MOV pc, lr

Exécution conditionnelle (1/2)


Sommaire L’exécution des instructions peut être rendue conditionnelle en
rajoutant les suffixes suivant :
EQ Equal Z == 1
1- Introduction générale NE Not equal Z == 0
2-Assembleur ARM CS/HS Carry set/unsigned higher or same C == 1
3-Jeu d'instructions CC/LO Carry clear/unsigned lower C == 0
MI Minus/negative N == 1
Manipulation des données PL Plus/positive or zero N == 0
Transfert des données VS Overflow V == 1
Branchement VC No overflow V == 0
HI Unsigned higher C == 1 and Z == 0
Exécution conditionnelle LS Unsigned lower or same C == 0 or Z == 1
4-Opérations binaires GE Signed greater than or equal N == V
LT Signed less than N != V
GT Signed greater than Z == 0 and N == V
63 LE Signed less than or equal Z == 1 or N != V
Exécution conditionnelle (2/2)
Sommaire
Exemples

1- Introduction générale
CMP r0,r1 comparer r0à r1
2-Assembleur ARM
SUBGE r0,r0,r1 si r0 ≥ r1alors r0 = r0-r1 3-Jeu d'instructions
SUBLT r0,r1,r0 si r0 < r1alors r0 = r1-r0 Manipulation des données
SUBS r0,r1,r2 r0 = r1-r2
BEQ address aller à adresse si le résultat est nul
Transfert des données
BNE address aller à adresse si différent Branchement
Exécution conditionnelle
4-Opérations binaires

66

Rôle des registres Opérateur ou logique, et logique


Certains bits servent à mettre en route les timers a 11001101
Nous devons être capable de modifier que les bits concernés | ou
Certaines options doivent être positionnées au préalable b 00010100
1 1011101
Cet opérateur affecte un 1 là ou le second opérande
est à 1 et laisse inchangés les autres

Opérateurs bit à bits en C


Il existe 6 opérateurs a 11001101
& et
b 00010100
00000100
Cet opérateur produit à zéro à tous les bits où il y a un
0 dans la seconde opérande.
Opérateur xor logique, inversion Qu'est ce qu'un masque ?

a 11001101 Un masque est un mot binaire que l'on construit pour servir de
^ xor
b 00010100 filtre pour manipuler les registres.

1 1011001
Cet opérateur produit 1 pour chaque bits différents Masque 11011011

a 11001101 comment construire ce masque ?


b=~a
b 10110010 a) Par sa valeur décimale 219
b) Par sa valeur binaire 0xDB
c) Par opérateur bit à bit

Qu'est ce qu'un masque ? Intérêt du masque


Masque 11011011
Intérêt
on remarque les bits à 0 et les bits à 1 1)On sait immédiatement quel bits passent à 1 et ceux à 0
Effectuer les décalages binaires nécessaire 2)Le compilateur se charge de calculer le masque
inverser si nécessaire
Comment les utiliser ?
Mettre à 1 un bit dans un registre (fabriquer un masque avec 1 à
l'emplacement voulu n)
Value =value| (1<<n)
1<<2 opérateur de décalage 2 fois à gauche /*équivalent à */
1<<5 opérateur de décalage 5 fois à gauche value |=(1<<n);
(1<<2)|(1<<5)= 0 0 1 0 0 1 0 0

~((1<<2)|(1<<5))= 1 1 0 1 1 0 1 1
Intérêt du masque plusieurs bits à 1

Mettre à 1 plusieurs bit dans un registre (fabriquer un masque


Intérêt
avec 1 à l'emplacement voulu n0, n1, n2)
1)On sait immédiatement quel bits passent à 1 et ceux à 0 Value =value| ((1<<n0)|(1<<n1)| (1<<n2)))
2)Le compilateur se charge de calculer le masque /*équivalent à */
value |=(1<<n);
Comment les utiliser ?
Mettre à 1 un bit dans un registre (fabriquer un masque avec 1 à Mettre à 0 un bit dans un registre (mettre à 1 le bit qui nous
l'emplacement voulu n) interesse puis inverser)
Value =value| (1<<n) Value =value& ~(1<<n);
/*équivalent à */ /*équivalent à */
value |=(1<<n); value &=~(1<<n);
/*Plusieurs bits */
value &=~((1<<n0)|(1<<n1)|(1<<n2));
On ne peut pas fabriquer un masque pour forcer à 1 et 0 en
même temps

Cas des registres timers Baremetal c'est quoi ?

Carte micro-controller est à ressources


limitées

Obligation de baremetal : tout


programmer soi même

tools : compiler, assembler, linker et


binary generator

lorsque exécutable généré .hex ou .bin


upload vers MCU

http://embedds.com/programming-stm32-discovery-using-
gnu-tools-linker-script/ 76
Baremetal c'est quoi ? linker script

startup ou démarreur :sert à se substituer Le linker script est un fichier qui décrit les
au système d'exploitation pour charger le spécification du microcontrôleur comme
code objet généré en se basant sur linker les adresses mémoire, les sections
script mémoires, allocation de pile et
emplacements.

Habituellement, les linkers scripts


d'éditeur de liens sont déjà écrits et
peuvent être trouvés sur des exemples
de projets.

MEMORY {
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
}

77 78

linker script linker script

SECTIONS {
.text : Le code de démarrage est exécuté juste après la réinitialisation
{ du microcontrôleur et est exécuté avant le programme principal.
. = ALIGN(4);
KEEP(*(.interrupt_vector)) En tant que script de liaison, le code de démarrage est
*(.text) généralement implémenté en tant que code universel pour tout le
*(.text*) même type de microcontrôleur.
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH

79 80
Présentation openstm32 : gcc La table des vecteurs d'interruptions

81
au reset le sp=_estack qui charge le vecteur 0 celui Reset_Handler
82

un projet stm32 : startup_stm32f30x.s section initialisation de memoire

section de
code

83 84
section branchement au main system_stm32f30x.c

Init périph dans


system_stm32f30x.c

branch with link


au main librairie libc avec
compilateur fonctions
résidentes comme malloc

85 86

Outils et environnement développement Vendors temps-réel

• Real Time Operating Systems


• USB Host/OTG and Ethernet stacks
Un large choix de compilateurs et environnement de
• Graphics libraries
développements.

87 88
STM32 vs Arduino

89

Vous aimerez peut-être aussi