<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://projets-se.plil.fr/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Cpayet</id>
	<title>projets-se.plil.fr - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://projets-se.plil.fr/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Cpayet"/>
	<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php/Sp%C3%A9cial:Contributions/Cpayet"/>
	<updated>2026-05-14T01:05:45Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.1</generator>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=9880</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=9880"/>
		<updated>2026-03-16T14:24:03Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Cahier des charges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le zip qui sauve :&lt;br /&gt;
[[Fichier:Recherche ARM.zip|centré|vignette|Recherche ARM]]&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons un ensemble de fonctions liées à notre matériel pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vues ensemble précedemment sont appelées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basée sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&lt;br /&gt;
Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrôleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite procéder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
====== Startup (.s) ======&lt;br /&gt;
Le Startup file est crucial en Bare Metal. Une erreur dans ce fichier peut empêcher le microcontrôleur de démarrer ou provoquer des crashs.&lt;br /&gt;
Rôles principaux :&lt;br /&gt;
&lt;br /&gt;
- Définir l'environnement nécessaire à l'exécution de main().&lt;br /&gt;
&lt;br /&gt;
- S'exécuter avant main() et lancer ensuite main().&lt;br /&gt;
&lt;br /&gt;
- Être adapté à la target (processeur) utilisée.&lt;br /&gt;
&lt;br /&gt;
- Placer correctement la table des vecteurs comme exigé par les ARM Cortex-M.&lt;br /&gt;
&lt;br /&gt;
- Initialiser la pile correctement.&lt;br /&gt;
&lt;br /&gt;
- Initialiser les sections .data et .bss dans la SRAM.&lt;br /&gt;
&lt;br /&gt;
====== Linker (.ld) ======&lt;br /&gt;
Le Linker Script détermine comment les sections du code sont placées en mémoire.&lt;br /&gt;
''Fonctionnalités :''&lt;br /&gt;
* Définir les adresses absolues des sections.&lt;br /&gt;
* Définir les zones mémoire, leurs tailles et adresses.&lt;br /&gt;
* Fournir les instructions au linker GNU via l'option -T.&lt;br /&gt;
* L'extension de fichier est .ld.&lt;br /&gt;
&lt;br /&gt;
Le code linker sert à guider le compilateur pour assembler toutes les sections d’un programme en un fichier binaire unique.  Sur un microcontrôleur comme le '''STM32F410RB''', il faut indiquer où chaque partie du code et des données doit être placée dans la '''mémoire du MCU :'''&lt;br /&gt;
&lt;br /&gt;
- Où commence le code executable, son point d'entrée ;&lt;br /&gt;
&lt;br /&gt;
- Définit chaque region de la memoire qui existe sur le microcontrôleur et leur taille ;&lt;br /&gt;
&lt;br /&gt;
- Où placer les sections differentes du code en memoire (exemple : interruption, etc...).&lt;br /&gt;
Dans l'idée on peut le faire à la main afin de mieux assimiler le linker puis récuperer celui générer automatiquement par le logiciel STM32IDE (également disponible sur github) afin de ne pas avoir a y retoucher a chaque fois et ne pas briquer accidentellement notre puce.&lt;br /&gt;
Pour realiser un linker on doit preciser la SRAM et sa flash. Cette partie décrit les zones mémoire physiques du STM32F410RB. Il faut se fier au plan memoire de la datasheet p.40/763&lt;br /&gt;
Sur celui-ci nous constatons que SRAM est à 32kB et commence à 0x2000 0000 et que la fash commence à 0x0800 0000 et sa taille est de 128kB (donné à la figure 1 page 37/763).&lt;br /&gt;
&lt;br /&gt;
On peut alors placer dans le linker :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY&lt;br /&gt;
{&lt;br /&gt;
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(rx) → read et execute, donc on peut lire et exécuter du code dedans.&lt;br /&gt;
(rwx) → read, write, execute (en pratique, on n’exécute pas depuis la RAM, mais certains MCU le permettent).&lt;br /&gt;
'''&amp;lt;u&amp;gt;Sections&amp;lt;/u&amp;gt;'''Chaque programme compilé contient plusieurs sections générées par le compilateur. L'ordre à laquelle ils sont cris découle de la convention utilisé dans CMSIS (fichier startup fournis par ARM).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tous les compilateurs suivent la même idée :&lt;br /&gt;
&lt;br /&gt;
- Sections nécessaires au CPU d’abord (vecteurs, code)&lt;br /&gt;
&lt;br /&gt;
- Sections de données ensuite (initialisées, non initialisées)&lt;br /&gt;
&lt;br /&gt;
- Mémoire dynamique à la fin (heap, stack)&lt;br /&gt;
La '''partie ISR_VECTOR''' indique les vecteurs d'interruptions :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
isr_vector :&lt;br /&gt;
{&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
} &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;.isr_vector&amp;quot; : C'est la section qui contient le vecteur d'interruptions (ISR - Interrupt Service Routine). Les vecteurs d'interruptions sont des adresses de fonction qui seront appelées lorsque des interruptions spécifiques se produisent.&lt;br /&gt;
&amp;quot;KEEP&amp;quot; : Cette directive indique au linker de conserver cette section dans le binaire final, même si elle semble inutilisée par le programme. Cela est crucial pour les vecteurs d'interruptions qui doivent absolument être présents dans le binaire.&lt;br /&gt;
&amp;quot;&amp;gt;FLASH&amp;quot; : Cela indique que cette section doit être placée en mémoire Flash, qui est généralement de la mémoire non-volatile, utilisée pour stocker le programme.&lt;br /&gt;
Elle est placée en Flash pour être disponible dès le reset.&lt;br /&gt;
La '''partie TEXT''' contient le code de notre programme :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.text = code exécutable (fonctions, instructions machine).&lt;br /&gt;
.rodata = données constantes (const int, chaînes de caractères, etc.).&lt;br /&gt;
&lt;br /&gt;
_etext marque la fin de la zone code, utile pour copier ensuite la section .data au démarrage.&lt;br /&gt;
ALIGN(4) permet d'aligner l'adresse courante sur une frontière de 4 octets. Cela garantit que le code est correctement aligné en mémoire, ce qui peut être nécessaire pour des performances optimales ou des restrictions matérielles.&lt;br /&gt;
La '''partie DATA''' contient les variables initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.data contient les variables globales initialisées (ex: int x = 5;).&lt;br /&gt;
Ces valeurs sont stockées dans la Flash au moment de la programmation, mais copiées dans la RAM lors du démarrage (d’où &amp;gt;SRAM AT&amp;gt;FLASH).&lt;br /&gt;
&lt;br /&gt;
_sdata et _edata servent au code d’initialisation (startup.s) pour savoir quoi copier et combien d’octets.&lt;br /&gt;
La partie BSS contient les variables non initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.bss contient les variables globales non initialisées (ex: int counter;).&lt;br /&gt;
Ces variables ne sont pas stockées en Flash, car elles ne contiennent pas de valeur initiale.&lt;br /&gt;
&lt;br /&gt;
Au démarrage, le startup code remplit cette zone avec des zéros (memset), d’où “zeroed during startup”.&lt;br /&gt;
Et voici le code complet du linker : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY{&lt;br /&gt;
    FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
    SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
  .isr_vector :&lt;br /&gt;
  {&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il a été tester sur ce main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // do two dummy reads after enabling the peripheral clock, as per the errata&lt;br /&gt;
  volatile uint32_t dummy;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et le makefile :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = faisALaMainLinker.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Fichiers objets&lt;br /&gt;
OBJS = main.o system_stm32f4xx.o startup_stm32f410rx.o&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: main.elf&lt;br /&gt;
&lt;br /&gt;
main.elf: $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
main.o: main.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
system_stm32f4xx.o: ../00-cmsis-device-f4-master/Source/Templates/system_stm32f4xx.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
startup_stm32f410rx.o: ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
firmware.bin: main.elf&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: firmware.bin&lt;br /&gt;
	st-flash write firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o *.elf *.bin&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size main.elf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Processus de compilation : =====&lt;br /&gt;
- Le linker assemble ces fichiers et les place correctement en mémoire, générant un fichier .elf.&lt;br /&gt;
&lt;br /&gt;
- Le fichier .elf est converti en .bin et téléversé dans le microcontrôleur via ST-LINK ou OpenOCD.&lt;br /&gt;
[[Fichier:SchemaCompil.png|centré|vignette|425x425px|Schéma chaine compilation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous retrouvons notre code principal en .c qui est compiler pour en ressortir des fichiers .o qui seront ensuite traiter dans le bloc Linker qui fera le lien entre le code et les blocs mémoires réelles. A la sortie nous aurons des .elf qui une fois dans le programmer le transformera en .bin et y sera téléversé dans le mircocontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== CMSIS ======&lt;br /&gt;
C'est à ce lien : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/cmsis-device-f4&amp;lt;/nowiki&amp;gt; qu'on vient télécharger la bibliothèque CMSIS (Cortex Microcontroller Software Interface Standard).&lt;br /&gt;
&lt;br /&gt;
CMSIS (Cortex Microcontroller Software Interface Standard) simplifie l'accès au matériel, l'équivalent d'un &amp;quot;#include &amp;lt;avr/io.h&amp;gt;&amp;quot; pour ARM.&lt;br /&gt;
&lt;br /&gt;
C'est une bibliothèque fournie par ARM et ST, qui simplifie l'accès au matériel en proposant :&lt;br /&gt;
&lt;br /&gt;
- Des définitions pour tous les registres du MCU&lt;br /&gt;
&lt;br /&gt;
- Les prototypes des fonctions système (comme SystemInit())&lt;br /&gt;
&lt;br /&gt;
- La table des vecteurs d'interruptions (startup code)&lt;br /&gt;
&lt;br /&gt;
Si on veut la bibliothèque contenant plus d'informations et des examples, on peut se fier à ce repertoire : https://github.com/STMicroelectronics/STM32CubeF4/tree/master.&lt;br /&gt;
&lt;br /&gt;
====== Aide à la compilation avec un simple Blink ======&lt;br /&gt;
Récuperation des fichiers &amp;quot;syscall.c&amp;quot; et &amp;quot;sysmem.c&amp;quot; : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32F410xx-Nucleo/Templates/STM32CubeIDE/Example/User&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Voici le Makefile correspondant :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib/User \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = generatedLinkerIDE.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Répertoire build et bin&lt;br /&gt;
BUILD = build&lt;br /&gt;
BIN = bin&lt;br /&gt;
&lt;br /&gt;
# Fichiers source&lt;br /&gt;
SRCS := main.c \&lt;br /&gt;
       ../01-lib/User/syscalls.c \&lt;br /&gt;
       ../01-lib/User/sysmem.c \&lt;br /&gt;
       ../01-lib/system_stm32f4xx.c&lt;br /&gt;
# Fichier startup asm&lt;br /&gt;
ASM_SRCS = ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
&lt;br /&gt;
# Objets&lt;br /&gt;
OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(SRCS))) \&lt;br /&gt;
       $(patsubst %.s,$(BUILD)/%.o,$(notdir $(ASM_SRCS)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: $(BUILD)/main.elf&lt;br /&gt;
&lt;br /&gt;
# Crée le dossier build/ et bin/&lt;br /&gt;
$(BUILD):&lt;br /&gt;
	mkdir -p $(BUILD)&lt;br /&gt;
&lt;br /&gt;
$(BIN):&lt;br /&gt;
	mkdir -p $(BIN)&lt;br /&gt;
&lt;br /&gt;
# Link&lt;br /&gt;
$(BUILD)/main.elf: $(BUILD) $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
$(BUILD)/%.o: %.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/User/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../00-cmsis-device-f4-master/Source/Templates/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/gcc/%.s | $(BUILD)&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
$(BIN)/firmware.bin: $(BUILD)/main.elf | $(BIN)&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: $(BIN)/firmware.bin&lt;br /&gt;
	st-flash write $(BIN)/firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf $(BUILD) $(BIN)&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size $(BUILD)/main.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et le main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous faisons simplement clignoter une led afin de préparer l'environnement de compilation et vérifier que tout fonctionne. Comme ça, si il y a un bug nous pourrons vite isoler cette étape.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de faire clignoter une LED il faut :&lt;br /&gt;
1. Activer l'horloge du GPIO correspondant. Pour ce faire, nous modifions le registre RCC-&amp;gt;AHB1ENR. (Cf datasheet RM0401 p.119/763)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIO&amp;lt;PORT&amp;gt;EN_Pos);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;2. Définir la direction du PIN via le registre&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;PB2&amp;quot; =&amp;gt; PORT = B et PIN_NUM = 2&lt;br /&gt;
Ici par exemple le PIN 3 correspond au 6ieme et 7ieme bit de MODER.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);    // Output&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3. Définir le type de sortie (Registre OTYPER)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;OTYPER &amp;amp;= ~(0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt;));    // Push pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;4. Définir le type de pull (Registre PUPDR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2)); // Forcage 00 : No pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;5. Etape finale : écriture sur le pin (Registre ODR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On peut également modifier de façon atomique (donc juste 1 bit pas tous) via le registre BSRR.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if (GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;))&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; + 16)); // Ici 16 est l'offset à ajouter afin d'avoir le bon registre pour eteindre&lt;br /&gt;
    else&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Lorsque BSRR est lu il est reset juste après et cela permet d'éviter les conflits si une interruption intervient et modifie ODR entre 2 instructions.&lt;br /&gt;
Après on peut également toucher à la vitesse du pin (Registre OSPEEDR) si l'on souhaite optimiser la consommation de batterie.&lt;br /&gt;
''Note : Remplacer &amp;lt;PORT&amp;gt; par A ou B ou C ou etc... et remplacer &amp;lt;PIN&amp;gt; par 1,2,etc.. selon ce qu'on veut activer''&lt;br /&gt;
&lt;br /&gt;
====== Acceder a PA2 et PA3 sur la Nucleo ======&lt;br /&gt;
&amp;quot;SB62 and SB63 must be ON, while SB13 and SB14 must be OFF.&amp;quot; Extrait de la datasheet UM1724 : Chapitre7.10 p26/91 &lt;br /&gt;
&lt;br /&gt;
Il faut dessouder les resistances bridges pour accéder à ces ports. Et ensuite déplacer le jumper sur U5V.&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&lt;br /&gt;
&lt;br /&gt;
La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
Nous allons programmer le SPI sur notre microcontrôleur et tester ce code pour la carte Sparkfun Serial 7-Segments Display. Sur ce git : &amp;lt;nowiki&amp;gt;https://github.com/sparkfun/Serial7SegmentDisplay/wiki/Serial-7-Segment-Display-Datasheet&amp;lt;/nowiki&amp;gt; se trouve la datasheet de cette carte que nous allons utiliser pour communiquer en SPI. Dans l'idée, nous allons programmer une bibliothèque suffisament complète afin de pouvoir simplement afficher &amp;quot;9&amp;quot; via une commande dans ce style dans notre main : &amp;quot;spi_sent('G');&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On peut tout d'abord centraliser la logique des initialisations des pins afin d'alléger le main (Cf fichier hardware_setup.c et .h) .&lt;br /&gt;
[[Fichier:Carte7SegmentSparkFunPinout.png|centré|vignette|446x446px|7 segments SparkFun Pinout]]&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testée nous avons fait plusieurs codes intermediaires pour tester chaque primitive séparément. Ici le détail du code final seulement.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liées au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de contrôler une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
Cette partie est assez complexe et sera implémentée plus tard.&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
// static uint8_t cursor_pos = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (WAITCHAR())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nUP - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nDOWN - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touches avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de gérer la détection des cartes filles (généralement après un appel de la fonction &amp;quot;devices&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaires pour une meilleure lisibilité. Elle permet aussi de contrôler et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importantes car elles étaient les plus difficiles à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présentes sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrôleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très intéressant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shield, ici nous avons une gestion dynamique avec une liste chaînée (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothèque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitute =====&lt;br /&gt;
Ce code à pour but de simplifier l'implémentation de la carte clavier ou la carte écran à moyen et long terme.&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
#define WAITCHAR()          !usart_buffer_available()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le strict minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouver les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importé dans nos différents projets afin d'avoir une détection de touches portable pour la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilée...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons plein de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensée pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnues comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est nécessaire seulement si vous avez un bootloader personnalisé, auquel cas faire un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal à trouver certains caractères mais le document présent ci-dessous m'a grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mappée (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se déplacer via la touche FN qui correspond aux fonctionnalités spéciales, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappées aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Cette partie nécessite d'être travaillée mais servira de point de départ pour les prochaines années je l'espère.&lt;br /&gt;
Dans un dossier SPI à part nous avons ajouté un fichier cmd.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_H&lt;br /&gt;
#define CMD_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CMD_NOCMD 0x00&lt;br /&gt;
#define CMD_ACK_SLAVE 0xFF&lt;br /&gt;
&lt;br /&gt;
// --------------  Detection presence carte fille --------------&lt;br /&gt;
#define CMD_PING 0xAA&lt;br /&gt;
#define CMD_PING_REPLY 0x55&lt;br /&gt;
&lt;br /&gt;
// --------------  Identification carte fille --------------&lt;br /&gt;
#define CMD_IDENTIFY 0x10&lt;br /&gt;
&lt;br /&gt;
#define ID_KEYBOARD 0x01&lt;br /&gt;
#define ID_ECRAN 0x02&lt;br /&gt;
#define ID_SON 0x03&lt;br /&gt;
#define ID_RESEAU 0x04&lt;br /&gt;
#define ID_FPGA 0x05&lt;br /&gt;
&lt;br /&gt;
// -------------- Specifique Keyboard --------------&lt;br /&gt;
#define CMD_READ_EVENT 0x20&lt;br /&gt;
&lt;br /&gt;
// -------- Structure keycode -------- //&lt;br /&gt;
typedef struct {&lt;br /&gt;
  char key;        // lettre correspondante&lt;br /&gt;
  uint8_t pressed; // 1 = press, 0 = release&lt;br /&gt;
} key_event_t;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il sert à simplifier l'implémentation des commandes qui sont communes à tous les groupes.&lt;br /&gt;
spi.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;spi.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// -------- SPI avec carte mere -------- //&lt;br /&gt;
#define INT_PORT PORTB&lt;br /&gt;
#define INT_DDR  DDRB&lt;br /&gt;
#define INT_PIN  PB0&lt;br /&gt;
&lt;br /&gt;
#define INT_LOW()   (INT_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
#define INT_HIGH()  (INT_PORT |=  (1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
&lt;br /&gt;
static volatile uint8_t spi_state = 0;&lt;br /&gt;
static volatile key_event_t current_event;&lt;br /&gt;
&lt;br /&gt;
// -------- Buffer circulaire -------- //&lt;br /&gt;
#define SPI_BUFFER_SIZE 16&lt;br /&gt;
static volatile key_event_t buffer[SPI_BUFFER_SIZE];&lt;br /&gt;
static volatile uint8_t head = 0;&lt;br /&gt;
static volatile uint8_t tail = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferIsEmpty(void) {&lt;br /&gt;
    return head == tail;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void __bufferPush(key_event_t ev) {&lt;br /&gt;
    uint8_t next = (head + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next == tail) {&lt;br /&gt;
        tail = (tail + 1) % SPI_BUFFER_SIZE; // overwrite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    buffer[head] = ev;&lt;br /&gt;
    head = next;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferPop(volatile key_event_t *ev) {&lt;br /&gt;
    if (__bufferIsEmpty())&lt;br /&gt;
        return 0;&lt;br /&gt;
&lt;br /&gt;
    *ev = buffer[tail];&lt;br /&gt;
    tail = (tail + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// -------- SPI -------- //&lt;br /&gt;
void spi_init(void) {&lt;br /&gt;
    /* Config SPI Slave : MISO =&amp;gt; output, MOSI/SCK/SS =&amp;gt; input */&lt;br /&gt;
    DDRB |=  (1 &amp;lt;&amp;lt; PB3);  // MISO&lt;br /&gt;
    DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; PB2) | (1 &amp;lt;&amp;lt; PB1) | (1 &amp;lt;&amp;lt; PB0)); // MOSI, SCK, SS&lt;br /&gt;
&lt;br /&gt;
    /* SPI enable + interruption */&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; SPIE);&lt;br /&gt;
&lt;br /&gt;
    /* INT pin */&lt;br /&gt;
    INT_DDR |= (1 &amp;lt;&amp;lt; INT_PIN);&lt;br /&gt;
    INT_HIGH();&lt;br /&gt;
&lt;br /&gt;
    SPDR = CMD_ACK_SLAVE; // On precharge ack pour le 1er cycle, a chaque reset c'est utile&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(SPI_STC_vect) {&lt;br /&gt;
    uint8_t rx = SPDR;&lt;br /&gt;
    uint8_t tx = CMD_NOCMD;&lt;br /&gt;
&lt;br /&gt;
    switch (rx) {&lt;br /&gt;
        case CMD_PING:&lt;br /&gt;
            tx = CMD_PING_REPLY;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_IDENTIFY:&lt;br /&gt;
            tx = ID_KEYBOARD;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_READ_EVENT:&lt;br /&gt;
            if (__bufferPop(&amp;amp;current_event)) {&lt;br /&gt;
                tx = current_event.key;&lt;br /&gt;
                spi_state = 1; // prochain octet = pressed&lt;br /&gt;
            } else {&lt;br /&gt;
                tx = CMD_NOCMD;&lt;br /&gt;
                INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        default:&lt;br /&gt;
            if (spi_state == 1) {&lt;br /&gt;
                tx = current_event.pressed;&lt;br /&gt;
                spi_state = 0;&lt;br /&gt;
&lt;br /&gt;
                if (__bufferIsEmpty())&lt;br /&gt;
                    INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    SPDR = tx; // a envoyer au prochain cycle&lt;br /&gt;
}&lt;br /&gt;
// Manque a coder dautre primitive&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef SPI_H&lt;br /&gt;
#define SPI_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;cmd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spi_init(void);&lt;br /&gt;
void spi_push_event(char key, uint8_t pressed);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=9879</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=9879"/>
		<updated>2026-03-16T14:21:10Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Cahier des charges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
[[Fichier:Recherche ARM.zip|centré|vignette|Recherche ARM]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le zip qui sauve : &lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons un ensemble de fonctions liées à notre matériel pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vues ensemble précedemment sont appelées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basée sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&lt;br /&gt;
Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrôleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite procéder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
====== Startup (.s) ======&lt;br /&gt;
Le Startup file est crucial en Bare Metal. Une erreur dans ce fichier peut empêcher le microcontrôleur de démarrer ou provoquer des crashs.&lt;br /&gt;
Rôles principaux :&lt;br /&gt;
&lt;br /&gt;
- Définir l'environnement nécessaire à l'exécution de main().&lt;br /&gt;
&lt;br /&gt;
- S'exécuter avant main() et lancer ensuite main().&lt;br /&gt;
&lt;br /&gt;
- Être adapté à la target (processeur) utilisée.&lt;br /&gt;
&lt;br /&gt;
- Placer correctement la table des vecteurs comme exigé par les ARM Cortex-M.&lt;br /&gt;
&lt;br /&gt;
- Initialiser la pile correctement.&lt;br /&gt;
&lt;br /&gt;
- Initialiser les sections .data et .bss dans la SRAM.&lt;br /&gt;
&lt;br /&gt;
====== Linker (.ld) ======&lt;br /&gt;
Le Linker Script détermine comment les sections du code sont placées en mémoire.&lt;br /&gt;
''Fonctionnalités :''&lt;br /&gt;
* Définir les adresses absolues des sections.&lt;br /&gt;
* Définir les zones mémoire, leurs tailles et adresses.&lt;br /&gt;
* Fournir les instructions au linker GNU via l'option -T.&lt;br /&gt;
* L'extension de fichier est .ld.&lt;br /&gt;
&lt;br /&gt;
Le code linker sert à guider le compilateur pour assembler toutes les sections d’un programme en un fichier binaire unique.  Sur un microcontrôleur comme le '''STM32F410RB''', il faut indiquer où chaque partie du code et des données doit être placée dans la '''mémoire du MCU :'''&lt;br /&gt;
&lt;br /&gt;
- Où commence le code executable, son point d'entrée ;&lt;br /&gt;
&lt;br /&gt;
- Définit chaque region de la memoire qui existe sur le microcontrôleur et leur taille ;&lt;br /&gt;
&lt;br /&gt;
- Où placer les sections differentes du code en memoire (exemple : interruption, etc...).&lt;br /&gt;
Dans l'idée on peut le faire à la main afin de mieux assimiler le linker puis récuperer celui générer automatiquement par le logiciel STM32IDE (également disponible sur github) afin de ne pas avoir a y retoucher a chaque fois et ne pas briquer accidentellement notre puce.&lt;br /&gt;
Pour realiser un linker on doit preciser la SRAM et sa flash. Cette partie décrit les zones mémoire physiques du STM32F410RB. Il faut se fier au plan memoire de la datasheet p.40/763&lt;br /&gt;
Sur celui-ci nous constatons que SRAM est à 32kB et commence à 0x2000 0000 et que la fash commence à 0x0800 0000 et sa taille est de 128kB (donné à la figure 1 page 37/763).&lt;br /&gt;
&lt;br /&gt;
On peut alors placer dans le linker :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY&lt;br /&gt;
{&lt;br /&gt;
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(rx) → read et execute, donc on peut lire et exécuter du code dedans.&lt;br /&gt;
(rwx) → read, write, execute (en pratique, on n’exécute pas depuis la RAM, mais certains MCU le permettent).&lt;br /&gt;
'''&amp;lt;u&amp;gt;Sections&amp;lt;/u&amp;gt;'''Chaque programme compilé contient plusieurs sections générées par le compilateur. L'ordre à laquelle ils sont cris découle de la convention utilisé dans CMSIS (fichier startup fournis par ARM).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tous les compilateurs suivent la même idée :&lt;br /&gt;
&lt;br /&gt;
- Sections nécessaires au CPU d’abord (vecteurs, code)&lt;br /&gt;
&lt;br /&gt;
- Sections de données ensuite (initialisées, non initialisées)&lt;br /&gt;
&lt;br /&gt;
- Mémoire dynamique à la fin (heap, stack)&lt;br /&gt;
La '''partie ISR_VECTOR''' indique les vecteurs d'interruptions :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
isr_vector :&lt;br /&gt;
{&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
} &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;.isr_vector&amp;quot; : C'est la section qui contient le vecteur d'interruptions (ISR - Interrupt Service Routine). Les vecteurs d'interruptions sont des adresses de fonction qui seront appelées lorsque des interruptions spécifiques se produisent.&lt;br /&gt;
&amp;quot;KEEP&amp;quot; : Cette directive indique au linker de conserver cette section dans le binaire final, même si elle semble inutilisée par le programme. Cela est crucial pour les vecteurs d'interruptions qui doivent absolument être présents dans le binaire.&lt;br /&gt;
&amp;quot;&amp;gt;FLASH&amp;quot; : Cela indique que cette section doit être placée en mémoire Flash, qui est généralement de la mémoire non-volatile, utilisée pour stocker le programme.&lt;br /&gt;
Elle est placée en Flash pour être disponible dès le reset.&lt;br /&gt;
La '''partie TEXT''' contient le code de notre programme :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.text = code exécutable (fonctions, instructions machine).&lt;br /&gt;
.rodata = données constantes (const int, chaînes de caractères, etc.).&lt;br /&gt;
&lt;br /&gt;
_etext marque la fin de la zone code, utile pour copier ensuite la section .data au démarrage.&lt;br /&gt;
ALIGN(4) permet d'aligner l'adresse courante sur une frontière de 4 octets. Cela garantit que le code est correctement aligné en mémoire, ce qui peut être nécessaire pour des performances optimales ou des restrictions matérielles.&lt;br /&gt;
La '''partie DATA''' contient les variables initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.data contient les variables globales initialisées (ex: int x = 5;).&lt;br /&gt;
Ces valeurs sont stockées dans la Flash au moment de la programmation, mais copiées dans la RAM lors du démarrage (d’où &amp;gt;SRAM AT&amp;gt;FLASH).&lt;br /&gt;
&lt;br /&gt;
_sdata et _edata servent au code d’initialisation (startup.s) pour savoir quoi copier et combien d’octets.&lt;br /&gt;
La partie BSS contient les variables non initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.bss contient les variables globales non initialisées (ex: int counter;).&lt;br /&gt;
Ces variables ne sont pas stockées en Flash, car elles ne contiennent pas de valeur initiale.&lt;br /&gt;
&lt;br /&gt;
Au démarrage, le startup code remplit cette zone avec des zéros (memset), d’où “zeroed during startup”.&lt;br /&gt;
Et voici le code complet du linker : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY{&lt;br /&gt;
    FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
    SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
  .isr_vector :&lt;br /&gt;
  {&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il a été tester sur ce main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // do two dummy reads after enabling the peripheral clock, as per the errata&lt;br /&gt;
  volatile uint32_t dummy;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et le makefile :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = faisALaMainLinker.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Fichiers objets&lt;br /&gt;
OBJS = main.o system_stm32f4xx.o startup_stm32f410rx.o&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: main.elf&lt;br /&gt;
&lt;br /&gt;
main.elf: $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
main.o: main.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
system_stm32f4xx.o: ../00-cmsis-device-f4-master/Source/Templates/system_stm32f4xx.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
startup_stm32f410rx.o: ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
firmware.bin: main.elf&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: firmware.bin&lt;br /&gt;
	st-flash write firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o *.elf *.bin&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size main.elf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Processus de compilation : =====&lt;br /&gt;
- Le linker assemble ces fichiers et les place correctement en mémoire, générant un fichier .elf.&lt;br /&gt;
&lt;br /&gt;
- Le fichier .elf est converti en .bin et téléversé dans le microcontrôleur via ST-LINK ou OpenOCD.&lt;br /&gt;
[[Fichier:SchemaCompil.png|centré|vignette|425x425px|Schéma chaine compilation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous retrouvons notre code principal en .c qui est compiler pour en ressortir des fichiers .o qui seront ensuite traiter dans le bloc Linker qui fera le lien entre le code et les blocs mémoires réelles. A la sortie nous aurons des .elf qui une fois dans le programmer le transformera en .bin et y sera téléversé dans le mircocontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== CMSIS ======&lt;br /&gt;
C'est à ce lien : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/cmsis-device-f4&amp;lt;/nowiki&amp;gt; qu'on vient télécharger la bibliothèque CMSIS (Cortex Microcontroller Software Interface Standard).&lt;br /&gt;
&lt;br /&gt;
CMSIS (Cortex Microcontroller Software Interface Standard) simplifie l'accès au matériel, l'équivalent d'un &amp;quot;#include &amp;lt;avr/io.h&amp;gt;&amp;quot; pour ARM.&lt;br /&gt;
&lt;br /&gt;
C'est une bibliothèque fournie par ARM et ST, qui simplifie l'accès au matériel en proposant :&lt;br /&gt;
&lt;br /&gt;
- Des définitions pour tous les registres du MCU&lt;br /&gt;
&lt;br /&gt;
- Les prototypes des fonctions système (comme SystemInit())&lt;br /&gt;
&lt;br /&gt;
- La table des vecteurs d'interruptions (startup code)&lt;br /&gt;
&lt;br /&gt;
Si on veut la bibliothèque contenant plus d'informations et des examples, on peut se fier à ce repertoire : https://github.com/STMicroelectronics/STM32CubeF4/tree/master.&lt;br /&gt;
&lt;br /&gt;
====== Aide à la compilation avec un simple Blink ======&lt;br /&gt;
Récuperation des fichiers &amp;quot;syscall.c&amp;quot; et &amp;quot;sysmem.c&amp;quot; : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32F410xx-Nucleo/Templates/STM32CubeIDE/Example/User&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Voici le Makefile correspondant :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib/User \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = generatedLinkerIDE.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Répertoire build et bin&lt;br /&gt;
BUILD = build&lt;br /&gt;
BIN = bin&lt;br /&gt;
&lt;br /&gt;
# Fichiers source&lt;br /&gt;
SRCS := main.c \&lt;br /&gt;
       ../01-lib/User/syscalls.c \&lt;br /&gt;
       ../01-lib/User/sysmem.c \&lt;br /&gt;
       ../01-lib/system_stm32f4xx.c&lt;br /&gt;
# Fichier startup asm&lt;br /&gt;
ASM_SRCS = ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
&lt;br /&gt;
# Objets&lt;br /&gt;
OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(SRCS))) \&lt;br /&gt;
       $(patsubst %.s,$(BUILD)/%.o,$(notdir $(ASM_SRCS)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: $(BUILD)/main.elf&lt;br /&gt;
&lt;br /&gt;
# Crée le dossier build/ et bin/&lt;br /&gt;
$(BUILD):&lt;br /&gt;
	mkdir -p $(BUILD)&lt;br /&gt;
&lt;br /&gt;
$(BIN):&lt;br /&gt;
	mkdir -p $(BIN)&lt;br /&gt;
&lt;br /&gt;
# Link&lt;br /&gt;
$(BUILD)/main.elf: $(BUILD) $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
$(BUILD)/%.o: %.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/User/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../00-cmsis-device-f4-master/Source/Templates/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/gcc/%.s | $(BUILD)&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
$(BIN)/firmware.bin: $(BUILD)/main.elf | $(BIN)&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: $(BIN)/firmware.bin&lt;br /&gt;
	st-flash write $(BIN)/firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf $(BUILD) $(BIN)&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size $(BUILD)/main.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et le main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous faisons simplement clignoter une led afin de préparer l'environnement de compilation et vérifier que tout fonctionne. Comme ça, si il y a un bug nous pourrons vite isoler cette étape.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de faire clignoter une LED il faut :&lt;br /&gt;
1. Activer l'horloge du GPIO correspondant. Pour ce faire, nous modifions le registre RCC-&amp;gt;AHB1ENR. (Cf datasheet RM0401 p.119/763)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIO&amp;lt;PORT&amp;gt;EN_Pos);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;2. Définir la direction du PIN via le registre&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;PB2&amp;quot; =&amp;gt; PORT = B et PIN_NUM = 2&lt;br /&gt;
Ici par exemple le PIN 3 correspond au 6ieme et 7ieme bit de MODER.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);    // Output&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3. Définir le type de sortie (Registre OTYPER)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;OTYPER &amp;amp;= ~(0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt;));    // Push pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;4. Définir le type de pull (Registre PUPDR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2)); // Forcage 00 : No pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;5. Etape finale : écriture sur le pin (Registre ODR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On peut également modifier de façon atomique (donc juste 1 bit pas tous) via le registre BSRR.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if (GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;))&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; + 16)); // Ici 16 est l'offset à ajouter afin d'avoir le bon registre pour eteindre&lt;br /&gt;
    else&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Lorsque BSRR est lu il est reset juste après et cela permet d'éviter les conflits si une interruption intervient et modifie ODR entre 2 instructions.&lt;br /&gt;
Après on peut également toucher à la vitesse du pin (Registre OSPEEDR) si l'on souhaite optimiser la consommation de batterie.&lt;br /&gt;
''Note : Remplacer &amp;lt;PORT&amp;gt; par A ou B ou C ou etc... et remplacer &amp;lt;PIN&amp;gt; par 1,2,etc.. selon ce qu'on veut activer''&lt;br /&gt;
&lt;br /&gt;
====== Acceder a PA2 et PA3 sur la Nucleo ======&lt;br /&gt;
&amp;quot;SB62 and SB63 must be ON, while SB13 and SB14 must be OFF.&amp;quot; Extrait de la datasheet UM1724 : Chapitre7.10 p26/91 &lt;br /&gt;
&lt;br /&gt;
Il faut dessouder les resistances bridges pour accéder à ces ports. Et ensuite déplacer le jumper sur U5V.&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&lt;br /&gt;
&lt;br /&gt;
La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
Nous allons programmer le SPI sur notre microcontrôleur et tester ce code pour la carte Sparkfun Serial 7-Segments Display. Sur ce git : &amp;lt;nowiki&amp;gt;https://github.com/sparkfun/Serial7SegmentDisplay/wiki/Serial-7-Segment-Display-Datasheet&amp;lt;/nowiki&amp;gt; se trouve la datasheet de cette carte que nous allons utiliser pour communiquer en SPI. Dans l'idée, nous allons programmer une bibliothèque suffisament complète afin de pouvoir simplement afficher &amp;quot;9&amp;quot; via une commande dans ce style dans notre main : &amp;quot;spi_sent('G');&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On peut tout d'abord centraliser la logique des initialisations des pins afin d'alléger le main (Cf fichier hardware_setup.c et .h) .&lt;br /&gt;
[[Fichier:Carte7SegmentSparkFunPinout.png|centré|vignette|446x446px|7 segments SparkFun Pinout]]&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testée nous avons fait plusieurs codes intermediaires pour tester chaque primitive séparément. Ici le détail du code final seulement.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liées au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de contrôler une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
Cette partie est assez complexe et sera implémentée plus tard.&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
// static uint8_t cursor_pos = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (WAITCHAR())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nUP - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nDOWN - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touches avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de gérer la détection des cartes filles (généralement après un appel de la fonction &amp;quot;devices&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaires pour une meilleure lisibilité. Elle permet aussi de contrôler et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importantes car elles étaient les plus difficiles à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présentes sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrôleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très intéressant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shield, ici nous avons une gestion dynamique avec une liste chaînée (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothèque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitute =====&lt;br /&gt;
Ce code à pour but de simplifier l'implémentation de la carte clavier ou la carte écran à moyen et long terme.&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
#define WAITCHAR()          !usart_buffer_available()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le strict minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouver les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importé dans nos différents projets afin d'avoir une détection de touches portable pour la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilée...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons plein de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensée pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnues comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est nécessaire seulement si vous avez un bootloader personnalisé, auquel cas faire un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal à trouver certains caractères mais le document présent ci-dessous m'a grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mappée (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se déplacer via la touche FN qui correspond aux fonctionnalités spéciales, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappées aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Cette partie nécessite d'être travaillée mais servira de point de départ pour les prochaines années je l'espère.&lt;br /&gt;
Dans un dossier SPI à part nous avons ajouté un fichier cmd.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_H&lt;br /&gt;
#define CMD_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CMD_NOCMD 0x00&lt;br /&gt;
#define CMD_ACK_SLAVE 0xFF&lt;br /&gt;
&lt;br /&gt;
// --------------  Detection presence carte fille --------------&lt;br /&gt;
#define CMD_PING 0xAA&lt;br /&gt;
#define CMD_PING_REPLY 0x55&lt;br /&gt;
&lt;br /&gt;
// --------------  Identification carte fille --------------&lt;br /&gt;
#define CMD_IDENTIFY 0x10&lt;br /&gt;
&lt;br /&gt;
#define ID_KEYBOARD 0x01&lt;br /&gt;
#define ID_ECRAN 0x02&lt;br /&gt;
#define ID_SON 0x03&lt;br /&gt;
#define ID_RESEAU 0x04&lt;br /&gt;
#define ID_FPGA 0x05&lt;br /&gt;
&lt;br /&gt;
// -------------- Specifique Keyboard --------------&lt;br /&gt;
#define CMD_READ_EVENT 0x20&lt;br /&gt;
&lt;br /&gt;
// -------- Structure keycode -------- //&lt;br /&gt;
typedef struct {&lt;br /&gt;
  char key;        // lettre correspondante&lt;br /&gt;
  uint8_t pressed; // 1 = press, 0 = release&lt;br /&gt;
} key_event_t;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il sert à simplifier l'implémentation des commandes qui sont communes à tous les groupes.&lt;br /&gt;
spi.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;spi.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// -------- SPI avec carte mere -------- //&lt;br /&gt;
#define INT_PORT PORTB&lt;br /&gt;
#define INT_DDR  DDRB&lt;br /&gt;
#define INT_PIN  PB0&lt;br /&gt;
&lt;br /&gt;
#define INT_LOW()   (INT_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
#define INT_HIGH()  (INT_PORT |=  (1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
&lt;br /&gt;
static volatile uint8_t spi_state = 0;&lt;br /&gt;
static volatile key_event_t current_event;&lt;br /&gt;
&lt;br /&gt;
// -------- Buffer circulaire -------- //&lt;br /&gt;
#define SPI_BUFFER_SIZE 16&lt;br /&gt;
static volatile key_event_t buffer[SPI_BUFFER_SIZE];&lt;br /&gt;
static volatile uint8_t head = 0;&lt;br /&gt;
static volatile uint8_t tail = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferIsEmpty(void) {&lt;br /&gt;
    return head == tail;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void __bufferPush(key_event_t ev) {&lt;br /&gt;
    uint8_t next = (head + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next == tail) {&lt;br /&gt;
        tail = (tail + 1) % SPI_BUFFER_SIZE; // overwrite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    buffer[head] = ev;&lt;br /&gt;
    head = next;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferPop(volatile key_event_t *ev) {&lt;br /&gt;
    if (__bufferIsEmpty())&lt;br /&gt;
        return 0;&lt;br /&gt;
&lt;br /&gt;
    *ev = buffer[tail];&lt;br /&gt;
    tail = (tail + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// -------- SPI -------- //&lt;br /&gt;
void spi_init(void) {&lt;br /&gt;
    /* Config SPI Slave : MISO =&amp;gt; output, MOSI/SCK/SS =&amp;gt; input */&lt;br /&gt;
    DDRB |=  (1 &amp;lt;&amp;lt; PB3);  // MISO&lt;br /&gt;
    DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; PB2) | (1 &amp;lt;&amp;lt; PB1) | (1 &amp;lt;&amp;lt; PB0)); // MOSI, SCK, SS&lt;br /&gt;
&lt;br /&gt;
    /* SPI enable + interruption */&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; SPIE);&lt;br /&gt;
&lt;br /&gt;
    /* INT pin */&lt;br /&gt;
    INT_DDR |= (1 &amp;lt;&amp;lt; INT_PIN);&lt;br /&gt;
    INT_HIGH();&lt;br /&gt;
&lt;br /&gt;
    SPDR = CMD_ACK_SLAVE; // On precharge ack pour le 1er cycle, a chaque reset c'est utile&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(SPI_STC_vect) {&lt;br /&gt;
    uint8_t rx = SPDR;&lt;br /&gt;
    uint8_t tx = CMD_NOCMD;&lt;br /&gt;
&lt;br /&gt;
    switch (rx) {&lt;br /&gt;
        case CMD_PING:&lt;br /&gt;
            tx = CMD_PING_REPLY;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_IDENTIFY:&lt;br /&gt;
            tx = ID_KEYBOARD;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_READ_EVENT:&lt;br /&gt;
            if (__bufferPop(&amp;amp;current_event)) {&lt;br /&gt;
                tx = current_event.key;&lt;br /&gt;
                spi_state = 1; // prochain octet = pressed&lt;br /&gt;
            } else {&lt;br /&gt;
                tx = CMD_NOCMD;&lt;br /&gt;
                INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        default:&lt;br /&gt;
            if (spi_state == 1) {&lt;br /&gt;
                tx = current_event.pressed;&lt;br /&gt;
                spi_state = 0;&lt;br /&gt;
&lt;br /&gt;
                if (__bufferIsEmpty())&lt;br /&gt;
                    INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    SPDR = tx; // a envoyer au prochain cycle&lt;br /&gt;
}&lt;br /&gt;
// Manque a coder dautre primitive&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef SPI_H&lt;br /&gt;
#define SPI_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;cmd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spi_init(void);&lt;br /&gt;
void spi_push_event(char key, uint8_t pressed);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Recherche_ARM.zip&amp;diff=9878</id>
		<title>Fichier:Recherche ARM.zip</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Recherche_ARM.zip&amp;diff=9878"/>
		<updated>2026-03-16T14:20:18Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Recherche ARM&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8757</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8757"/>
		<updated>2026-01-05T14:55:33Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Ajout partie clavier */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
====== Startup (.s) ======&lt;br /&gt;
Le Startup file est crucial en Bare Metal. Une erreur dans ce fichier peut empêcher le microcontrôleur de démarrer ou provoquer des crashs.&lt;br /&gt;
Rôles principaux :&lt;br /&gt;
&lt;br /&gt;
- Définir l'environnement nécessaire à l'exécution de main().&lt;br /&gt;
&lt;br /&gt;
- S'exécuter avant main() et lancer ensuite main().&lt;br /&gt;
&lt;br /&gt;
- Être adapté à la target (processeur) utilisée.&lt;br /&gt;
&lt;br /&gt;
- Placer correctement la table des vecteurs comme exigé par les ARM Cortex-M.&lt;br /&gt;
&lt;br /&gt;
- Initialiser la pile correctement.&lt;br /&gt;
&lt;br /&gt;
- Initialiser les sections .data et .bss dans la SRAM.&lt;br /&gt;
&lt;br /&gt;
====== Linker (.ld) ======&lt;br /&gt;
Le Linker Script détermine comment les sections du code sont placées en mémoire.&lt;br /&gt;
''Fonctionnalités :''&lt;br /&gt;
* Définir les adresses absolues des sections.&lt;br /&gt;
* Définir les zones mémoire, leurs tailles et adresses.&lt;br /&gt;
* Fournir les instructions au linker GNU via l'option -T.&lt;br /&gt;
* L'extension de fichier est .ld.&lt;br /&gt;
&lt;br /&gt;
Le code linker sert à guider le compilateur pour assembler toutes les sections d’un programme en un fichier binaire unique.  Sur un microcontrôleur comme le '''STM32F410RB''', il faut indiquer où chaque partie du code et des données doit être placée dans la '''mémoire du MCU :'''&lt;br /&gt;
&lt;br /&gt;
- Où commence le code executable, son point d'entrée ;&lt;br /&gt;
&lt;br /&gt;
- Définit chaque region de la memoire qui existe sur le microcontrolleur et leur taille ;&lt;br /&gt;
&lt;br /&gt;
- Où placer les sections differentes du code en memoire (exemple : interruption, etc...).&lt;br /&gt;
Dans l'idée on peut le faire à la main afin de mieux assimiler le linker puis récuperer celui générer automatiquement par le logiciel STM32IDE (également disponible sur github) afin de ne pas avoir a y retoucher a chaque fois et ne pas briquer accidentellement notre puce.&lt;br /&gt;
Pour realiser un linker on doit preciser la SRAM et sa flash. Cette partie décrit les zones mémoire physiques du STM32F410RB. Il faut se fier au plan memoire de la datasheet p.40/763&lt;br /&gt;
Sur celui-ci nous constatons que SRAM est à 32kB et commence à 0x2000 0000&lt;br /&gt;
&lt;br /&gt;
et que la fash commence à 0x0800 0000 et sa taille est de 128kB (donné à la figure 1 page 37/763)&lt;br /&gt;
On peut alors placer dans le linker :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY&lt;br /&gt;
{&lt;br /&gt;
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(rx) → read et execute, donc on peut lire et exécuter du code dedans.&lt;br /&gt;
(rwx) → read, write, execute (en pratique, on n’exécute pas depuis la RAM, mais certains MCU le permettent).&lt;br /&gt;
'''&amp;lt;u&amp;gt;Sections&amp;lt;/u&amp;gt;'''Chaque programme compilé contient plusieurs sections générées par le compilateur. L'ordre à laquelle ils sont cris découle de la convention utilisé dans CMSIS (fichier startup fournis par ARM).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tous les compilateurs suivent la même idée :&lt;br /&gt;
&lt;br /&gt;
- Sections nécessaires au CPU d’abord (vecteurs, code)&lt;br /&gt;
&lt;br /&gt;
- Sections de données ensuite (initialisées, non initialisées)&lt;br /&gt;
&lt;br /&gt;
- Mémoire dynamique à la fin (heap, stack)&lt;br /&gt;
La '''partie ISR_VECTOR''' indique les vecteurs d'interruptions :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
isr_vector :&lt;br /&gt;
{&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
} &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;.isr_vector&amp;quot; : C'est la section qui contient le vecteur d'interruptions (ISR - Interrupt Service Routine). Les vecteurs d'interruptions sont des adresses de fonction qui seront appelées lorsque des interruptions spécifiques se produisent.&lt;br /&gt;
&amp;quot;KEEP&amp;quot; : Cette directive indique au linker de conserver cette section dans le binaire final, même si elle semble inutilisée par le programme. Cela est crucial pour les vecteurs d'interruptions qui doivent absolument être présents dans le binaire.&lt;br /&gt;
&amp;quot;&amp;gt;FLASH&amp;quot; : Cela indique que cette section doit être placée en mémoire Flash, qui est généralement de la mémoire non-volatile, utilisée pour stocker le programme.&lt;br /&gt;
Elle est placée en Flash pour être disponible dès le reset.&lt;br /&gt;
La '''partie TEXT''' contient le code de notre programme :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.text = code exécutable (fonctions, instructions machine).&lt;br /&gt;
.rodata = données constantes (const int, chaînes de caractères, etc.).&lt;br /&gt;
&lt;br /&gt;
_etext marque la fin de la zone code, utile pour copier ensuite la section .data au démarrage.&lt;br /&gt;
ALIGN(4) permet d'aligner l'adresse courante sur une frontière de 4 octets. Cela garantit que le code est correctement aligné en mémoire, ce qui peut être nécessaire pour des performances optimales ou des restrictions matérielles.&lt;br /&gt;
La '''partie DATA''' contient les variables initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.data contient les variables globales initialisées (ex: int x = 5;).&lt;br /&gt;
Ces valeurs sont stockées dans la Flash au moment de la programmation, mais copiées dans la RAM lors du démarrage (d’où &amp;gt;SRAM AT&amp;gt;FLASH).&lt;br /&gt;
&lt;br /&gt;
_sdata et _edata servent au code d’initialisation (startup.s) pour savoir quoi copier et combien d’octets.&lt;br /&gt;
La partie BSS contient les variables non initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.bss contient les variables globales non initialisées (ex: int counter;).&lt;br /&gt;
Ces variables ne sont pas stockées en Flash, car elles ne contiennent pas de valeur initiale.&lt;br /&gt;
&lt;br /&gt;
Au démarrage, le startup code remplit cette zone avec des zéros (memset), d’où “zeroed during startup”.&lt;br /&gt;
Et voici le code complet du linker : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY{&lt;br /&gt;
    FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
    SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
  .isr_vector :&lt;br /&gt;
  {&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il a été tester sur ce main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // do two dummy reads after enabling the peripheral clock, as per the errata&lt;br /&gt;
  volatile uint32_t dummy;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et le makefile :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = faisALaMainLinker.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Fichiers objets&lt;br /&gt;
OBJS = main.o system_stm32f4xx.o startup_stm32f410rx.o&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: main.elf&lt;br /&gt;
&lt;br /&gt;
main.elf: $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
main.o: main.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
system_stm32f4xx.o: ../00-cmsis-device-f4-master/Source/Templates/system_stm32f4xx.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
startup_stm32f410rx.o: ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
firmware.bin: main.elf&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: firmware.bin&lt;br /&gt;
	st-flash write firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o *.elf *.bin&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size main.elf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Processus de compilation : =====&lt;br /&gt;
- Le linker assemble ces fichiers et les place correctement en mémoire, générant un fichier .elf.&lt;br /&gt;
&lt;br /&gt;
- Le fichier .elf est converti en .bin et téléversé dans le microcontrôleur via ST-LINK ou OpenOCD.&lt;br /&gt;
[[Fichier:SchemaCompil.png|centré|vignette|425x425px|Schéma chaine compilation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous retrouvons notre code principale en .c qui est compiler pour en ressortir des fichiers .o qui seront ensuite traiter dans le bloc Linker qui fera le lien entre le code et les blocs mémoires réelles. A la sortie nous aurons des .elf qui un fois dans le programmer le transformera en .bin et y sera téléverser dans le uControleur.&lt;br /&gt;
&lt;br /&gt;
====== CMSIS ======&lt;br /&gt;
C'est à ce lien : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/cmsis-device-f4&amp;lt;/nowiki&amp;gt; qu'on vient télécharger la bibliothèque CMSIS (Cortex Microcontroller Software Interface Standard).&lt;br /&gt;
&lt;br /&gt;
CMSIS (Cortex Microcontroller Software Interface Standard) simplifie l'accès au matériel, l'équivalent d'un &amp;quot;#include &amp;lt;avr/io.h&amp;gt;&amp;quot; pour ARM.&lt;br /&gt;
&lt;br /&gt;
C'est une bibliothèque fournie par ARM et ST, qui simplifie l'accès au matériel en proposant :&lt;br /&gt;
&lt;br /&gt;
- Des définitions pour tous les registres du MCU&lt;br /&gt;
&lt;br /&gt;
- Les prototypes des fonctions système (comme SystemInit())&lt;br /&gt;
&lt;br /&gt;
- La table des vecteurs d'interruptions (startup code)&lt;br /&gt;
&lt;br /&gt;
Si on veut la bibliothèque contenant plus d'information et des examples on peut se fier a ce repertoire : https://github.com/STMicroelectronics/STM32CubeF4/tree/master.&lt;br /&gt;
&lt;br /&gt;
====== Aide à la compilation avec un simple Blink ======&lt;br /&gt;
Récuperation des fichiers &amp;quot;syscall.c&amp;quot; et &amp;quot;sysmem.c&amp;quot; : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32F410xx-Nucleo/Templates/STM32CubeIDE/Example/User&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Voici le Makefile correspondant :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib/User \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = generatedLinkerIDE.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Répertoire build et bin&lt;br /&gt;
BUILD = build&lt;br /&gt;
BIN = bin&lt;br /&gt;
&lt;br /&gt;
# Fichiers source&lt;br /&gt;
SRCS := main.c \&lt;br /&gt;
       ../01-lib/User/syscalls.c \&lt;br /&gt;
       ../01-lib/User/sysmem.c \&lt;br /&gt;
       ../01-lib/system_stm32f4xx.c&lt;br /&gt;
# Fichier startup asm&lt;br /&gt;
ASM_SRCS = ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
&lt;br /&gt;
# Objets&lt;br /&gt;
OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(SRCS))) \&lt;br /&gt;
       $(patsubst %.s,$(BUILD)/%.o,$(notdir $(ASM_SRCS)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: $(BUILD)/main.elf&lt;br /&gt;
&lt;br /&gt;
# Crée le dossier build/ et bin/&lt;br /&gt;
$(BUILD):&lt;br /&gt;
	mkdir -p $(BUILD)&lt;br /&gt;
&lt;br /&gt;
$(BIN):&lt;br /&gt;
	mkdir -p $(BIN)&lt;br /&gt;
&lt;br /&gt;
# Link&lt;br /&gt;
$(BUILD)/main.elf: $(BUILD) $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
$(BUILD)/%.o: %.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/User/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../00-cmsis-device-f4-master/Source/Templates/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/gcc/%.s | $(BUILD)&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
$(BIN)/firmware.bin: $(BUILD)/main.elf | $(BIN)&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: $(BIN)/firmware.bin&lt;br /&gt;
	st-flash write $(BIN)/firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf $(BUILD) $(BIN)&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size $(BUILD)/main.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et le main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous faisons simplement clignoter une led afin de préparer l'environnement de compilation et vérifier que tout fonctionne. Comme ça, si il y a un bug nous pourrons vite isoler cette étape.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de faire clignoter une LED il faut :&lt;br /&gt;
1. Activer l'horloge du GPIO correspondant. Pour ce faire, nous modifions le registre RCC-&amp;gt;AHB1ENR. (Cf datasheet RM0401 p.119/763)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIO&amp;lt;PORT&amp;gt;EN_Pos);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;2. Définir la direction du PIN via le registre&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;PB2&amp;quot; =&amp;gt; PORT = B et PIN_NUM = 2&lt;br /&gt;
Ici par exemple le PIN 3 correspond au 6ieme et 7ieme bit de MODER.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);    // Output&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3. Définir le type de sortie (Registre OTYPER)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;OTYPER &amp;amp;= ~(0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt;));    // Push pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;4. Définir le type de pull (Registre PUPDR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2)); // Forcage 00 : No pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;5. Etape finale : écriture sur le pin (Registre ODR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On peut également modifier de façon atomique (donc juste 1 bit pas tous) via le registre BSRR.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if (GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;))&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; + 16)); // Ici 16 est l'offset à ajouter afin d'avoir le bon registre pour eteindre&lt;br /&gt;
    else&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Lorsque BSRR est lu il est reset juste après et cela permet d'éviter les conflits si une interruption intervient et modifie ODR entre 2 instructions.&lt;br /&gt;
Après on peut également toucher a la vitesse du pin (Registre OSPEEDR) si l'on souhaite optimiser la consommation de batterie.&lt;br /&gt;
''Note : Remplacer &amp;lt;PORT&amp;gt; par A ou B ou C ou etc... et remplacer &amp;lt;PIN&amp;gt; par 1,2,etc.. selon ce qu'on veut activer''&lt;br /&gt;
&lt;br /&gt;
====== Acceder a PA2 et PA3 sur la Nucleo ======&lt;br /&gt;
&amp;quot;SB62 and SB63 must be ON, while SB13 and SB14 must be OFF.&amp;quot; Extrait de la datasheet UM1724 : Chapitre7.10 p26/91 &lt;br /&gt;
&lt;br /&gt;
Il faut dessouder les resistances bridges pour acceder ces ports. Et ensuite deplacer le jumper sur U5V.&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&lt;br /&gt;
&lt;br /&gt;
La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
Nous allons programmer le SPI sur notre microcontrolleur et tester ce code pour la carte Sparkfun Serial 7-Segments Display. Sur ce git : &amp;lt;nowiki&amp;gt;https://github.com/sparkfun/Serial7SegmentDisplay/wiki/Serial-7-Segment-Display-Datasheet&amp;lt;/nowiki&amp;gt; se trouve la datasheet de cette carte que nous allons utiliser pour communiquer en SPI. Dans l'idée, nous allons programmer une bibliothèque suffisament complète afin de pouvoir simplement d'afficher &amp;quot;9&amp;quot; via une commande dans ce style dans notre main : &amp;quot;spi_sent('G');&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On peut tout d'abord centraliser la logique des initialisations des pins afin d'alleger le main (Cf fichier hardware_setup.c et .h) .&lt;br /&gt;
[[Fichier:Carte7SegmentSparkFunPinout.png|centré|vignette|446x446px|7 segments SparkFun Pinout]]&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
Cette partie est assez complexe et sera implémenter plus tard.&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
// static uint8_t cursor_pos = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (WAITCHAR())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nUP - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\nDOWN - Gestion historique a implementer\r\nPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de gérer la détection des cartes filles (générealement après un appel de la fonction &amp;quot;devices&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shield, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothèque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitute =====&lt;br /&gt;
Ce code à pour but de simplifier l'implémentation de la carte clavier ou la carte écran à moyen et long terme.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
#define WAITCHAR()          !usart_buffer_available()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Cette partie nécessite d'être travaillé mais servira de point de départ pour les prochaines années je l'espère.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dans un dossier SPI à part nous avons ajouté un fichier cmd.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_H&lt;br /&gt;
#define CMD_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CMD_NOCMD 0x00&lt;br /&gt;
#define CMD_ACK_SLAVE 0xFF&lt;br /&gt;
&lt;br /&gt;
// --------------  Detection presence carte fille --------------&lt;br /&gt;
#define CMD_PING 0xAA&lt;br /&gt;
#define CMD_PING_REPLY 0x55&lt;br /&gt;
&lt;br /&gt;
// --------------  Identification carte fille --------------&lt;br /&gt;
#define CMD_IDENTIFY 0x10&lt;br /&gt;
&lt;br /&gt;
#define ID_KEYBOARD 0x01&lt;br /&gt;
#define ID_ECRAN 0x02&lt;br /&gt;
#define ID_SON 0x03&lt;br /&gt;
#define ID_RESEAU 0x04&lt;br /&gt;
#define ID_FPGA 0x05&lt;br /&gt;
&lt;br /&gt;
// -------------- Specifique Keyboard --------------&lt;br /&gt;
#define CMD_READ_EVENT 0x20&lt;br /&gt;
&lt;br /&gt;
// -------- Structure keycode -------- //&lt;br /&gt;
typedef struct {&lt;br /&gt;
  char key;        // lettre correspondante&lt;br /&gt;
  uint8_t pressed; // 1 = press, 0 = release&lt;br /&gt;
} key_event_t;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il sert à simplifier l'implémentation des commandes qui sont communes à tous les groupes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
spi.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;spi.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// -------- SPI avec carte mere -------- //&lt;br /&gt;
#define INT_PORT PORTB&lt;br /&gt;
#define INT_DDR  DDRB&lt;br /&gt;
#define INT_PIN  PB0&lt;br /&gt;
&lt;br /&gt;
#define INT_LOW()   (INT_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
#define INT_HIGH()  (INT_PORT |=  (1 &amp;lt;&amp;lt; INT_PIN))&lt;br /&gt;
&lt;br /&gt;
static volatile uint8_t spi_state = 0;&lt;br /&gt;
static volatile key_event_t current_event;&lt;br /&gt;
&lt;br /&gt;
// -------- Buffer circulaire -------- //&lt;br /&gt;
#define SPI_BUFFER_SIZE 16&lt;br /&gt;
static volatile key_event_t buffer[SPI_BUFFER_SIZE];&lt;br /&gt;
static volatile uint8_t head = 0;&lt;br /&gt;
static volatile uint8_t tail = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferIsEmpty(void) {&lt;br /&gt;
    return head == tail;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void __bufferPush(key_event_t ev) {&lt;br /&gt;
    uint8_t next = (head + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next == tail) {&lt;br /&gt;
        tail = (tail + 1) % SPI_BUFFER_SIZE; // overwrite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    buffer[head] = ev;&lt;br /&gt;
    head = next;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t __bufferPop(volatile key_event_t *ev) {&lt;br /&gt;
    if (__bufferIsEmpty())&lt;br /&gt;
        return 0;&lt;br /&gt;
&lt;br /&gt;
    *ev = buffer[tail];&lt;br /&gt;
    tail = (tail + 1) % SPI_BUFFER_SIZE;&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// -------- SPI -------- //&lt;br /&gt;
void spi_init(void) {&lt;br /&gt;
    /* Config SPI Slave : MISO =&amp;gt; output, MOSI/SCK/SS =&amp;gt; input */&lt;br /&gt;
    DDRB |=  (1 &amp;lt;&amp;lt; PB3);  // MISO&lt;br /&gt;
    DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; PB2) | (1 &amp;lt;&amp;lt; PB1) | (1 &amp;lt;&amp;lt; PB0)); // MOSI, SCK, SS&lt;br /&gt;
&lt;br /&gt;
    /* SPI enable + interruption */&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; SPIE);&lt;br /&gt;
&lt;br /&gt;
    /* INT pin */&lt;br /&gt;
    INT_DDR |= (1 &amp;lt;&amp;lt; INT_PIN);&lt;br /&gt;
    INT_HIGH();&lt;br /&gt;
&lt;br /&gt;
    SPDR = CMD_ACK_SLAVE; // On precharge ack pour le 1er cycle, a chaque reset c'est utile&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(SPI_STC_vect) {&lt;br /&gt;
    uint8_t rx = SPDR;&lt;br /&gt;
    uint8_t tx = CMD_NOCMD;&lt;br /&gt;
&lt;br /&gt;
    switch (rx) {&lt;br /&gt;
        case CMD_PING:&lt;br /&gt;
            tx = CMD_PING_REPLY;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_IDENTIFY:&lt;br /&gt;
            tx = ID_KEYBOARD;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case CMD_READ_EVENT:&lt;br /&gt;
            if (__bufferPop(&amp;amp;current_event)) {&lt;br /&gt;
                tx = current_event.key;&lt;br /&gt;
                spi_state = 1; // prochain octet = pressed&lt;br /&gt;
            } else {&lt;br /&gt;
                tx = CMD_NOCMD;&lt;br /&gt;
                INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        default:&lt;br /&gt;
            if (spi_state == 1) {&lt;br /&gt;
                tx = current_event.pressed;&lt;br /&gt;
                spi_state = 0;&lt;br /&gt;
&lt;br /&gt;
                if (__bufferIsEmpty())&lt;br /&gt;
                    INT_HIGH(); // RAS pour la carte mere&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    SPDR = tx; // a envoyer au prochain cycle&lt;br /&gt;
}&lt;br /&gt;
// Manque a coder dautre primitive&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef SPI_H&lt;br /&gt;
#define SPI_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;cmd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spi_init(void);&lt;br /&gt;
void spi_push_event(char key, uint8_t pressed);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8738</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8738"/>
		<updated>2026-01-05T13:59:18Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Carte ecran */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
====== Startup (.s) ======&lt;br /&gt;
Le Startup file est crucial en Bare Metal. Une erreur dans ce fichier peut empêcher le microcontrôleur de démarrer ou provoquer des crashs.&lt;br /&gt;
Rôles principaux :&lt;br /&gt;
&lt;br /&gt;
- Définir l'environnement nécessaire à l'exécution de main().&lt;br /&gt;
&lt;br /&gt;
- S'exécuter avant main() et lancer ensuite main().&lt;br /&gt;
&lt;br /&gt;
- Être adapté à la target (processeur) utilisée.&lt;br /&gt;
&lt;br /&gt;
- Placer correctement la table des vecteurs comme exigé par les ARM Cortex-M.&lt;br /&gt;
&lt;br /&gt;
- Initialiser la pile correctement.&lt;br /&gt;
&lt;br /&gt;
- Initialiser les sections .data et .bss dans la SRAM.&lt;br /&gt;
&lt;br /&gt;
====== Linker (.ld) ======&lt;br /&gt;
Le Linker Script détermine comment les sections du code sont placées en mémoire.&lt;br /&gt;
''Fonctionnalités :''&lt;br /&gt;
- Définir les adresses absolues des sections.&lt;br /&gt;
&lt;br /&gt;
- Définir les zones mémoire, leurs tailles et adresses.&lt;br /&gt;
&lt;br /&gt;
- Fournir les instructions au linker GNU via l'option -T.&lt;br /&gt;
&lt;br /&gt;
- L'extension de fichier est .ld.&lt;br /&gt;
&lt;br /&gt;
Le code linker sert à guider le compilateur pour assembler toutes les sections d’un programme en un fichier binaire unique.  Sur un microcontrôleur comme le '''STM32F410RB''', il faut indiquer où chaque partie du code et des données doit être placée dans la '''mémoire du MCU :'''&lt;br /&gt;
&lt;br /&gt;
- Où commence le code executable, son point d'entrée ;&lt;br /&gt;
&lt;br /&gt;
- Définit chaque region de la memoire qui existe sur le microcontrolleur et leur taille ;&lt;br /&gt;
&lt;br /&gt;
- Où placer les sections differentes du code en memoire (exemple : interruption, etc...).&lt;br /&gt;
Dans l'idée on peut le faire à la main afin de mieux assimiler le linker puis récuperer celui générer automatiquement par le logiciel STM32IDE (également disponible sur github) afin de ne pas avoir a y retoucher a chaque fois et ne pas briquer accidentellement notre puce.&lt;br /&gt;
Pour realiser un linker on doit preciser la SRAM et sa flash. Cette partie décrit les zones mémoire physiques du STM32F410RB. Il faut se fier au plan memoire de la datasheet p.40/763&lt;br /&gt;
Sur celui-ci nous constatons que SRAM est à 32kB et commence à 0x2000 0000&lt;br /&gt;
&lt;br /&gt;
et que la fash commence à 0x0800 0000 et sa taille est de 128kB (donné à la figure 1 page 37/763)&lt;br /&gt;
On peut alors placer dans le linker :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY&lt;br /&gt;
{&lt;br /&gt;
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(rx) → read et execute, donc on peut lire et exécuter du code dedans.&lt;br /&gt;
(rwx) → read, write, execute (en pratique, on n’exécute pas depuis la RAM, mais certains MCU le permettent).&lt;br /&gt;
'''&amp;lt;u&amp;gt;Sections&amp;lt;/u&amp;gt;'''Chaque programme compilé contient plusieurs sections générées par le compilateur. L'ordre à laquelle ils sont cris découle de la convention utilisé dans CMSIS (fichier startup fournis par ARM).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tous les compilateurs suivent la même idée :&lt;br /&gt;
&lt;br /&gt;
- Sections nécessaires au CPU d’abord (vecteurs, code)&lt;br /&gt;
&lt;br /&gt;
- Sections de données ensuite (initialisées, non initialisées)&lt;br /&gt;
&lt;br /&gt;
- Mémoire dynamique à la fin (heap, stack)&lt;br /&gt;
La '''partie ISR_VECTOR''' indique les vecteurs d'interruptions :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
isr_vector :&lt;br /&gt;
{&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
} &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;.isr_vector&amp;quot; : C'est la section qui contient le vecteur d'interruptions (ISR - Interrupt Service Routine). Les vecteurs d'interruptions sont des adresses de fonction qui seront appelées lorsque des interruptions spécifiques se produisent.&lt;br /&gt;
&amp;quot;KEEP&amp;quot; : Cette directive indique au linker de conserver cette section dans le binaire final, même si elle semble inutilisée par le programme. Cela est crucial pour les vecteurs d'interruptions qui doivent absolument être présents dans le binaire.&lt;br /&gt;
&amp;quot;&amp;gt;FLASH&amp;quot; : Cela indique que cette section doit être placée en mémoire Flash, qui est généralement de la mémoire non-volatile, utilisée pour stocker le programme.&lt;br /&gt;
Elle est placée en Flash pour être disponible dès le reset.&lt;br /&gt;
La '''partie TEXT''' contient le code de notre programme :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.text = code exécutable (fonctions, instructions machine).&lt;br /&gt;
.rodata = données constantes (const int, chaînes de caractères, etc.).&lt;br /&gt;
&lt;br /&gt;
_etext marque la fin de la zone code, utile pour copier ensuite la section .data au démarrage.&lt;br /&gt;
ALIGN(4) permet d'aligner l'adresse courante sur une frontière de 4 octets. Cela garantit que le code est correctement aligné en mémoire, ce qui peut être nécessaire pour des performances optimales ou des restrictions matérielles.&lt;br /&gt;
La '''partie DATA''' contient les variables initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.data contient les variables globales initialisées (ex: int x = 5;).&lt;br /&gt;
Ces valeurs sont stockées dans la Flash au moment de la programmation, mais copiées dans la RAM lors du démarrage (d’où &amp;gt;SRAM AT&amp;gt;FLASH).&lt;br /&gt;
&lt;br /&gt;
_sdata et _edata servent au code d’initialisation (startup.s) pour savoir quoi copier et combien d’octets.&lt;br /&gt;
La partie BSS contient les variables non initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.bss contient les variables globales non initialisées (ex: int counter;).&lt;br /&gt;
Ces variables ne sont pas stockées en Flash, car elles ne contiennent pas de valeur initiale.&lt;br /&gt;
&lt;br /&gt;
Au démarrage, le startup code remplit cette zone avec des zéros (memset), d’où “zeroed during startup”.&lt;br /&gt;
Et voici le code complet du linker : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY{&lt;br /&gt;
    FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
    SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
  .isr_vector :&lt;br /&gt;
  {&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il a été tester sur ce main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // do two dummy reads after enabling the peripheral clock, as per the errata&lt;br /&gt;
  volatile uint32_t dummy;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et le makefile :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = faisALaMainLinker.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Fichiers objets&lt;br /&gt;
OBJS = main.o system_stm32f4xx.o startup_stm32f410rx.o&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: main.elf&lt;br /&gt;
&lt;br /&gt;
main.elf: $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
main.o: main.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
system_stm32f4xx.o: ../00-cmsis-device-f4-master/Source/Templates/system_stm32f4xx.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
startup_stm32f410rx.o: ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
firmware.bin: main.elf&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: firmware.bin&lt;br /&gt;
	st-flash write firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o *.elf *.bin&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size main.elf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Processus de compilation : =====&lt;br /&gt;
- Le linker assemble ces fichiers et les place correctement en mémoire, générant un fichier .elf.&lt;br /&gt;
&lt;br /&gt;
- Le fichier .elf est converti en .bin et téléversé dans le microcontrôleur via ST-LINK ou OpenOCD.&lt;br /&gt;
[[Fichier:SchemaCompil.png|centré|vignette|425x425px|Schéma chaine compilation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous retrouvons notre code principale en .c qui est compiler pour en ressortir des fichiers .o qui seront ensuite traiter dans le bloc Linker qui fera le lien entre le code et les blocs mémoires réelles. A la sortie nous aurons des .elf qui un fois dans le programmer le transformera en .bin et y sera téléverser dans le uControleur.&lt;br /&gt;
&lt;br /&gt;
====== CMSIS ======&lt;br /&gt;
C'est à ce lien : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/cmsis-device-f4&amp;lt;/nowiki&amp;gt; qu'on vient télécharger la bibliothèque CMSIS (Cortex Microcontroller Software Interface Standard).&lt;br /&gt;
&lt;br /&gt;
CMSIS (Cortex Microcontroller Software Interface Standard) simplifie l'accès au matériel, l'équivalent d'un &amp;quot;#include &amp;lt;avr/io.h&amp;gt;&amp;quot; pour ARM.&lt;br /&gt;
&lt;br /&gt;
C'est une bibliothèque fournie par ARM et ST, qui simplifie l'accès au matériel en proposant :&lt;br /&gt;
&lt;br /&gt;
- Des définitions pour tous les registres du MCU&lt;br /&gt;
&lt;br /&gt;
- Les prototypes des fonctions système (comme SystemInit())&lt;br /&gt;
&lt;br /&gt;
- La table des vecteurs d'interruptions (startup code)&lt;br /&gt;
&lt;br /&gt;
Si on veut la bibliothèque contenant plus d'information et des examples on peut se fier a ce repertoire : https://github.com/STMicroelectronics/STM32CubeF4/tree/master.&lt;br /&gt;
&lt;br /&gt;
====== Aide à la compilation avec un simple Blink ======&lt;br /&gt;
Récuperation des fichiers &amp;quot;syscall.c&amp;quot; et &amp;quot;sysmem.c&amp;quot; : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32F410xx-Nucleo/Templates/STM32CubeIDE/Example/User&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Voici le Makefile correspondant :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib/User \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = generatedLinkerIDE.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Répertoire build et bin&lt;br /&gt;
BUILD = build&lt;br /&gt;
BIN = bin&lt;br /&gt;
&lt;br /&gt;
# Fichiers source&lt;br /&gt;
SRCS := main.c \&lt;br /&gt;
       ../01-lib/User/syscalls.c \&lt;br /&gt;
       ../01-lib/User/sysmem.c \&lt;br /&gt;
       ../01-lib/system_stm32f4xx.c&lt;br /&gt;
# Fichier startup asm&lt;br /&gt;
ASM_SRCS = ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
&lt;br /&gt;
# Objets&lt;br /&gt;
OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(SRCS))) \&lt;br /&gt;
       $(patsubst %.s,$(BUILD)/%.o,$(notdir $(ASM_SRCS)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: $(BUILD)/main.elf&lt;br /&gt;
&lt;br /&gt;
# Crée le dossier build/ et bin/&lt;br /&gt;
$(BUILD):&lt;br /&gt;
	mkdir -p $(BUILD)&lt;br /&gt;
&lt;br /&gt;
$(BIN):&lt;br /&gt;
	mkdir -p $(BIN)&lt;br /&gt;
&lt;br /&gt;
# Link&lt;br /&gt;
$(BUILD)/main.elf: $(BUILD) $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
$(BUILD)/%.o: %.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/User/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../00-cmsis-device-f4-master/Source/Templates/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/gcc/%.s | $(BUILD)&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
$(BIN)/firmware.bin: $(BUILD)/main.elf | $(BIN)&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: $(BIN)/firmware.bin&lt;br /&gt;
	st-flash write $(BIN)/firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf $(BUILD) $(BIN)&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size $(BUILD)/main.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et le main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous faisons simplement clignoter une led afin de préparer l'environnement de compilation et vérifier que tout fonctionne. Comme ça, si il y a un bug nous pourrons vite isoler cette étape.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de faire clignoter une LED il faut :&lt;br /&gt;
1. Activer l'horloge du GPIO correspondant. Pour ce faire, nous modifions le registre RCC-&amp;gt;AHB1ENR. (Cf datasheet RM0401 p.119/763)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIO&amp;lt;PORT&amp;gt;EN_Pos);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;2. Définir la direction du PIN via le registre&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;PB2&amp;quot; =&amp;gt; PORT = B et PIN_NUM = 2&lt;br /&gt;
Ici par exemple le PIN 3 correspond au 6ieme et 7ieme bit de MODER.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);    // Output&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3. Définir le type de sortie (Registre OTYPER)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;OTYPER &amp;amp;= ~(0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt;));    // Push pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;4. Définir le type de pull (Registre PUPDR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2)); // Forcage 00 : No pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;5. Etape finale : écriture sur le pin (Registre ODR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On peut également modifier de façon atomique (donc juste 1 bit pas tous) via le registre BSRR.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if (GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;))&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; + 16)); // Ici 16 est l'offset à ajouter afin d'avoir le bon registre pour eteindre&lt;br /&gt;
    else&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Lorsque BSRR est lu il est reset juste après et cela permet d'éviter les conflits si une interruption intervient et modifie ODR entre 2 instructions.&lt;br /&gt;
Après on peut également toucher a la vitesse du pin (Registre OSPEEDR) si l'on souhaite optimiser la consommation de batterie.&lt;br /&gt;
''Note : Remplacer &amp;lt;PORT&amp;gt; par A ou B ou C ou etc... et remplacer &amp;lt;PIN&amp;gt; par 1,2,etc.. selon ce qu'on veut activer''&lt;br /&gt;
&lt;br /&gt;
====== Acceder a PA2 et PA3 sur la Nucleo ======&lt;br /&gt;
&amp;quot;SB62 and SB63 must be ON, while SB13 and SB14 must be OFF.&amp;quot; Extrait de la datasheet UM1724 : Chapitre7.10 p26/91 &lt;br /&gt;
&lt;br /&gt;
Il faut dessouder les resistances bridges pour acceder ces ports. Et ensuite deplacer le jumper sur U5V.&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
Nous allons programmer le SPI sur notre microcontrolleur et tester ce code pour la carte Sparkfun Serial 7-Segments Display. Sur ce git : &amp;lt;nowiki&amp;gt;https://github.com/sparkfun/Serial7SegmentDisplay/wiki/Serial-7-Segment-Display-Datasheet&amp;lt;/nowiki&amp;gt; se trouve la datasheet de cette carte que nous allons utiliser pour communiquer en SPI. Dans l'idée, nous allons programmer une bibliothèque suffisament complète afin de pouvoir simplement d'afficher &amp;quot;9&amp;quot; via une commande dans ce style dans notre main : &amp;quot;spi_sent('G');&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On peut tout d'abord centraliser la logique des initialisations des pins afin d'alleger le main (Cf fichier hardware_setup.c et .h) .&lt;br /&gt;
[[Fichier:Carte7SegmentSparkFunPinout.png|centré|vignette|446x446px|7 segments SparkFun Pinout]]&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8737</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8737"/>
		<updated>2026-01-05T13:54:03Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Bcp de detail la miam */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
====== Startup (.s) ======&lt;br /&gt;
Le Startup file est crucial en Bare Metal. Une erreur dans ce fichier peut empêcher le microcontrôleur de démarrer ou provoquer des crashs.&lt;br /&gt;
Rôles principaux :&lt;br /&gt;
&lt;br /&gt;
- Définir l'environnement nécessaire à l'exécution de main().&lt;br /&gt;
&lt;br /&gt;
- S'exécuter avant main() et lancer ensuite main().&lt;br /&gt;
&lt;br /&gt;
- Être adapté à la target (processeur) utilisée.&lt;br /&gt;
&lt;br /&gt;
- Placer correctement la table des vecteurs comme exigé par les ARM Cortex-M.&lt;br /&gt;
&lt;br /&gt;
- Initialiser la pile correctement.&lt;br /&gt;
&lt;br /&gt;
- Initialiser les sections .data et .bss dans la SRAM.&lt;br /&gt;
&lt;br /&gt;
====== Linker (.ld) ======&lt;br /&gt;
Le Linker Script détermine comment les sections du code sont placées en mémoire.&lt;br /&gt;
''Fonctionnalités :''&lt;br /&gt;
- Définir les adresses absolues des sections.&lt;br /&gt;
&lt;br /&gt;
- Définir les zones mémoire, leurs tailles et adresses.&lt;br /&gt;
&lt;br /&gt;
- Fournir les instructions au linker GNU via l'option -T.&lt;br /&gt;
&lt;br /&gt;
- L'extension de fichier est .ld.&lt;br /&gt;
&lt;br /&gt;
Le code linker sert à guider le compilateur pour assembler toutes les sections d’un programme en un fichier binaire unique.  Sur un microcontrôleur comme le '''STM32F410RB''', il faut indiquer où chaque partie du code et des données doit être placée dans la '''mémoire du MCU :'''&lt;br /&gt;
&lt;br /&gt;
- Où commence le code executable, son point d'entrée ;&lt;br /&gt;
&lt;br /&gt;
- Définit chaque region de la memoire qui existe sur le microcontrolleur et leur taille ;&lt;br /&gt;
&lt;br /&gt;
- Où placer les sections differentes du code en memoire (exemple : interruption, etc...).&lt;br /&gt;
Dans l'idée on peut le faire à la main afin de mieux assimiler le linker puis récuperer celui générer automatiquement par le logiciel STM32IDE (également disponible sur github) afin de ne pas avoir a y retoucher a chaque fois et ne pas briquer accidentellement notre puce.&lt;br /&gt;
Pour realiser un linker on doit preciser la SRAM et sa flash. Cette partie décrit les zones mémoire physiques du STM32F410RB. Il faut se fier au plan memoire de la datasheet p.40/763&lt;br /&gt;
Sur celui-ci nous constatons que SRAM est à 32kB et commence à 0x2000 0000&lt;br /&gt;
&lt;br /&gt;
et que la fash commence à 0x0800 0000 et sa taille est de 128kB (donné à la figure 1 page 37/763)&lt;br /&gt;
On peut alors placer dans le linker :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY&lt;br /&gt;
{&lt;br /&gt;
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(rx) → read et execute, donc on peut lire et exécuter du code dedans.&lt;br /&gt;
(rwx) → read, write, execute (en pratique, on n’exécute pas depuis la RAM, mais certains MCU le permettent).&lt;br /&gt;
'''&amp;lt;u&amp;gt;Sections&amp;lt;/u&amp;gt;'''Chaque programme compilé contient plusieurs sections générées par le compilateur. L'ordre à laquelle ils sont cris découle de la convention utilisé dans CMSIS (fichier startup fournis par ARM).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tous les compilateurs suivent la même idée :&lt;br /&gt;
&lt;br /&gt;
- Sections nécessaires au CPU d’abord (vecteurs, code)&lt;br /&gt;
&lt;br /&gt;
- Sections de données ensuite (initialisées, non initialisées)&lt;br /&gt;
&lt;br /&gt;
- Mémoire dynamique à la fin (heap, stack)&lt;br /&gt;
La '''partie ISR_VECTOR''' indique les vecteurs d'interruptions :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
isr_vector :&lt;br /&gt;
{&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
} &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;.isr_vector&amp;quot; : C'est la section qui contient le vecteur d'interruptions (ISR - Interrupt Service Routine). Les vecteurs d'interruptions sont des adresses de fonction qui seront appelées lorsque des interruptions spécifiques se produisent.&lt;br /&gt;
&amp;quot;KEEP&amp;quot; : Cette directive indique au linker de conserver cette section dans le binaire final, même si elle semble inutilisée par le programme. Cela est crucial pour les vecteurs d'interruptions qui doivent absolument être présents dans le binaire.&lt;br /&gt;
&amp;quot;&amp;gt;FLASH&amp;quot; : Cela indique que cette section doit être placée en mémoire Flash, qui est généralement de la mémoire non-volatile, utilisée pour stocker le programme.&lt;br /&gt;
Elle est placée en Flash pour être disponible dès le reset.&lt;br /&gt;
La '''partie TEXT''' contient le code de notre programme :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.text = code exécutable (fonctions, instructions machine).&lt;br /&gt;
.rodata = données constantes (const int, chaînes de caractères, etc.).&lt;br /&gt;
&lt;br /&gt;
_etext marque la fin de la zone code, utile pour copier ensuite la section .data au démarrage.&lt;br /&gt;
ALIGN(4) permet d'aligner l'adresse courante sur une frontière de 4 octets. Cela garantit que le code est correctement aligné en mémoire, ce qui peut être nécessaire pour des performances optimales ou des restrictions matérielles.&lt;br /&gt;
La '''partie DATA''' contient les variables initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.data contient les variables globales initialisées (ex: int x = 5;).&lt;br /&gt;
Ces valeurs sont stockées dans la Flash au moment de la programmation, mais copiées dans la RAM lors du démarrage (d’où &amp;gt;SRAM AT&amp;gt;FLASH).&lt;br /&gt;
&lt;br /&gt;
_sdata et _edata servent au code d’initialisation (startup.s) pour savoir quoi copier et combien d’octets.&lt;br /&gt;
La partie BSS contient les variables non initialisées :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;.bss contient les variables globales non initialisées (ex: int counter;).&lt;br /&gt;
Ces variables ne sont pas stockées en Flash, car elles ne contiennent pas de valeur initiale.&lt;br /&gt;
&lt;br /&gt;
Au démarrage, le startup code remplit cette zone avec des zéros (memset), d’où “zeroed during startup”.&lt;br /&gt;
Et voici le code complet du linker : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
MEMORY{&lt;br /&gt;
    FLASH (rx): ORIGIN = 0x08000000, LENGTH = 128K&lt;br /&gt;
    SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 32K&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
  .isr_vector :&lt;br /&gt;
  {&lt;br /&gt;
    KEEP(*(.isr_vector))&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .text :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
		&lt;br /&gt;
    *(.text)&lt;br /&gt;
    *(.rodata)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _etext = .;&lt;br /&gt;
  } &amp;gt;FLASH&lt;br /&gt;
&lt;br /&gt;
  .data :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sdata = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.data)&lt;br /&gt;
&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _edata = .;&lt;br /&gt;
  } &amp;gt;SRAM AT&amp;gt; FLASH&lt;br /&gt;
&lt;br /&gt;
  .bss :&lt;br /&gt;
  {&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _sbss = .;&lt;br /&gt;
		&lt;br /&gt;
    *(.bss)&lt;br /&gt;
		&lt;br /&gt;
    . = ALIGN(4);&lt;br /&gt;
    _ebss = .;&lt;br /&gt;
  } &amp;gt;SRAM&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Il a été tester sur ce main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // do two dummy reads after enabling the peripheral clock, as per the errata&lt;br /&gt;
  volatile uint32_t dummy;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
  dummy = RCC-&amp;gt;AHB1ENR;&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et le makefile :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = faisALaMainLinker.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Fichiers objets&lt;br /&gt;
OBJS = main.o system_stm32f4xx.o startup_stm32f410rx.o&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: main.elf&lt;br /&gt;
&lt;br /&gt;
main.elf: $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
main.o: main.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
system_stm32f4xx.o: ../00-cmsis-device-f4-master/Source/Templates/system_stm32f4xx.c&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
startup_stm32f410rx.o: ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
firmware.bin: main.elf&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: firmware.bin&lt;br /&gt;
	st-flash write firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o *.elf *.bin&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size main.elf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Processus de compilation : =====&lt;br /&gt;
- Le linker assemble ces fichiers et les place correctement en mémoire, générant un fichier .elf.&lt;br /&gt;
&lt;br /&gt;
- Le fichier .elf est converti en .bin et téléversé dans le microcontrôleur via ST-LINK ou OpenOCD.&lt;br /&gt;
[[Fichier:SchemaCompil.png|centré|vignette|425x425px|Schéma chaine compilation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous retrouvons notre code principale en .c qui est compiler pour en ressortir des fichiers .o qui seront ensuite traiter dans le bloc Linker qui fera le lien entre le code et les blocs mémoires réelles. A la sortie nous aurons des .elf qui un fois dans le programmer le transformera en .bin et y sera téléverser dans le uControleur.&lt;br /&gt;
&lt;br /&gt;
====== CMSIS ======&lt;br /&gt;
C'est à ce lien : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/cmsis-device-f4&amp;lt;/nowiki&amp;gt; qu'on vient télécharger la bibliothèque CMSIS (Cortex Microcontroller Software Interface Standard).&lt;br /&gt;
&lt;br /&gt;
CMSIS (Cortex Microcontroller Software Interface Standard) simplifie l'accès au matériel, l'équivalent d'un &amp;quot;#include &amp;lt;avr/io.h&amp;gt;&amp;quot; pour ARM.&lt;br /&gt;
&lt;br /&gt;
C'est une bibliothèque fournie par ARM et ST, qui simplifie l'accès au matériel en proposant :&lt;br /&gt;
&lt;br /&gt;
- Des définitions pour tous les registres du MCU&lt;br /&gt;
&lt;br /&gt;
- Les prototypes des fonctions système (comme SystemInit())&lt;br /&gt;
&lt;br /&gt;
- La table des vecteurs d'interruptions (startup code)&lt;br /&gt;
&lt;br /&gt;
Si on veut la bibliothèque contenant plus d'information et des examples on peut se fier a ce repertoire : https://github.com/STMicroelectronics/STM32CubeF4/tree/master.&lt;br /&gt;
&lt;br /&gt;
====== Aide à la compilation avec un simple Blink ======&lt;br /&gt;
Récuperation des fichiers &amp;quot;syscall.c&amp;quot; et &amp;quot;sysmem.c&amp;quot; : &amp;lt;nowiki&amp;gt;https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32F410xx-Nucleo/Templates/STM32CubeIDE/Example/User&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Voici le Makefile correspondant :&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
# Compilateur et options&lt;br /&gt;
CC = arm-none-eabi-gcc&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Wextra&lt;br /&gt;
CPPFLAGS = -DSTM32F410Rx \&lt;br /&gt;
	-I../01-lib/gcc \&lt;br /&gt;
	-I../01-lib/Core \&lt;br /&gt;
	-I../01-lib/User \&lt;br /&gt;
	-I../01-lib&lt;br /&gt;
&lt;br /&gt;
# Linker&lt;br /&gt;
LINKER_FILE = generatedLinkerIDE.ld&lt;br /&gt;
LDFLAGS = -T $(LINKER_FILE)&lt;br /&gt;
&lt;br /&gt;
# Répertoire build et bin&lt;br /&gt;
BUILD = build&lt;br /&gt;
BIN = bin&lt;br /&gt;
&lt;br /&gt;
# Fichiers source&lt;br /&gt;
SRCS := main.c \&lt;br /&gt;
       ../01-lib/User/syscalls.c \&lt;br /&gt;
       ../01-lib/User/sysmem.c \&lt;br /&gt;
       ../01-lib/system_stm32f4xx.c&lt;br /&gt;
# Fichier startup asm&lt;br /&gt;
ASM_SRCS = ../01-lib/gcc/startup_stm32f410rx.s&lt;br /&gt;
&lt;br /&gt;
# Objets&lt;br /&gt;
OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(SRCS))) \&lt;br /&gt;
       $(patsubst %.s,$(BUILD)/%.o,$(notdir $(ASM_SRCS)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Cible principale&lt;br /&gt;
all: $(BUILD)/main.elf&lt;br /&gt;
&lt;br /&gt;
# Crée le dossier build/ et bin/&lt;br /&gt;
$(BUILD):&lt;br /&gt;
	mkdir -p $(BUILD)&lt;br /&gt;
&lt;br /&gt;
$(BIN):&lt;br /&gt;
	mkdir -p $(BIN)&lt;br /&gt;
&lt;br /&gt;
# Link&lt;br /&gt;
$(BUILD)/main.elf: $(BUILD) $(OBJS)&lt;br /&gt;
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -lc -lm -lnosys -o $@&lt;br /&gt;
&lt;br /&gt;
# Compilation des fichiers C&lt;br /&gt;
$(BUILD)/%.o: %.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/User/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
$(BUILD)/%.o: ../00-cmsis-device-f4-master/Source/Templates/%.c | $(BUILD)&lt;br /&gt;
	$(CC) $(CFLAGS) $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Assemblage du startup&lt;br /&gt;
$(BUILD)/%.o: ../01-lib/gcc/%.s | $(BUILD)&lt;br /&gt;
	arm-none-eabi-as -mcpu=cortex-m4 -mthumb $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
# Converti ELF en BIN&lt;br /&gt;
$(BIN)/firmware.bin: $(BUILD)/main.elf | $(BIN)&lt;br /&gt;
	arm-none-eabi-objcopy -O binary $&amp;lt; $@&lt;br /&gt;
&lt;br /&gt;
# Upload &lt;br /&gt;
flash: $(BIN)/firmware.bin&lt;br /&gt;
	st-flash write $(BIN)/firmware.bin 0x8000000&lt;br /&gt;
	&lt;br /&gt;
# Nettoyage&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf $(BUILD) $(BIN)&lt;br /&gt;
&lt;br /&gt;
size:&lt;br /&gt;
	arm-none-eabi-size $(BUILD)/main.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et le main.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../01-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 5&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOA&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOAEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOA-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOA-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOA-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous faisons simplement clignoter une led afin de préparer l'environnement de compilation et vérifier que tout fonctionne. Comme ça, si il y a un bug nous pourrons vite isoler cette étape.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de faire clignoter une LED il faut :&lt;br /&gt;
1. Activer l'horloge du GPIO correspondant. Pour ce faire, nous modifions le registre RCC-&amp;gt;AHB1ENR. (Cf datasheet RM0401 p.119/763)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIO&amp;lt;PORT&amp;gt;EN_Pos);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;2. Définir la direction du PIN via le registre&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;PB2&amp;quot; =&amp;gt; PORT = B et PIN_NUM = 2&lt;br /&gt;
Ici par exemple le PIN 3 correspond au 6ieme et 7ieme bit de MODER.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2);    // Output&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3. Définir le type de sortie (Registre OTYPER)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;OTYPER &amp;amp;= ~(0x1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt;));    // Push pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;4. Définir le type de pull (Registre PUPDR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; * 2)); // Forcage 00 : No pull&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;5. Etape finale : écriture sur le pin (Registre ODR)&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On peut également modifier de façon atomique (donc juste 1 bit pas tous) via le registre BSRR.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if (GPIO&amp;lt;PORT&amp;gt;-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;))&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (&amp;lt;PIN_NUM&amp;gt; + 16)); // Ici 16 est l'offset à ajouter afin d'avoir le bon registre pour eteindre&lt;br /&gt;
    else&lt;br /&gt;
      GPIO&amp;lt;PORT&amp;gt;-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; &amp;lt;PIN_NUM&amp;gt;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Lorsque BSRR est lu il est reset juste après et cela permet d'éviter les conflits si une interruption intervient et modifie ODR entre 2 instructions.&lt;br /&gt;
Après on peut également toucher a la vitesse du pin (Registre OSPEEDR) si l'on souhaite optimiser la consommation de batterie.&lt;br /&gt;
''Note : Remplacer &amp;lt;PORT&amp;gt; par A ou B ou C ou etc... et remplacer &amp;lt;PIN&amp;gt; par 1,2,etc.. selon ce qu'on veut activer''&lt;br /&gt;
&lt;br /&gt;
====== Acceder a PA2 et PA3 sur la Nucleo ======&lt;br /&gt;
&amp;quot;SB62 and SB63 must be ON, while SB13 and SB14 must be OFF.&amp;quot; Extrait de la datasheet UM1724 : Chapitre7.10 p26/91 &lt;br /&gt;
&lt;br /&gt;
Il faut dessouder les resistances bridges pour acceder ces ports. Et ensuite deplacer le jumper sur U5V.&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
Nous allons programmer le SPI sur notre microcontrolleur et tester ce code pour la carte Sparkfun Serial 7-Segments Display.&lt;br /&gt;
Sur ce git : &amp;lt;nowiki&amp;gt;https://github.com/sparkfun/Serial7SegmentDisplay/wiki/Serial-7-Segment-Display-Datasheet&amp;lt;/nowiki&amp;gt; se trouve la datasheet de cette carte que nous allons utiliser pour communiquer en SPI.&lt;br /&gt;
Dans l'idée, nous allons programmer une bibliothèque suffisament complète afin de pouvoir simplement d'afficher &amp;quot;9&amp;quot; via une commande dans ce style dans notre main : &amp;quot;spi_sent('G');&amp;quot;&lt;br /&gt;
On peut tout d'abord centraliser la logique des initialisations des pins afin d'alleger le main (Cf fichier hardware_setup.c et .h) .&lt;br /&gt;
[[Fichier:Carte7SegmentSparkFunPinout.png|centré|vignette|446x446px|7 segments SparkFun Pinout]]&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Carte7SegmentSparkFunPinout.png&amp;diff=8736</id>
		<title>Fichier:Carte7SegmentSparkFunPinout.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Carte7SegmentSparkFunPinout.png&amp;diff=8736"/>
		<updated>2026-01-05T13:52:18Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Carte7SegmentSparkFunPinout&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:SchemaCompil.png&amp;diff=8722</id>
		<title>Fichier:SchemaCompil.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:SchemaCompil.png&amp;diff=8722"/>
		<updated>2026-01-05T13:10:08Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;SchemaCompil&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8685</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8685"/>
		<updated>2026-01-03T22:27:16Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* LUFA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A FAIRE PROPREMENT&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
  if (btn51)&lt;br /&gt;
    KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). Update : Dernier bouton trouvé :) Clavier entierement mappé ! &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8684</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8684"/>
		<updated>2025-12-30T22:51:53Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : Ajout code 1ere partie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A FAIRE PROPREMENT&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
    // if (btn51) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = ; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8683</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8683"/>
		<updated>2025-12-30T22:50:37Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : Ajout code 1ere partie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A FAIRE PROPREMENT&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
    // if (btn51) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = ; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
&lt;br /&gt;
A AJOUTER APRES&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8682</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8682"/>
		<updated>2025-12-30T22:48:50Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : Ajout code 1ere partie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A FAIRE PROPREMENT&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
    // if (btn51) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = ; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8681</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8681"/>
		<updated>2025-12-30T22:44:51Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* HARWARE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Le routage à été modifié sur kicad par la suite et l'erreur réparé comme on peut le voir sur la photo.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Faire un ordonnanceur sur une architecture AVR est nécessaire si l'on souhaite ensuite le faire sur une architecture ARM Cortex M4 qui est bien plus complexe. &lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARDWARE ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(volatile uint8_t *PINx, uint8_t pin) {&lt;br /&gt;
  return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef HARDWARE_H&lt;br /&gt;
#define HARDWARE_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN PIND&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ------------------ Prototypes ------------------ //&lt;br /&gt;
void setupClock();&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode);&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin);&lt;br /&gt;
void setupHardware();&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1();&lt;br /&gt;
void toggleLedCS2();&lt;br /&gt;
void toggleLedCS3();&lt;br /&gt;
void toggleLedCS4();&lt;br /&gt;
&lt;br /&gt;
void taskToggleCS1();&lt;br /&gt;
void taskToggleCS2();&lt;br /&gt;
void taskToggleCS3();&lt;br /&gt;
void taskToggleCS4();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dans la librairie hardware.c nous avons es ensembles de fonctions lié à notre matérielle pour son initialisation et son contrôle ou sa lecture (de pin).&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans la librarie USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et usart.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
// Baud rate variable que l'on peut modifier&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU / 16 / BAUD_RATE) - 1)&lt;br /&gt;
&lt;br /&gt;
void init_usart();&lt;br /&gt;
void usart_send(unsigned char data);&lt;br /&gt;
unsigned char usart_receive();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
Task.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    {taskSendSerialB, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task) / sizeof(task[0]);&lt;br /&gt;
&lt;br /&gt;
void init_tasks() {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nbTasks; i++) {&lt;br /&gt;
    init_pile(i);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS1();&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS2();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS3();&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    toggleLedCS4();&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Task.h: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  AWAKE,&lt;br /&gt;
  SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  void (*taskAddress)(void); // fonction de la tache&lt;br /&gt;
  uint16_t stackPointer;     // pointeur de pile&lt;br /&gt;
  state_task state;          // AWAKE ou SLEEP&lt;br /&gt;
  uint16_t sleepTime;        // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
extern process task[];&lt;br /&gt;
extern const uint8_t nbTasks;&lt;br /&gt;
&lt;br /&gt;
void init_tasks();&lt;br /&gt;
void task_led(void (*toggleFunc)(void), uint16_t ms);&lt;br /&gt;
&lt;br /&gt;
void taskCS1();&lt;br /&gt;
void taskCS2();&lt;br /&gt;
void taskCS3();&lt;br /&gt;
void taskCS4();&lt;br /&gt;
void taskSendSerialA();&lt;br /&gt;
void taskSendSerialB();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
On définit un tableau de tâches ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le nombre de tâche est calculé automatique pour ne pas s'embêter à le faire à chaque fois que l'on modifie de le tableau de tâche.&lt;br /&gt;
&lt;br /&gt;
Si certaines tâches sont commentés c'est parceque sur cette 1ere version d'ordonnanceur, nous avons fais au plus simple sans contrôle de tache endormi, nous ne pourrons donc pas avoir un résultat propre avec plusieurs tâches simultanément.&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
ordonnanceur.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
uint16_t tick_ms = 0;&lt;br /&gt;
&lt;br /&gt;
// ------------------ TIMER ------------------ //&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms) {&lt;br /&gt;
  tick_ms = periode_ms;&lt;br /&gt;
&lt;br /&gt;
  TCCR1A = 0;&lt;br /&gt;
  TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
  switch (diviseur) {&lt;br /&gt;
  case 64:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS11) | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
    break;&lt;br /&gt;
  case 256:&lt;br /&gt;
    TCCR1B |= (1 &amp;lt;&amp;lt; CS12);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
  TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ PILE ------------------ //&lt;br /&gt;
void init_pile(int n) {&lt;br /&gt;
  uint16_t savedSP = SP;&lt;br /&gt;
  uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
  SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
  // PC (low puis high)&lt;br /&gt;
  asm volatile(&amp;quot;push %A0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
  asm volatile(&amp;quot;push %B0&amp;quot; ::&amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
  // r0-r31&lt;br /&gt;
  for (int i = 0; i &amp;lt; 32; i++)&lt;br /&gt;
    asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // SREG = I=1&lt;br /&gt;
  uint8_t s = 0x80;&lt;br /&gt;
  asm volatile(&amp;quot;push %0&amp;quot; ::&amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
  task[n].stackPointer = SP;&lt;br /&gt;
  SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ SCHEDULER ------------------ //&lt;br /&gt;
void scheduler(void) {&lt;br /&gt;
  currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ------------------ ISR TIMER1 ------------------ //&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED) {&lt;br /&gt;
&lt;br /&gt;
  /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
  /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
  scheduler();&lt;br /&gt;
&lt;br /&gt;
  /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
  SP = task[currentTask].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons ici un ordonnanceur préemptif : une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine qui va : &lt;br /&gt;
* Sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* Appeller l'ordonnanceur qui va faire la bascule des tâches.&lt;br /&gt;
* Restorer le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité sous sa forme la plus minimaliste.&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, voici comment les librairies vu ensemble precedemment sont appelés :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/ORDONNANCEUR/ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;./lib/TASK/task.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microcontrolleur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A sur notre Git.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6 basé sur un Cortex-M4.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Toutes les datasheets ayant servi pour la creation du Kicad de la carte se trouve dans le dossier Datasheet.&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir le bloc mémoire  :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour tester en premier lieu le microcontrolleur puis l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour la programmation de la carte :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;C'est un peu l'équivalent arduino, on code avec l'abstraction matérielle. Nous allons par la suite proceder au niveau bare metal.&lt;br /&gt;
&lt;br /&gt;
===== Bare metal =====&lt;br /&gt;
Ce chapitre présente la programmation Bare Metal sur microcontrôleurs ARM, en utilisant un STM32 NUCLEO-F410RB comme exemple. Nous aborderons le startup, le linker, les exemples de clignotement LED, l'utilisation de CMSIS, ainsi que la gestion de périphériques comme le SPI, l'UART, etc...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A FAIRE PROPREMENT&lt;br /&gt;
&lt;br /&gt;
===== Système de fichier =====&lt;br /&gt;
&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Maintenant que la carte à été testé nous avons fais plusieurs code intermediaire pour tester chaque primitive séparemment. Je vais detailler seulement code finale pour ne pas se répéter.  &lt;br /&gt;
&lt;br /&gt;
===== Carte =====&lt;br /&gt;
Dans ce dossier nous retrouverons toutes les librairies liés au contrôle des cartes filles correspondantes.&lt;br /&gt;
&lt;br /&gt;
====== Carte ecran ======&lt;br /&gt;
L'idée est d'avoir une librairie permettant de controller une carte écran et d'afficher un compteur sur celui-ci. Notre carte écran est ici l'afficheur 7 segments de Sparkfun. &lt;br /&gt;
&lt;br /&gt;
carteEcran.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void _ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Ces pins s'active à l'etat bas&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // CS OFF&lt;br /&gt;
&lt;br /&gt;
  offPin(RST5_GPIO, RST5_PIN); // RST ON&lt;br /&gt;
  onPin(RST5_GPIO, RST5_PIN);  // RST OFF&lt;br /&gt;
&lt;br /&gt;
  // Non utilisé ici&lt;br /&gt;
  //   onPin(INT5_GPIO, INT5_PIN); // S'active à l'etat bas ?&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_select_digit(uint8_t digit) {&lt;br /&gt;
  // Sécurité maximum digit&lt;br /&gt;
  if (digit &amp;gt;= 3)&lt;br /&gt;
    digit = 3;&lt;br /&gt;
&lt;br /&gt;
  ecran_spi_write(0x79);  // Cursor command&lt;br /&gt;
  ecran_spi_write(digit); // 0 à 3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et son .h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Fonction inter librairie&lt;br /&gt;
void _ecran_init();&lt;br /&gt;
&lt;br /&gt;
// Fonctions pour l'utilisateur&lt;br /&gt;
void ecran_spi_write(uint8_t data);&lt;br /&gt;
void ecran_brightness(uint8_t intensite);&lt;br /&gt;
void ecran_select_digit(uint8_t digit);&lt;br /&gt;
&lt;br /&gt;
// Fonction exemple pour utilisateur&lt;br /&gt;
void ecran_compteur();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Les commandes de l'écran ont été trouvé sur leur site : https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
Il faut noter que la vitesse du SPI peut influencer sur la précision du SPI et donc faire échouer certains messages si la vitesse dépasse 9600 bauds rate.&lt;br /&gt;
&lt;br /&gt;
====== Carte clavier ======&lt;br /&gt;
A COMPLETER&lt;br /&gt;
&lt;br /&gt;
===== Commande OS =====&lt;br /&gt;
Cette librairie permet d'implémenter les commandes liés à notre OS tel que &amp;quot;version&amp;quot;, &amp;quot;echo, et &amp;quot;devices&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
cmd_os.c: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;cmd_os.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX_ARG_ECHO 20&lt;br /&gt;
#define CMD_SIZE 128&lt;br /&gt;
&lt;br /&gt;
#define CHAR_CODE_ESC 0x1B&lt;br /&gt;
#define CHAR_CODE_ENTER '\r'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE1 '\b'&lt;br /&gt;
#define CHAR_CODE_BACKSPACE2 0x7F&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  KEY_NONE,&lt;br /&gt;
  KEY_CHAR,&lt;br /&gt;
  KEY_ENTER,&lt;br /&gt;
  KEY_BACKSPACE,&lt;br /&gt;
  KEY_ARROW_UP,&lt;br /&gt;
  KEY_ARROW_DOWN,&lt;br /&gt;
  KEY_ARROW_LEFT,&lt;br /&gt;
  KEY_ARROW_RIGHT&lt;br /&gt;
} KeyType;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NORMAL_CHAR = 0,&lt;br /&gt;
  ESC_CHAR = 1,&lt;br /&gt;
  ARROW_CHAR = 2,&lt;br /&gt;
} _stateKey;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
  KeyType type;&lt;br /&gt;
  char ch;&lt;br /&gt;
} KeyEvent;&lt;br /&gt;
&lt;br /&gt;
char current_cmd[CMD_SIZE + 1] = {'\0'};&lt;br /&gt;
static uint8_t cmd_len = 0;&lt;br /&gt;
&lt;br /&gt;
_stateKey esc_state = NORMAL_CHAR;&lt;br /&gt;
KeyEvent ev = {KEY_NONE, 0};&lt;br /&gt;
&lt;br /&gt;
KeyEvent _decode_key(char c) {&lt;br /&gt;
  switch (esc_state) {&lt;br /&gt;
  case NORMAL_CHAR:&lt;br /&gt;
    ev.type = KEY_NONE;&lt;br /&gt;
    ev.ch = 0;&lt;br /&gt;
    if (c == CHAR_CODE_ESC) { // ESC&lt;br /&gt;
      esc_state = ESC_CHAR;&lt;br /&gt;
      return ev; // attente suite&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (c == CHAR_CODE_ENTER) {&lt;br /&gt;
      ev.type = KEY_ENTER;&lt;br /&gt;
    } else if (c == CHAR_CODE_BACKSPACE1 || c == CHAR_CODE_BACKSPACE2) {&lt;br /&gt;
      ev.type = KEY_BACKSPACE;&lt;br /&gt;
    } else {&lt;br /&gt;
      ev.type = KEY_CHAR;&lt;br /&gt;
      ev.ch = c;&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ESC_CHAR:&lt;br /&gt;
    if (c == '[' || c == 'O') {&lt;br /&gt;
      esc_state = ARROW_CHAR;&lt;br /&gt;
    } else {&lt;br /&gt;
      esc_state = NORMAL_CHAR; // ESC seul = abandon&lt;br /&gt;
    }&lt;br /&gt;
    return ev;&lt;br /&gt;
&lt;br /&gt;
  case ARROW_CHAR:&lt;br /&gt;
    switch (c) {&lt;br /&gt;
    case 'A':&lt;br /&gt;
      ev.type = KEY_ARROW_UP;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'B':&lt;br /&gt;
      ev.type = KEY_ARROW_DOWN;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'C':&lt;br /&gt;
      ev.type = KEY_ARROW_RIGHT;&lt;br /&gt;
      break;&lt;br /&gt;
    case 'D':&lt;br /&gt;
      ev.type = KEY_ARROW_LEFT;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      // ignore les autres codes&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    esc_state = NORMAL_CHAR;&lt;br /&gt;
    return ev;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ev.type = KEY_NONE;&lt;br /&gt;
  ev.ch = 0;&lt;br /&gt;
  esc_state = NORMAL_CHAR;&lt;br /&gt;
  return ev;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_inconnu() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nCommande inconnue : &amp;quot;);&lt;br /&gt;
  PRINT_STRING(current_cmd);&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_version() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nOS : version 0.0.2\n\r&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_echo(char *input) {&lt;br /&gt;
  char *argv[MAX_ARG_ECHO];&lt;br /&gt;
  int argc = 0;&lt;br /&gt;
&lt;br /&gt;
  // Découpe la ligne en mots&lt;br /&gt;
  char *token = strtok(input, &amp;quot; &amp;quot;);&lt;br /&gt;
  while (token != NULL &amp;amp;&amp;amp; argc &amp;lt; MAX_ARG_ECHO) {&lt;br /&gt;
    argv[argc++] = token;&lt;br /&gt;
    token = strtok(NULL, &amp;quot; &amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (argc &amp;gt; 0) {&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r&amp;quot;);&lt;br /&gt;
    for (int i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
      PRINT_STRING(argv[i]);&lt;br /&gt;
      if (i + 1 &amp;lt; argc)&lt;br /&gt;
        PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _cmdOS_devices(void) {&lt;br /&gt;
  scan_devices();&lt;br /&gt;
  print_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _execute_command(char *cmd) {&lt;br /&gt;
  if (strcmp(cmd, &amp;quot;version&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_version();&lt;br /&gt;
  else if (strcmp(cmd, &amp;quot;devices&amp;quot;) == 0)&lt;br /&gt;
    _cmdOS_devices();&lt;br /&gt;
  else if (strncmp(cmd, &amp;quot;echo&amp;quot;, strlen(&amp;quot;echo&amp;quot;)) == 0)&lt;br /&gt;
    _cmdOS_echo(cmd);&lt;br /&gt;
  else if (cmd_len &amp;gt; 0)&lt;br /&gt;
    _cmdOS_inconnu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_os() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;Initialisation du Pico ordinateur &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; 6; i++) {&lt;br /&gt;
    task_delay(100);&lt;br /&gt;
    PRINT_STRING(&amp;quot;.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  PRINT_STRING(&amp;quot;\n\rPicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void) {&lt;br /&gt;
  _init_os();&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    while (!usart_buffer_available())&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    char c = GETCHAR();&lt;br /&gt;
    KeyEvent key = _decode_key(c);&lt;br /&gt;
&lt;br /&gt;
    switch (key.type) {&lt;br /&gt;
    case KEY_CHAR:&lt;br /&gt;
      if (cmd_len &amp;lt; CMD_SIZE) {&lt;br /&gt;
        current_cmd[cmd_len++] = key.ch;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_CHAR(key.ch);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_BACKSPACE:&lt;br /&gt;
      if (cmd_len &amp;gt; 0) {&lt;br /&gt;
        cmd_len--;&lt;br /&gt;
        current_cmd[cmd_len] = '\0';&lt;br /&gt;
        PRINT_STRING(&amp;quot;\b \b&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ENTER:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
      _execute_command(current_cmd);&lt;br /&gt;
      cmd_len = 0;&lt;br /&gt;
      current_cmd[0] = '\0';&lt;br /&gt;
      PRINT_STRING(&amp;quot;PicoOrdi&amp;gt;&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_UP:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_DOWN:&lt;br /&gt;
// A ajouter à la fin&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_LEFT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[D&amp;quot;);&lt;br /&gt;
      cmd_len--;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    case KEY_ARROW_RIGHT:&lt;br /&gt;
      PRINT_STRING(&amp;quot;\x1B[C&amp;quot;);&lt;br /&gt;
      cmd_len++;&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;cmd_os.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CMD_OS_H&lt;br /&gt;
#define CMD_OS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Permet de lancer les commandes suivantes : &lt;br /&gt;
    &lt;br /&gt;
    version =&amp;gt; Affiche la version du système d'exploitation&lt;br /&gt;
    echo =&amp;gt; Affiche les arguments de la commande&lt;br /&gt;
    devices =&amp;gt; Scan toutes les cartes et retourne celle connecté&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void cmd_os(void); // Affiche la liste des cartes filles connectées&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour le moment la gestion du terminal permet de gérer la détection des flèches grâce à une implémentation de la détection de touche avec plusieurs états.&lt;br /&gt;
&lt;br /&gt;
===== Devices =====&lt;br /&gt;
Cette librairie permet de &lt;br /&gt;
&lt;br /&gt;
device.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../Substitute/printf.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const GPIO_TypeDef *cs_gpios[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = GPIOC, // PC0&lt;br /&gt;
    [CS2] = GPIOA, // PA7&lt;br /&gt;
    [CS3] = GPIOC, // PC9&lt;br /&gt;
    [CS4] = GPIOA, // PA2&lt;br /&gt;
    [CS5] = GPIOC, // PC11&lt;br /&gt;
    [CS6] = GPIOA, // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static const uint8_t cs_pins[MAX_DEVICES] = {&lt;br /&gt;
    [CS1] = 0,  // PC0&lt;br /&gt;
    [CS2] = 7,  // PA7&lt;br /&gt;
    [CS3] = 9,  // PC9&lt;br /&gt;
    [CS4] = 2,  // PA2&lt;br /&gt;
    [CS5] = 11, // PC11&lt;br /&gt;
    [CS6] = 4,  // PA4&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Device devices[MAX_DEVICES];&lt;br /&gt;
&lt;br /&gt;
static const char *device_names[] = {&lt;br /&gt;
    &amp;quot;NOT CONNECTED&amp;quot;,&lt;br /&gt;
    &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
    &amp;quot;KEYBOARD&amp;quot;,&lt;br /&gt;
    &amp;quot;SCREEN&amp;quot;,&lt;br /&gt;
    &amp;quot;NETWORK&amp;quot;,&lt;br /&gt;
    &amp;quot;SOUND&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *cs_names[] = {&lt;br /&gt;
    &amp;quot;PC0&amp;quot;,&lt;br /&gt;
    &amp;quot;PA7&amp;quot;,&lt;br /&gt;
    &amp;quot;PC9&amp;quot;,&lt;br /&gt;
    &amp;quot;PA2&amp;quot;,&lt;br /&gt;
    &amp;quot;PC11&amp;quot;,&lt;br /&gt;
    &amp;quot;PA4&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
static const char *names[] = {&lt;br /&gt;
    &amp;quot;Clavier&amp;quot;,&lt;br /&gt;
    &amp;quot;FPGA&amp;quot;,&lt;br /&gt;
    &amp;quot;Son&amp;quot;,&lt;br /&gt;
    &amp;quot;Reseau&amp;quot;,&lt;br /&gt;
    &amp;quot;Ecran&amp;quot;,&lt;br /&gt;
    &amp;quot;SD&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
void _devices_init() {&lt;br /&gt;
  // Init tableau&lt;br /&gt;
  for (int i = 0; i &amp;lt; MAX_DEVICES; i++) {&lt;br /&gt;
    devices[i].GPIO_CS = (GPIO_TypeDef *)cs_gpios[i];&lt;br /&gt;
    devices[i].PIN_CS = cs_pins[i];&lt;br /&gt;
    devices[i].type = UNKNOWN;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // SD toujours ici&lt;br /&gt;
  devices[5].type = SD; // Peut etre tester si connecte ou non&lt;br /&gt;
&lt;br /&gt;
  // Scan du démarrage&lt;br /&gt;
  scan_devices();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan_devices() {&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
// A ajouter a la fin&lt;br /&gt;
// --------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void print_devices() {&lt;br /&gt;
  PRINT_STRING(&amp;quot;\r\nDevice list:\r\n&amp;quot;);&lt;br /&gt;
  for (int device = 0; device &amp;lt; MAX_DEVICES; device++) {&lt;br /&gt;
    char buffer[15];&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device);&lt;br /&gt;
    PRINT_STRING(&amp;quot;N°&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(device_names[devices-&amp;gt;type]);&lt;br /&gt;
&lt;br /&gt;
    sprintf(buffer, &amp;quot;%d&amp;quot;, device + 1);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tCS&amp;quot;);&lt;br /&gt;
    PRINT_STRING(buffer);&lt;br /&gt;
    PRINT_STRING(&amp;quot;: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(cs_names[device]);&lt;br /&gt;
&lt;br /&gt;
    if (device &amp;lt; 10)&lt;br /&gt;
      PRINT_STRING(&amp;quot; &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    PRINT_STRING(&amp;quot;\t\tName port: &amp;quot;);&lt;br /&gt;
    PRINT_STRING(names[device]);&lt;br /&gt;
    PRINT_STRING(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Device get_state_device(DeviceCS CSx) {&lt;br /&gt;
  return devices[CSx];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
device.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef DEVICES_H&lt;br /&gt;
#define DEVICES_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DEVICES 6&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    NOT_CONNECTED = 0,&lt;br /&gt;
    UNKNOWN,&lt;br /&gt;
    KEYBOARD,&lt;br /&gt;
    SCREEN,&lt;br /&gt;
    NETWORK,&lt;br /&gt;
    SOUND,&lt;br /&gt;
    SD,&lt;br /&gt;
} DeviceType;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    GPIO_TypeDef *GPIO_CS;        // GPIO Device Select&lt;br /&gt;
    uint8_t PIN_CS;               // Pin Device Select&lt;br /&gt;
    DeviceType type;              // Type de device&lt;br /&gt;
} Device;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
    CS1 = 0, // PC0&lt;br /&gt;
    CS2 = 1, // PA7&lt;br /&gt;
    CS3 = 2, // PC9&lt;br /&gt;
    CS4 = 3, // PA2&lt;br /&gt;
    CS5 = 4, // PC11&lt;br /&gt;
    CS6 = 5, // PA4&lt;br /&gt;
} DeviceCS;&lt;br /&gt;
&lt;br /&gt;
void _devices_init(void);&lt;br /&gt;
void scan_devices(void);&lt;br /&gt;
Device get_state_device(DeviceCS CSx);&lt;br /&gt;
void print_devices(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== GPIO =====&lt;br /&gt;
Cette librairie permet d'initialiser les pins de notre carte via des fonctions intermediaire pour une meilleure lisibilité. Elle permet aussi de controller et lire les pins si besoin.&lt;br /&gt;
&lt;br /&gt;
gpio.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../CARTE/Ecran/carteEcran.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../DEVICES/devices.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../SPI/spi.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
  case ALTERNATE_FUNCTION_USART:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    // GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2); // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    if (GPIOx == GPIOA &amp;amp;&amp;amp; (PINx == 9 || PINx == 10)) {&lt;br /&gt;
      // Sélection AF7 pour USART1 (PA9, PA10)&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] &amp;amp;= ~(0xF &amp;lt;&amp;lt; ((PINx - 8) * 4));&lt;br /&gt;
      GPIOx-&amp;gt;AFR[1] |= (7 &amp;lt;&amp;lt; ((PINx - 8) * 4)); // AF7 = USART1&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  return (GPIOx-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; PINx));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (readPin(GPIOx, PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  _devices_init();&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  _ecran_init();&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, INPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT); // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT); // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // SPI&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // On eteint tous les RST&lt;br /&gt;
  onPin(GPIOC, 1); // PC1, RST1&lt;br /&gt;
&lt;br /&gt;
  usart_init(115200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;gpio.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef GPIO_H&lt;br /&gt;
#define GPIO_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ALTERNATE_FUNCTION_USART,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
#define CLOCK_MHZ 16 // HSI = 16MHz Cf STM32F410 datasheet p82/142&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode);&lt;br /&gt;
int readPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
void setupCarte();&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Cette librairie ainsi que TASK sont les plus importante car elles étaient les plus dur à implémenter. Aucun étudiant n'avait déjà travailler sur un ordonnanceur sur une architecture ARM et peu d'information sont présente sur internet sur ce sujet. Les professeurs m'ont confiés ce sujet afin que je puisse aider à l'évolution du module pico ordinateur avec peut être plus de microcontrolleur sur ARM pour les prochaines années.&lt;br /&gt;
&lt;br /&gt;
C'était donc un défi très interessant de réussir à coder les primitives d'un ordonnanceur sur ARM.&lt;br /&gt;
&lt;br /&gt;
ordonnanceur.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
uint32_t g_tick_count = 0;&lt;br /&gt;
uint32_t INCREMENT_TIMER = 0;&lt;br /&gt;
&lt;br /&gt;
extern TCB_t *current_task_ptr;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
uint32_t _get_psp_addr(void) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return 0;&lt;br /&gt;
  return (uint32_t)current_task_ptr-&amp;gt;psp_addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _save_psp_addr(uint32_t addr) {&lt;br /&gt;
  if (!current_task_ptr)&lt;br /&gt;
    return;&lt;br /&gt;
  current_task_ptr-&amp;gt;psp_addr = (uint32_t *)addr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _setupTimer5(uint32_t ms) {&lt;br /&gt;
  INCREMENT_TIMER = ms;&lt;br /&gt;
  // Choix TIM5 car gestion d'un timer simple&lt;br /&gt;
&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE TIM5 SPECIFIQUE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM5EN;&lt;br /&gt;
&lt;br /&gt;
  // Prescaler Register&lt;br /&gt;
  TIM5-&amp;gt;PSC = (CLOCK_MHZ - 1); // Diviser par Clock pour avoir 1 MHz&lt;br /&gt;
&lt;br /&gt;
  // ARR : Auto Reload Register&lt;br /&gt;
  TIM5-&amp;gt;ARR = (ms * 1000) - 1; // 1/1MHz * 1000 devient des millisecondes&lt;br /&gt;
  // valeur à laquelle le timer reset et déclenche une interruption Cf p341&lt;br /&gt;
&lt;br /&gt;
  // Counter Register&lt;br /&gt;
  TIM5-&amp;gt;CNT = 0; // On commencer a compter à 0&lt;br /&gt;
&lt;br /&gt;
  // DMA/Interrupt enable register&lt;br /&gt;
  TIM5-&amp;gt;DIER |= TIM_DIER_UIE; //  Update interrupt enable&lt;br /&gt;
&lt;br /&gt;
  // Activer TIM5 en mode compteur&lt;br /&gt;
  TIM5-&amp;gt;CR1 |= TIM_CR1_CEN;&lt;br /&gt;
&lt;br /&gt;
  NVIC_SetPriority(USART1_IRQn, 0x01);&lt;br /&gt;
  NVIC_SetPriority(TIM5_IRQn, 0x10);&lt;br /&gt;
  NVIC_SetPriority(PendSV_IRQn, 0x3);&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(TIM5_IRQn);&lt;br /&gt;
  NVIC_EnableIRQ(PendSV_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void TIM5_IRQHandler(void) {&lt;br /&gt;
  // Clear flag&lt;br /&gt;
  TIM5-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br /&gt;
&lt;br /&gt;
  g_tick_count += INCREMENT_TIMER;&lt;br /&gt;
  _unblock_tasks();&lt;br /&gt;
&lt;br /&gt;
  // Déclenche PendSV (switch context)&lt;br /&gt;
  SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Bascule MSP à PSP&lt;br /&gt;
__attribute__((naked)) void launch_os(void) {&lt;br /&gt;
  // Initialisation PSP avec la pile de la tâche courante&lt;br /&gt;
  __asm volatile(&amp;quot;PUSH {LR}&amp;quot;);        // Sauvegarde LR (adresse du main() )&lt;br /&gt;
  __asm volatile(&amp;quot;BL _get_psp_addr&amp;quot;); // Appelle _get_psp_addr(), retourne le PSP de la tâche courante dans r0&lt;br /&gt;
  __asm volatile(&amp;quot;MSR PSP,R0&amp;quot;);       // Met à jour le PSP avec la nouvelle valeur&lt;br /&gt;
  __asm volatile(&amp;quot;POP {LR}&amp;quot;);         // Restaure LR sauvegardé avant les appels BL&lt;br /&gt;
&lt;br /&gt;
  // Bascule du Stack Pointer actif de MSP vers PSP&lt;br /&gt;
  __asm volatile(&amp;quot;MOV R0,#0X02&amp;quot;);   // Selection du PSP en Thread mode (CONTROL.SPSEL = 1)&lt;br /&gt;
  __asm volatile(&amp;quot;MSR CONTROL,R0&amp;quot;); // Mise à jour du registre CONTROL&lt;br /&gt;
  __asm volatile(&amp;quot;CPSIE I&amp;quot;);        // Active les IRQ&lt;br /&gt;
  __asm volatile(&amp;quot;BX LR&amp;quot;);          // Retour à l'appelant en utilisant désormais le PSP&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quand PendSV est executé, le systeme enregistre le contexte de la tâche courante automatiquement.&lt;br /&gt;
// Ici, il est utilisé en naked donc on enregistre à la mano.&lt;br /&gt;
__attribute__((naked)) void PendSV_Handler(void) {&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
  // PC saute à l'adresse&lt;br /&gt;
  __asm volatile(&amp;quot;BL _scheduler&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  RESTORE_REGISTERS()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void init_os(void) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
  _setupTimer5(1);&lt;br /&gt;
  _init_tasks();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;ordonnanceur.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef ORDONNANCEUR_H&lt;br /&gt;
#define ORDONNANCEUR_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../TASK/task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;MRS r0, PSP                 @ r0 = PSP courant\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;STMDB r0!, {r4-r11}         @ Sauvegarde registre R4 à R11 sur la pile PSP\n\t&amp;quot;  \&lt;br /&gt;
                   &amp;quot;PUSH {LR}                   @ Sauvegarde LR sur la pile MSP avant BL\n\t&amp;quot; \&lt;br /&gt;
                   &amp;quot;BL _save_psp_addr            @ Appelle save_psp_addr(r0) pour mémoriser le PSP\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
    __asm volatile(&amp;quot;BL _get_psp_addr             @ Appelle get_psp_addr(), retourne le PSP de la tâche suivante dans r0\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;LDMIA r0!, {r4-r11}                    @ Restaure R4 à R11 depuis la pile de la nouvelle tâche\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;MSR PSP, r0                            @ Met à jour le PSP avec la nouvelle valeur\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;POP {LR}                               @ Restaure LR sauvegardé avant les appels BL\n\t&amp;quot;\&lt;br /&gt;
        &amp;quot;BX LR                                  @ Retour d'exception : sortie de PendSV vers la tâche sélectionnée\n\t&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Function Prototypes ------------------ */&lt;br /&gt;
void init_os(void);&lt;br /&gt;
void launch_os(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tâche =====&lt;br /&gt;
Contrairement à la gestion des tâches sur la carte shielld, ici nous avons une gestion dynamique avec une listé chainé (vu au semestre 6 avec M. FORGET). &lt;br /&gt;
&lt;br /&gt;
task.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;task.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DUMMY_XPSR 0x01000000 // xPSR (32bits), 24eme bit indique thumb mode sinon crash&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Variables globales ------------------ */&lt;br /&gt;
TCB_t *task_list_head = NULL;   // tête de liste chaînée&lt;br /&gt;
TCB_t *current_task_ptr = NULL; // tâche courante&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions interne ------------------ */&lt;br /&gt;
// Initialise les piles de chaque taches&lt;br /&gt;
void _init_task_stack(uint32_t **psp_addr, void (*task_handler)(void)) {&lt;br /&gt;
  uint32_t *pPSP = *psp_addr + STACK_SIZE;&lt;br /&gt;
&lt;br /&gt;
  *(--pPSP) = DUMMY_XPSR;             // xPSR&lt;br /&gt;
  *(--pPSP) = (uint32_t)task_handler; // PC&lt;br /&gt;
  *(--pPSP) = 0xFFFFFFFD;             // LR, retour en Thread mode avec PSP&lt;br /&gt;
&lt;br /&gt;
  // Initialisation registres R0-R12&lt;br /&gt;
  for (int i = 0; i &amp;lt; 13; i++) {&lt;br /&gt;
    *(--pPSP) = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *psp_addr = pPSP; // Met à jour l'adresse PSP dans la TCB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sécurité dans le cas où il n'y aurait plus aucune tâche&lt;br /&gt;
void _idle_task(void) {&lt;br /&gt;
  while (1)&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _add_task_to_list(TCB_t *new_task) {&lt;br /&gt;
  // Sécurité : la liste ne doit jamais être vide, _idle_task doit exister&lt;br /&gt;
  if (task_list_head == NULL) {&lt;br /&gt;
    // Créer automatiquement _idle_task&lt;br /&gt;
    TCB_t *idle = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
    if (!idle)&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
    if (!idle-&amp;gt;psp_addr) {&lt;br /&gt;
      free(idle);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    idle-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    idle-&amp;gt;block_count = 0;&lt;br /&gt;
    idle-&amp;gt;task_handler = _idle_task;&lt;br /&gt;
    _init_task_stack(&amp;amp;idle-&amp;gt;psp_addr, _idle_task);&lt;br /&gt;
&lt;br /&gt;
    task_list_head = idle;&lt;br /&gt;
    idle-&amp;gt;next = idle;       // liste circulaire&lt;br /&gt;
    current_task_ptr = idle; // pointe sur idle&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ajout à la fin de la liste&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  while (tmp-&amp;gt;next != task_list_head) {&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  }&lt;br /&gt;
  tmp-&amp;gt;next = new_task;&lt;br /&gt;
  new_task-&amp;gt;next = task_list_head;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Fonctions externe ------------------ */&lt;br /&gt;
// update_next_task&lt;br /&gt;
void _scheduler(void) {&lt;br /&gt;
  if (!current_task_ptr) {&lt;br /&gt;
    current_task_ptr = task_list_head; // première tâche&lt;br /&gt;
  } else {&lt;br /&gt;
    TCB_t *start = current_task_ptr;&lt;br /&gt;
    do {&lt;br /&gt;
      current_task_ptr = current_task_ptr-&amp;gt;next;&lt;br /&gt;
      if (current_task_ptr-&amp;gt;current_state == TASK_READY_STATE)&lt;br /&gt;
        return;&lt;br /&gt;
    } while (current_task_ptr != start);&lt;br /&gt;
&lt;br /&gt;
    // Si aucune tâche prête, idle&lt;br /&gt;
    current_task_ptr = task_list_head; // ou idle task&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Débloque les taches endormi&lt;br /&gt;
void _unblock_tasks(void) {&lt;br /&gt;
  if (!task_list_head)&lt;br /&gt;
    return;&lt;br /&gt;
&lt;br /&gt;
  TCB_t *tmp = task_list_head;&lt;br /&gt;
  do {&lt;br /&gt;
    if (tmp-&amp;gt;current_state == TASK_BLOCKED_STATE &amp;amp;&amp;amp; tmp-&amp;gt;block_count &amp;lt;= g_tick_count) {&lt;br /&gt;
      tmp-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
    }&lt;br /&gt;
    tmp = tmp-&amp;gt;next;&lt;br /&gt;
  } while (tmp != task_list_head);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction qui endors une tache le temps précisé&lt;br /&gt;
void task_delay(uint32_t ms) {&lt;br /&gt;
  __disable_irq();&lt;br /&gt;
&lt;br /&gt;
  if (current_task_ptr &amp;amp;&amp;amp; current_task_ptr-&amp;gt;task_handler != _idle_task) { // On touche pas à idle c'est une securite&lt;br /&gt;
    current_task_ptr-&amp;gt;block_count = g_tick_count + ms;&lt;br /&gt;
    current_task_ptr-&amp;gt;current_state = TASK_BLOCKED_STATE;&lt;br /&gt;
&lt;br /&gt;
    // Déclenche PendSV pour basculement de tâche&lt;br /&gt;
    SCB-&amp;gt;ICSR |= SCB_ICSR_PENDSVSET_Msk;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  __enable_irq();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add_task(void (*task_handler)(void)) {&lt;br /&gt;
  // Allocation TCB&lt;br /&gt;
  TCB_t *new_task = (TCB_t *)malloc(sizeof(TCB_t));&lt;br /&gt;
  if (!new_task)&lt;br /&gt;
    return; // échec allocation&lt;br /&gt;
&lt;br /&gt;
  // Allocation pile&lt;br /&gt;
  new_task-&amp;gt;psp_addr = (uint32_t *)malloc(STACK_SIZE * sizeof(uint32_t));&lt;br /&gt;
  if (!new_task-&amp;gt;psp_addr) {&lt;br /&gt;
    free(new_task);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisation TCB&lt;br /&gt;
  new_task-&amp;gt;current_state = TASK_READY_STATE;&lt;br /&gt;
  new_task-&amp;gt;block_count = 0;&lt;br /&gt;
  new_task-&amp;gt;task_handler = task_handler;&lt;br /&gt;
&lt;br /&gt;
  // Initialisation pile PSP&lt;br /&gt;
  _init_task_stack(&amp;amp;new_task-&amp;gt;psp_addr, task_handler);&lt;br /&gt;
&lt;br /&gt;
  // Ajouter à la liste chaînée (à la fin)&lt;br /&gt;
  _add_task_to_list(new_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void _init_tasks(void) {&lt;br /&gt;
  // Initialise la liste de tâches avec _idle_task&lt;br /&gt;
  task_list_head = NULL;&lt;br /&gt;
  current_task_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
  // Crée automatiquement _idle_task&lt;br /&gt;
  add_task(_idle_task);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;task.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef TASK_H&lt;br /&gt;
#define TASK_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ Types ------------------ */&lt;br /&gt;
typedef enum {&lt;br /&gt;
    TASK_READY_STATE = 0,&lt;br /&gt;
    TASK_BLOCKED_STATE = 1,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct TCB{&lt;br /&gt;
  uint32_t *psp_addr;&lt;br /&gt;
  void (*task_handler)(void);&lt;br /&gt;
  uint32_t block_count;&lt;br /&gt;
  state_task current_state;&lt;br /&gt;
&lt;br /&gt;
  struct TCB *next; &lt;br /&gt;
} TCB_t;&lt;br /&gt;
&lt;br /&gt;
extern uint32_t g_tick_count;&lt;br /&gt;
&lt;br /&gt;
/* ------------------ TASK ------------------ */&lt;br /&gt;
#define IDLE_STACK_START 0x20001000&lt;br /&gt;
#define SCHED_STACK_START 0x20006000&lt;br /&gt;
#define STACK_SIZE         256 &lt;br /&gt;
&lt;br /&gt;
// Fonction pour dépendance ordonnanceur.c&lt;br /&gt;
void _scheduler(void);&lt;br /&gt;
void _unblock_tasks(void);&lt;br /&gt;
void _init_tasks(void);&lt;br /&gt;
&lt;br /&gt;
// Fonction à partager à l'utilisateur&lt;br /&gt;
void task_delay(uint32_t ms);&lt;br /&gt;
void add_task(void (*task_handler)(void));&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SPI =====&lt;br /&gt;
spi.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define AFR_NumberBitParPin 0x4 //  Cf RM0401 p155/763&lt;br /&gt;
#define AFR_OFFSET_HIGH 8       //  Cf RM0401 p155/763&lt;br /&gt;
&lt;br /&gt;
#define SPI_AFR 0b0101 // SPI sur AF5, Cf RM0401 p143/763&lt;br /&gt;
&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx) { offPin(GPIOx, PINx); }&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx) { onPin(GPIOx, PINx); }&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t spi_lock;&lt;br /&gt;
&lt;br /&gt;
void spiInit() {&lt;br /&gt;
  // SPI configuration instruction Cf RM0401 p682/763&lt;br /&gt;
&lt;br /&gt;
  // STEP 1: Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK&lt;br /&gt;
  // pins.&lt;br /&gt;
&lt;br /&gt;
  // PB15, MOSI&lt;br /&gt;
  setupPin(GPIOB, 15, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((15 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB14, MISO&lt;br /&gt;
  setupPin(GPIOB, 14, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((14 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // PB13, SCK&lt;br /&gt;
  setupPin(GPIOB, 13, ALTERNATE_FUNCTION);&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] &amp;amp;= ~(0b1111 &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin)); // Clear&lt;br /&gt;
  GPIOB-&amp;gt;AFR[1] |= SPI_AFR &amp;lt;&amp;lt; ((13 - AFR_OFFSET_HIGH) * AFR_NumberBitParPin);   // AFR[1] = AFRH&lt;br /&gt;
&lt;br /&gt;
  // STEP 2 : Write to the SPI_CR1 register:&lt;br /&gt;
&lt;br /&gt;
  // ACTIVER L'HORLOGE AVANT TOUT SINON NE MARCHE&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_SPI2EN;&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 = 0; // Reset tout&lt;br /&gt;
&lt;br /&gt;
  // Cf RM0401 page 711/763 le tableau des états&lt;br /&gt;
&lt;br /&gt;
  // LES SPECS DU SPI&lt;br /&gt;
  // https://learn.sparkfun.com/tutorials/using-the-serial-7-segment-display/all&lt;br /&gt;
&lt;br /&gt;
  // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 3).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 0b101 &amp;lt;&amp;lt; SPI_CR1_BR_Pos; // 101 : fPCLK/64&lt;br /&gt;
&lt;br /&gt;
  // fPCLK /32 fait 250kHz car fpclk = 8MHz et&lt;br /&gt;
  // on à 250kHz maximum clock, cf sparkfun spec&lt;br /&gt;
  // On prend alors en dessous car sinon des&lt;br /&gt;
  // erreurs viennent se glisser pendant l'envoie&lt;br /&gt;
&lt;br /&gt;
  //  b) Configure the CPOL and CPHA bits combination to define one of the four&lt;br /&gt;
  //  relationships between the data transfer and the serial clock. (Note: 2 -&lt;br /&gt;
  //  except the case when CRC is enabled at TI mode).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPOL_Pos); // 0 : 0 when idle&lt;br /&gt;
  // And, data is clocked in on the rising edge of the clock (when it goes from&lt;br /&gt;
  // 0V to 5V).&lt;br /&gt;
&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CPHA_Pos); // 0 :&lt;br /&gt;
  // first clock transition is first data capture edge&lt;br /&gt;
&lt;br /&gt;
  // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and&lt;br /&gt;
  // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_RXONLY_Pos); // 0 : full-duplex&lt;br /&gt;
&lt;br /&gt;
  // d) Configure the LSBFIRST bit to define the frame format (Note: 2).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_LSBFIRST_Pos); // 0 : MSB transmitted first&lt;br /&gt;
&lt;br /&gt;
  // e) Configure the CRCEN and CRCEN bits if CRC is needed (while SCK clock&lt;br /&gt;
  // signal is at idle state).&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_CRCEN_Pos); // 0: CRC calculation disabled&lt;br /&gt;
&lt;br /&gt;
  // f) Configure SSM and SSI (Note: 2).&lt;br /&gt;
  // When the SSM bit is set, the NSS pin input is replaced with the value from&lt;br /&gt;
  // the SSI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSM_Pos); // Software slave management&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= (1 &amp;lt;&amp;lt; SPI_CR1_SSI_Pos); //&lt;br /&gt;
&lt;br /&gt;
  // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict&lt;br /&gt;
  // state on NSS if master is configured to prevent MODF error).&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_MSTR_Pos; // 1 : Master configuration&lt;br /&gt;
&lt;br /&gt;
  // Data frame format&lt;br /&gt;
  SPI2-&amp;gt;CR1 &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_CR1_DFF_Pos); // 0: 8-bit data frame format is&lt;br /&gt;
                                        // selected for transmission/reception&lt;br /&gt;
  // Enable SPI&lt;br /&gt;
  SPI2-&amp;gt;CR1 |= 1 &amp;lt;&amp;lt; SPI_CR1_SPE_Pos; // 1 : Peripheral enabled&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx) {&lt;br /&gt;
  uint8_t dataRecu;&lt;br /&gt;
&lt;br /&gt;
  while (spi_lock)&lt;br /&gt;
    ;&lt;br /&gt;
  spi_lock = 1;&lt;br /&gt;
&lt;br /&gt;
  // Activer CS&lt;br /&gt;
  spi_cs_on(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  // Attendre que TXE soit prêt&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  // Envoyer la donnée&lt;br /&gt;
  SPI2-&amp;gt;DR = data;&lt;br /&gt;
&lt;br /&gt;
  // Attendre que le buffer RXNE soit plein pour lire et vider&lt;br /&gt;
  while (!(SPI2-&amp;gt;SR &amp;amp; SPI_SR_RXNE))&lt;br /&gt;
    ;&lt;br /&gt;
  dataRecu = SPI2-&amp;gt;DR;&lt;br /&gt;
&lt;br /&gt;
  // Désactiver CS&lt;br /&gt;
  spi_cs_off(CS_GPIOx, CS_PINx);&lt;br /&gt;
&lt;br /&gt;
  spi_lock = 0;&lt;br /&gt;
  return dataRecu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;spi.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#pragma once&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== USART =====&lt;br /&gt;
Bibliothéque permettant de gérer proprement l'UART et afficher et lire des caractères via le port série d'un PC.&lt;br /&gt;
&lt;br /&gt;
usart.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;usart.h&amp;quot;&lt;br /&gt;
#include &amp;quot;../GPIO/gpio.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define RX_BUFFER_SIZE 64&lt;br /&gt;
&lt;br /&gt;
volatile char rx_buffer[RX_BUFFER_SIZE];&lt;br /&gt;
volatile uint8_t rx_head = 0;&lt;br /&gt;
volatile uint8_t rx_tail = 0;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate) {&lt;br /&gt;
  setupPin(GPIOA, 9, ALTERNATE_FUNCTION_USART);  // PA9 =&amp;gt; TX&lt;br /&gt;
  setupPin(GPIOA, 10, ALTERNATE_FUNCTION_USART); // PA10 =&amp;gt; RX&lt;br /&gt;
&lt;br /&gt;
  // Evidemment on active l'horloge du bus&lt;br /&gt;
  RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_USART1EN;&lt;br /&gt;
&lt;br /&gt;
  // Procedure (Cf STM32F410 p.627/763):&lt;br /&gt;
  // 2. Program the M bit in USART_CR1 to define the word length.&lt;br /&gt;
  USART1-&amp;gt;CR1 = 0; // M=0, 1 Start bit, 8 Data bits, n Stop bit&lt;br /&gt;
&lt;br /&gt;
  // 3. Program the number of stop bits in USART_CR2.&lt;br /&gt;
  USART1-&amp;gt;CR2 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 4. Select DMA enable (DMAR) in USART_CR3 if multibuffer communication is to take&lt;br /&gt;
  // place. Configure the DMA register as explained in multibuffer communication. STEP 3&lt;br /&gt;
  USART1-&amp;gt;CR3 = 0;&lt;br /&gt;
&lt;br /&gt;
  // 5. Select the desired baud rate using the baud rate register USART_BRR&lt;br /&gt;
  uint32_t usartclk = 16000000;      // APB2 ~16MHz (Nucleo F4)&lt;br /&gt;
  USART1-&amp;gt;BRR = usartclk / baudrate; // BRR : baudrate = fclk / USARTDIV&lt;br /&gt;
&lt;br /&gt;
  // 6. Set the RE bit USART_CR1. This enables the receiver that begins searching for a start&lt;br /&gt;
  // bit.&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_PS; // Parity selection, 0 = Even parity&lt;br /&gt;
&lt;br /&gt;
  // 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.&lt;br /&gt;
  // Bon la datasheet dis etape 1 mais faut vraiment le faire à la fin l'activation sinon marche pas&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_TE | USART_CR1_RE; // TX &amp;amp; RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_RXNEIE; // activer interruption RX&lt;br /&gt;
&lt;br /&gt;
  USART1-&amp;gt;CR1 |= USART_CR1_UE; // USART&lt;br /&gt;
&lt;br /&gt;
  NVIC_EnableIRQ(USART1_IRQn);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send_char(char c) {&lt;br /&gt;
  while (!(USART1-&amp;gt;SR &amp;amp; USART_SR_TXE))&lt;br /&gt;
    ;&lt;br /&gt;
  USART1-&amp;gt;DR = (c &amp;amp; 0xFF);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_print(char *s) {&lt;br /&gt;
  while (*s) { // Tant que le caractère != '\0'&lt;br /&gt;
    usart_send_char(*s);&lt;br /&gt;
    s++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void USART1_IRQHandler() {&lt;br /&gt;
  if (USART1-&amp;gt;SR &amp;amp; USART_SR_RXNE) {&lt;br /&gt;
    char c = USART1-&amp;gt;DR &amp;amp; 0xFF;&lt;br /&gt;
    uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
&lt;br /&gt;
    if (next != rx_tail) { // buffer pas plein&lt;br /&gt;
      rx_buffer[rx_head] = c;&lt;br /&gt;
      rx_head = next;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available() {&lt;br /&gt;
  return (rx_head != rx_tail);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart_read() {&lt;br /&gt;
  if (rx_head == rx_tail)&lt;br /&gt;
    return 0; // rien dispo&lt;br /&gt;
&lt;br /&gt;
  char c = rx_buffer[rx_tail];&lt;br /&gt;
  rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;&lt;br /&gt;
  return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;usart.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef USART_H&lt;br /&gt;
#define USART_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../../../00-lib/stm32f410rx.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usart_init(uint32_t baudrate);&lt;br /&gt;
void usart_send_char(char c);&lt;br /&gt;
void usart_print(char *s);&lt;br /&gt;
void usart_print_c(char c);&lt;br /&gt;
&lt;br /&gt;
int usart_buffer_available(void);&lt;br /&gt;
char usart_read(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Substitue =====&lt;br /&gt;
printf.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef PRINTF_H&lt;br /&gt;
#define PRINTF_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../USART/usart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define PRINT_STRING(str)        usart_print((char*)str)&lt;br /&gt;
#define PRINT_CHAR(c)        usart_send_char(c)&lt;br /&gt;
#define GETCHAR()          usart_read()&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à cette fonction macro on pourra changer facilement la dépendance USART en combinaison carte clavier et/ou écran.&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope qui semble donner du bruit dans le cas de notre pcb dysfonctionnelle. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le scan se fait facilement une fois la logique assimilé...  &lt;br /&gt;
&lt;br /&gt;
Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite, nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bibliothèque à été pensé pour être facilement réadaptable et lisible pour un utilisateur souhaitant re-coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste coder/utiliser le clavier existant.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries et notre code de logique aux fichiers Keyboard.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
    // if (btn51) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = ; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (reprise de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.) (Bouton 51). &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
==== Communication carte mère ====&lt;br /&gt;
Partie a détailler plus tard.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8680</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8680"/>
		<updated>2025-12-30T20:31:27Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Lufa */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment dû mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARWARE ======&lt;br /&gt;
La première étape est de faire clignoter les leds qui serviront de futures tâches. On implémente donc les fonctions suivantes : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On réutilise ensuite la fonction setupPin qui permet d'initialiser nos leds.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On initialise ensuite l'horloge :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Puis l'hardware complet :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans le dossier USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU/16/BAUD_RATE)-1)&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
On pense ensuite aux tâches que l'on va implémenter dans notre ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
On implémente des tâches pour les leds :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS1();&lt;br /&gt;
        _delay_ms(1000);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {     &lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS2();&lt;br /&gt;
        _delay_ms(500);&lt;br /&gt;
    }  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() { &lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS3();&lt;br /&gt;
        _delay_ms(500);&lt;br /&gt;
    }  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS4();&lt;br /&gt;
        _delay_ms(250);&lt;br /&gt;
    }  &lt;br /&gt;
}  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et pour le série :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On définit ensuite la structure générale pour les tâches. &lt;br /&gt;
&lt;br /&gt;
Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum{&lt;br /&gt;
    AWAKE,&lt;br /&gt;
    SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    void (*taskAddress)(void);  // fonction de la tache&lt;br /&gt;
    uint16_t stackPointer;      // pointeur de pile&lt;br /&gt;
    state_task state;              // AWAKE ou SLEEP&lt;br /&gt;
    uint16_t sleepTime;         // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On définit donc un tableau de tâches et une constante pour savoir le nombre de tâches que l'on a. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
Pour débuter notre ordonnanceur, on commence par créér un minuteur afin de faire clignoter deux leds à des périodes différentes. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms){&lt;br /&gt;
    tick_ms = periode_ms;  &lt;br /&gt;
&lt;br /&gt;
    TCCR1A = 0;&lt;br /&gt;
    TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
    switch(diviseur){&lt;br /&gt;
        case 64:  TCCR1B |= (1&amp;lt;&amp;lt;CS11)|(1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
        case 256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
    TCNT1 = 0;&lt;br /&gt;
    TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine, qui prend le relai. &lt;br /&gt;
&lt;br /&gt;
L'ISR a besoin de la pile, on implémente donc une fonction afin d'initialiser la pile. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_pile(int n){&lt;br /&gt;
    uint16_t savedSP = SP;&lt;br /&gt;
    uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
    SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
    // PC (low puis high)&lt;br /&gt;
    asm volatile(&amp;quot;push %A0&amp;quot; :: &amp;quot;r&amp;quot;(addr));&lt;br /&gt;
    asm volatile(&amp;quot;push %B0&amp;quot; :: &amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
    // r0-r31&lt;br /&gt;
    for(int i=0; i&amp;lt;32; i++)&lt;br /&gt;
        asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // SREG = I=1&lt;br /&gt;
    uint8_t s = 0x80;&lt;br /&gt;
    asm volatile(&amp;quot;push %0&amp;quot; :: &amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
    task[n].stackPointer = SP;&lt;br /&gt;
    SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L'ISR procède ainsi :&lt;br /&gt;
&lt;br /&gt;
* Elle commence par sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* On appelle ensuite l'ordonnanceur qui définit la manière dont les tâches sont exécutées.&lt;br /&gt;
* On restore ensuite le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void scheduler(void){&lt;br /&gt;
    currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Fonction afin de mettre une tâche en sommeil :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void sleep_ms(uint16_t t) {&lt;br /&gt;
    task[currentTask].state = SLEEP;&lt;br /&gt;
    task[currentTask].sleepTime = t / tick_ms;&lt;br /&gt;
}  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, on appelle diverses fonctions notamment le timer, qui appellera donc l'ISR puis le scheduler. On commence également par charger la première tâche sur la pile.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm. Notre ordonnanceur était originellement composé uniquement des fichiers ordonnanceur.c et ordonnanceur.h mais on a implémenté pas mal de fonctions donc on place celles-ci dans un nouveau fichier processus.c accompagné de son processus.h. Ci-dessous la description du contenu de ces 4 fichiers.&lt;br /&gt;
* processus.h :&lt;br /&gt;
Il inclut les librairies nécéssaires et les prototypes des fonctions implémentées dans processus.c&lt;br /&gt;
* processus.c :&lt;br /&gt;
&lt;br /&gt;
# Fonction Delay : la fonction delay n'est pas présente sous arm donc on l'implémente ici.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(volatile uint32_t t){&lt;br /&gt;
    while (t--){&lt;br /&gt;
        __asm__(&amp;quot;nop&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Tâches pour la led : ci-dessous des fonctions permettant d'allumer, éteindre et faire clignoter la led LD2 de la nucleo. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_led2(void){&lt;br /&gt;
    //active l’horloge pour GPIOA car ds les stm32, les peripheriques sont eteints pr economiser de l energie et on doit appeler la clock pr réveiller les gpio&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
    //on veut écrire 01 (état haut) et pas 11 (analogique) pour les broches 10 et 11 donc on doit procéder en deux étapes : d'abord effacer puis mettre en sortie&lt;br /&gt;
    GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (5*2));&lt;br /&gt;
    GPIOA-&amp;gt;MODER |=  (0x1 &amp;lt;&amp;lt; (5*2));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_on(void){&lt;br /&gt;
    GPIOA-&amp;gt;ODR |= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_off(void){&lt;br /&gt;
    GPIOA-&amp;gt;ODR &amp;amp;= ~(1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        delay(1000000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink_task(void){&lt;br /&gt;
    static uint32_t counter = 0;&lt;br /&gt;
    counter++;&lt;br /&gt;
    if(counter &amp;gt;= 10){ // clignote toutes les 10 interruptions&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        counter = 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Tâches pour le série : envoyer un caractère, une chaîne de caractère, recevoir et allumer une led quand l'utilisateur écrit la lettre 'a' dans le minicom.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_usart2(void){&lt;br /&gt;
    RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_USART2EN;&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= RCC_AHB1ENR_GPIOAEN;&lt;br /&gt;
    //PA2 pour tx et PA3 pour rx&lt;br /&gt;
    GPIOA-&amp;gt;MODER &amp;amp;= ~((3 &amp;lt;&amp;lt; (2*2)) | (3 &amp;lt;&amp;lt; (3*2)));&lt;br /&gt;
    GPIOA-&amp;gt;MODER |=  (2 &amp;lt;&amp;lt; (2*2)) | (2 &amp;lt;&amp;lt; (3*2));&lt;br /&gt;
    GPIOA-&amp;gt;AFR[0] &amp;amp;= ~((0xF &amp;lt;&amp;lt; (4*2)) | (0xF &amp;lt;&amp;lt; (4*3))); //efface registre alternate function&lt;br /&gt;
    GPIOA-&amp;gt;AFR[0] |=  (7 &amp;lt;&amp;lt; (4*2)) | (7 &amp;lt;&amp;lt; (4*3)); //attribue AF7 dédié à usart2&lt;br /&gt;
    USART2-&amp;gt;BRR = 0x0683; //baudrate à 9600&lt;br /&gt;
    USART2-&amp;gt;CR1 = USART_CR1_TE | USART_CR1_RE; //transmit et receive enable pour tx et rx&lt;br /&gt;
    USART2-&amp;gt;CR1 |= USART_CR1_UE; //usart enable&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart2_send_char(char c){&lt;br /&gt;
    while (!(USART2-&amp;gt;SR &amp;amp; USART_SR_TXE)); //qd le bit transmit data register empty est à 0 (donc data register possède des données)&lt;br /&gt;
    USART2-&amp;gt;DR = c; //écrire le caractère&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart2_send_string(char *s){&lt;br /&gt;
    while (*s){&lt;br /&gt;
        usart2_send_char(*s++);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart2_receive(void){&lt;br /&gt;
    while (!(USART2-&amp;gt;SR &amp;amp; USART_SR_RXNE)); //qd caractère&lt;br /&gt;
    return (char)(USART2-&amp;gt;DR &amp;amp; 0xFF); //char sur 16bits on garde que bits de données&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink_serial(void){&lt;br /&gt;
    for (int i=0; i&amp;lt;10; i++){&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        delay(1000000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_serial(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        char c = usart2_receive();&lt;br /&gt;
        usart2_send_char(c); //echo vers Minicom&lt;br /&gt;
        if(c == 'a')&lt;br /&gt;
            led2_blink_serial();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Commande minicom à taper dans le terminal (-o permet d'éviter que minicom envoie des commandes d'initialisation) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Exemple de tâche (non ordonnancée pour le moment) où l'on affiche un message voulu, ici &amp;quot;heyyy&amp;quot; sur minicom[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
# Tâche avec le bouton : allumer la led LD2 quand on appuie sur le bouton user (en bleu, le noir étant dédié au reset).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_user_button(void){&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= RCC_AHB1ENR_GPIOCEN;&lt;br /&gt;
    GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (13 * 2)); //PC13 en entrée&lt;br /&gt;
    //pull up&lt;br /&gt;
    GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (13 * 2));&lt;br /&gt;
    GPIOC-&amp;gt;PUPDR |=  (0x1 &amp;lt;&amp;lt; (13 * 2)); //01 : pull up&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t user_button_pressed(void){&lt;br /&gt;
    return (GPIOC-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; 13)) == 0; //bouton actif à 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void task_button_led(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        if(user_button_pressed()){&lt;br /&gt;
            led2_on();&lt;br /&gt;
        } else {&lt;br /&gt;
            led2_off();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies, processus.h et la liste des fonctions implémentées dans ordonnanceur.c.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
Ce fichier gère le minuteur, les interruptions, la gestion des tâches.&lt;br /&gt;
&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern uint32_t SystemCoreClock; //freq horloge&lt;br /&gt;
&lt;br /&gt;
void init_minuteur(uint16_t prescaler, uint16_t periode_ms){&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM6EN; //active l'horloge de TIM6&lt;br /&gt;
  TIM6-&amp;gt;PSC = prescaler - 1; //prescaler&lt;br /&gt;
  uint32_t tick_freq = SystemCoreClock/prescaler; //période&lt;br /&gt;
  //TIM6-&amp;gt;ARR = (tick_freq * periode_ms)/1000-1; //Auto-Reload Register est la période avant que le timer fasse une interruption. (tick_freq * periode_ms)/1000 correspond au nb de ticks durant cette période.&lt;br /&gt;
  TIM6-&amp;gt;ARR = (uint32_t)(((uint64_t)tick_freq * periode_ms)/1000 - 1);&lt;br /&gt;
  TIM6-&amp;gt;CNT = 0; //reset compteur&lt;br /&gt;
  TIM6-&amp;gt;DIER |= TIM_DIER_UIE; //interruption activée quand compteur atteint nb de ticks de ARR&lt;br /&gt;
  TIM6-&amp;gt;CR1 |= TIM_CR1_CEN; //compteur&lt;br /&gt;
  NVIC_SetPriority(TIM6_DAC_IRQn, 0x1); //prio haute&lt;br /&gt;
  NVIC_EnableIRQ(TIM6_DAC_IRQn); //active l'interruption dans le NVIC (Nested Vector Interrupt Controller)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
On ajoute ensuite le shield sur la nucleo afin de pouvoir tester l'ordonnanceur avec plus de tâches puisque la nucleo seule possède très peu de leds.[[Fichier:Nucleo ordonnanceur.jpg|center|500px|alt=nucleo_ordonnanceur|vignette|nucleo_ordonnanceur]]&lt;br /&gt;
====== Système de fichier ======&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&lt;br /&gt;
&lt;br /&gt;
La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
===== Test led =====&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test écran =====&lt;br /&gt;
L'idée est d'afficher un compteur sur un afficheur 7 segments, notre &amp;quot;carte écran&amp;quot; : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_mere/Notre_PCB/02-Compteur.&lt;br /&gt;
&lt;br /&gt;
Notre test écran est structuré de la manière suivante :&lt;br /&gt;
* un main.c&lt;br /&gt;
* un dossier pour la carte écran&lt;br /&gt;
* un dossier hardware_setup&lt;br /&gt;
* un dossier pour le spi&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== hardware_setup ======&lt;br /&gt;
&lt;br /&gt;
On reprend notre fonction setupPin que l'on adapte à arm afin d'initialiser les pins de notre carte :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On implémente également des fonctions qui permettent d'activer, de désactiver ou de faire clignoter (une led en l'occurence) des pins.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (GPIOx-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On possède également une fonction qui initialise notre carte mère, en activant les différents ports des cartes filles, le spi, les leds et les boutons.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, OUTPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT);  // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT);  // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // Test LEDs allumé&lt;br /&gt;
  offPin(GPIOC, 0); // PC0, CS1&lt;br /&gt;
  offPin(GPIOB, 1); // PA7, CS2&lt;br /&gt;
  offPin(GPIOC, 9); // PC9, CS3&lt;br /&gt;
  offPin(GPIOA, 2); // PA2, CS4&lt;br /&gt;
  offPin(GPIOB, 0); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  onPin(GPIOC, 12); // PB0, CS_FPGA&lt;br /&gt;
  // Setup SPI =&amp;gt; MOSI, MISO et SCK&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  ecran_init();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== spi ======&lt;br /&gt;
On initialise également les fonctions pour le SPI, à retrouver ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_mere/Notre_PCB/02-Compteur/lib/SPI.&lt;br /&gt;
&lt;br /&gt;
On implémente les fonctions suivantes, commentées en détail dans le spi.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== écran ======&lt;br /&gt;
On écrit plusieurs fonctions pour notre carte écran, qui est pour l'instant testée par le biais de l'afficheur 7 segments d'où certainess lignes commentées. &lt;br /&gt;
&lt;br /&gt;
Ces fonctions servent à :&lt;br /&gt;
&lt;br /&gt;
* initialiser l'écran&lt;br /&gt;
* écrire dessus en activant le spi&lt;br /&gt;
* régler l'intensité de l'écran&lt;br /&gt;
* nettoyer l'écran&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
void ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // S'active à l'etat bas&lt;br /&gt;
  // offPin(RST5_GPIO, RST5_PIN); // S'active à l'etat bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On écrit ensuite le code du compteur :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== main ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
  setupCarte();&lt;br /&gt;
  ecran_brightness(100);&lt;br /&gt;
  int j = 0;&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    ecran_compteur();&lt;br /&gt;
    j++;&lt;br /&gt;
&lt;br /&gt;
    if (j &amp;gt; 10) {&lt;br /&gt;
      j = 0;&lt;br /&gt;
      togglePin(GPIOB, 8); // LED1&lt;br /&gt;
      togglePin(GPIOA, 6); // LED2&lt;br /&gt;
      togglePin(GPIOB, 7); // LED3&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 25000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
Nous avons soudé le stricte minimum sur notre carte pour le faire fonctioner avant tout puisque que nous souhaitons trouvé les éventuelles anomalies de soudure ou de conception avant chaque grosse étape. Sur la PCB rouge et la 1ère pcb verte tout est ok. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]La seconde PCB verte en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope. On se rend compte que l'on obtient que du bruit par rapport au pcb rouge. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Bouton&lt;br /&gt;
  //setupPin(BTNs_PORT, BTNs_DDR, BTN_Right, INPUT_PULL_UP);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Détection de notre matrice de boutons ====&lt;br /&gt;
&lt;br /&gt;
On créer un fichier qui pourra être facilement importer dans nos différents projets afin d'avoir une détection de touche facilement importer dans la suite : la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici alors clavier.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;../lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// --------- Colonnes ---------&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_pins_reg[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PINF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PINC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PINB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PIND, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&lt;br /&gt;
// --------- Lignes ---------&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void) {&lt;br /&gt;
  // Configuration colonnes en entrée avec pull-up&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne en sortie&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], OUTPUT);&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // mettre toutes les lignes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void scan() {&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    offPin(row_ports[r], row_pins[r]); // activer ligne (LOW)&lt;br /&gt;
&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++)&lt;br /&gt;
      key_state[c][r] = !(*col_pins_reg[c] &amp;amp; (1 &amp;lt;&amp;lt; col_pins[c]));&lt;br /&gt;
&lt;br /&gt;
    onPin(row_ports[r], row_pins[r]); // désactiver ligne (HIGH)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et clavier.h : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_H&lt;br /&gt;
#define CLAVIER_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;keyswitch.h&amp;quot;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;clavier_conversion.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
void init_matrix_button(void);&lt;br /&gt;
void scan(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Et ensuite nous avons pleins de define afin de lire chaque bouton individuellement au lieu d'appeler un tableau (pas intuitif pour l'utilisateur) dans un fichier keyswitch.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef KEYSWITCH_H&lt;br /&gt;
#define KEYSWITCH_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// COL0 PF0 | COL1 PF1 | COL2 PF4 | COL3  PF5 | COL4  PF6 | COL5  PF7 | COL6  PC7 | &lt;br /&gt;
// COL7 PC6 | COL8 PB6 | COL9 PB5 | COL10 PB4 | COL11 PD7 | COL12 PD6 | COL13 PD4&lt;br /&gt;
typedef enum{&lt;br /&gt;
    COL0 = 0,&lt;br /&gt;
    COL1 = 1,&lt;br /&gt;
    COL2 = 2,&lt;br /&gt;
    COL3 = 3,&lt;br /&gt;
    COL4 = 4,&lt;br /&gt;
    COL5 = 5,&lt;br /&gt;
    COL6 = 6,&lt;br /&gt;
    COL7 = 7,&lt;br /&gt;
    COL8 = 8,&lt;br /&gt;
    COL9 = 9,&lt;br /&gt;
    COL10 = 10,&lt;br /&gt;
    COL11 = 11,&lt;br /&gt;
    COL12 = 12,&lt;br /&gt;
    COL13 = 13,&lt;br /&gt;
} COLs;&lt;br /&gt;
&lt;br /&gt;
// ROW0 PD5 | ROW1 PD3 | ROW2 PD2 | ROW3 PD1 | ROW4 PD0&lt;br /&gt;
typedef enum{&lt;br /&gt;
    ROW0 = 0,&lt;br /&gt;
    ROW1 = 1,&lt;br /&gt;
    ROW2 = 2,&lt;br /&gt;
    ROW3 = 3,&lt;br /&gt;
    ROW4 = 4,&lt;br /&gt;
} ROWs;&lt;br /&gt;
&lt;br /&gt;
extern uint8_t key_state[TOTAL_COL][TOTAL_ROW];&lt;br /&gt;
&lt;br /&gt;
#define btn1  key_state[COL0][ROW0]&lt;br /&gt;
#define btn2  key_state[COL1][ROW0]&lt;br /&gt;
#define btn3  key_state[COL2][ROW0]&lt;br /&gt;
#define btn4  key_state[COL3][ROW0]&lt;br /&gt;
#define btn5  key_state[COL4][ROW0]&lt;br /&gt;
#define btn6  key_state[COL5][ROW0]&lt;br /&gt;
#define btn7  key_state[COL6][ROW0]&lt;br /&gt;
#define btn8  key_state[COL7][ROW0]&lt;br /&gt;
#define btn9  key_state[COL8][ROW0]&lt;br /&gt;
#define btn10 key_state[COL9][ROW0]&lt;br /&gt;
#define btn11 key_state[COL10][ROW0]&lt;br /&gt;
#define btn12 key_state[COL11][ROW0]&lt;br /&gt;
#define btn13 key_state[COL12][ROW0]&lt;br /&gt;
#define btn14 key_state[COL13][ROW0]&lt;br /&gt;
&lt;br /&gt;
#define btn15 key_state[COL0][ROW1]&lt;br /&gt;
#define btn16 key_state[COL1][ROW1]&lt;br /&gt;
#define btn17 key_state[COL2][ROW1]&lt;br /&gt;
#define btn18 key_state[COL3][ROW1]&lt;br /&gt;
#define btn19 key_state[COL4][ROW1]&lt;br /&gt;
#define btn20 key_state[COL5][ROW1]&lt;br /&gt;
#define btn21 key_state[COL6][ROW1]&lt;br /&gt;
#define btn22 key_state[COL7][ROW1]&lt;br /&gt;
#define btn23 key_state[COL8][ROW1]&lt;br /&gt;
#define btn24 key_state[COL9][ROW1]&lt;br /&gt;
#define btn25 key_state[COL10][ROW1]&lt;br /&gt;
#define btn26 key_state[COL11][ROW1]&lt;br /&gt;
#define btn27 key_state[COL12][ROW1]&lt;br /&gt;
#define btn28 key_state[COL13][ROW1]&lt;br /&gt;
&lt;br /&gt;
#define btn29 key_state[COL0][ROW2]&lt;br /&gt;
#define btn30 key_state[COL1][ROW2]&lt;br /&gt;
#define btn31 key_state[COL2][ROW2]&lt;br /&gt;
#define btn32 key_state[COL3][ROW2]&lt;br /&gt;
#define btn33 key_state[COL4][ROW2]&lt;br /&gt;
#define btn34 key_state[COL5][ROW2]&lt;br /&gt;
#define btn35 key_state[COL6][ROW2]&lt;br /&gt;
#define btn36 key_state[COL7][ROW2]&lt;br /&gt;
#define btn37 key_state[COL8][ROW2]&lt;br /&gt;
#define btn38 key_state[COL9][ROW2]&lt;br /&gt;
#define btn39 key_state[COL10][ROW2]&lt;br /&gt;
#define btn40 key_state[COL11][ROW2]&lt;br /&gt;
#define btn41 key_state[COL12][ROW2]&lt;br /&gt;
&lt;br /&gt;
#define btn42 key_state[COL0][ROW3]&lt;br /&gt;
#define btn43 key_state[COL1][ROW3]&lt;br /&gt;
#define btn44 key_state[COL2][ROW3]&lt;br /&gt;
#define btn45 key_state[COL3][ROW3]&lt;br /&gt;
#define btn46 key_state[COL4][ROW3]&lt;br /&gt;
#define btn47 key_state[COL5][ROW3]&lt;br /&gt;
#define btn48 key_state[COL6][ROW3]&lt;br /&gt;
#define btn49 key_state[COL7][ROW3]&lt;br /&gt;
#define btn50 key_state[COL8][ROW3]&lt;br /&gt;
#define btn51 key_state[COL9][ROW3]&lt;br /&gt;
#define btn52 key_state[COL10][ROW3]&lt;br /&gt;
#define btn53 key_state[COL11][ROW3]&lt;br /&gt;
#define btn54 key_state[COL13][ROW3]&lt;br /&gt;
&lt;br /&gt;
#define btn55 key_state[COL0][ROW4]&lt;br /&gt;
#define btn56 key_state[COL1][ROW4]&lt;br /&gt;
#define btn57 key_state[COL2][ROW4]&lt;br /&gt;
#define btn58 key_state[COL6][ROW4]&lt;br /&gt;
#define btn59 key_state[COL10][ROW4]&lt;br /&gt;
#define btn60 key_state[COL11][ROW4]&lt;br /&gt;
#define btn61 key_state[COL12][ROW4]&lt;br /&gt;
#define btn62 key_state[COL13][ROW4]&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La bivliothèque à été penser pour être facilement réadaptable et lisible pour un utilisateur souhaitant re coder un clavier mais également pour quelqu'un ne souhaitant pas refaire l'hardware et juste plug and play celui déjà fait.&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA. Rien de bien compliqué, on retrouve le projet LUFA sur ce lien github : https://github.com/abcminiuser/lufa&lt;br /&gt;
&lt;br /&gt;
On télécharge le projet et on vient extraire la librairie LUFA et l'exemple  Keyboard présent dans le dossier Démo. On modifie le Makefile présent ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
MES_LIBS = lib/HARDWARE/hardware.c lib/CLAVIER/clavier.c&lt;br /&gt;
&lt;br /&gt;
MCU          = atmega32u4&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = Keyboard&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c $(MES_LIBS) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)&lt;br /&gt;
LUFA_PATH    = ../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
&lt;br /&gt;
# Default target&lt;br /&gt;
all:&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = avr109&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(MCU) -c $(AVRDUDE_PROGRAMMER) \&lt;br /&gt;
		-P $(AVRDUDE_PORT) -b $(AVRDUDE_BAUD) -D \&lt;br /&gt;
		-U flash:w:$(TARGET).hex:i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;La ligne upload est necessaire seulement si vous avez un bootloader personnalisé, auquel cas faite un simple make dfu pour téléverser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite on vient ajouter nos libraries aux fichiers Keyboard.c : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;./lib/CLAVIER/clavier.h&amp;quot;&lt;br /&gt;
#include &amp;quot;./lib/HARDWARE/hardware.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
void SetupHardware() {&lt;br /&gt;
  /* Disable watchdog if enabled by bootloader/fuses */&lt;br /&gt;
  MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
&lt;br /&gt;
  /* Disable clock division */&lt;br /&gt;
  clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
  /* Hardware Initialization */&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  init_matrix_button();&lt;br /&gt;
  &lt;br /&gt;
  // Initialisation USB obligatoire&lt;br /&gt;
  USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... je passe les détails basique du fichier&lt;br /&gt;
&lt;br /&gt;
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                         uint8_t *const ReportID,&lt;br /&gt;
                                         const uint8_t ReportType,&lt;br /&gt;
                                         void *ReportData,&lt;br /&gt;
                                         uint16_t *const ReportSize) {&lt;br /&gt;
&lt;br /&gt;
  USB_KeyboardReport_Data_t *KeyboardReport = (USB_KeyboardReport_Data_t *)ReportData;&lt;br /&gt;
	uint8_t UsedKeyCodes = 0;&lt;br /&gt;
&lt;br /&gt;
  scan();&lt;br /&gt;
  &lt;br /&gt;
     // ROW0&lt;br /&gt;
    if (btn1)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ESCAPE);&lt;br /&gt;
    if (btn2 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_1_AND_EXCLAMATION);&lt;br /&gt;
    if (btn3 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_2_AND_AT);&lt;br /&gt;
    if (btn4 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_3_AND_HASHMARK);&lt;br /&gt;
    if (btn5 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_4_AND_DOLLAR);&lt;br /&gt;
    if (btn6 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_5_AND_PERCENTAGE);&lt;br /&gt;
    if (btn7 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_6_AND_CARET);&lt;br /&gt;
    if (btn8 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_7_AND_AMPERSAND);&lt;br /&gt;
    if (btn9 &amp;amp;&amp;amp; !btn62)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_8_AND_ASTERISK);&lt;br /&gt;
    if (btn10 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_9_AND_OPENING_PARENTHESIS);&lt;br /&gt;
    if (btn11 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS);&lt;br /&gt;
    if (btn12 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_MINUS_AND_UNDERSCORE); // ) ° ]&lt;br /&gt;
    if (btn13 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_EQUAL_AND_PLUS); // = + }&lt;br /&gt;
    if (btn14) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_BACKSPACE);&lt;br /&gt;
&lt;br /&gt;
     // ROW1&lt;br /&gt;
    if (btn15) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_TAB);&lt;br /&gt;
    if (btn16) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_A);&lt;br /&gt;
    if (btn17) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Z);&lt;br /&gt;
    if (btn18) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_E);&lt;br /&gt;
    if (btn19) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_R);&lt;br /&gt;
    if (btn20) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_T);&lt;br /&gt;
    if (btn21) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Y);&lt;br /&gt;
    if (btn22) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_U);&lt;br /&gt;
    if (btn23) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_I);&lt;br /&gt;
    if (btn24) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_O);&lt;br /&gt;
    if (btn25) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_P);&lt;br /&gt;
    if (btn26) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_OPENING_BRACKET_AND_OPENING_BRACE); // ^¨&lt;br /&gt;
    if (btn27) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CLOSING_BRACKET_AND_CLOSING_BRACE); // $£¤&lt;br /&gt;
    if (btn28) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_ENTER);&lt;br /&gt;
&lt;br /&gt;
     // ROW2&lt;br /&gt;
    if (btn29) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_CAPS_LOCK);&lt;br /&gt;
    if (btn30) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_Q);&lt;br /&gt;
    if (btn31) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_S);&lt;br /&gt;
    if (btn32) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_D);&lt;br /&gt;
    if (btn33) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F); &lt;br /&gt;
    if (btn34) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_G);&lt;br /&gt;
    if (btn35) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_H);&lt;br /&gt;
    if (btn36) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_J);&lt;br /&gt;
    if (btn37) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_K);&lt;br /&gt;
    if (btn38) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_L);&lt;br /&gt;
    if (btn39) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_M);&lt;br /&gt;
    if (btn40) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_APOSTROPHE_AND_QUOTE); // % ù&lt;br /&gt;
    if (btn41) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_HASHMARK_AND_TILDE); // µ * &lt;br /&gt;
&lt;br /&gt;
     // ROW3&lt;br /&gt;
    if (btn42) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_SHIFT);&lt;br /&gt;
    if (btn43) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE);&lt;br /&gt;
    if (btn44) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_W);&lt;br /&gt;
    if (btn45) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_X);&lt;br /&gt;
    if (btn46) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_C);&lt;br /&gt;
    if (btn47) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_V);&lt;br /&gt;
    if (btn48) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_B);&lt;br /&gt;
    if (btn49) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_N);&lt;br /&gt;
    if (btn50) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN); // ,?&lt;br /&gt;
    // if (btn51) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = ; // ;.&lt;br /&gt;
    if (btn52) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN); // :/&lt;br /&gt;
    if (btn53 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SLASH_AND_QUESTION_MARK);  // !§&lt;br /&gt;
    if (btn54) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_SHIFT);&lt;br /&gt;
&lt;br /&gt;
  // ROW4&lt;br /&gt;
  if (btn55) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTCTRL);&lt;br /&gt;
  if (btn56) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_LEFTGUI);&lt;br /&gt;
  if (btn57) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ALT);&lt;br /&gt;
  if (btn58) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_SPACE);&lt;br /&gt;
  if (btn59 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTALT);&lt;br /&gt;
  // if (btn60 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Keyboard |= QWERTY_to_AZERTY(); // Trouver une fonction a celui ci&lt;br /&gt;
  if (btn61 &amp;amp;&amp;amp; !btn62) KeyboardReport-&amp;gt;Modifier |= QWERTY_to_AZERTY(HID_KEYBOARD_MODIFIER_RIGHTCTRL);&lt;br /&gt;
&lt;br /&gt;
  // Fonction spéciale du clavier côté HARDWARE&lt;br /&gt;
  if(btn62){&lt;br /&gt;
    // Fonction F1 à F12&lt;br /&gt;
    if (btn2)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F1);&lt;br /&gt;
    if (btn3)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F2);&lt;br /&gt;
    if (btn4)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F3);&lt;br /&gt;
    if (btn5)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F4);&lt;br /&gt;
    if (btn6)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F5);&lt;br /&gt;
    if (btn7)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F6);&lt;br /&gt;
    if (btn8)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F7);&lt;br /&gt;
    if (btn9)  KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F8);&lt;br /&gt;
    if (btn10) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F9);&lt;br /&gt;
    if (btn11) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F10);&lt;br /&gt;
    if (btn12) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F11);&lt;br /&gt;
    if (btn13) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_F12);&lt;br /&gt;
&lt;br /&gt;
    // Déplacement&lt;br /&gt;
    if (btn60) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_DOWN_ARROW);&lt;br /&gt;
    if (btn59) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_LEFT_ARROW);&lt;br /&gt;
    if (btn61) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_RIGHT_ARROW);&lt;br /&gt;
    if (btn53) KeyboardReport-&amp;gt;KeyCode[UsedKeyCodes++] = QWERTY_to_AZERTY(HID_KEYBOARD_SC_UP_ARROW);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo,&lt;br /&gt;
                                          const uint8_t ReportID,&lt;br /&gt;
                                          const uint8_t ReportType,&lt;br /&gt;
                                          const void *ReportData,&lt;br /&gt;
                                          const uint16_t ReportSize) {&lt;br /&gt;
  &lt;br /&gt;
  uint8_t *LEDReport = (uint8_t *)ReportData;&lt;br /&gt;
&lt;br /&gt;
  if (*LEDReport &amp;amp; HID_KEYBOARD_LED_CAPSLOCK)&lt;br /&gt;
    onPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
  else&lt;br /&gt;
    offPin(LEDs_PORT,LED_CapsLock);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
J'ai fais une petite macro (repris de l'année dernière, projet manette) pour convertir les caractères sur un clavier AZERTY dans le fichier clavier_conversion.h :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef CLAVIER_CONVERSION_H&lt;br /&gt;
#define CLAVIER_CONVERSION_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;../../LUFA/Drivers/USB/USB.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t QWERTY_to_AZERTY(uint8_t qwerty_code) {&lt;br /&gt;
    switch (qwerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_SEMICOLON_AND_COLON; &lt;br /&gt;
        case HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN: return HID_KEYBOARD_SC_M;         &lt;br /&gt;
        default: return qwerty_code; &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t AZERTY_to_QWERTY(uint8_t azerty_code) {&lt;br /&gt;
    switch (azerty_code) {&lt;br /&gt;
        // Lettres&lt;br /&gt;
        case HID_KEYBOARD_SC_A: return HID_KEYBOARD_SC_Q;&lt;br /&gt;
        case HID_KEYBOARD_SC_Z: return HID_KEYBOARD_SC_W;&lt;br /&gt;
        case HID_KEYBOARD_SC_Q: return HID_KEYBOARD_SC_A;&lt;br /&gt;
        case HID_KEYBOARD_SC_W: return HID_KEYBOARD_SC_Z;&lt;br /&gt;
        case HID_KEYBOARD_SC_SEMICOLON_AND_COLON: return HID_KEYBOARD_SC_M; &lt;br /&gt;
        case HID_KEYBOARD_SC_M: return HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN;&lt;br /&gt;
        default: return azerty_code;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;J'ai eu beaucoup de mal a trouver certains caractères mais le document présent ci dessous m'as grandement aidé :&lt;br /&gt;
[[Fichier:HID Usage Tables.pdf|centré|vignette|HID Usage Tables]]&lt;br /&gt;
A partir de la page 89 nous avons l'ensemble des codes et certains détails pour certains d'entre eux. Malgré tout une touche n'as pas réussi à être mapper (je n'ai pas trouvé le code équivalent pour mon clavier AZERTY), cette touche correspond au point virgule / point (;.). &lt;br /&gt;
&lt;br /&gt;
Cependant le code reste fonctionnelle, j'y ai ajouté la possiblité de se deplacer via la touche FN qui correspond aux fonctionnalité spéciale, comme toutes les touches F1,F2,...F10,F11,F12 qui sont mappé aux touches 1,2,...,0,°,+.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:HID_Usage_Tables.pdf&amp;diff=8679</id>
		<title>Fichier:HID Usage Tables.pdf</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:HID_Usage_Tables.pdf&amp;diff=8679"/>
		<updated>2025-12-30T20:19:46Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;HID Usage Tables&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8678</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8678"/>
		<updated>2025-12-30T20:05:17Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Carte mère */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
[[Fichier:Correction pcb.jpg|right|alt=correction_pcb|vignette|correction_pcb]]&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment dû mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
On ne programme pas la carte SD ici, on le fait directement sur la nucleo.&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_shield/ordonnanceur&lt;br /&gt;
&lt;br /&gt;
Notre ordonnanceur est structuré de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* un main.c&lt;br /&gt;
* lib qui contient les fichiers nécéssaires pour le main.c&lt;br /&gt;
*# Un dossier Hardware &lt;br /&gt;
*# Un dossier ordonnanceur&lt;br /&gt;
*# Un dossier Task&lt;br /&gt;
*# Un dossier USART&lt;br /&gt;
* build : un dossier qui stocke les exécutables à part&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== HARWARE ======&lt;br /&gt;
La première étape est de faire clignoter les leds qui serviront de futures tâches. On implémente donc les fonctions suivantes : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
&lt;br /&gt;
#define LED_CS1 3&lt;br /&gt;
#define LED_CS2 2&lt;br /&gt;
#define LED_CS3 1&lt;br /&gt;
#define LED_CS4 0&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS1(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS2(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS3(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS3);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void toggleLedCS4(){&lt;br /&gt;
  *LEDs_PORT ^= (1 &amp;lt;&amp;lt; LED_CS4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On réutilise ensuite la fonction setupPin qui permet d'initialiser nos leds.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  INPUT_PULL_UP,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
&lt;br /&gt;
void setupPin(volatile uint8_t *PORTx, volatile uint8_t *DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
  switch (mode) {&lt;br /&gt;
  case INPUT: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
    *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  case OUTPUT: // Forcage pin à 1&lt;br /&gt;
    *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On initialise ensuite l'horloge :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupClock() {&lt;br /&gt;
    // Activer possibilité de changer le prescaler&lt;br /&gt;
    CLKPR = (1 &amp;lt;&amp;lt; CLKPCE);&lt;br /&gt;
    // Choix diviseur&lt;br /&gt;
    CLKPR = 0;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Puis l'hardware complet :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware(){&lt;br /&gt;
  setupClock();&lt;br /&gt;
&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS1, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS2, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS3, OUTPUT);&lt;br /&gt;
  setupPin(LEDs_PORT, LEDs_DDR, LED_CS4, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  init_usart();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== USART ======&lt;br /&gt;
Dans le dossier USART, on définit les fonctions basiques pour initialiser, envoyer et recevoir depuis l'usart. L'usart nous servira par la suite pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BAUD_RATE 9600&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
#define UBRR_VALUE ((F_CPU/16/BAUD_RATE)-1)&lt;br /&gt;
&lt;br /&gt;
void init_usart() {&lt;br /&gt;
  // Serial Initialization&lt;br /&gt;
  /*Set baud rate 9600 */&lt;br /&gt;
  UBRR0H = (unsigned char)(UBRR_VALUE &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRR0L = (unsigned char)UBRR_VALUE;&lt;br /&gt;
&lt;br /&gt;
  /* Enable receiver and transmitter */&lt;br /&gt;
  UCSR0B = (1 &amp;lt;&amp;lt; RXEN0) | (1 &amp;lt;&amp;lt; TXEN0);&lt;br /&gt;
&lt;br /&gt;
  /* Frame format: 8data, No parity, 1stop bit */&lt;br /&gt;
  UCSR0C = (1 &amp;lt;&amp;lt; UCSZ01) | (1 &amp;lt;&amp;lt; UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart_send(unsigned char data) {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; UDRE0)));&lt;br /&gt;
  UDR0 = data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char usart_receive() {&lt;br /&gt;
  while (!(UCSR0A &amp;amp; (1 &amp;lt;&amp;lt; RXC0)));&lt;br /&gt;
  return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== TASK ======&lt;br /&gt;
On pense ensuite aux tâches que l'on va implémenter dans notre ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
On implémente des tâches pour les leds :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void taskCS1() {&lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS1();&lt;br /&gt;
        _delay_ms(1000);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS2() {     &lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS2();&lt;br /&gt;
        _delay_ms(500);&lt;br /&gt;
    }  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS3() { &lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS3();&lt;br /&gt;
        _delay_ms(500);&lt;br /&gt;
    }  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskCS4() {&lt;br /&gt;
    while(1){   &lt;br /&gt;
        toggleLedCS4();&lt;br /&gt;
        _delay_ms(250);&lt;br /&gt;
    }  &lt;br /&gt;
}  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;et pour le série :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialA() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('A');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void taskSendSerialB() {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    usart_send('B');&lt;br /&gt;
    usart_send('\n');&lt;br /&gt;
    usart_send('\r');&lt;br /&gt;
    _delay_ms(250);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On définit ensuite la structure générale pour les tâches. &lt;br /&gt;
&lt;br /&gt;
Une tâche possède :&lt;br /&gt;
&lt;br /&gt;
* un pointeur de fonction avec le nom de la tâche&lt;br /&gt;
* un pointeur pour l'adresse mémoire afin de savoir où est située la tâche dans la pile&lt;br /&gt;
* l'état de la tâche : actif ou non (endormi)&lt;br /&gt;
* le temps pour lequel la tâche reste endormie&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum{&lt;br /&gt;
    AWAKE,&lt;br /&gt;
    SLEEP,&lt;br /&gt;
} state_task;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
    void (*taskAddress)(void);  // fonction de la tache&lt;br /&gt;
    uint16_t stackPointer;      // pointeur de pile&lt;br /&gt;
    state_task state;              // AWAKE ou SLEEP&lt;br /&gt;
    uint16_t sleepTime;         // temps restant en ms&lt;br /&gt;
} process;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On définit donc un tableau de tâches et une constante pour savoir le nombre de tâches que l'on a. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
process task[] = {&lt;br /&gt;
    // {taskSendSerialA, 0x0780, AWAKE, 0},&lt;br /&gt;
    {taskCS1, 0x0730, AWAKE, 0},&lt;br /&gt;
    // {taskSendSerialB, 0x06E0 , AWAKE, 0},&lt;br /&gt;
    // {taskCS2, 0x0690, AWAKE, 0},&lt;br /&gt;
    // {taskCS3, 0x06E0, AWAKE, 0},&lt;br /&gt;
    // {taskCS4, 0x0620, AWAKE, 0},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const uint8_t nbTasks = sizeof(task)/sizeof(task[0]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== ORDONNANCEUR ======&lt;br /&gt;
Pour débuter notre ordonnanceur, on commence par créér un minuteur afin de faire clignoter deux leds à des périodes différentes. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_timer1(int diviseur, long periode_ms){&lt;br /&gt;
    tick_ms = periode_ms;  &lt;br /&gt;
&lt;br /&gt;
    TCCR1A = 0;&lt;br /&gt;
    TCCR1B = (1 &amp;lt;&amp;lt; WGM12); // CTC mode&lt;br /&gt;
&lt;br /&gt;
    switch(diviseur){&lt;br /&gt;
        case 64:  TCCR1B |= (1&amp;lt;&amp;lt;CS11)|(1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
        case 256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    OCR1A = (F_CPU / diviseur) * periode_ms / 1000;&lt;br /&gt;
    TCNT1 = 0;&lt;br /&gt;
    TIMSK1 = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Une fois que le minuteur a atteint son nombre de &amp;quot;ticks&amp;quot;, il appelle l'ISR : Interrupt Service Routine, qui prend le relai. &lt;br /&gt;
&lt;br /&gt;
L'ISR a besoin de la pile, on implémente donc une fonction afin d'initialiser la pile. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_pile(int n){&lt;br /&gt;
    uint16_t savedSP = SP;&lt;br /&gt;
    uint16_t addr = (uint16_t)task[n].taskAddress;&lt;br /&gt;
&lt;br /&gt;
    SP = task[n].stackPointer;&lt;br /&gt;
&lt;br /&gt;
    // PC (low puis high)&lt;br /&gt;
    asm volatile(&amp;quot;push %A0&amp;quot; :: &amp;quot;r&amp;quot;(addr));&lt;br /&gt;
    asm volatile(&amp;quot;push %B0&amp;quot; :: &amp;quot;r&amp;quot;(addr));&lt;br /&gt;
&lt;br /&gt;
    // r0-r31&lt;br /&gt;
    for(int i=0; i&amp;lt;32; i++)&lt;br /&gt;
        asm volatile(&amp;quot;push __zero_reg__&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // SREG = I=1&lt;br /&gt;
    uint8_t s = 0x80;&lt;br /&gt;
    asm volatile(&amp;quot;push %0&amp;quot; :: &amp;quot;r&amp;quot;(s));&lt;br /&gt;
&lt;br /&gt;
    task[n].stackPointer = SP;&lt;br /&gt;
    SP = savedSP;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L'ISR procède ainsi :&lt;br /&gt;
&lt;br /&gt;
* Elle commence par sauvegarder les registres grâce à la fonction assembleur SAVE_REGISTERS définie dans ordonnanceur.h, qui permet de sauvegarder les registres de la tâche interrompue&lt;br /&gt;
* On appelle ensuite l'ordonnanceur qui définit la manière dont les tâches sont exécutées.&lt;br /&gt;
* On restore ensuite le contexte, et tous les registres de la tâche que l'on va exécuter.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED){&lt;br /&gt;
    &lt;br /&gt;
    /* Sauvegarde du contexte de la tâche interrompue */&lt;br /&gt;
    SAVE_REGISTERS();&lt;br /&gt;
    task[currentTask].stackPointer = SP;&lt;br /&gt;
&lt;br /&gt;
    /* Appel à l'ordonnanceur qui choisi la prochaine tache */&lt;br /&gt;
    scheduler();&lt;br /&gt;
&lt;br /&gt;
    /* Récupération du contexte de la tâche ré-activée */&lt;br /&gt;
    SP = task[currentTask].stackPointer;&lt;br /&gt;
    RESTORE_REGISTERS();&lt;br /&gt;
&lt;br /&gt;
    asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'ordonnanceur est ici round-robin, il exécute donc les tâches les unes après les autres sans priorité pour certaines tâches.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void scheduler(void){&lt;br /&gt;
    currentTask = (currentTask + 1) % nbTasks;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Fonction afin de mettre une tâche en sommeil :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void sleep_ms(uint16_t t) {&lt;br /&gt;
    task[currentTask].state = SLEEP;&lt;br /&gt;
    task[currentTask].sleepTime = t / tick_ms;&lt;br /&gt;
}  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== MAIN ======&lt;br /&gt;
Dans le fichier main.c, on appelle diverses fonctions notamment le timer, qui appellera donc l'ISR puis le scheduler. On commence également par charger la première tâche sur la pile.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
  cli(); // désactiver interruptions&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  // initialisation des piles&lt;br /&gt;
  init_tasks();&lt;br /&gt;
  // TIMER1 config à 20 ms&lt;br /&gt;
  init_timer1(64, 20);&lt;br /&gt;
  // charger la pile de la premi??re t??che&lt;br /&gt;
  SP = task[0].stackPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
  asm volatile(&amp;quot;reti&amp;quot;);&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant : https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On a ici deux horloges : &lt;br /&gt;
&lt;br /&gt;
* Première horloge : on peut soit choisir l'oscillateur RC de 16 MHz ou une horloge externe comprise entre 4 et 26 MHz (p18/142) &lt;br /&gt;
* Deuxième horloge : horloge pour le temps réel de 32kHZ (donc pas une application qu'on vise) (p22/142)&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
INSERER PHOTO&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm. Notre ordonnanceur était originellement composé uniquement des fichiers ordonnanceur.c et ordonnanceur.h mais on a implémenté pas mal de fonctions donc on place celles-ci dans un nouveau fichier processus.c accompagné de son processus.h. Ci-dessous la description du contenu de ces 4 fichiers.&lt;br /&gt;
* processus.h :&lt;br /&gt;
Il inclut les librairies nécéssaires et les prototypes des fonctions implémentées dans processus.c&lt;br /&gt;
* processus.c :&lt;br /&gt;
&lt;br /&gt;
# Fonction Delay : la fonction delay n'est pas présente sous arm donc on l'implémente ici.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(volatile uint32_t t){&lt;br /&gt;
    while (t--){&lt;br /&gt;
        __asm__(&amp;quot;nop&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Tâches pour la led : ci-dessous des fonctions permettant d'allumer, éteindre et faire clignoter la led LD2 de la nucleo. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_led2(void){&lt;br /&gt;
    //active l’horloge pour GPIOA car ds les stm32, les peripheriques sont eteints pr economiser de l energie et on doit appeler la clock pr réveiller les gpio&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
    //on veut écrire 01 (état haut) et pas 11 (analogique) pour les broches 10 et 11 donc on doit procéder en deux étapes : d'abord effacer puis mettre en sortie&lt;br /&gt;
    GPIOA-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (5*2));&lt;br /&gt;
    GPIOA-&amp;gt;MODER |=  (0x1 &amp;lt;&amp;lt; (5*2));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_on(void){&lt;br /&gt;
    GPIOA-&amp;gt;ODR |= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_off(void){&lt;br /&gt;
    GPIOA-&amp;gt;ODR &amp;amp;= ~(1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        delay(1000000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink_task(void){&lt;br /&gt;
    static uint32_t counter = 0;&lt;br /&gt;
    counter++;&lt;br /&gt;
    if(counter &amp;gt;= 10){ // clignote toutes les 10 interruptions&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        counter = 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Tâches pour le série : envoyer un caractère, une chaîne de caractère, recevoir et allumer une led quand l'utilisateur écrit la lettre 'a' dans le minicom.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_usart2(void){&lt;br /&gt;
    RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_USART2EN;&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= RCC_AHB1ENR_GPIOAEN;&lt;br /&gt;
    //PA2 pour tx et PA3 pour rx&lt;br /&gt;
    GPIOA-&amp;gt;MODER &amp;amp;= ~((3 &amp;lt;&amp;lt; (2*2)) | (3 &amp;lt;&amp;lt; (3*2)));&lt;br /&gt;
    GPIOA-&amp;gt;MODER |=  (2 &amp;lt;&amp;lt; (2*2)) | (2 &amp;lt;&amp;lt; (3*2));&lt;br /&gt;
    GPIOA-&amp;gt;AFR[0] &amp;amp;= ~((0xF &amp;lt;&amp;lt; (4*2)) | (0xF &amp;lt;&amp;lt; (4*3))); //efface registre alternate function&lt;br /&gt;
    GPIOA-&amp;gt;AFR[0] |=  (7 &amp;lt;&amp;lt; (4*2)) | (7 &amp;lt;&amp;lt; (4*3)); //attribue AF7 dédié à usart2&lt;br /&gt;
    USART2-&amp;gt;BRR = 0x0683; //baudrate à 9600&lt;br /&gt;
    USART2-&amp;gt;CR1 = USART_CR1_TE | USART_CR1_RE; //transmit et receive enable pour tx et rx&lt;br /&gt;
    USART2-&amp;gt;CR1 |= USART_CR1_UE; //usart enable&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart2_send_char(char c){&lt;br /&gt;
    while (!(USART2-&amp;gt;SR &amp;amp; USART_SR_TXE)); //qd le bit transmit data register empty est à 0 (donc data register possède des données)&lt;br /&gt;
    USART2-&amp;gt;DR = c; //écrire le caractère&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void usart2_send_string(char *s){&lt;br /&gt;
    while (*s){&lt;br /&gt;
        usart2_send_char(*s++);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char usart2_receive(void){&lt;br /&gt;
    while (!(USART2-&amp;gt;SR &amp;amp; USART_SR_RXNE)); //qd caractère&lt;br /&gt;
    return (char)(USART2-&amp;gt;DR &amp;amp; 0xFF); //char sur 16bits on garde que bits de données&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led2_blink_serial(void){&lt;br /&gt;
    for (int i=0; i&amp;lt;10; i++){&lt;br /&gt;
        GPIOA-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; 5);&lt;br /&gt;
        delay(1000000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_serial(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        char c = usart2_receive();&lt;br /&gt;
        usart2_send_char(c); //echo vers Minicom&lt;br /&gt;
        if(c == 'a')&lt;br /&gt;
            led2_blink_serial();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Commande minicom à taper dans le terminal (-o permet d'éviter que minicom envoie des commandes d'initialisation) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Exemple de tâche (non ordonnancée pour le moment) où l'on affiche un message voulu, ici &amp;quot;heyyy&amp;quot; sur minicom[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
# Tâche avec le bouton : allumer la led LD2 quand on appuie sur le bouton user (en bleu, le noir étant dédié au reset).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_user_button(void){&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= RCC_AHB1ENR_GPIOCEN;&lt;br /&gt;
    GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (13 * 2)); //PC13 en entrée&lt;br /&gt;
    //pull up&lt;br /&gt;
    GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (13 * 2));&lt;br /&gt;
    GPIOC-&amp;gt;PUPDR |=  (0x1 &amp;lt;&amp;lt; (13 * 2)); //01 : pull up&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t user_button_pressed(void){&lt;br /&gt;
    return (GPIOC-&amp;gt;IDR &amp;amp; (1 &amp;lt;&amp;lt; 13)) == 0; //bouton actif à 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void task_button_led(void){&lt;br /&gt;
    while(1){&lt;br /&gt;
        if(user_button_pressed()){&lt;br /&gt;
            led2_on();&lt;br /&gt;
        } else {&lt;br /&gt;
            led2_off();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies, processus.h et la liste des fonctions implémentées dans ordonnanceur.c.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
Ce fichier gère le minuteur, les interruptions, la gestion des tâches.&lt;br /&gt;
&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern uint32_t SystemCoreClock; //freq horloge&lt;br /&gt;
&lt;br /&gt;
void init_minuteur(uint16_t prescaler, uint16_t periode_ms){&lt;br /&gt;
  RCC-&amp;gt;APB1ENR |= RCC_APB1ENR_TIM6EN; //active l'horloge de TIM6&lt;br /&gt;
  TIM6-&amp;gt;PSC = prescaler - 1; //prescaler&lt;br /&gt;
  uint32_t tick_freq = SystemCoreClock/prescaler; //période&lt;br /&gt;
  //TIM6-&amp;gt;ARR = (tick_freq * periode_ms)/1000-1; //Auto-Reload Register est la période avant que le timer fasse une interruption. (tick_freq * periode_ms)/1000 correspond au nb de ticks durant cette période.&lt;br /&gt;
  TIM6-&amp;gt;ARR = (uint32_t)(((uint64_t)tick_freq * periode_ms)/1000 - 1);&lt;br /&gt;
  TIM6-&amp;gt;CNT = 0; //reset compteur&lt;br /&gt;
  TIM6-&amp;gt;DIER |= TIM_DIER_UIE; //interruption activée quand compteur atteint nb de ticks de ARR&lt;br /&gt;
  TIM6-&amp;gt;CR1 |= TIM_CR1_CEN; //compteur&lt;br /&gt;
  NVIC_SetPriority(TIM6_DAC_IRQn, 0x1); //prio haute&lt;br /&gt;
  NVIC_EnableIRQ(TIM6_DAC_IRQn); //active l'interruption dans le NVIC (Nested Vector Interrupt Controller)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
On ajoute ensuite le shield sur la nucleo afin de pouvoir tester l'ordonnanceur avec plus de tâches puisque la nucleo seule possède très peu de leds.[[Fichier:Nucleo ordonnanceur.jpg|center|500px|alt=nucleo_ordonnanceur|vignette|nucleo_ordonnanceur]]&lt;br /&gt;
====== Système de fichier ======&lt;br /&gt;
Afin de débuter le système de fichiers, on connecte notre mémoire (la carte SD) à notre nucleo.[[Fichier:Nulceo sd.jpg|center|500px|alt=nucleo_sd|vignette|nucleo_sd]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Test d'initialisation'''&lt;br /&gt;
&lt;br /&gt;
On commence par initialiser notre carte SD grâce à fichier.c et en premier lieu cela ne fonctionnait pas on avait status=1 et erreur=8 (et en conséquent une taille nulle puisqu'il n'arrive pas à initialiser la carte). La carte n'était pas de bonne qualité et ne communiquait pas en SPI mais sans doute avec un autre protocole. Mais par la suite avec la carte SD donnée par M. Redon, on a réussi à la phase d'initialisation. &lt;br /&gt;
&lt;br /&gt;
On obtient alors :&lt;br /&gt;
&lt;br /&gt;
* son type, ici 2 (l'autre type étant 1 pour les micro cartes sd moins performantes) &lt;br /&gt;
* ainsi que sa taille : 3 911 860 secteurs. Un secteur étant de 512 octets, on retrouve bien la taille écrite sur notre carte à savoir 2Gb.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd init.png|center|600px|alt=Sd init|vignette|Sd init]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on teste l'écriture, qui s'avère opérationelle (status = 1) puis l'écriture, elle aussi fonctionnelle puisque l'on affiche bien les 3 &amp;quot;blocs&amp;quot; voulus.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Sd read test.png|600px|alt=Sd read test|vignette|Sd read test]]&lt;br /&gt;
[[Fichier:Sd write test.png|600px|alt=Sd write test|vignette|Sd write test|gauche]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''Commandes pour le système de fichier'''&lt;br /&gt;
&lt;br /&gt;
La prochaine étape est de coder les commandes nécéssaires telles que append, read, remove, rename, copy.&lt;br /&gt;
&lt;br /&gt;
# format : efface l'entièreté du système de fichier.&lt;br /&gt;
# list : liste les noms des fichiers contenus dans le système de fichier, équivalent du &amp;quot;ls&amp;quot; sous linux.&lt;br /&gt;
# append : créé un fichier si non existant et ajoute du texte si le fichier existe déjà. Commande de la forme append fichier/données. Combine le &amp;quot;touch&amp;quot; (create dans l'énoncé) et l'écriture de données.&lt;br /&gt;
# read : permet de lire le contenu d'un fichier, équivalent du &amp;quot;cat&amp;quot;.&lt;br /&gt;
# remove : supprime le fichier en paramètre, équivalent du &amp;quot;rm&amp;quot;.&lt;br /&gt;
# rename : renommer un fichier, équivalent du &amp;quot;mv&amp;quot; en moins puissant.&lt;br /&gt;
# copy : copie un fichier et ses données dans un second fichier, équivalent du &amp;quot;cp&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Demo fileSystem.webm|center|500px|vignette|demo_fileSystem]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Notre carte mère ====&lt;br /&gt;
&lt;br /&gt;
===== Test led =====&lt;br /&gt;
Afin de vérifier que notre PCB reçu fonctionne, on teste notre carte en faisant clignoter une led.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_PIN 9&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  // Activer horloge GPIOC&lt;br /&gt;
  RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOCEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  GPIOC-&amp;gt;MODER &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // Clear&lt;br /&gt;
  GPIOC-&amp;gt;MODER |= 0x1 &amp;lt;&amp;lt; (LED_PIN * 2);    // Output&lt;br /&gt;
  GPIOC-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_PIN);        // Push-pull&lt;br /&gt;
  GPIOC-&amp;gt;PUPDR &amp;amp;= ~(0x3 &amp;lt;&amp;lt; (LED_PIN * 2)); // No pull&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    GPIOC-&amp;gt;ODR ^= (1 &amp;lt;&amp;lt; LED_PIN);&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 1000000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test écran =====&lt;br /&gt;
L'idée est d'afficher un compteur sur un afficheur 7 segments, notre &amp;quot;carte écran&amp;quot; : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_mere/Notre_PCB/02-Compteur.&lt;br /&gt;
&lt;br /&gt;
Notre test écran est structuré de la manière suivante :&lt;br /&gt;
* un main.c&lt;br /&gt;
* un dossier pour la carte écran&lt;br /&gt;
* un dossier hardware_setup&lt;br /&gt;
* un dossier pour le spi&lt;br /&gt;
&lt;br /&gt;
Description des fichiers et fonctions implémentées :&lt;br /&gt;
&lt;br /&gt;
====== hardware_setup ======&lt;br /&gt;
&lt;br /&gt;
On reprend notre fonction setupPin que l'on adapte à arm afin d'initialiser les pins de notre carte :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define OFFSET_BSRR_OFF 16&lt;br /&gt;
&lt;br /&gt;
#define MODER_CLEAR 0x3&lt;br /&gt;
#define MODER_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define PUPDR_CLEAR 0x3&lt;br /&gt;
#define PUPDR_NumberBitParPin 0x2&lt;br /&gt;
&lt;br /&gt;
#define OTYPER_CLEAR 0x1&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  INPUT,&lt;br /&gt;
  OUTPUT,&lt;br /&gt;
  ALTERNATE_FUNCTION,&lt;br /&gt;
  ANALOG,&lt;br /&gt;
} portModeRegister;&lt;br /&gt;
&lt;br /&gt;
typedef enum {&lt;br /&gt;
  NO_PULL,&lt;br /&gt;
  PULL_UP,&lt;br /&gt;
  PULL_DOWN,&lt;br /&gt;
} portPullUpPullDownRegister;&lt;br /&gt;
&lt;br /&gt;
void setupPin(GPIO_TypeDef *GPIOx, uint8_t PINx, portModeRegister portMode) {&lt;br /&gt;
  // ACTIVATION DE L'HORLOGE GPIO SPECIFIQUE&lt;br /&gt;
  uint32_t RCC_AHB1ENR_GPIOxEN_Pos;&lt;br /&gt;
&lt;br /&gt;
  if (GPIOx == GPIOA)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOAEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOB)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOBEN_Pos;&lt;br /&gt;
  else if (GPIOx == GPIOC)&lt;br /&gt;
    RCC_AHB1ENR_GPIOxEN_Pos = RCC_AHB1ENR_GPIOCEN_Pos;&lt;br /&gt;
  else&lt;br /&gt;
    return; // GPIO non existant sur ce microcontroleur&lt;br /&gt;
&lt;br /&gt;
  if (!(RCC-&amp;gt;AHB1ENR &amp;amp; (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos)))&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= (1 &amp;lt;&amp;lt; RCC_AHB1ENR_GPIOxEN_Pos);&lt;br /&gt;
&lt;br /&gt;
  // CLEAR AVANT MODIFICATION&lt;br /&gt;
  // On clear après l'activation d'horloge !&lt;br /&gt;
  GPIOx-&amp;gt;MODER &amp;amp;= ~(MODER_CLEAR &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR &amp;amp;= ~(PUPDR_CLEAR &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin));&lt;br /&gt;
  GPIOx-&amp;gt;OTYPER &amp;amp;= ~(OTYPER_CLEAR &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PORT (Input, Output, Alternative, Analogique)&lt;br /&gt;
  portPullUpPullDownRegister typePull = NO_PULL;&lt;br /&gt;
  uint8_t optionPort = 0x00;&lt;br /&gt;
&lt;br /&gt;
  switch (portMode) {&lt;br /&gt;
  case INPUT:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = PULL_UP;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case OUTPUT:&lt;br /&gt;
    optionPort = 0x01;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
&lt;br /&gt;
    if (1)&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
    else&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER |= (1 &amp;lt;&amp;lt; PINx); // Open-drain&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ALTERNATE_FUNCTION:&lt;br /&gt;
    optionPort = 0x02;&lt;br /&gt;
&lt;br /&gt;
    if (1) {&lt;br /&gt;
      typePull = NO_PULL;&lt;br /&gt;
      GPIOx-&amp;gt;OTYPER &amp;amp;= ~(1 &amp;lt;&amp;lt; PINx); // Push-pull&lt;br /&gt;
      GPIOx-&amp;gt;OSPEEDR |= 11 &amp;lt;&amp;lt; (PINx * 2);&lt;br /&gt;
      // Very high speed =&amp;gt; Cf STM32F410 datasheet tableau p95/142&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  case ANALOG:&lt;br /&gt;
    optionPort = 0x03;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    optionPort = 0x00;&lt;br /&gt;
    typePull = NO_PULL;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;MODER |= optionPort &amp;lt;&amp;lt; (PINx * MODER_NumberBitParPin);&lt;br /&gt;
&lt;br /&gt;
  // TYPE DE PULL (No pull, Pull Down, Pull Up)&lt;br /&gt;
  uint8_t optionPull = 0x00;&lt;br /&gt;
&lt;br /&gt;
  // Ecriture type pull&lt;br /&gt;
  switch (typePull) {&lt;br /&gt;
  case NO_PULL:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_UP:&lt;br /&gt;
    optionPull = 0x01;&lt;br /&gt;
    break;&lt;br /&gt;
  case PULL_DOWN:&lt;br /&gt;
    optionPull = 0x02;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    optionPull = 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Ecriture&lt;br /&gt;
  GPIOx-&amp;gt;PUPDR |= optionPull &amp;lt;&amp;lt; (PINx * PUPDR_NumberBitParPin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On implémente également des fonctions qui permettent d'activer, de désactiver ou de faire clignoter (une led en l'occurence) des pins.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void onPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; PINx); // set&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void offPin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  GPIOx-&amp;gt;BSRR = (1 &amp;lt;&amp;lt; (PINx + OFFSET_BSRR_OFF)); // reset&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void togglePin(GPIO_TypeDef *GPIOx, uint8_t PINx) {&lt;br /&gt;
  // Après sa lecture, le registre BSRR reset à 0 automatiquement&lt;br /&gt;
  if (GPIOx-&amp;gt;ODR &amp;amp; (1 &amp;lt;&amp;lt; PINx)) // Lecture pin, si 1 alors eteindre&lt;br /&gt;
    offPin(GPIOx, PINx);&lt;br /&gt;
  else // Sinon allumer&lt;br /&gt;
    onPin(GPIOx, PINx);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On possède également une fonction qui initialise notre carte mère, en activant les différents ports des cartes filles, le spi, les leds et les boutons.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setupCarte() {&lt;br /&gt;
  // 1&lt;br /&gt;
  setupPin(GPIOC, 0, OUTPUT); // PC0, CS1&lt;br /&gt;
  setupPin(GPIOC, 1, OUTPUT); // PC1, RST1&lt;br /&gt;
  setupPin(GPIOC, 2, OUTPUT); // PC2, INT1&lt;br /&gt;
&lt;br /&gt;
  // 2&lt;br /&gt;
  setupPin(GPIOA, 7, OUTPUT); // PA7, CS2&lt;br /&gt;
  setupPin(GPIOB, 1, OUTPUT); // PB1, RST2&lt;br /&gt;
  setupPin(GPIOC, 4, OUTPUT); // PC4, INT2&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  setupPin(GPIOC, 9, OUTPUT); // PC9, CS3&lt;br /&gt;
  setupPin(GPIOB, 6, OUTPUT); // PB6, RST3&lt;br /&gt;
  setupPin(GPIOB, 5, OUTPUT); // PB5, INT3&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  setupPin(GPIOA, 2, OUTPUT); // PA2, CS4&lt;br /&gt;
  setupPin(GPIOA, 1, OUTPUT); // PA1, RST4&lt;br /&gt;
  setupPin(GPIOA, 0, OUTPUT); // PA0, INT4&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  setupPin(GPIOA, 4, OUTPUT); // PA4, CS6&lt;br /&gt;
&lt;br /&gt;
  // FPGA&lt;br /&gt;
  setupPin(GPIOB, 0, OUTPUT); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  // LEDs&lt;br /&gt;
  setupPin(GPIOB, 8, OUTPUT); // PB8, LED1&lt;br /&gt;
  setupPin(GPIOA, 6, OUTPUT); // PA6, LED2&lt;br /&gt;
  setupPin(GPIOB, 7, OUTPUT); // PB7, LED3&lt;br /&gt;
&lt;br /&gt;
  // BTNs&lt;br /&gt;
  setupPin(GPIOC, 12, OUTPUT); // PC12, SW_1&lt;br /&gt;
  setupPin(GPIOB, 11, INPUT);  // PB11, SW_2&lt;br /&gt;
  setupPin(GPIOC, 10, INPUT);  // PC10, SW_3&lt;br /&gt;
&lt;br /&gt;
  // Test LEDs allumé&lt;br /&gt;
  offPin(GPIOC, 0); // PC0, CS1&lt;br /&gt;
  offPin(GPIOB, 1); // PA7, CS2&lt;br /&gt;
  offPin(GPIOC, 9); // PC9, CS3&lt;br /&gt;
  offPin(GPIOA, 2); // PA2, CS4&lt;br /&gt;
  offPin(GPIOB, 0); // PB0, CS_FPGA&lt;br /&gt;
&lt;br /&gt;
  onPin(GPIOC, 12); // PB0, CS_FPGA&lt;br /&gt;
  // Setup SPI =&amp;gt; MOSI, MISO et SCK&lt;br /&gt;
  spiInit();&lt;br /&gt;
&lt;br /&gt;
  // Ecran&lt;br /&gt;
  ecran_init();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== spi ======&lt;br /&gt;
On initialise également les fonctions pour le SPI, à retrouver ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03-Code/Carte_mere/Notre_PCB/02-Compteur/lib/SPI.&lt;br /&gt;
&lt;br /&gt;
On implémente les fonctions suivantes, commentées en détail dans le spi.c :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spiInit();&lt;br /&gt;
void spi_cs_on(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_cs_off(GPIO_TypeDef *GPIOx, uint8_t PINx);&lt;br /&gt;
void spi_write(uint8_t data, GPIO_TypeDef *CS_GPIOx, uint8_t CS_PINx);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== écran ======&lt;br /&gt;
On écrit plusieurs fonctions pour notre carte écran, qui est pour l'instant testée par le biais de l'afficheur 7 segments d'où certainess lignes commentées. &lt;br /&gt;
&lt;br /&gt;
Ces fonctions servent à :&lt;br /&gt;
&lt;br /&gt;
* initialiser l'écran&lt;br /&gt;
* écrire dessus en activant le spi&lt;br /&gt;
* régler l'intensité de l'écran&lt;br /&gt;
* nettoyer l'écran&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// PC11, CS5&lt;br /&gt;
#define CS5_GPIO GPIOC&lt;br /&gt;
#define CS5_PIN 11&lt;br /&gt;
&lt;br /&gt;
// PB9, RST5&lt;br /&gt;
#define RST5_GPIO GPIOB&lt;br /&gt;
#define RST5_PIN 9&lt;br /&gt;
&lt;br /&gt;
// PC13, INT5&lt;br /&gt;
#define INT5_GPIO GPIOC&lt;br /&gt;
#define INT5_PIN 13&lt;br /&gt;
&lt;br /&gt;
void ecran_init() {&lt;br /&gt;
  setupPin(CS5_GPIO, CS5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(RST5_GPIO, RST5_PIN, OUTPUT);&lt;br /&gt;
  // setupPin(INT5_GPIO, INT5_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  onPin(CS5_GPIO, CS5_PIN); // S'active à l'etat bas&lt;br /&gt;
  // offPin(RST5_GPIO, RST5_PIN); // S'active à l'etat bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_spi_write(uint8_t data) {&lt;br /&gt;
  spi_write(data, CS5_GPIO, CS5_PIN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_brightness(uint8_t intensite) {&lt;br /&gt;
  ecran_spi_write(0x7A);      // Commande &amp;quot;Brightness&amp;quot;&lt;br /&gt;
  ecran_spi_write(intensite); // Sécurité passive 2^8-1 = 255 qui est le maximum&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ecran_clear() {&lt;br /&gt;
  ecran_spi_write(0x76); // Commande &amp;quot;Clear&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On écrit ensuite le code du compteur :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t isChange = 0b1111;&lt;br /&gt;
&lt;br /&gt;
int unite = 0;&lt;br /&gt;
int decimal = 0;&lt;br /&gt;
int centaine = 0;&lt;br /&gt;
int mil = 0;&lt;br /&gt;
&lt;br /&gt;
void ecran_compteur() {&lt;br /&gt;
&lt;br /&gt;
  ecran_select_digit(3);&lt;br /&gt;
  ecran_spi_write(unite); // Data&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0010) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0010;&lt;br /&gt;
    ecran_select_digit(2);&lt;br /&gt;
    ecran_spi_write(decimal); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b0100) {&lt;br /&gt;
    isChange &amp;amp;= ~0b0100;&lt;br /&gt;
    ecran_select_digit(1);&lt;br /&gt;
    ecran_spi_write(centaine); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (isChange &amp;amp; 0b1000) {&lt;br /&gt;
    isChange &amp;amp;= ~0b1000;&lt;br /&gt;
    ecran_select_digit(0);&lt;br /&gt;
    ecran_spi_write(mil); // Data&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (unite &amp;lt; 9) {&lt;br /&gt;
    unite++;&lt;br /&gt;
  } else {&lt;br /&gt;
    isChange |= 0b0010;&lt;br /&gt;
    unite = 0;&lt;br /&gt;
    if (decimal &amp;lt; 9)&lt;br /&gt;
      decimal++;&lt;br /&gt;
    else {&lt;br /&gt;
      decimal = 0;&lt;br /&gt;
      isChange |= 0b0100;&lt;br /&gt;
&lt;br /&gt;
      if (centaine &amp;lt; 9) {&lt;br /&gt;
        centaine++;&lt;br /&gt;
      } else {&lt;br /&gt;
        centaine = 0;&lt;br /&gt;
        isChange |= 0b1000;&lt;br /&gt;
&lt;br /&gt;
        if (mil &amp;lt; 9) {&lt;br /&gt;
          mil++;&lt;br /&gt;
        } else {&lt;br /&gt;
          mil = 0;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== main ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
  setupCarte();&lt;br /&gt;
  ecran_brightness(100);&lt;br /&gt;
  int j = 0;&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    ecran_compteur();&lt;br /&gt;
    j++;&lt;br /&gt;
&lt;br /&gt;
    if (j &amp;gt; 10) {&lt;br /&gt;
      j = 0;&lt;br /&gt;
      togglePin(GPIOB, 8); // LED1&lt;br /&gt;
      togglePin(GPIOA, 6); // LED2&lt;br /&gt;
      togglePin(GPIOB, 7); // LED3&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (volatile uint32_t i = 0; i &amp;lt; 25000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Boutons utilisés ====&lt;br /&gt;
Nous voulions implémenter un clavier à touches mécaniques. Monsieur Redon avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh.jpg|alt=Boite Kailh|vignette|center|Boite Kailh Switch]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo de droite, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|left|300px|vignette|Kailh Hot swap socket|300x300px]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|right|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fait gagner un temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoqué avec Monsieur Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasure ====&lt;br /&gt;
On brase les composants sur nos deux PCB. On commence par le clavier rouge, qui s'avère bien reconnu par lsusb. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier brasé.jpg|500px|alt=clavier brasé|vignette|clavier brasé|centré]]Le PCB vert en revanche n'est pas reconnu. On teste les différentes connexions au multimètre. Le 5V est bien là. On teste alors le quartz à l'oscilloscope. On se rend compte que l'on obtient que du bruit par rapport au pcb rouge. Cependant après avoir changé le quartz, le problème est toujours présent. On finit alors par se rendre compte que l'on a inversé une capacité avec une resistance, le problème est donc résolu rapidement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Test oscillo.jpg|left|400px|alt=test_oscillo|vignette|test_oscillo]]&lt;br /&gt;
[[Fichier:Oscillo vert.jpg|right|300px|alt=oscillo_vert|vignette|oscillo_vert]]&lt;br /&gt;
[[Fichier:Oscillo rouge.jpg|right|300px|alt=oscillo_rouge|vignette|oscillo_rouge]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== Test Led ====&lt;br /&gt;
Afin de vérifier que notre clavier fonctionne, on fait un test afin de faire clignoter nos deux leds : led d'alimentation et led pour Cap Lock (qui nous servira par la suite pour savoir si notre carte est en mode majuscule ou non).&lt;br /&gt;
&lt;br /&gt;
Remarque : la fonction setupPin est la même que celle présentée dans la section ordonnanceur de la carte shield.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 16000000UL&lt;br /&gt;
&lt;br /&gt;
#define LEDs_PORT PORTE&lt;br /&gt;
#define LEDs_DDR DDRE&lt;br /&gt;
#define LEDs_PIN PINE&lt;br /&gt;
#define LED_CapsLock PE6&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
  setupClock();&lt;br /&gt;
  // Leds&lt;br /&gt;
  setupPin(&amp;amp;LEDs_PORT, &amp;amp;LEDs_DDR, LED_CapsLock, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
  // Bouton&lt;br /&gt;
  //setupPin(BTNs_PORT, BTNs_DDR, BTN_Right, INPUT_PULL_UP);&lt;br /&gt;
&lt;br /&gt;
  // Permet de liberer le portF pour utiliser les boutons !&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // 1ère écriture&lt;br /&gt;
  MCUCR |= (1 &amp;lt;&amp;lt; JTD); // Désactiver JTAG (2ème écriture obligatoire !)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
  while (1) {&lt;br /&gt;
      LEDs_PORT |= (1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      LEDs_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; LED_CapsLock); // toggle LED&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Matrice de boutons ====&lt;br /&gt;
Suite à cela, on définit une matrice de boutons. L'objectif est de reconnaître quand une touche est activée (les touches étant pour le moment sans signification).&lt;br /&gt;
&lt;br /&gt;
On définit les colonnes :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define TOTAL_COL 14&lt;br /&gt;
&lt;br /&gt;
// --- Colonnes ---&lt;br /&gt;
volatile uint8_t *col_ports[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;PORTF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;PORTC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;PORTB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;PORTD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *col_ddr[TOTAL_COL] = {&lt;br /&gt;
    [0 ... 5] = &amp;amp;DDRF,   // COL0 à COL5&lt;br /&gt;
    [6 ... 7] = &amp;amp;DDRC,   // COL6 à COL7&lt;br /&gt;
    [8 ... 10] = &amp;amp;DDRB,  // COL8 à COL10&lt;br /&gt;
    [11 ... 13] = &amp;amp;DDRD, // COL11 à COL13&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t col_pins[TOTAL_COL] = {0, 1, 4, 5, 6, 7, 7, 6, 6, 5, 4, 7, 6, 4};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;puis les lignes :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define TOTAL_ROW 5&lt;br /&gt;
&lt;br /&gt;
// --- Lignes ---&lt;br /&gt;
volatile uint8_t *row_ports[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PORTD,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t *row_ddr[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;DDRD,&lt;br /&gt;
};&lt;br /&gt;
volatile uint8_t *row_pins_reg[TOTAL_ROW] = {&lt;br /&gt;
    [0 ... 4] = &amp;amp;PIND,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint8_t row_pins[TOTAL_ROW] = {5, 3, 2, 1, 0};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Par la suite, on configure les colonnes en sortie, les lignes en entrées avec pull-up puis on procède au scan matriciel. Pour vérifier que l'appui sur un bouton est bien detecté, on allume la led CapLock.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define TOTAL_KEYSWITCH 62&lt;br /&gt;
uint8_t key_state[TOTAL_COL][TOTAL_ROW] = {0};&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
  setupHardware();&lt;br /&gt;
&lt;br /&gt;
  // Configuration colonnes en sortie&lt;br /&gt;
  for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
    setupPin(col_ports[c], col_ddr[c], col_pins[c], OUTPUT);&lt;br /&gt;
    onPin(col_ports[c], col_pins[c]); // mettre toutes les colonnes à 1 pour les desactiver&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Configuration ligne ROW0 en entrée avec pull-up&lt;br /&gt;
  for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
    setupPin(row_ports[r], row_ddr[r], row_pins[r], INPUT_PULL_UP);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  while (1) {&lt;br /&gt;
    // Scan matriciel&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
      offPin(col_ports[c], col_pins[c]); // activer colonne (LOW)&lt;br /&gt;
&lt;br /&gt;
      for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++)&lt;br /&gt;
        key_state[c][r] = !(*row_pins_reg[r] &amp;amp; (1 &amp;lt;&amp;lt; row_pins[r]));&lt;br /&gt;
&lt;br /&gt;
      onPin(col_ports[c], col_pins[c]); // désactiver colonne (HIGH)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int isPressed = 0;&lt;br /&gt;
    for (uint8_t c = 0; c &amp;lt; TOTAL_COL; c++) {&lt;br /&gt;
      for (uint8_t r = 0; r &amp;lt; TOTAL_ROW; r++) {&lt;br /&gt;
        if (key_state[c][r] == 1) {&lt;br /&gt;
          isPressed = 1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (isPressed)&lt;br /&gt;
      onPin(LEDs_PORT, LED_CapsLock);&lt;br /&gt;
    else {&lt;br /&gt;
      offPin(LEDs_PORT, LED_CapsLock);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lufa ====&lt;br /&gt;
Afin que nos touches soient reconnus comme des lettres de l'alphabet que l'on peut voir sur notre écran, on utilise la LUFA.&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
Les notes liées à la conception de la schématique se trouvent dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
==== Puce ====&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes :&lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&lt;br /&gt;
==== Liste et Description des sheets ====&lt;br /&gt;
'''''FPGA_Power :'''''&lt;br /&gt;
&lt;br /&gt;
Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposée donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonnée car non supportée par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquençage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8348</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8348"/>
		<updated>2025-10-30T15:43:23Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Notre carte fille comporte plusieurs éléments : */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Concevons un clavier ! ===&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fais gagner un peu de temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
=== Kicad - Schématique  ===&lt;br /&gt;
'''&amp;lt;u&amp;gt;Notre carte fille comporte plusieurs éléments :&amp;lt;/u&amp;gt;'''&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
=== Kicad - Vue 3D ===&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Kicad - Schématique ===&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
Les notes liées à la conception de la schématique se trouve dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
===== Liste et Description des sheets =====&lt;br /&gt;
'''''FPGA_Power :''''' Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposé et étant donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonné car non supporté par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquencage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8347</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8347"/>
		<updated>2025-10-30T15:42:47Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Kicad - Schématique  Notre carte fille comporte plusieurs éléments : */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Concevons un clavier ! ===&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fais gagner un peu de temps précieux sur le routage.&lt;br /&gt;
&lt;br /&gt;
=== Kicad - Schématique  ===&lt;br /&gt;
&lt;br /&gt;
==== Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
=== Kicad - Vue 3D ===&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Kicad - Schématique ===&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
Les notes liées à la conception de la schématique se trouve dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
===== Liste et Description des sheets =====&lt;br /&gt;
'''''FPGA_Power :''''' Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposé et étant donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonné car non supporté par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquencage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8346</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8346"/>
		<updated>2025-10-30T15:42:14Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Boutons utilisés */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Concevons un clavier ! ===&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fais gagner un peu de temps précieux sur le routage.&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
=== Kicad - Vue 3D ===&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Kicad - Schématique ===&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
Les notes liées à la conception de la schématique se trouve dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
===== Liste et Description des sheets =====&lt;br /&gt;
'''''FPGA_Power :''''' Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposé et étant donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonné car non supporté par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquencage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8338</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8338"/>
		<updated>2025-10-28T23:57:37Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Kicad - Schématique  Notre carte fille comporte plusieurs éléments : */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également sauvegarder notre configuration en exportant sous format &amp;quot;json&amp;quot;. Via ce format on peut utiliser une extension de kicad qui se prénomme &amp;quot;'''Keyboard footprints placer'''&amp;quot; et qui permet de placer automatiquement les boutons si on les intancie dans le bon ordre (exemple : bouton 1 =&amp;gt; SW1 , etc...). L'outil est un peu capricieux mais fais gagner un peu de temps précieux sur le routage.&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
Une '''carte FPGA''' est actuellement en développement en parallèle. Il s’agit d’un '''défi technique majeur''' visant à faire évoluer le projet de '''pico-ordinateur''' vers une nouvelle étape.&lt;br /&gt;
&lt;br /&gt;
L’objectif, à terme, est de concevoir un '''pico-ordinateur complet''' capable de '''gérer des flux vidéo et audio''', ainsi que différents '''protocoles HID''', notamment en intégrant un '''microcontrôleur''' (comme sur la carte '''Nexys A7''', par exemple). Dans cette optique, le développement de la carte FPGA doit '''progresser au mieux''', mais il est '''possible que le travail se poursuive sur le semestre S8''', comme convenu avec '''M. Boé'''.&lt;br /&gt;
=== Kicad - Schématique ===&lt;br /&gt;
La référence de la puce FPGA à router est celle ci : XC7A15T-1FTG256C.  C'est une puce de la famille Artix-7.&lt;br /&gt;
Les notes liées à la conception de la schématique se trouve dans ce répertoire : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA .&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La schématique comporte elle même toutes les explications, il est donc inutile de revenir sur chacun de ces points ici.&lt;br /&gt;
&lt;br /&gt;
===== Liste et Description des sheets =====&lt;br /&gt;
'''''FPGA_Power :''''' Feuille regroupant l'alimentation critique de notre puce :&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
'''''FPGA_Banks_14-15''''' et '''''FPGA_Banks_34-35''''' ''':''' Feuille disposant de l'ensemble des entrées et sorties du FPGA n'ayant pas de fonction prédisposé et étant donc libre pour ajouter nos composants.&lt;br /&gt;
&lt;br /&gt;
'''''FPGA_Config :''''' Feuille regroupant les pins de programmation de la puce en fonction du mode choisi au préalable ainsi que la logique data USB-C (2.0 ici) .&lt;br /&gt;
&lt;br /&gt;
'''''Switch&amp;amp;Button :''''' ''Feuille contenant les boutons et les switchs.'' Joue le rôle de la carte matrice de boutons''.''&lt;br /&gt;
&lt;br /&gt;
'''''LEDs&amp;amp;7seg :''''' ''Feuille contenant les LEDs et le 7 segments.''&lt;br /&gt;
&lt;br /&gt;
'''''Ethernet :''''' ''Feuille contenant la logique Ethernet de notre carte.'' Joue le rôle de la carte réseau.&lt;br /&gt;
&lt;br /&gt;
'''''VGA :''''' ''Feuille contenant toute la logique du VGA.'' Joue le rôle de la carte écran.&lt;br /&gt;
&lt;br /&gt;
'''''HDMI :''''' ''Feuille abandonné car non supporté par notre puce FPGA.''&lt;br /&gt;
&lt;br /&gt;
'''''Power :''''' Feuille sur la gestion de l'alimentation avec son séquencage.&lt;br /&gt;
&lt;br /&gt;
'''''Memory :''''' Feuille contenant la mémoire SRAM de notre carte. Joue le rôle de la carte mémoire.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8337</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8337"/>
		<updated>2025-10-28T23:26:42Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Vue 3D */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8336</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8336"/>
		<updated>2025-10-28T23:26:06Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Kicad - Schématique  Notre carte fille comporte plusieurs éléments : */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles. Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8335</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8335"/>
		<updated>2025-10-28T23:25:29Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8334</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8334"/>
		<updated>2025-10-28T23:23:50Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Concevons un clavier ! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8333</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8333"/>
		<updated>2025-10-28T23:22:59Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Concevons un clavier ! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8332</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8332"/>
		<updated>2025-10-28T23:22:37Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Concevons un clavier ! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8331</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8331"/>
		<updated>2025-10-28T23:22:12Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Concevons un clavier ! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8330</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8330"/>
		<updated>2025-10-28T23:21:35Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Boutons utilisés */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8329</id>
		<title>SE4Binome2025-6</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2025-6&amp;diff=8329"/>
		<updated>2025-10-28T23:21:09Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Carte fille Clavier */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cahier des charges ==&lt;br /&gt;
L'objectif pour notre groupe est de réaliser une carte mère.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lien GIT==&lt;br /&gt;
Lien du git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Shield ==&lt;br /&gt;
La première étape est de réaliser un shield au cas où notre carte mère s'avérerait non fonctionnelle, afin de ne pas bloquer l'avancée des groupes des cartes filles.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Composants ====&lt;br /&gt;
Afin de réaliser notre bouclier qui combiné à un arduino uno fera guise de carte mère, nous utilisons les composants suivants :&lt;br /&gt;
&lt;br /&gt;
- Puce ATMega328-A en tant que microprocesseur&lt;br /&gt;
&lt;br /&gt;
- 5 connecteurs 2*4 pour les cartes filles (clavier, écran, réseau, son) et un connecteur 2*4 pour connecter la carte mémoire.&lt;br /&gt;
&lt;br /&gt;
- Des convertisseurs de niveaux logiques 5V vers 3,3V pour l'utilisation de la carte mémoire (même si il aurait été préférable de mettre la partie conversion directement sur la carte mémoire).&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique et vue 3D ====&lt;br /&gt;
[[Fichier:Pico-shield schematique.pdf|left|600px|alt=Pico-shield_schematique|vignette|Pico-shield_schematique]]&lt;br /&gt;
[[Fichier:CarteShield 3D.png|right|800px|alt=CarteShield 3D|vignette|CarteShield 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Carte mémoire ====&lt;br /&gt;
En extension de notre shield ou de notre future carte mère, on ajoute la gestion de la mémoire avec la carte SD sur une carte mémoire distincte.[[Fichier:Memoire schematic.pdf|left|600px|alt=Memoire_schematic|vignette|Memoire_schematic]]&lt;br /&gt;
[[Fichier:Memoire 3D.png|right|800px|alt=Memoire 3D|vignette|Memoire 3D]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Brasage ====&lt;br /&gt;
&lt;br /&gt;
On procède au brasage des cartes shield et mémoire.[[Fichier:Cartes shield et memoire brasées.jpg|left|600px|alt=cartes shield et memoire brasées|vignette|cartes shield et memoire brasées]]&lt;br /&gt;
[[Fichier:Cartes shield et memoire brasées 2.jpg|right|600px|alt=cartes shield et memoire brasées 2|vignette|cartes shield et memoire brasées 2]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Tests ====&lt;br /&gt;
&lt;br /&gt;
===== Test leds =====&lt;br /&gt;
&lt;br /&gt;
On teste les leds et on constate que notre carte shield est fonctionelle. [[Fichier:Carte shield test leds.jpg|alt=carte shield test leds|left|600px|vignette|carte shield test leds]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Test carte SD =====&lt;br /&gt;
On teste ensuite un programme arduino simple au préalable pour voir si la carte SD est détectée. La carte n'étant pas détectée, on regarde alors :&lt;br /&gt;
&lt;br /&gt;
# que la connexion série est bien établie pour voir si le problème ne vient pas de l'IDE Arduino -&amp;gt; ce n'est pas le cas.&lt;br /&gt;
# si la carte SD est défaillante en testant le programme avec une autre carte SD. On teste aussi sur un lecteur de carte SD pour voir si elle est détectée sur le pc ce qui est le cas -&amp;gt; le pb ne vient pas de là non plus.&lt;br /&gt;
# on s'intéresse maintenant à l'aspect matériel, on vérifie les soudures -&amp;gt; toujours pas de souci particulier.&lt;br /&gt;
# schématique et routage de la carte : on s'aperçoit alors que l'on a inversé le sens du 74LVC125 de l'unité U1A pour la conversion de niveau logique du MOSI en appuyant par erreur sur le raccourci clavier x qui inverse en &amp;quot;miroir&amp;quot; le sens du composant. Remarque : on aurait aussi vraiment du mettre le convertisseur de niveau logique sur la carte mémoire pour voir le problème plus vite. Conséquence : le routage n'est pas correct, on rajoute donc des fils pour faire les bonnes connexions et on coupe au cutter les mauvaises.&lt;br /&gt;
&lt;br /&gt;
[INSERER PHOTO]&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
===== Programmation carte SD =====&lt;br /&gt;
Maintenant que la carte SD fonctionne, on va s'atteler à essayer de comprendre son fonctionnement en stockant des données dedans à l'aide de code en C.&lt;br /&gt;
&lt;br /&gt;
[CONTINUER LA PROG]&lt;br /&gt;
&lt;br /&gt;
===== Ordonnanceur =====&lt;br /&gt;
Maintenant que notre shield est fonctionnel, nous pouvons réaliser notre ordonnanceur. A voir ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/03%20-%20Code/Carte%20Shield/ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
[EXPLICATIONS A DETAILLER]&lt;br /&gt;
&lt;br /&gt;
==Carte mère==&lt;br /&gt;
La deuxième carte à réaliser est la carte mère avec une spécificité cependant, à savoir une puce STM32F410R8T6 en tant que microprocesseur.&lt;br /&gt;
&lt;br /&gt;
Remarque : M. Redon a également fait une carte mère mais basée sur un ATSAMD21G8A-A. Celle-ci pourra nous être utile si on rencontre des soucis pour le code avec notre stm32.&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Microprocesseur ====&lt;br /&gt;
On utilise la puce STM32F410R8T6.&lt;br /&gt;
&lt;br /&gt;
===== Signification du nom de la puce =====&lt;br /&gt;
Signification (cf p 134 de la short datasheet) :&lt;br /&gt;
&lt;br /&gt;
Arm based 32-bit microcontroller&lt;br /&gt;
&lt;br /&gt;
* F = General-purpose&lt;br /&gt;
* R = 64 pins&lt;br /&gt;
* 8 = 64 Kbytes of Flash memory&lt;br /&gt;
* T = package LQFP&lt;br /&gt;
* 6 = Industrial temperature range, - 40 to 85 °C&lt;br /&gt;
&lt;br /&gt;
===== Datasheets =====&lt;br /&gt;
Datasheet de la puce  STM32F410R8T6 :&lt;br /&gt;
[[Fichier:STM32 datasheet.pdf|left|400px|alt=STM32_datasheet|vignette|STM32_datasheet]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On peut également retrouver des pages supplémentaires afin de bien dimensionner notre quartz et les capacités aux alentours.&lt;br /&gt;
&lt;br /&gt;
Document AN2867- Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPUs à retrouver sur le lien suivant :&lt;br /&gt;
https://www.st.com/en/microcontrollers-microprocessors/stm32f410/documentation.html&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schématique ====&lt;br /&gt;
Notre schématique est composée de quatres sous feuilles, respectivement pour l'alimentation, le microcontrôleur, la carte mémoire et les cartes filles.&lt;br /&gt;
&lt;br /&gt;
===== Alimentation =====&lt;br /&gt;
L'alimentation se fait via du 5V et est ensuite directement convertie en 3,3V par le biais du régulateur afin d'alimenter le microcontrôleur. C'est pourquoi les bus de données USB ne sont pas utilisées car l'USB servira ici uniquement à l'alimentation et pas à la transmission de données. On ajouté également des mounting holes pour fixer la carte.&lt;br /&gt;
&lt;br /&gt;
===== Carte mémoire =====&lt;br /&gt;
La carte mémoire ou carte fille SD est sensiblement la même que celle pour le shield. On a juste rajouté une capacité de découplage car la carte SD va recevoir et envoyer beaucoup de données rapidement.&lt;br /&gt;
&lt;br /&gt;
===== Microcontrôleur =====&lt;br /&gt;
Le microcontrôleur est composé de beaucoup de broches dédiées à l'alimentation, aux horloges, aux boots, à la communication, aux cartes filles, aux switchs, aux leds et au JTAG (voir sections suivantes).&lt;br /&gt;
&lt;br /&gt;
====== Alimentation ======&lt;br /&gt;
Les broches VDD servent à l'alimentation numérique et VDDA à l'alimentation analogique, ici séparée pour filtrer de manière plus précise car  plus sensible que le numérique. En effet, pour filtrer les hautes fréquences en numérique, les capacités de découplage suffisent alors qu'en analogique le signal d'entrée nécessite une gestion plus précise avec une ferrite.&lt;br /&gt;
&lt;br /&gt;
====== Horloges ======&lt;br /&gt;
On utilise ici deux sources d'horloge : on peut soit choisir l'oscillateur RC ou une horloge externe comprise entre 4-26 MHz (p18/142) ou bien la deuxième horloge est une horloge pour temps réels (donc pas une application qu'on vise) (p22/142).&lt;br /&gt;
&lt;br /&gt;
[PRECISER VALEURS ET REFERENCES]&lt;br /&gt;
&lt;br /&gt;
====== Boot et configuration ======&lt;br /&gt;
Les boot 0 et 1 permettent de choisir la partie ou le bloc mémoire que l'on souhaite réinitialiser. En sélectionnant 0 bit, un bit de l'un ou de l'autre ou les deux, on choisit ce que l'on souhaite réinitialiser. On peut réinitialiser :&lt;br /&gt;
&lt;br /&gt;
- la flash (boot0 à 0) : mémoire non volatile pour le code principal. Adresse : 0x0800 0000 - 0x0801 FFFF d'après le memory mapping (p43/142).&lt;br /&gt;
&lt;br /&gt;
- la ROM - Read Only Memory (boot0 à 1 et boot1 à 0) : mémoire non volatile que l'on change rarement sauf si besoin de changer mode communication par exemple (passage en spi, uart ...). Adresse : 0x1FFF 0000 - 0x1FFF 77FF.&lt;br /&gt;
&lt;br /&gt;
- la SRAM - Static Random Access Memory (boot0 à 1 et boot1 à 1) : mémoire volatile pour le débogage. Adresse : 0x2000 0000 - 0x2000 7FFF.&lt;br /&gt;
&lt;br /&gt;
On a aussi le pin NRST (Not Reset car actif à l'état bas) pour réinitialiser le microcontrôleur.&lt;br /&gt;
&lt;br /&gt;
====== Switchs ======&lt;br /&gt;
On a 3 switchs qui peuvent servir pour choisir les modes de boot ?. [PRECISER UTILITE]&lt;br /&gt;
&lt;br /&gt;
====== Communication ======&lt;br /&gt;
On a prévu différents types de communications selon les utilisations : SPI pour les cartes filles mais aussi UART et I2C amélioré si besoin pour une potentielle carte FPGA.&lt;br /&gt;
&lt;br /&gt;
====== Cartes filles ======&lt;br /&gt;
On a prévu de la place pour 5 cartes filles, sans compter la carte mémoire et la carte FPGA potentielle.&lt;br /&gt;
&lt;br /&gt;
====== Leds ======&lt;br /&gt;
3 leds supplémentaires ont étés ajoutées pour différents tests, utile pour l'ordonnanceur potentiellement par exemple.&lt;br /&gt;
&lt;br /&gt;
====== JTAG et SWD ======&lt;br /&gt;
Le bloc JTAG sert pour le débogage de la carte. Ici sur le stm32, c'est combiné à un deuxième mode de débogage spécifique aux stm32 : le SWD, une version simplifiée de JTAG.&lt;br /&gt;
&lt;br /&gt;
JTAG (Joint Test Action Group) :&lt;br /&gt;
&lt;br /&gt;
* TCK : Test Clock, l'horloge du JTAG&lt;br /&gt;
* TMS : Test Mode Select&lt;br /&gt;
* TDI : Test Data In pour envoyer des données depuis le JTAG au microcontrôleur.&lt;br /&gt;
* TDO : Test Data Out pour envoyer des données depuis le microcontrôleur au JTAG.&lt;br /&gt;
* RTCK : Return Test Clock&lt;br /&gt;
&lt;br /&gt;
SWD (Serial Wire Debug) :&lt;br /&gt;
&lt;br /&gt;
* SWCLK : comme TCK&lt;br /&gt;
* SWDIO : comme TMS&lt;br /&gt;
* SWO : comme TDO&lt;br /&gt;
&lt;br /&gt;
===== Cartes filles =====&lt;br /&gt;
Notre carte mère peut acceuillir 5 cartes filles communicantes en SPI parmi lesquelles : &lt;br /&gt;
&lt;br /&gt;
* carte clavier&lt;br /&gt;
* carte écran&lt;br /&gt;
* carte réseau&lt;br /&gt;
* carte son&lt;br /&gt;
* une autre carte&lt;br /&gt;
&lt;br /&gt;
Et en plus de cela, on a aussi la carte &amp;quot;fille&amp;quot; pour la gestion de la mémoire = le boîtier SD (en SPI également) ainsi que la carte fille FPGA ou d'autre cartes qui peuvent communiquer en UART ou I2C amélioré (car SMBA).[[Fichier:Mere schematique.pdf|left|600px|alt=Mere schematique|vignette|Mere schematique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque : pour voir les différentes pages de la schématique, cliquer sur le fichier.&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Carte mere 3D.png|left|650px|alt=Carte mere 3D|vignette|Carte mere 3D]]&lt;br /&gt;
[[Fichier:Carte mere 3D backside.png|right|650px|alt=Carte mere 3D backside|vignette|Carte mere 3D backside]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
&lt;br /&gt;
==== NUCLEO-F410RB ====&lt;br /&gt;
En attendant de recevoir notre carte mère et afin de prendre en main la programmation quelque peu spécifique des arm, on s'entraîne sur la carte NUCLEO-F410RB qui possède le même microcontrôleur STM32F410R8T6 que celui de notre carte mère. &lt;br /&gt;
&lt;br /&gt;
===== Spécifications =====&lt;br /&gt;
La carte NUCLEO-F410RB est &amp;quot;séparée&amp;quot; en deux parties : &lt;br /&gt;
&lt;br /&gt;
* la partie haute de la carte : c'est le programmateur spécifique à STM32 nommé ST-LINK qui permet de programmer :&lt;br /&gt;
* la partie basse de la carte : qui est elle dédiée à l'utilisateur.&lt;br /&gt;
&lt;br /&gt;
===== STM32CubeIDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Pour commencer à comprendre, on utilise dans un premier temps l'IDE STM32CubeIDE dont l'on se passera ensuite afin d'être proche du matériel et de ne pas passer par plein de librairies.&lt;br /&gt;
Dans notre fichier nucleo.ioc, on a toutes les spécifications de notre carte dont le pinout.&lt;br /&gt;
Premier programme test : on fait clignoter la led LD2. On voit sur le fichier nucleo.ioc que LD2 est sur le pin PA5 du microcontrôleur.&lt;br /&gt;
En allant dans l'arborescence à gauche, on va dans Core -&amp;gt; Src -&amp;gt; main.c.&lt;br /&gt;
On ouvre le main et dans la boucle while, on va faire clignoter notre led D2 grâce à la librairie HAL (Hardware Abstraction Layer) dont l'on se passera par la suite (c'est juste pour les premiers tests).&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 while (1)&lt;br /&gt;
  {&lt;br /&gt;
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); //change d'état&lt;br /&gt;
	  HAL_Delay(500); //attend 500ms&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Nucleo clignote.mp4|center|500px|vignette|nucleo_clignote]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Sans IDE =====&lt;br /&gt;
&lt;br /&gt;
====== Test led ======&lt;br /&gt;
Après avoir testé le code dans l'IDE, on va maintenant coder avec moins de librairies subsidiaires avec juste les fichiers utiles pour la puce STM32F410RB que l'on va chercher sur le site de STMicroelectronics .&lt;br /&gt;
&lt;br /&gt;
On va dans la catégorie Tools et software : https://www.st.com/en/microcontrollers-microprocessors/stm32f410rb.html#tools-software. &lt;br /&gt;
&lt;br /&gt;
On pourrait sélectionner directement l'evaluation tools de la nucleo mais ce n'est pas l'objectif ici puisque la nucleo nous permet juste de s'entraîner sur le microcontrôleur F410 le temps que l'on recoive notre carte. Donc on va plutôt télécharger STM32CubeF4.&lt;br /&gt;
&lt;br /&gt;
Suite à cela, on ajoutera différents fichiers :&lt;br /&gt;
&lt;br /&gt;
# stm32cubef4-v1-28-3 : le firmware de STMicroelectronics&lt;br /&gt;
# main.c : code principal&lt;br /&gt;
# main.h &lt;br /&gt;
# ordonnanceur.c : uniquement pour l'ordonnanceur, le main.c gère le reste, les différents fichiers et variables à inclure&lt;br /&gt;
# ordonnanceur.h &lt;br /&gt;
# un makefile  : pour compiler notre projet et les fichiers du firmware. On compile avec arm-none-eabi-gcc. On rajoute les chemins d'accès nécéssaires à la compilation du main.h qui a besoin du fichier stm32f410rx.h. On a également besoin du fichier core_cm4.h, d'où le second chemin :  INCLUDES = -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \ -Istm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Include &lt;br /&gt;
# un fichier nommé linker.ld : à la différence des atmega, notre microcontrôleur a une organisation mémoire plus complexe donc ce fichier nous permet de choisir où placer chaque type de données (cf les sections elf) dans la mémoire du microcontrôleur. On y précise la taille de la flash : 128ko et la taille de la RAM : 32 ko ainsi que leurs adresses trouvées page 40/763 de la datasheet. On y initialise également les sections .text (le code), .data (variables initialisées) et .bss (variables non initialisées).&lt;br /&gt;
# un fichier startup_stm32f410rx.s : un fichier assembleur qui gère les interruptions, les resets, l'initialisation des données que l'on copie colle depuis /stm32/stm32cubef4-v1-28-3/STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc. Remarque sur ce qu'est thumb : sur notre stm32, on utilise ARM et thumb 2 dont le rôle est d'avoir un code plus compact en mélangeant des instructions à la fois de 16 et 32 bits.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Remarque :  commande à taper quand la carte &amp;quot;fail to connect&amp;quot; : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
st-flash --connect-under-reset erase&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ordonnanceur ======&lt;br /&gt;
Il faut également faire l'ordonnanceur que l'on doit maintenant adapter non plus au shield et aux avr mais à la carte mère et aux arm.&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.h :&lt;br /&gt;
&lt;br /&gt;
On y ajoute les librairies et la liste des fonctions implémentées dans ordonnanceur.c&lt;br /&gt;
&lt;br /&gt;
* ordonnanceur.c :&lt;br /&gt;
&lt;br /&gt;
On y implémente des fonctions pour les leds, le minuteur, la communication série.&lt;br /&gt;
&lt;br /&gt;
# leds : fonctions pour allumer, éteindre et faire clignoter (exemple de tâche).&lt;br /&gt;
# minuteur : pour pouvoir effectuer une tâche pendant un certain temps.&lt;br /&gt;
# communication série (deuxième exemple de tâche). Commande minicom à taper dans le terminal (-o permet d'éviter que mincom envoie des commandes d'initialisation) : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -D /dev/ttyACM0 -b 9600 -o&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:Minicom2.png|center|500px|alt=Minicom_|vignette|Minicom_]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte fille Clavier ==&lt;br /&gt;
=== Boutons utilisés ===&lt;br /&gt;
Nous voulions implémenter un clavier à touche mécanique. Monsieur Xavier REDON avait des switchs qui convenait donc nous n'avions pas besoin d'en recommander. &lt;br /&gt;
&lt;br /&gt;
Les switchs sont de la marque KAILH :&lt;br /&gt;
[[Fichier:Boite Kailh Switch.jpg|centré|vignette|Boite Kailh Switch]]&lt;br /&gt;
[[Fichier:Kailh Hot swap socket.png|gauche|vignette|Kailh Hot swap socket]]&lt;br /&gt;
[[Fichier:Bouton kailh comparaison hot swap.jpg|vignette|300x300px|Bouton kailh comparaison hot swap socket]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Petit bémol : les '''sockets hot-swap''' ne correspondaient pas à nos boutons. Comme on peut le voir sur la photo, il existe '''deux types de sockets''' pour nos modules hot-swap. Nous avons donc dû commander les modèles adaptés.&lt;br /&gt;
&lt;br /&gt;
L’intérêt de ces modules est de '''pouvoir insérer ou retirer les boutons à volonté''', sans qu’ils soient soudés directement au PCB — ce sont les sockets qui, eux, sont soudés. Cette solution présente plusieurs avantages : elle '''facilite la réutilisation des boutons''' d’un projet à un autre et '''améliore la réparabilité''' du clavier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Concevons un clavier ! ====&lt;br /&gt;
Notre clavier doit comporter '''62 touches''', conformément au '''format standard ISO 60 %''', et sera capable d’'''assurer l’ensemble des combinaisons de touches attendues pour un clavier moderne en 2025'''.&lt;br /&gt;
&lt;br /&gt;
Afin de customiser notre clavier, on se rend sur le site [https://www.keyboard-layout-editor.com/#/ keyboard-layout-editor] .&lt;br /&gt;
&lt;br /&gt;
Nous pouvons partir d'un modèle de base ou alors d'un preset :&lt;br /&gt;
[[Fichier:Site keyboard layout .png|centré|vignette|534x534px|Site keyboard layout preset ISO 60%]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes alors inspirés des '''claviers disponibles sur le marché''' afin d’adopter un placement des touches conforme aux dispositions les plus courantes.&lt;br /&gt;
[[Fichier:Keyboard-layout.jpg|centré|vignette|575x575px|Keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’utilisation de cet outil présente plusieurs intérêts : elle permet d’'''imaginer et définir la disposition du clavier''', de '''le découper en lignes et colonnes''' afin de concevoir la '''matrice de touches''', et enfin d’'''identifier les bonnes empreintes''' à utiliser sur le futur PCB grâce au '''sommaire illustré ci-dessous'''.&lt;br /&gt;
[[Fichier:Summary keyboard layout.png|centré|vignette|399x399px|Summary keyboard layout]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ce sommaire indique la '''taille indicative de chaque type de touche''' ainsi que '''le nombre de touches associées''' à chacune d’elles.Le &amp;quot;coloriage&amp;quot; est utile pour voir visuellement quelle touche correspond à quelle taille.&lt;br /&gt;
&lt;br /&gt;
==== Kicad - Schématique  Notre carte fille comporte plusieurs éléments : ====&lt;br /&gt;
# Le microcontrôleur ATMega32U4 avec un cristal de 16 MHz, des capacités de découplage et une ferrite (Cf AVR042) ;&lt;br /&gt;
# L'USB pour la programmation et l'alimentation pendant la phase programmation du projet ;&lt;br /&gt;
# Le connecteur ISP ;&lt;br /&gt;
# Les boutons RST et HWB ;&lt;br /&gt;
# Le connecteur SPI pour la communication avec la carte mère ;&lt;br /&gt;
# La led pour l'alimentation de la carte ;&lt;br /&gt;
# La led pour l'état du clavier (rôle ?) ;&lt;br /&gt;
# La matrice de touches évidemment ;&lt;br /&gt;
# Des mounting holes.&lt;br /&gt;
&lt;br /&gt;
Remarque : Pas de leds RGB, pas assez de pins et nous ne voulions pas nous éparpiller sur trop d'idées (sujet evoquer avec M Boé).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Clavier schematique.pdf|center|700px|alt=Clavier schematique|vignette|Clavier schematique]]&lt;br /&gt;
&lt;br /&gt;
==== Vue 3D ====&lt;br /&gt;
[[Fichier:Keyboard 3D up v2.png|gauche|vignette|652x652px|Keyboard 3D up]]&lt;br /&gt;
[[Fichier:Keyboard 3D back v2.png|vignette|649x649px|Keyboard 3D back]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Carte Fille FPGA ==&lt;br /&gt;
On a prévu de faire une carte fille FPGA mais ceci n'est pas notre projet principal, on commande la carte et on espère avoir le temps d'avancer sur le code de cette carte, le code pour la carte mère restant prioritaire. M. Boé nous a dit qu'on pourrait continuer au s8 si besoin.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
==== Puce FPGA ====&lt;br /&gt;
Puce utilisée : XC7A15T-1FTG256C&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datasheets ====&lt;br /&gt;
La carte FPGA étant complexe, nous avons beaucoup de datasheets donc plutôt que de les ajouter une par une, nous vous conseillons d'aller directement dans l'onglet concerné de notre git : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet .&lt;br /&gt;
&lt;br /&gt;
Certains extraits de ces datasheets illustrent bien comment concevoir notre schématique. Les images sont ici : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA  ou décrites plus bas dans le wiki.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception ====&lt;br /&gt;
&lt;br /&gt;
===== Puce =====&lt;br /&gt;
La puce FPGA étant assez complexe, elle ne peut être représentée en une seule entité sous kicad, elle est donc décomposée dans les unités suivantes : &lt;br /&gt;
&lt;br /&gt;
* U1A dans la sheet FPGA_Banks_14-15&lt;br /&gt;
* U1B dans la sheet FPGA_Banks_34-35&lt;br /&gt;
* U1C dans la sheet FPGA_Config&lt;br /&gt;
* U1D dans la sheet FPGA_Power&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Liste des sheets =====&lt;br /&gt;
&lt;br /&gt;
# Sheet principale FPGA&lt;br /&gt;
# FPGA_Banks_14-15&lt;br /&gt;
# FPGA_Banks_34-35&lt;br /&gt;
# FPGA_Config&lt;br /&gt;
# FPGA_Power&lt;br /&gt;
# LEDs&amp;amp;7seg&lt;br /&gt;
# Switch&amp;amp;Button&lt;br /&gt;
# VGA&lt;br /&gt;
# HDMI&lt;br /&gt;
# Ethernet&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Description des sheets =====&lt;br /&gt;
&lt;br /&gt;
====== Sheet principale FPGA ======&lt;br /&gt;
&lt;br /&gt;
* Ajout de 4 mounting holes si l'on veut fixer la carte quelque part ou la protéger de la poussière avec une plaque en verre.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_14-15 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_14 et PWR_BANK_15. Pour chacune de ces sources, on ajoute :&lt;br /&gt;
&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF &lt;br /&gt;
* 4 capacités de découplage de 0,47 uF &lt;br /&gt;
Ces valeurs proviennent de la page 15 de la datasheet UG483 (voir capture DecouplingInfo ci-dessous) :[[Fichier:DecouplingInfo.png|left|500px|alt=DecouplingInfo|vignette|DecouplingInfo]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Banks_34-35 ======&lt;br /&gt;
On a deux sources de tension PWR_BANK_34 et PWR_BANK_35. Pour chacune de ces sources, tout comme pour la sheet  FPGA_Banks_14-15, on ajoute :&lt;br /&gt;
* 1 capacité de découplage de 100 uF&lt;br /&gt;
* 2 capacités de découplage de 4,7 uF&lt;br /&gt;
* 4 capacités de découplage de 0,47 uF&lt;br /&gt;
Ces données proviennent également de la capture DecouplingInfo.&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Config ======&lt;br /&gt;
Différents blocs :&lt;br /&gt;
&lt;br /&gt;
# Sur l'unité U1C, on ajoute une capacité de 47 uF (cf image DecouplingInfo de la section FPGA_Banks_14-15). &lt;br /&gt;
# Bloc JTAG : un connecteur 2*6 pins pour la programmation (pins TMS, TCK, TDO, TDI). Se reférer à JTAG de la carte mère pour explications.&lt;br /&gt;
# USB Programming : FT32H : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FT232H.pdf. On ajoute différentes capacités de découplage, ferrites et un quartz (regarder pages 43/61).&lt;br /&gt;
# USB C : diode de protection PGB1010603MR et résistance pour protéger de l'ESD (ElectroStatic Discharge) - https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/ESDPROTECTION_PGB1010603MR.pdf&lt;br /&gt;
# FPGA Configuration Modes : 3 connecteurs de 1*3 pins pour les 3 modes de configuration du FPGA (cf image ci-dessous de la page 17 de la datasheet UG470 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug470_7Series_Config.pdf).&lt;br /&gt;
[[Fichier:ConfigMode.png|left|500px|alt=ConfigMode|vignette|ConfigMode]]&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== FPGA_Power ======&lt;br /&gt;
On a 4 sources d'alimentation :&lt;br /&gt;
&lt;br /&gt;
# VCCAUX : Auxiliary voltage (tension auxiliaire), alimente circuits internes non critiques en puissance.&lt;br /&gt;
# VCCINT : Internal core voltage (tension interne du cœur logique), alimente la logique principale du FPGA (LUTs, Flip-flops).&lt;br /&gt;
# VCCADC/BATT : &lt;br /&gt;
#* VCCADC : tension pour le module ADC si le FPGA en a un.&lt;br /&gt;
#* VBATT : alimenter registres de configuration non volatiles ou horloge temps réel.&lt;br /&gt;
# VCCBRAM : Block RAM, alimente les blocs mémoire. Cela permet de séparer l’alimentation de la mémoire afin de réduire le bruit.&lt;br /&gt;
Valeurs issues de :&lt;br /&gt;
&lt;br /&gt;
* Pour toutes les sources, se réferer à DecouplingInfo pour les valeurs des capacités de découplage (datasheet UG483 p15) sauf pour VCCADC/VBATT (regarder XADC_DecouplingCapa page 65 de la datasheet UG480 : https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/FPGA_ug480_7Series_XADC.pdf). Capture présente ci-dessous.&lt;br /&gt;
* Et pour les valeurs des alimentations, regarder AlimRecommande (extrait de la datasheet DS181 pages 2 et 3) ci-dessous.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:AlimRecommande.png|left|500px|alt=AlimRecommande|vignette|AlimRecommande]]&lt;br /&gt;
[[Fichier:XADC DecouplingCapa.png|right|600px|alt=XADC DecouplingCapa|vignette|XADC DecouplingCapa]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== LEDs&amp;amp;7seg ======&lt;br /&gt;
&lt;br /&gt;
* Leds : on ajoute 4 leds avec des résistances de 330 Ω.&lt;br /&gt;
* Afficheur 7 segments : TDCG1050M (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/7SEG_VISH-S-A0004391181-1.pdf). &lt;br /&gt;
*# Cathodes :  reliés à des résistances de 60 Ω.&lt;br /&gt;
*#* Cathodes A à G et DP pour les 7 segments et le point.&lt;br /&gt;
*#* Cathodes L1L2 et L3.&lt;br /&gt;
*#Anodes : reliées à des transistors PNP BC807 (https://gitea.plil.fr/ahouduss/SE4-Pico-B6/src/branch/master/01-Kicad/Recherche%20FGPA/Datasheet/TRANSISTOR-PNP_SBC807-25LT1G.pdf) par le biais d'une résistance de 5,1 kΩ.&lt;br /&gt;
*#*Anodes D1 à D4 pour allumer les 4 chiffres.&lt;br /&gt;
*#*Anodes L2L2 et L3 &lt;br /&gt;
[[Fichier:Afficheur fpga.png|left|500px|alt=afficheur_fpga|vignette|afficheur_fpga]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Switch&amp;amp;Button ======&lt;br /&gt;
&lt;br /&gt;
* Switchs : 4 switchs SPDT - Single Pole Double Throw pour choisir 3V3 ou la masse par le biais de l'entrée, reliée à une résistance de 10 kΩ.&lt;br /&gt;
* 5 boutons avec 2 résistances de 10 kΩ pour protéger si un court-circuit venait à arriver.&lt;br /&gt;
&lt;br /&gt;
====== VGA ======&lt;br /&gt;
Depuis le TP avec M. Wichmann, on comprend un peu mieux la norme vidéo VGA (Video Graphics Array).  On sait donc que le VGA possède 5 sorties : &lt;br /&gt;
&lt;br /&gt;
* 3 sorties pour les leds RVB de l'écran : VGA_RED, VGA_BLUE, VGA_GREEN. Chacune de ces sorties est sur 4 bits et la conversion DAC s'effectue à l'aide d'une échelle de résistance de 500Ω, 1k, 2k puis 4k (cf cours CNSPS).&lt;br /&gt;
* 2 sorties pour les signaux de synchronisations horizontal et vertical : VGA_H-SYNC et VGA_V-SYNC reliés à des résistances de 100Ω.&lt;br /&gt;
&lt;br /&gt;
On rajoute également une capcité de découplage de 1nF en parallèle avec une résistance de 1M pour le shield.&lt;br /&gt;
&lt;br /&gt;
====== HDMI ======&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Connecteur HDMI (High-Definition Multimedia Interface) si on a le temps pour programmer sur plus moderne que le VGA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ethernet ======&lt;br /&gt;
Utilisation d'un port RJ45&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard_3D_back_v2.png&amp;diff=8328</id>
		<title>Fichier:Keyboard 3D back v2.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard_3D_back_v2.png&amp;diff=8328"/>
		<updated>2025-10-28T23:20:36Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Keyboard 3D back v2&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard_3D_up_v2.png&amp;diff=8327</id>
		<title>Fichier:Keyboard 3D up v2.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard_3D_up_v2.png&amp;diff=8327"/>
		<updated>2025-10-28T23:19:57Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Keyboard 3D up v2&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Summary_keyboard_layout.png&amp;diff=8326</id>
		<title>Fichier:Summary keyboard layout.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Summary_keyboard_layout.png&amp;diff=8326"/>
		<updated>2025-10-28T23:06:42Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Summary keyboard layout&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard-layout.jpg&amp;diff=8325</id>
		<title>Fichier:Keyboard-layout.jpg</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Keyboard-layout.jpg&amp;diff=8325"/>
		<updated>2025-10-28T22:59:08Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Keyboard-layout&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Site_keyboard_layout_.png&amp;diff=8324</id>
		<title>Fichier:Site keyboard layout .png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Site_keyboard_layout_.png&amp;diff=8324"/>
		<updated>2025-10-28T22:50:39Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Site keyboard layout&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Tuto_site_keyboard_layout.png&amp;diff=8323</id>
		<title>Fichier:Tuto site keyboard layout.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Tuto_site_keyboard_layout.png&amp;diff=8323"/>
		<updated>2025-10-28T22:49:38Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuto site keyboard layout&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Bouton_kailh_comparaison_hot_swap.jpg&amp;diff=8322</id>
		<title>Fichier:Bouton kailh comparaison hot swap.jpg</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Bouton_kailh_comparaison_hot_swap.jpg&amp;diff=8322"/>
		<updated>2025-10-28T22:43:20Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bouton kailh comparaison hot swap&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Kailh_Hot_swap_socket.png&amp;diff=8321</id>
		<title>Fichier:Kailh Hot swap socket.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Kailh_Hot_swap_socket.png&amp;diff=8321"/>
		<updated>2025-10-28T22:38:48Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kailh Hot swap socket&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Boite_Kailh_Switch.jpg&amp;diff=8320</id>
		<title>Fichier:Boite Kailh Switch.jpg</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Boite_Kailh_Switch.jpg&amp;diff=8320"/>
		<updated>2025-10-28T22:36:38Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Boite Kailh Switch&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8062</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8062"/>
		<updated>2025-06-15T23:13:03Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Affichage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de programmer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par le capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le '''capteur de mouvement HC-SR501''' est un '''capteur infrarouge''' passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la '''quantité d’infrarouge''' captée change '''entre les deux cellules''', '''créant un déséquilibre'''. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
[[Fichier:LentilleFresnel.png|centré|vignette|Lentille Fresnel du capteur de mouvement]]&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C (le [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29/ds18b20.c fichier .c] et le [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29/ds18b20.h fichier .h]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles à la communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir notre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette partie n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer sur l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublié de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permet également de tester les cartes aen cas de dysfonctionnement, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
Ce code permet de faire un Hello World. Il à été nettoyé au préalable. Il est également disponible [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/CapteurHumidite_ET_NRF/Lufa/SE/HelloWorld dans ce repertoire].&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;VirtualSerial.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
static CDC_LineEncoding_t LineEncoding = { .BaudRateBPS = 0,&lt;br /&gt;
                                           .CharFormat  = CDC_LINEENCODING_OneStopBit,&lt;br /&gt;
                                           .ParityType  = CDC_PARITY_None,&lt;br /&gt;
                                           .DataBits    = 8 };&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    SetupHardware();&lt;br /&gt;
    LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);&lt;br /&gt;
    GlobalInterruptEnable();&lt;br /&gt;
&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        CDC_Task();&lt;br /&gt;
        USB_USBTask();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetupHardware(void)&lt;br /&gt;
{&lt;br /&gt;
    MCUSR &amp;amp;= ~(1 &amp;lt;&amp;lt; WDRF);&lt;br /&gt;
    wdt_disable();&lt;br /&gt;
    clock_prescale_set(clock_div_1);&lt;br /&gt;
&lt;br /&gt;
    USB_Init();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void EVENT_USB_Device_Connect(void)&lt;br /&gt;
{&lt;br /&gt;
    LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void EVENT_USB_Device_Disconnect(void)&lt;br /&gt;
{&lt;br /&gt;
    LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void EVENT_USB_Device_ConfigurationChanged(void)&lt;br /&gt;
{&lt;br /&gt;
    bool ConfigSuccess = true;&lt;br /&gt;
    ConfigSuccess &amp;amp;= Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPADDR, EP_TYPE_INTERRUPT, CDC_NOTIFICATION_EPSIZE, 1);&lt;br /&gt;
    ConfigSuccess &amp;amp;= Endpoint_ConfigureEndpoint(CDC_TX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1);&lt;br /&gt;
    ConfigSuccess &amp;amp;= Endpoint_ConfigureEndpoint(CDC_RX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1);&lt;br /&gt;
    LineEncoding.BaudRateBPS = 0;&lt;br /&gt;
    LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void EVENT_USB_Device_ControlRequest(void)&lt;br /&gt;
{&lt;br /&gt;
    switch (USB_ControlRequest.bRequest)&lt;br /&gt;
    {&lt;br /&gt;
        case CDC_REQ_GetLineEncoding:&lt;br /&gt;
            if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))&lt;br /&gt;
            {&lt;br /&gt;
                Endpoint_ClearSETUP();&lt;br /&gt;
                Endpoint_Write_Control_Stream_LE(&amp;amp;LineEncoding, sizeof(LineEncoding));&lt;br /&gt;
                Endpoint_ClearOUT();&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
        case CDC_REQ_SetLineEncoding:&lt;br /&gt;
            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))&lt;br /&gt;
            {&lt;br /&gt;
                Endpoint_ClearSETUP();&lt;br /&gt;
                Endpoint_Read_Control_Stream_LE(&amp;amp;LineEncoding, sizeof(LineEncoding));&lt;br /&gt;
                Endpoint_ClearIN();&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
        case CDC_REQ_SetControlLineState:&lt;br /&gt;
            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))&lt;br /&gt;
            {&lt;br /&gt;
                Endpoint_ClearSETUP();&lt;br /&gt;
                Endpoint_ClearStatusStage();&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void CDC_Task(void)&lt;br /&gt;
{&lt;br /&gt;
    // Vérifie si le périphérique USB est bien configuré avant de continuer&lt;br /&gt;
    if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
        return;&lt;br /&gt;
&lt;br /&gt;
    // Chaîne de caractères à envoyer sur la liaison série USB&lt;br /&gt;
    char msg[] = &amp;quot;Hello world\r\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    // Sélectionne l'endpoint d'envoi (TX) pour préparer l'envoi de données&lt;br /&gt;
    Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
&lt;br /&gt;
    // Écrit le message dans le buffer de l'endpoint USB, en mode Little Endian&lt;br /&gt;
    Endpoint_Write_Stream_LE(msg, sizeof(msg)-1, NULL);&lt;br /&gt;
&lt;br /&gt;
    // Vérifie si le buffer de l'endpoint est plein après l'écriture&lt;br /&gt;
    bool full = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
&lt;br /&gt;
    // Vide (envoie) le contenu du buffer IN vers l'hôte&lt;br /&gt;
    Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
    // Si le buffer était plein, attend que l'endpoint soit prêt pour un autre envoi&lt;br /&gt;
    if (full)&lt;br /&gt;
    {&lt;br /&gt;
        // Attend que l'endpoint soit prêt pour un nouvel envoi (acknowledgement de l'hôte)&lt;br /&gt;
        Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
        // Vide de nouveau le buffer pour s'assurer que tout est bien envoyé&lt;br /&gt;
        Endpoint_ClearIN();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Sélectionne l'endpoint de réception (RX) pour traiter d'éventuelles données entrantes&lt;br /&gt;
    Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
&lt;br /&gt;
    // Si des données ont été reçues par l'hôte on vide le buffer&lt;br /&gt;
    if (Endpoint_IsOUTReceived())&lt;br /&gt;
        Endpoint_ClearOUT();&lt;br /&gt;
&lt;br /&gt;
    // Pause de 300 ms pour ralentir l'exécution de la tâche (évite les envois en boucle trop rapides)&lt;br /&gt;
    _delay_ms(300);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous n'avons pas réussi à souder notre NRF, malheuresmeent le code avec celui ne fonctionnera donc pas. Le code avec NRF sera fait avec un Arduino Uno et UART comme l'atmega328p ne supporte pas la LUFA.&lt;br /&gt;
&lt;br /&gt;
Voici tout de même une piste qui pourrait fonctionner : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/CapteurHumidite_ET_NRF/Lufa/SE/VirtualSerial&lt;br /&gt;
&lt;br /&gt;
=== '''Docker''' ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== '''Node-RED''' ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre site final :&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== '''Récupération ID microcontroleur''' ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8060</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8060"/>
		<updated>2025-06-15T21:40:02Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Énergie */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de programmer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par le capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le '''capteur de mouvement HC-SR501''' est un '''capteur infrarouge''' passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la '''quantité d’infrarouge''' captée change '''entre les deux cellules''', '''créant un déséquilibre'''. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
[[Fichier:LentilleFresnel.png|centré|vignette|Lentille Fresnel du capteur de mouvement]]&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C (le [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29/ds18b20.c fichier .c] et le [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29/ds18b20.h fichier .h]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles à la communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir notre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette partie n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer sur l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublié de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permet également de tester les cartes aen cas de dysfonctionnement, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
[[Fichier:DatahsheetPbLUFA1.png|centré|vignette|632x632px|Extrait datasheet numéro 1 relevant le problème de quartz ]]&lt;br /&gt;
[[Fichier:Extrait datasheet v2.png|centré|vignette|406x406px|Extrait datasheet numéro 2 relevant la configuration de l'USB où l'on est ]]&lt;br /&gt;
Probablement des fuses à changer pour pouvoir&lt;br /&gt;
&lt;br /&gt;
=== '''Docker''' ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== '''Node-RED''' ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre site final :&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== '''Récupération ID microcontroleur''' ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Extrait_datasheet_v2.png&amp;diff=8059</id>
		<title>Fichier:Extrait datasheet v2.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Extrait_datasheet_v2.png&amp;diff=8059"/>
		<updated>2025-06-15T21:32:37Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Extrait datasheet v2&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:DatahsheetPbLUFA1.png&amp;diff=8058</id>
		<title>Fichier:DatahsheetPbLUFA1.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:DatahsheetPbLUFA1.png&amp;diff=8058"/>
		<updated>2025-06-15T21:24:43Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DatahsheetPbLUFA1&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:LentilleFresnel.png&amp;diff=8057</id>
		<title>Fichier:LentilleFresnel.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:LentilleFresnel.png&amp;diff=8057"/>
		<updated>2025-06-15T21:04:42Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;l&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8056</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8056"/>
		<updated>2025-06-15T20:56:34Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* LUFA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de programmer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par cle capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs..&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le capteur de mouvement HC-SR501 est un capteur infrarouge passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la quantité d’infrarouge captée change entre les deux cellules, créant un déséquilibre. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles àla communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir ntre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette parti n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer su l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublier de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permettait également de tester les cartes au cas où celles-ci dysfonctionnaient, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
&lt;br /&gt;
=== '''Docker''' ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== '''Node-RED''' ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== '''Récupération ID microcontroleur''' ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8055</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8055"/>
		<updated>2025-06-15T20:53:38Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* LUFA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de prgrammer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par cle capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs..&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le capteur de mouvement HC-SR501 est un capteur infrarouge passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la quantité d’infrarouge captée change entre les deux cellules, créant un déséquilibre. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles àla communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir ntre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette parti n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer su l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublier de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permettait également de tester les cartes au cas où celles-ci dysfonctionnaient, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Node-RED ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== Récupération ID microcontrôleur ======&lt;br /&gt;
Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8054</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8054"/>
		<updated>2025-06-15T20:48:31Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Docker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de prgrammer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par cle capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs..&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le capteur de mouvement HC-SR501 est un capteur infrarouge passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la quantité d’infrarouge captée change entre les deux cellules, créant un déséquilibre. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles àla communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir ntre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette parti n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer su l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublier de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permettait également de tester les cartes au cas où celles-ci dysfonctionnaient, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Node-RED ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== Récupération ID microcontroleur ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8053</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8053"/>
		<updated>2025-06-15T20:40:21Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* Communication */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de prgrammer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par cle capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs..&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le capteur de mouvement HC-SR501 est un capteur infrarouge passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la quantité d’infrarouge captée change entre les deux cellules, créant un déséquilibre. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles àla communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir ntre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette parti n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer su l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublier de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permettait également de tester les cartes au cas où celles-ci dysfonctionnaient, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
&lt;br /&gt;
=== '''Docker''' ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== '''Node-RED''' ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
[[Fichier:VideoDemoFinalv1.mp4|centré|vignette|Vidéo demonstration du projet fonctionnel]]&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== '''Récupération ID microcontroleur''' ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:VideoDemoFinalv1.mp4&amp;diff=8052</id>
		<title>Fichier:VideoDemoFinalv1.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:VideoDemoFinalv1.mp4&amp;diff=8052"/>
		<updated>2025-06-15T20:39:24Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Video&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8051</id>
		<title>SE3Groupe2024-2</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Groupe2024-2&amp;diff=8051"/>
		<updated>2025-06-15T20:29:09Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : /* LUFA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lien GIT ==&lt;br /&gt;
Voici le lien git pour accéder aux différents fichiers relatifs à notre projet : https://gitea.plil.fr/ahouduss/se3_2024_B2.git&lt;br /&gt;
&lt;br /&gt;
== Description du projet ==&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
L'objectif de ce projet est de concevoir une station domotique capable de collecter et d'afficher des mesures provenant de capteurs. Elle devra également être capable d'activer des actionneurs, tels que des LEDs, des cadenas ou tout autre dispositif, en fonction des besoins.  &lt;br /&gt;
  &lt;br /&gt;
=== Cahier des charges ===&lt;br /&gt;
La station domotique devra permettre l'affichage des informations suivantes concernant une pièce :&lt;br /&gt;
* Température ambiante ;&lt;br /&gt;
* Taux d'humidité ;&lt;br /&gt;
* Présence humaine (via capteur de mouvement) ;&lt;br /&gt;
* D'autres paramètres pourront être ajoutés en fonction de l'avancement du projet.&lt;br /&gt;
&lt;br /&gt;
Elle devra aussi permettre de contrôler différents actionneurs dans la pièce, tels que :&lt;br /&gt;
* L'éclairage, en fonction de la présence d'une personne (via un capteur de mouvement) ;&lt;br /&gt;
* D'autres dispositifs pourront être intégrés en fonction des besoins.&lt;br /&gt;
&lt;br /&gt;
Des capteurs et actionneurs supplémentaires pourront être ajoutés si le projet atteint ses objectifs initiaux.  &lt;br /&gt;
&lt;br /&gt;
=== Spécification techniques ===&lt;br /&gt;
&lt;br /&gt;
==== Microcontrôleur ====&lt;br /&gt;
Le projet nécessite un microcontrôleur, qui contiendra le programme, et qui communiquera avec les autres composants via les ''GPIOs''. &lt;br /&gt;
&lt;br /&gt;
Nous avons le choix entre &amp;lt;u&amp;gt;plusieurs modèles de microcontrôleur&amp;lt;/u&amp;gt; : '''ATmega16u4, AT90USB1286, AT90USB1287.'''&lt;br /&gt;
&lt;br /&gt;
Voici un tableau comparatif afin de sélectionner le plus adapté pour notre usage :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Caractéristiques&lt;br /&gt;
|ATmega16U4&lt;br /&gt;
|AT90USB1286&lt;br /&gt;
|AT90USB1287&lt;br /&gt;
|-&lt;br /&gt;
|Architecture&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Mémoire Flash&lt;br /&gt;
|16 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|128 KB&lt;br /&gt;
|-&lt;br /&gt;
|RAM (SRAM)&lt;br /&gt;
|1.25 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|EEPROM&lt;br /&gt;
|512 Bytes&lt;br /&gt;
|4 KB&lt;br /&gt;
|4 KB&lt;br /&gt;
|-&lt;br /&gt;
|Fréquence d'horloge max.&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches GPIO&lt;br /&gt;
|26&lt;br /&gt;
|48&lt;br /&gt;
|48&lt;br /&gt;
|-&lt;br /&gt;
|Interfaces de communication&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|Contrôleur USB intégré&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|Taille des registres&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|Nombre de broches&lt;br /&gt;
|32&lt;br /&gt;
|64&lt;br /&gt;
|64&lt;br /&gt;
|-&lt;br /&gt;
|Différences principales&lt;br /&gt;
|Conçu pour des applications compactes avec&lt;br /&gt;
&lt;br /&gt;
moins de mémoire et d'E/S&lt;br /&gt;
|Plus de mémoire, adapté à des projets complexes nécessitant de nombreuses E/S et de la mémoire&lt;br /&gt;
|Similaire au AT90USB1286 mais avec des fonctionnalités spécifiques&lt;br /&gt;
&lt;br /&gt;
pour certaines configurations USB (e.g., modes host/OTG).&lt;br /&gt;
|-&lt;br /&gt;
|Lien documentation&lt;br /&gt;
|https://www.microchip.com/en-us/product/atmega16u4&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1286&lt;br /&gt;
|https://www.microchip.com/en-us/product/at90usb1287&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Avec ce tableau, on constate que l'ATmega16U4 ne possède pas suffisamment de broches GPIOs. Cependant l'AT90USB1286 et son homologue l'AT90USB1287 dépassent notre cadre d'usage (utilisation mode USB spécifique HOST/OTG, etc... ). &lt;br /&gt;
&lt;br /&gt;
Le compromis est donc d'opter pour un ATmega32u4 afin d'avoir suffisamment de broches et de mémoire.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Caractéristiques&lt;br /&gt;
!ATmega32U4&lt;br /&gt;
|-&lt;br /&gt;
|'''Architecture'''&lt;br /&gt;
|AVR 8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Mémoire Flash'''&lt;br /&gt;
|32 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM (SRAM)'''&lt;br /&gt;
|2.5 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''EEPROM'''&lt;br /&gt;
|1 KB&lt;br /&gt;
|-&lt;br /&gt;
|'''Fréquence d'horloge max.'''&lt;br /&gt;
|16 MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches GPIO'''&lt;br /&gt;
|26&lt;br /&gt;
|-&lt;br /&gt;
|'''Interfaces de communication'''&lt;br /&gt;
|UART, SPI, I²C, USB 2.0&lt;br /&gt;
|-&lt;br /&gt;
|'''Contrôleur USB intégré'''&lt;br /&gt;
|Oui (USB 2.0)&lt;br /&gt;
|-&lt;br /&gt;
|'''Taille des registres'''&lt;br /&gt;
|8 bits&lt;br /&gt;
|-&lt;br /&gt;
|'''Nombre de broches'''&lt;br /&gt;
|32&lt;br /&gt;
|-&lt;br /&gt;
|'''Différences principales'''&lt;br /&gt;
|Conçu pour des applications nécessitant un contrôleur USB intégré, avec une mémoire et un nombre de broches intermédiaires&lt;br /&gt;
|}&lt;br /&gt;
''Datasheet ATmega32u4 :''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA32U4.pdf|199x199px|vignette|Datasheet du microcontroleur : ATMEGA32U4|gauche]][[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
==== Communication ====&lt;br /&gt;
La station utilisera une puce '''NRF24L01''' pour la communication sans fil entre les différents actionneurs et capteurs.&lt;br /&gt;
&lt;br /&gt;
La communication entre le pc et la station se fera quant à elle en USB.&lt;br /&gt;
&lt;br /&gt;
Lien tutoriel utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
''Datasheet NRF24L01 :''&lt;br /&gt;
[[Fichier:Datasheet NRF24L01.pdf|200x200px|vignette|Datasheet module de communication : NRF24L01|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Énergie ====&lt;br /&gt;
La station sera alimentée de manière hybride, selon les scénarios suivants  :&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration avec affichage sur moniteur PC ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour une utilisation prolongée (avec affichage écran LCD dans un second temps).&lt;br /&gt;
&lt;br /&gt;
Les capteurs/actionneurs seront alimentées de manière hybride, selon les scénarios suivants :&lt;br /&gt;
&lt;br /&gt;
'''- Par un port USB''', pour la programmation, les tests et la configuration ;&lt;br /&gt;
&lt;br /&gt;
'''- Par une batterie Lithium''', en mode autonome pour son usage définitif.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Modèles de batterie à notre disposition :&amp;lt;/u&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* Batterie 3.7V 100 mAh, connecteur molex mâle ;&lt;br /&gt;
* Batterie 3.7V 300 mAh, connecteur molex mâle ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons ajouter la possibilité de recharger notre batterie depuis notre carte via le composant MAX1811. La carte se rechargera lorsqu'elle sera branché en USB mais l'USB fournit du 5V, ce qui peut nuire à certains de nos composants...&lt;br /&gt;
&lt;br /&gt;
Pour proteger les composants, nous allons ajouter un régulateur de tension pour garder une tension de 3,3V sur l'ensemble de notre carte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Datasheets du chargeur et du régulateur :''[[Fichier:Datasheet MAX1811.pdf|gauche|194x194px|vignette|Datasheet du chargeur : MAX1811]]&lt;br /&gt;
&lt;br /&gt;
*&lt;br /&gt;
[[Fichier:Datasheet LTC3531.pdf|194x194px|vignette|Datasheet du régulateur : LTC3531|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Affichage ====&lt;br /&gt;
Dans un premier temps, les informations seront remontées via la connexion USB à un programme sur PC (selon les exigences du cahier des charges).&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, un écran LCD sera utilisé pour afficher les données directement sur la station, offrant ainsi une solution autonome, sous réserve du temps disponible pour cette implémentation.&lt;br /&gt;
&lt;br /&gt;
''Datasheet de l'écran graphique utilisé :''&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet NHD‐C12832A1Z‐FSW‐FBW‐3V3.pdf|194x194px|vignette|Datasheet de l'écran : NHD‐C12832A1Z‐FSW‐FBW‐3V3|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;On décide de prgrammer l'écran en C. On code donc notre écran via l'API &amp;quot;glcd.h&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
L'écran sera composé d'un menu permettant de naviguer parmi les différents capteurs enregistrés afin de consulter la valeur renvoyée par cle capteur choisi.&lt;br /&gt;
&lt;br /&gt;
Les boutons intégrés sur la carte ainsi que l'encodeur rotatif permettront à l'utilisateur de naviguer entre les différents capteurs..&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Diverses ====&lt;br /&gt;
La carte comportera également une led afin d'indiquer son état d'alimentation ainsi que deux autres leds permettant de faire des tests de programmation.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:Kicad station .pdf|centré|vignette|Schéma électrique V1 KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Comprendre notre schéma ====&lt;br /&gt;
[[Fichier:ComprendreSchematique.pdf|centré|vignette|Comprendre la schématique]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:Station vue 3D ARRIERE.png|gauche|vignette|Carte station en 3D - Vue arrière|461x461px]]&lt;br /&gt;
[[Fichier:Station vue 3D AVANT.png|centré|vignette|Carte station en 3D - Vue avant|432x432px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Capteurs&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de mouvement - HC-SR501&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Le capteur de mouvement HC-SR501 est un capteur infrarouge passif (PIR), ce qui signifie qu’il ne produit aucun rayonnement mais détecte celui émis naturellement par les objets chauds, notamment le corps humain.&lt;br /&gt;
&lt;br /&gt;
Ces deux cellules pyroélectriques sont disposées de manière à percevoir deux zones distinctes du champ de vision. En l'absence de mouvement, les deux reçoivent une quantité similaire d'infrarouge, et le signal reste équilibré. &lt;br /&gt;
&lt;br /&gt;
Lorsqu'un corps chaud passe devant le capteur, la quantité d’infrarouge captée change entre les deux cellules, créant un déséquilibre. Ce changement est interprété comme un mouvement.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Un dôme en plastique blanc recouvre le capteur : c’est une lentille de Fresnel.&lt;br /&gt;
&lt;br /&gt;
Elle concentre et divise la lumière infrarouge en plusieurs zones, augmentant ainsi la portée et la sensibilité du capteur en &amp;quot;segmentant&amp;quot; son champ de vision. Ainsi, même un petit mouvement crée une variation significative de rayonnement perçu.&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise un capteur de mouvement HC-SR501 (voir datasheet ci-dessous) afin de détecter ou non la présence de quelqu'un dans une pièce. L'intérêt est de pouvoir ensuite allumer la lumière si une personne est présente ou a contrario l'éteindre si la pièce est vide. Ici la lumière sera modélisée par une led présente sur l'arduino UNO en guise de démonstration. &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet mvmt.pdf|alt=datasheet_mvmt|vignette|datasheet_mvmt]] &lt;br /&gt;
[[Fichier:Mvmt.png|alt=mvmt|vignette|capteur_mvmt|479x479px|gauche]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
* Jumper Set&lt;br /&gt;
&lt;br /&gt;
Les deux modes, repeat ou single trigger, permettent de régler le trigger de schmith permettant la détection d'une présence. En single, on effectue un seul trigger afin de détecter spontanément une présence (ex : Cas alarme intrusion).  &lt;br /&gt;
&lt;br /&gt;
Dans l'autre mode on souhaite détecter un mouvement peu importe si celui-ci est déjà détecter. Par exemple, besoin d'un mouvement après un certains nombre de temps pour que la led reste allumée ou bien besoin de réactiver de temps à autre le capteur en bougeant. &lt;br /&gt;
&lt;br /&gt;
* Sensitivty Adjust&lt;br /&gt;
&lt;br /&gt;
On modifie le potentiomètre à l'aide d'un tournevis afin d'ajuster la sensibilité à la présence de notre main, par exemple pour l'amplitude de nos mouvements.  &lt;br /&gt;
&lt;br /&gt;
*Time Delay Adjust&lt;br /&gt;
&lt;br /&gt;
Ici le potentiomètre permet d'ajuster le temps entre deux seuils de détection afin d'éviter la détection après des mouvements parasites, par exemple pour déclencher sans erreur une alarme intrusion.  &lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On connecte le GND à la masse de l'arduino, le power au 5V et la sortie au pin PB0 (digital 8). Voir la vidéo de démonstration pour plus de détails.&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
Voici le code afin d'allumer une led dès qu'une présence est détectée. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    DDRB |= (1 &amp;lt;&amp;lt; PB5); //led D13 en sortie&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (PINB &amp;amp; (1 &amp;lt;&amp;lt; PB0)) { //si mvmt&lt;br /&gt;
            PORTB |= (1 &amp;lt;&amp;lt; PB5);  //led allumée&lt;br /&gt;
        } else { //si absence&lt;br /&gt;
            PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5); //led éteinte&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(500); // Peut être baisser ou augmenter pour regler la sensibilité de détection&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la led s'allume lorsqu'une présence est détectée.&lt;br /&gt;
[[Fichier:Capteur presence.mp4|centré|vignette|500px|Capteur presence]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules de communication radio (NRF24L01) afin d'afficher l'état de la pièce sur l'écran et également transmettre ces données via le port série.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Capteur de température - DS18B20&amp;lt;/big&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
===== Principe physique =====&lt;br /&gt;
Ce capteur fonctionne grâce à un principe physique appelé '''variation de la résistance électrique avec la température'''. À l’intérieur du capteur, il y a un composant semi-conducteur, souvent une '''diode ou une jonction PN''', qui change son comportement électrique selon la température.&lt;br /&gt;
&lt;br /&gt;
Quand la température augmente, la façon dont les électrons se déplacent dans ce matériau change, ce qui modifie la tension ou le courant électrique mesuré.&lt;br /&gt;
&lt;br /&gt;
Un convertisseur analogique-numérique (CAN) est intégré au circuit afin de transferer par la suite la mesure via un protocole 1-Wire.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Spécifications techniques =====&lt;br /&gt;
On utilise aussi le capteur de température DS18B20 (voir datasheet ci-dessous) afin de mesurer la température dans une matière tel que l'eau ou bien la terre (pour une piscine ou une plante par exemple).&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet temp eau.pdf|alt=Datasheet_temp_eau|vignette|Datasheet_temp_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Circuit =====&lt;br /&gt;
On branche les 3 broches de notre sonde de la manière suivante :&lt;br /&gt;
&lt;br /&gt;
* le GND est relié à la masse&lt;br /&gt;
* le power est relié au 3,3V (peut également être relié sur le +5V si besoin)&lt;br /&gt;
* le fil de données est branché sur un pin digital (valeur TOR) et ici sur PD2 (digital 2).&lt;br /&gt;
&lt;br /&gt;
On doit brancher une résistance de 4,7kΩ entre le fil de données et le 3,3V (ou +5V).[[Fichier:Capteur eau.jpg|alt=capteur_eau|vignette|capteur_eau|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Programmation =====&lt;br /&gt;
&lt;br /&gt;
====== Arduino ======&lt;br /&gt;
Pour commencer, on a testé si notre sonde fonctionnait correctement à l'aide d'un code arduino et on a constaté aucun souci ([https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/Arduino Code Arduino]).&lt;br /&gt;
====== C ======&lt;br /&gt;
Dans le cadre de ce projet, nous sommes partis d’un code initialement fonctionnel développé sur Arduino, puis nous l’avons adapté à notre propre environnement en langage C, plus proche du matériel et sans dépendance aux bibliothèques Arduino. Pour cela, nous avons fusionné deux ressources trouvées sur GitHub afin d'obtenir une base de code cohérente, fonctionnelle et surtout structurée de manière à répondre à nos besoins techniques. &lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé les fichiers disponibles dans le répertoire suivant : [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/Capteurs/Temperature%20eau/1er%20code%20%28C%29 Code C]  &lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Protocole OneWire&amp;lt;/u&amp;gt;'''''  &lt;br /&gt;
&lt;br /&gt;
Le '''protocole OneWire''' est un protocole de communication série développé par '''Maxim Integrated'''. Il permet à un microcontrôleur de communiquer avec un ou plusieurs périphériques (comme des capteurs de température, EEPROM, etc.) via '''un seul fil de données''' (en plus du GND). Ce fil est '''bidirectionnel''' et transporte à la fois les données et l’horloge synchronisée par le maître (généralement le microcontrôleur). &lt;br /&gt;
&lt;br /&gt;
Ce protocole est particulièrement utilisé avec des capteurs qui mesurent la température et la transmettent sous forme numérique. Le principal avantage du OneWire est sa '''simplicité matérielle''' : un seul fil suffit pour communiquer avec plusieurs périphériques, chacun ayant une adresse unique codée en ROM.&lt;br /&gt;
&lt;br /&gt;
Ce [https://blog.domadoo.fr/guides/principe-du-protocole-1-wire/ lien/] est un tutoriel qui nous explique comment fonctionne le protocole OneWire et sur [https://kampi.gitbook.io/avr/1-wire-implementation-for-avr ce lien] on retrouve un exemple de code complet mais pour notre usage nous nous sommes limités aux fonctionnalités essentielles ( à savoir écriture et lecture pour un unique appareil connecté).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;onewire.h, onewire.c &amp;lt;/u&amp;gt;: pour remplacer la librairie OneWire.h de l'arduino afin de communiquer avec l'unique fil de données de la sonde en pure C. &lt;br /&gt;
&lt;br /&gt;
* onewireInit : reset le bus de données et renvoie une erreur si le capteur de répond pas.&lt;br /&gt;
* onewireWriteBit : envoie un bit sur le bus de données en respectant le temps d'envoi du protocole Onewire.&lt;br /&gt;
* onewireWrite : transmet un octet en utilisant la fonction précédente.&lt;br /&gt;
* onewireReadbit : lit un bit sur le bus de données.&lt;br /&gt;
* onewireRead : lit un octet sur le bus de données.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Code de notre sonde de température&amp;lt;/u&amp;gt;'''''&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons étudier le fonctionnement de notre sonde de temperature via sa [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/Capteurs/DS18B20.pdf datasheet]. Nous nous sommes aidés de celle ci et de son code équivalent Arduino afin de pouvoir programmer une librairie en C.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;u&amp;gt;ds18b20.h, ds18b20.c&amp;lt;/u&amp;gt; : pour les fonctions principales utiles àla communication entre notre sonde et notre microcontroleur.&lt;br /&gt;
* ds18B20crc8 : CRC signifie cyclic redundacy check est l'octet renvoyé par cette fonction qui permet de savoir si la transmission s'est effectuée sans erreurs.&lt;br /&gt;
* ds18b20match : utile si il y a plusieurs capteurs (pas le cas ici).&lt;br /&gt;
* ds18b20convert : la valeur de la température est stockée sur les deux premiers octets de la mémoire scratchpad. ds18b20convert permet de convertir ces octets en degré celsius.&lt;br /&gt;
* ds18b20rsp : lit le scratchpad (mémoire temporaire) pour récupérer la valeur de la température (sur les deux premiers octets).&lt;br /&gt;
* ds18b20wsp : écrit dans le scratchpad.&lt;br /&gt;
* ds18b20csp  : copie les données du scratchpad dans l'eeprom du capteur.&lt;br /&gt;
* ds18b20read : lit la température.  &lt;br /&gt;
* ds18b20rom : lit l'adresse du capteur rom (pas utile ici car un seul capteur).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;UART&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
Cette partie à été codé uniquement pour le debug car l'usage de l'UART sera négligé plus tard. Effectivement le but final c'est d'avoir un périphérique USB complet donc à coder via la LUFA.  Le port série virtuel USB (CDC) créé par LUFA est reconnu par la plupart des OS modernes sans besoin de drivers spécifiques. On aura alors un projet modulaire !&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;UART.h, UART.c&amp;lt;/u&amp;gt; : pour afficher la température sur la liaison série.  On définit deux fonctions :     &lt;br /&gt;
&lt;br /&gt;
* USART_SendChar pour afficher un caractère sur le minicom.  &lt;br /&gt;
* USART_SendString pour afficher des mots sur le minicom. Rq : utiliser le retour chariot \r pour un affichage correct.  &lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;'''''&amp;lt;u&amp;gt;Main&amp;lt;/u&amp;gt;'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;main.c&amp;lt;/u&amp;gt; : pour le code principal&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;UART.h&amp;quot;&lt;br /&gt;
#include &amp;quot;ds18b20.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS18B20_DDR   DDRD&lt;br /&gt;
#define DS18B20_PORT  PORTD&lt;br /&gt;
#define DS18B20_PIN   PIND&lt;br /&gt;
#define DS18B20_MASK  (1 &amp;lt;&amp;lt; PD2)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t temperature_raw;&lt;br /&gt;
    char buffer[32];&lt;br /&gt;
    uint8_t error;&lt;br /&gt;
&lt;br /&gt;
    USART_init(9600);&lt;br /&gt;
    USART_SendString(&amp;quot;Debut lecture DS18B20...\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Démarrer conversion&lt;br /&gt;
        error = ds18b20convert(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL);&lt;br /&gt;
        _delay_ms(800);  // attendre la fin de conversion&lt;br /&gt;
&lt;br /&gt;
        if (error != DS18B20_ERROR_OK) {&lt;br /&gt;
            USART_SendString(&amp;quot;Erreur conversion\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
&lt;br /&gt;
            // Lire la température&lt;br /&gt;
            error = ds18b20read(&amp;amp;DS18B20_PORT, &amp;amp;DS18B20_DDR, &amp;amp;DS18B20_PIN, DS18B20_MASK, NULL, &amp;amp;temperature_raw);&lt;br /&gt;
            if (error == DS18B20_ERROR_OK) {&lt;br /&gt;
                float temperature_celsius = temperature_raw / 16.0;&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Temp: %.2f C\r\n&amp;quot;, temperature_celsius);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            } else {&lt;br /&gt;
                snprintf(buffer, sizeof(buffer), &amp;quot;Erreur lecture: %d\r\n&amp;quot;, error);&lt;br /&gt;
                USART_SendString(buffer);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Dans notre fonction main :&lt;br /&gt;
* on initialise la liaison série.&lt;br /&gt;
&lt;br /&gt;
* on convertit les octets de la mémoire du capteur en une température en degré celsius. &lt;br /&gt;
* on lit la température afin de l'afficher dans le minicom. Pour cela, il faut au préalable convertir ntre température en flottant en des caractères avec une taille adaptée au buffer à l'aide de la fonction snprintf (string numbered print format).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le DS18B20 mesure la température avec une '''résolution de 0,0625 °C''', ce qui correspond à '''1/16 de degré Celsius'''. Si le capteur renvoyait directement la température en °C sous forme entière, il serait '''impossible d’exprimer des fractions précises''', comme 23,0625 °C.&lt;br /&gt;
&lt;br /&gt;
En utilisant une '''valeur entière (int16_t)''' codant des '''fractions binaires''', on peut :&lt;br /&gt;
&lt;br /&gt;
* Éviter les calculs en virgule flottante dans les systèmes embarqués (coûteux en ressources).&lt;br /&gt;
* Avoir une grande précision avec un codage simple :&amp;lt;blockquote&amp;gt;1 bit de poids faible = 0,0625 °C → résolution sur 12 bits.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''&amp;lt;u&amp;gt;Makefile&amp;lt;/u&amp;gt;''''' :  &amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CC = avr-gcc&lt;br /&gt;
OBJCOPY = avr-objcopy&lt;br /&gt;
SIZE = avr-size&lt;br /&gt;
&lt;br /&gt;
MCU = atmega328p&lt;br /&gt;
FCPU = 16000000UL&lt;br /&gt;
&lt;br /&gt;
FLAGS = -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt -lm&lt;br /&gt;
CFLAGS = -Wall $(FLAGS) -DF_CPU=$(FCPU) -Os&lt;br /&gt;
LDFLAGS = $(FLAGS)&lt;br /&gt;
&lt;br /&gt;
PROGRAMMER = avrdude&lt;br /&gt;
AVRDUDE_MCU = atmega328p&lt;br /&gt;
AVRDUDE_PORT = /dev/ttyACM0  # À adapter&lt;br /&gt;
AVRDUDE_BAUD = 115200&lt;br /&gt;
AVRDUDE_PROGRAMMER = arduino&lt;br /&gt;
&lt;br /&gt;
TARGET = main&lt;br /&gt;
SOURCES = $(wildcard *.c)&lt;br /&gt;
OBJECTS = $(SOURCES:.c=.o)&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o $(TARGET).hex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJECTS)&lt;br /&gt;
	$(CC) -o $@ $^ $(LDFLAGS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	$(OBJCOPY) -j .text -j .data -O ihex $&amp;lt; $@&lt;br /&gt;
	$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; \&lt;br /&gt;
		--change-section-lma .eeprom=0 -O ihex $&amp;lt; eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	$(PROGRAMMER) -v -p $(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P $(AVRDUDE_PORT) \&lt;br /&gt;
		-b $(AVRDUDE_BAUD) -D -U flash:w:$(TARGET).hex:i&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	$(SIZE) --format=avr --mcu=$(MCU) $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Démonstration =====&lt;br /&gt;
Voici le résultat en vidéo ci-dessous. On constate bien que la température affichée sur le minicom augmente lorsque la sonde reste longtemps dans la bouilloire chauffée au préalable.[[Fichier:Capteur temperature.mp4|centré|vignette|video capteur eau]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans un second temps, on fera communiquer ce capteur avec notre carte station domotique par le biais des modules nrf24 afin d'afficher la température de la pièce sur l'écran.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Actionneur&amp;lt;/big&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;big&amp;gt;Lumière&amp;lt;/big&amp;gt; ====&lt;br /&gt;
On peut décider d'allumer une lumière selon certains critères (par exemple lorsque le capteur de présence détecte quelqu'un). Ici allumer une led par exemple. Cette parti n'as pas encore été mise en place, faute de temps. Nous avons préférez avancer su l'écran avant tout.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Ecran&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Pour offrir une interface utilisateur intuitive, nous avons décidé d’afficher sur un écran les données issues des capteurs ainsi que l’état des actionneurs. Par exemple, il doit être possible de visualiser la température d’une pièce ou la détection de présence en temps réel.&lt;br /&gt;
&lt;br /&gt;
Nous avons choisi d’utiliser un écran [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/99%20-%20Datasheets/ECRAN_NHD%E2%80%90C12832A1Z%E2%80%90FSW%E2%80%90FBW%E2%80%903V3.pdf '''NHD‐C12832A1Z‐FSW‐FBW‐3V3'''] '''(stock de M. Boé)'''. Initialement, nous avons tenté d’utiliser la bibliothèque graphique '''u8g2''', réputée pour sa compatibilité avec de nombreux écrans. Cependant, malgré plusieurs essais de programmes issus de cette bibliothèque, l’écran restait vierge sans aucune information affichée.&lt;br /&gt;
&lt;br /&gt;
Face à cette difficulté, nous avons décidé de simplifier notre approche en codant une instruction basique destinée à allumer l’ensemble des pixels de l’écran, en nous appuyant directement sur la datasheet du composant. Mais là encore, aucun résultat visible. &lt;br /&gt;
&lt;br /&gt;
Cette absence de réaction nous a conduit à suspecter un problème matériel lié à la communication entre le microcontrôleur et l’écran. Nous avons alors découvert que notre écran ne fonctionne pas en I2C, contrairement à ce que nous avions initialement supposé, mais bien en '''SPI'''. &lt;br /&gt;
&lt;br /&gt;
Pour corriger cela, nous avons modifié le câblage en coupant les pistes SDA et SCL (liées au bus I2C) puis connecté le pin SCL de l’écran au SCK du microcontrôleur, et le pin SI de l’écran au MOSI du microcontrôleur.  &lt;br /&gt;
[[Fichier:ModifPiste.jpg|centré|vignette|Modification de nos piste pour échnager avec l'écran|536x536px]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Malgré ces ajustements, l’écran restait toujours noir. Nous avons donc vérifié à l’oscilloscope la présence des signaux SPI transmis à l’écran, ce qui nous a confirmé que les données étaient bien envoyées. [[Fichier:Signaux SCL et MOSI.png|alt=signaux SCL et MOSI|centré|vignette|signaux SCL et MOSI|585x585px]]&lt;br /&gt;
&lt;br /&gt;
Nous avons ensuite affiné notre code d’initialisation de l’écran, notamment en veillant à éteindre l’affichage pendant la configuration, puis à le rallumer une fois les paramètres correctement envoyés. Voici un extrait de la fonction d’initialisation :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_init() {&lt;br /&gt;
    lcd_reset();&lt;br /&gt;
    lcd_command(0xA0); // ADC select&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0xC8); // COM direction scan&lt;br /&gt;
    lcd_command(0xA2); // LCD bias set&lt;br /&gt;
    lcd_command(0x2F); // Power Control set&lt;br /&gt;
    lcd_command(0x21); // Resistor Ratio Set&lt;br /&gt;
    lcd_command(0x81); // Electronic Volume Command (set contrast) Double Btye: 1 of 2&lt;br /&gt;
    lcd_command(0x20); // Electronic Volume value (contrast value) Double Byte: 2 of 2&lt;br /&gt;
    lcd_command(0xA6); // Display normal (non inverser) &lt;br /&gt;
    lcd_command(0xAF); // Display ON &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avions également oublier de désactiver le JTAG afin de pouvoir utiliser correctement le PORTF.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
MCUCR |= (1 &amp;lt;&amp;lt; JTD);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également du allonger notre temps de delay pour notre fonction lcd_reset :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_reset() {&lt;br /&gt;
    PORTF &amp;amp;= ~(1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
    PORTF |= (1 &amp;lt;&amp;lt; RESET);&lt;br /&gt;
    _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Grâce à ces modifications, nous avons réussi à allumer un carré de pixels sur l’écran. Nous nous sommes renseigné pour afficher des objet plus complexe tel qu'un logo. Nous avons récuperer le logo et le code pour afficher une image sur [https://support.newhavendisplay.com/hc/en-us/articles/4415264814231-NHD-C12832A1Z-with-Arduino le site du constructeur].&lt;br /&gt;
[[Fichier:Logo1.mp4|centré|vignette|Visualisation code écran avec logo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Affichage d'une image&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour créer notre propre logo sous forme de tableau il faut s'aider de cette outil : https://javl.github.io/image2cpp/&lt;br /&gt;
&lt;br /&gt;
Voici la configuration à adopter pour exporter correctement sur notre écran :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Image Settings''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Canvas size(s) = 128x32 (résolution de notre écran)&lt;br /&gt;
&lt;br /&gt;
* Background color : Black (afin de n’afficher aucune couleur car écran monochrome&lt;br /&gt;
* Scaling : Scale to fit (pour redimensionner l’image selon nos nouvelles proportions)&lt;br /&gt;
&lt;br /&gt;
Les autres paramètres de la section Image Settings restent inchangés (à l’exception de Center image, qui reste une option de personnalisation).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''Output''&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Code output format : plain bytes (pour obtenir uniquement les octets qui nous intéressent, on précisera nous-mêmes le type du tableau)&lt;br /&gt;
* Draw mode : Vertical - 1 bit per pixel (l’écran utilise un système de pages pour écrire les pixels)&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Logo2.mp4|centré|vignette|Visualisation code écran avec logo personnalisé]]&lt;br /&gt;
&lt;br /&gt;
Et voici la fonction permettant d'afficher une image :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DispPic(unsigned char* lcd_string)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char page = 0xB0;&lt;br /&gt;
    lcd_command(0xAE); // Display OFF&lt;br /&gt;
    lcd_command(0x40); // Display start address + 0x40 (base RAM écran)&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; 4; i++) { // Parcourt les 4 pages&lt;br /&gt;
        lcd_command(page); // Envoie l'adresse de la page actuelle (0xB0 + i)&lt;br /&gt;
        lcd_command(0x10); // column address upper 4 bits + 0x10&lt;br /&gt;
        lcd_command(0x00); // column address lower 4 bits + 0x00&lt;br /&gt;
        for (unsigned int j = 0; j &amp;lt; 128; j++){ // Parcourt toutes les colonnes (128 colonnes)&lt;br /&gt;
            lcd_data(*lcd_string); // Envoie un octet de données (une colonne verticale de 8 pixels)&lt;br /&gt;
            lcd_string++; // Passe à l'octet suivant dans lcd_string&lt;br /&gt;
        }&lt;br /&gt;
        page++; // after 128 columns, go to next page&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(0xAF);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''&amp;lt;u&amp;gt;Affichage d'un texte&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour afficher du texte, nous avons créé un tableau appelé '''font bitmap''' en terme courant. Chaque caractère est représenté par une matrice de pixels 5x7, où chaque bit indique si un pixel doit être allumé ou non. Ce format compact nous permet d’afficher les lettres de manière claire et efficace, tout en s’adaptant à la taille souhaitée. Ici nous n'avons qu'une seule taille (5x7) pour répondre à l'objectif embarqué que nous nous sommes fixés (moindre code et moindre consommation).&lt;br /&gt;
&lt;br /&gt;
Voici un tableau donnant une idée de ce à quoi cela peut ressembler :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t font5x7[][5] = {&lt;br /&gt;
    // ASCII 32 à 127&lt;br /&gt;
    {0x00,0x00,0x00,0x00,0x00}, // (space)&lt;br /&gt;
    {0x00,0x00,0x5F,0x00,0x00}, // !&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x00,0x60,0x60,0x00,0x00}, // .&lt;br /&gt;
    {0x20,0x10,0x08,0x04,0x02}, // /&lt;br /&gt;
    {0x3E,0x51,0x49,0x45,0x3E}, // 0&lt;br /&gt;
    {0x00,0x42,0x7F,0x40,0x00}, // 1&lt;br /&gt;
    {0x42,0x61,0x51,0x49,0x46}, // 2&lt;br /&gt;
    [...]&lt;br /&gt;
    {0x7E,0x11,0x11,0x11,0x7E}, // A&lt;br /&gt;
    [...]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Le tableau &amp;lt;code&amp;gt;font5x7&amp;lt;/code&amp;gt; est organisé en '''5 colonnes par caractère''' parce que chaque caractère est représenté sur une matrice de pixels 5 colonnes (largeur) par 7 lignes (hauteur). Chaque caractère de la police bitmap fait '''5 pixels de large''' et '''7 pixels de haut'''. Ce genre de tableau peut être généré ou repris d'une librairie graphique tel que [https://github.com/andygock/glcd/blob/master/fonts/font5x7.h glcd].&lt;br /&gt;
&lt;br /&gt;
Une fois que nous avons le tableau il suffit d'envoyer les données de celle ci ainsi : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void lcd_char(char c) {&lt;br /&gt;
    if (c == 248 || c == 176) { // '°' = ASCII étendu 248 ou parfois 176&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(deg_symbol[i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
    else if (c &amp;gt;= 32 &amp;amp;&amp;amp; c &amp;lt;= 126) {&lt;br /&gt;
        for (uint8_t i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;
            lcd_data(font5x7[c - 32][i]);&lt;br /&gt;
        }&lt;br /&gt;
        lcd_data(0x00); // espace&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_goto(uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_command(0xB0 | page);            // Page = 0 à 3&lt;br /&gt;
    lcd_command(0x10 | (column &amp;gt;&amp;gt; 4));   // MSB&lt;br /&gt;
    lcd_command(0x00 | (column &amp;amp; 0x0F)); // LSB&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_print(char *str, uint8_t page, uint8_t column) {&lt;br /&gt;
    lcd_goto(page, column);&lt;br /&gt;
    while (*str) lcd_char(*str++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;L'envoie d'un espace après chaque envoie de caractère permet de creer un espacement '''entre les caractères affichés''', pour que les lettres '''ne soient pas collées''' les unes aux autres. '''L’écran est divisé en pages''' (souvent 8 pixels de hauteur par page, ici on a 4 pages si la hauteur est 32 pixel. Par exemple, &amp;lt;code&amp;gt;page 0&amp;lt;/code&amp;gt; correspond à la ligne verticale 0–7, &amp;lt;code&amp;gt;page 1&amp;lt;/code&amp;gt; à 8–15, etc...&lt;br /&gt;
&lt;br /&gt;
'''La position horizontale se fait par colonnes''' (chaque colonne correspondant à une tranche verticale de pixels, souvent 1 octet = 8 pixels en hauteur).&lt;br /&gt;
&lt;br /&gt;
Le contrôleur de l’écran LCD gère en interne une '''adresse mémoire d’écriture''' composée d’une page (ligne) et d’une colonne (position horizontale. Le contrôleur '''incrémente automatiquement la colonne''' pour la prochaine donnée.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_goto()&amp;lt;/code&amp;gt; sert à positionner le '''curseur initial'''.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lcd_data()&amp;lt;/code&amp;gt; '''incrémente''' la colonne '''automatiquement''' après chaque octet envoyé.&lt;br /&gt;
[[Fichier:TexteLCD.jpg|centré|vignette|Photographie de l'écran avec du texte|519x519px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ce projet nous a demandé beaucoup de temps et de persévérance, mais il nous a permis de comprendre en profondeur le fonctionnement d’un écran graphique. Nous sommes désormais capables de coder notre propre bibliothèque pour piloter l’écran, ce qui représentait auparavant un défi majeur.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;big&amp;gt;Communication&amp;lt;/big&amp;gt; ===&lt;br /&gt;
Ici, nous traiterons du code implémenté afin de communiquer entre les différents capteurs/actionneurs et notre carte principale. Nous avons utilisé un NRF24L01 pour communiquer entre notre station domotique et nos capteurs/actionneurs. Voici le lien du tutoriel pour l’utilisation de puces à distance : [https://passionelectronique.fr/tutorial-nrf24l01 NRF24L01]&lt;br /&gt;
&lt;br /&gt;
Ce tutoriel nous a aidés à tester ce module via [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/Arduino Arduino]. Nous avons d'abord essayé de coder un simple &amp;quot;hello world&amp;quot; à envoyer et recevoir afin de comprendre le fonctionnement du NRF. Ce code permettait également de tester les cartes au cas où celles-ci dysfonctionnaient, comme c'était le cas avec un Arduino fourni par le professeur. Nous avons ensuite utilisé un Arduino qu’un d’entre nous possédait afin de coder.&lt;br /&gt;
[[Fichier:ArduinoDemo.mp4|centré|vignette]]&lt;br /&gt;
Par la suite, nous avons codé en C une bibliothèque pour le NRF permettant de communiquer avec celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.c .c] n'était pas fourni par [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/99%20-%20nrf24L01_plus-master%20%28lib%20utilise%29 cette bibliothèque] et le fichier [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/02%20-%20Station%20Domotique/02%20-%20Programmation/NRF24L01/C/01%20-%201er%20code%20sans%20opti/rx/nRF24L01.h .h] a été complété par les fonctions ajoutées ci-dessous :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;nRF24L01.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Définition des ports et broches&lt;br /&gt;
#define MISO_DDR DDRB&lt;br /&gt;
#define MISO_PORT PORTB&lt;br /&gt;
#define MISO_PIN PINB&lt;br /&gt;
#define MISO_BIT PB4&lt;br /&gt;
&lt;br /&gt;
#define MOSI_DDR DDRB&lt;br /&gt;
#define MOSI_PORT PORTB&lt;br /&gt;
#define MOSI_BIT PB3&lt;br /&gt;
&lt;br /&gt;
#define SCK_DDR DDRB&lt;br /&gt;
#define SCK_PORT PORTB&lt;br /&gt;
#define SCK_BIT PB5&lt;br /&gt;
&lt;br /&gt;
#define CSN_DDR DDRB&lt;br /&gt;
#define CSN_PORT PORTB&lt;br /&gt;
#define CSN_BIT PB2&lt;br /&gt;
&lt;br /&gt;
#define CE_DDR DDRB&lt;br /&gt;
#define CE_PORT PORTB&lt;br /&gt;
#define CE_BIT PB1&lt;br /&gt;
&lt;br /&gt;
// Initialisation des broches NRF24L01&lt;br /&gt;
void nrf24_setupPins() {&lt;br /&gt;
    // MISO en entrée&lt;br /&gt;
    MISO_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; MISO_BIT);&lt;br /&gt;
&lt;br /&gt;
    // MOSI, SCK, CSN, CE en sortie&lt;br /&gt;
    MOSI_DDR |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_DDR |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CSN_DDR |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    CE_DDR |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
&lt;br /&gt;
    // Valeurs initiales : tout à 0 sauf CSN à 1 (désactivé)&lt;br /&gt;
    MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);  // CSN HIGH (non sélectionné)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CE&lt;br /&gt;
void nrf24_ce_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CE_PORT |= (1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CE_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CE_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche CSN&lt;br /&gt;
void nrf24_csn_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        CSN_PORT |= (1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        CSN_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; CSN_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche SCK&lt;br /&gt;
void nrf24_sck_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        SCK_PORT |= (1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        SCK_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SCK_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Contrôle de la broche MOSI&lt;br /&gt;
void nrf24_mosi_digitalWrite(uint8_t state) {&lt;br /&gt;
    if (state)&lt;br /&gt;
        MOSI_PORT |= (1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
    else&lt;br /&gt;
        MOSI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; MOSI_BIT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lecture de la broche MISO&lt;br /&gt;
uint8_t nrf24_miso_digitalRead() {&lt;br /&gt;
    return (MISO_PIN &amp;amp; (1 &amp;lt;&amp;lt; MISO_BIT)) ? 1 : 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Nous avons déclaré en &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt; les DDR, PORT, PIN et BIT de chaque broche afin d'avoir un code plus lisible.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Initialisation des broches du NRF&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Pour commencer, nous avons &amp;lt;code&amp;gt;nrf24_init()&amp;lt;/code&amp;gt; qui sert à configurer les broches utilisées par le module (MISO, MOSI, SCK, CSN, CE). Ça permet de préparer la communication SPI logicielle. Ensuite, on mets CE à LOW et CSN à HIGH, ce qui correspond à l’état « repos » du module.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Configuration du module NRF&amp;lt;/u&amp;gt;'''La fonction &amp;lt;code&amp;gt;nrf24_config&amp;lt;/code&amp;gt; sert à configurer le module selon le canal radio (fréquence) et la taille des paquets (payload).&lt;br /&gt;
&lt;br /&gt;
* Elle la longueur de la charge utile (payload) dans une variable globale.&lt;br /&gt;
* Elle configure les différents registres : le canal RF, la taille du payload pour les pipes (canaux de réception), la puissance d’émission, le CRC, l’auto-acknowledgment (reconnaissance automatique de réception), les adresses RX activées, la retransmission automatique.&lt;br /&gt;
* Puis elle mets le module en mode écoute (réception).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion des adresses TX et RX&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_tx_address()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rx_address()&amp;lt;/code&amp;gt; servent à définir les adresses pour l’envoi et la réception. Ces adresses doivent être cohérentes pour que la communication fonctionne.&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Envoi et réception des données&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_send()&amp;lt;/code&amp;gt; permet d’envoyer un paquet. Elle prépare le module, vide le FIFO d’émission, puis écris le payload et démarre la transmission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_getData()&amp;lt;/code&amp;gt; lit les données reçues depuis le module en SPI, puis remet à zéro le flag d’interruption réception.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Vérification de l’état&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_dataReady()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_rxFifoEmpty()&amp;lt;/code&amp;gt; permettent de savoir si des données sont prêtes à être lues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_isSending()&amp;lt;/code&amp;gt; indique si le module est encore en train d’envoyer un message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_lastMessageStatus()&amp;lt;/code&amp;gt;  dit si la dernière transmission a réussi ou a échoué (nombre max de retransmissions atteint).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_retransmissionCount()&amp;lt;/code&amp;gt; donne le nombre de tentatives de retransmission.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Gestion de la puissance&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nrf24_powerUpRx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerUpTx()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nrf24_powerDown()&amp;lt;/code&amp;gt; sont des fonctions pour mettre le module en mode réception, émission, ou veille.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''&amp;lt;u&amp;gt;Communication SPI en logiciel (bit-banging)&amp;lt;/u&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
Comme on n’utilise pas le matériel SPI natif, &amp;lt;code&amp;gt;spi_transfer()&amp;lt;/code&amp;gt; envoie et reçoit un octet via manipulation manuelle des broches MOSI, MISO et SCK.&lt;br /&gt;
&lt;br /&gt;
Les fonctions &amp;lt;code&amp;gt;nrf24_transferSync()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_transmitSync()&amp;lt;/code&amp;gt; permettent d’envoyer ou recevoir plusieurs octets à la suite.&lt;br /&gt;
Cela à été fait de cette façon afin d'avoir le code le plus portatif possible, ce qui explique le contenu de cette librairie.&lt;br /&gt;
Nous n'avons pas implémenté le SPI matériel puisque le code fonctionne très bien sans.&lt;br /&gt;
'''&amp;lt;u&amp;gt;Lecture/écriture des registres&amp;lt;/u&amp;gt;'''&lt;br /&gt;
Pour lire ou écrire un registre du nRF24, &amp;lt;code&amp;gt;nrf24_readRegister()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;nrf24_writeRegister()&amp;lt;/code&amp;gt;, envoient la commande adéquate en SPI puis récupèrent ou envoient les données.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons commencé par étudier la documentation officielle du module '''nRF24L01''', en particulier le '''datasheet''', afin de comprendre le protocole SPI, les registres internes et les commandes à utiliser. Ensuite, nous avons consulté différents exemples sur '''GitHub''' ainsi que des tutoriels pour Arduino. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Je ne vais pas remontrer la vidéo de démonstration c'est redondant ici. Il y en aura une pour la prochaine étape qui est ...&lt;br /&gt;
&lt;br /&gt;
=== IHM PC ===&lt;br /&gt;
Dans cette section, nous expliquons comment la '''carte domotique''' communique avec un '''PC via USB''', en utilisant la bibliothèque '''LUFA'''. Le code utilisé est un exemple issu du  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/Demos/Device/LowLevel/VirtualSerial repertoire suivant], déjà employé dans un projet annexe de '''programmateur AVR'''. Ce code permet l’envoi simple de données via une '''liaison série USB''' (USB CDC).  &lt;br /&gt;
&lt;br /&gt;
Pour récupérer ces données côté PC et les afficher, nous avons choisi d’utiliser d’abord '''Node-RED''' pour la gestion des flux de données, puis '''Grafana''' (outil recommandé par M. Boé) pour l’affichage graphique qui sera implémenté plus tard. Ce choix nous permet de gagner du temps sur la partie interface web, qui peut être longue à développer manuellement.  &lt;br /&gt;
&lt;br /&gt;
Grafana ne sera peut être pas déployer car la liaison entre Node-RED et Grafana doit se faire depuis une base de donnée (surement InfluxDB) et cela prend du temps à être mis en place.  &lt;br /&gt;
&lt;br /&gt;
Voici le repertoire de cette partie : https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/03%20-%20%20ui_web_interface  &lt;br /&gt;
&lt;br /&gt;
=== LUFA ===&lt;br /&gt;
&lt;br /&gt;
=== '''Docker''' ===&lt;br /&gt;
Ces outils sont déployés à l’aide de '''Docker''', une technologie (open source et créer par des ingénieurs français !)  de virtualisation légère qui permet d’exécuter des applications dans des conteneurs isolés.   &lt;br /&gt;
&lt;br /&gt;
Docker permet de '''packager toute une application et ses dépendances''' dans un conteneur. Plus besoin de réinstaller des bibliothèques, configurer l’environnement, ou se soucier du “ça marche sur mon PC mais pas ailleurs”.  Cela marchera donc aussi de votre côté ;).    &lt;br /&gt;
&lt;br /&gt;
Nous utilisons également '''Docker Compose''' pour automatiser le lancement coordonné de plusieurs services (ici Node-RED et Grafana) à partir d’un simple fichier de configuration.  &lt;br /&gt;
&lt;br /&gt;
Voici le fichier de configuration de '''Docker Compose''' :  &amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
version: '3.8'&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
&lt;br /&gt;
  nodered:&lt;br /&gt;
    image: nodered/node-red:latest&lt;br /&gt;
    container_name: nodered&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;1880:1880&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./nodered_data:/data&lt;br /&gt;
    devices:&lt;br /&gt;
      - /dev/ttyACM0&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
  grafana:&lt;br /&gt;
    image: grafana/grafana-oss&lt;br /&gt;
    container_name: grafana&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./grafana_data:/var/lib/grafana&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;On configure le port où le service sera lancé et nous laissons les accès devices à nodered pour quelle puisse écouter le port série où notre LUFA écrit. La mention &amp;lt;code&amp;gt;- ${SERIAL_DEV:-/dev/null}:/dev/ttyACM0&amp;lt;/code&amp;gt;  peut être ajouté afin de pouvoir lancer le dock sans problème de compilation car Docker ne démarre pas le conteneur si un périphérique mentionné dans &amp;lt;code&amp;gt;devices:&amp;lt;/code&amp;gt; est '''introuvable.''' &lt;br /&gt;
&lt;br /&gt;
La mention&amp;lt;code&amp;gt;${SERIAL_DEV:-/dev/null}:&amp;lt;/code&amp;gt; créera un lien vers &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; (un périphérique vide), évitant ainsi l’erreur. Je connaissais Docker parceque j'utilisais une application qui se lançait sur celle-ci et je m'y suis intéressé. J'estimais intéressant de l'intégrer au projet !&lt;br /&gt;
&lt;br /&gt;
Voici un fichier mémo qui nous a aider à nous rappeler des commandes importantes sur Docker :&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Liste des docker actif sur le pc :&lt;br /&gt;
docker ps -a&lt;br /&gt;
&lt;br /&gt;
Dans ce repertoire, lancer les dockers via :&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour relancer copie tout :&lt;br /&gt;
docker compose down&lt;br /&gt;
docker compose up -d&lt;br /&gt;
&lt;br /&gt;
Pour stopper un docker : &lt;br /&gt;
docker stop &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour supprimer un docker :&lt;br /&gt;
docker rm -f &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si un pb, voir log :&lt;br /&gt;
docker logs &amp;lt;nomDock&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== '''Node-RED''' ===&lt;br /&gt;
Voici à quoi ressemble notre configuration :&lt;br /&gt;
[[Fichier:NodeRedConfig.png|centré|vignette|778x778px|Configuration Node-RED]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 1 : Entrée série (Serial In)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc permet d'écouter un port série. Il lit les données envoyées par la carte domotique sur le port &amp;lt;code&amp;gt;/dev/ttyACM0&amp;lt;/code&amp;gt;. Les données sont transmises sous forme de texte brut, souvent une chaîne JSON. Voici la configuration :&lt;br /&gt;
[[Fichier:ConnfigLEcturePortSerie.png|centré|vignette|596x596px|Configuration bloc 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 2 : Conversion JSON'''&lt;br /&gt;
&lt;br /&gt;
Les données reçues sont des chaînes de caractères au format JSON. Ce bloc les convertit en objet JavaScript pour que Node-RED puisse les manipuler plus facilement dans les blocs suivants.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 3 : Traitement de la donnée (Function)'''&lt;br /&gt;
&lt;br /&gt;
Ce bloc exécute une petite fonction JavaScript pour isoler la température contenue dans l'objet JSON.&lt;br /&gt;
&lt;br /&gt;
Voici le contenu de la fonction :&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
msg.payload = msg.payload.temperature;&lt;br /&gt;
return msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;msg.payload&amp;lt;/code&amp;gt; contient l'objet JSON complet, par exemple :  &amp;lt;code&amp;gt;{ &amp;quot;temperature&amp;quot;: 22.5, &amp;quot;...&amp;quot;: 45, &amp;quot;...&amp;quot;: 20, &amp;quot;...&amp;quot;: &amp;quot;oui&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* La ligne &amp;lt;code&amp;gt;msg.payload = msg.payload.temperature;&amp;lt;/code&amp;gt; remplace le contenu du message pour ne garder que la valeur de la température (ici &amp;lt;code&amp;gt;22.5&amp;lt;/code&amp;gt;).  Cela permet d’envoyer uniquement la température au bloc suivant, comme une simple valeur numérique.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;return msg;&amp;lt;/code&amp;gt; renvoie ce nouveau message modifié pour qu’il continue à circuler dans le flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Bloc 4 : Affichage ou base de données'''&lt;br /&gt;
&lt;br /&gt;
Le message contenant uniquement la température peut ensuite être affiché dans un tableau de bord, envoyé à Grafana, ou enregistré dans une base de données.&lt;br /&gt;
&lt;br /&gt;
Pour le moment on utilisera pas de base donnée mais une interface beaucoup plus minimaliste sur Node-RED. &lt;br /&gt;
&lt;br /&gt;
Voici la configuration du bloc :&lt;br /&gt;
[[Fichier:CaptureConfigDataDisplay.png|centré|vignette|573x573px|Configuration bloc 3 permettant l'affichage de la température]]&lt;br /&gt;
Pour avoir accès au noeud du bloc 1 et 3 il a fallu ajouter des &amp;quot;nodes&amp;quot; à notre palette. Pour y acceder c'est ici :&lt;br /&gt;
[[Fichier:PaletteNodeAccess.png|centré|vignette|Acces à Palette ]]&lt;br /&gt;
Et voici les noeuds installés :&lt;br /&gt;
[[Fichier:PaletteNoodes.png|centré|vignette|408x408px|Palette de nodes du projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant regardons notre&lt;br /&gt;
[[Fichier:AccesVueRedNode.png|gauche|vignette|249x249px|Accès au résultat de notre Node-RED]]&lt;br /&gt;
[[Fichier:Vue sur notre projet Node RED.png|centré|vignette|301x301px|Accès vue sur notre projet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On clique sur la petite icône est nous sommes renvoyé sur cette url : http://localhost:1880/ui/&lt;br /&gt;
&lt;br /&gt;
Voici notre interface finale :&lt;br /&gt;
[[Fichier:Dashboard.png|centré|vignette|1456x1456px|Dashboard site web]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Et une démonstration du projet dans son intégralité en vidéo ci dessous :&lt;br /&gt;
&lt;br /&gt;
=== Programmateur AVR (Projet annexe) ===&lt;br /&gt;
&lt;br /&gt;
=== Objectif ===&lt;br /&gt;
Réaliser un programmateur AVR afin d'envoyer notre code C sur un microcontrôleur. Ce projet est une introduction au logiciel et à la programmation sur microcontroleur AVR.&lt;br /&gt;
&lt;br /&gt;
Nous nous sommes aider du cours afin de réaliser notre projet : https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme063.html&lt;br /&gt;
=== Schématique ===&lt;br /&gt;
&lt;br /&gt;
==== Notre schéma électrique ====&lt;br /&gt;
[[Fichier:SE3_2024_G2_prog_schema.pdf|center|thumb|Schéma électrique KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Conception de notre schéma électrique ====&lt;br /&gt;
[[Fichier:Comprendre le schéma.pdf|vignette|centré|Document expliquant point par point le schéma réalisé sur KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
'''Documents relatifs à la conception du kicad de la carte'''&lt;br /&gt;
[[Fichier:Datasheet ATMEGA8U2.pdf|gauche|194x194px|vignette|Datasheet ATMEGA8U2]]&lt;br /&gt;
[[Fichier:AVR042.pdf|199x199px|vignette|AVR Hardware Design Considerations|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue 3D ===&lt;br /&gt;
[[Fichier:3D Kicad Programmmateur AVR.png|centré|sans_cadre|521x521px|Programmmateur AVR - 3D KICAD]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fichier kicad ===&lt;br /&gt;
[[Fichier:2024-PSE-G2-Prog VFinale sans erreur.zip|alt=2024-PSE-G2-Prog VFinale|centré]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Brasure ===&lt;br /&gt;
[[Fichier:Brasure avant carte prog avr.jpg|gauche|vignette|Carte programmateur AVR - Vue avant]]&lt;br /&gt;
[[Fichier:Brasure arriere carte prog avr.jpg|centré|vignette|Carte programmateur AVR - Vue arrière]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Programmation ===&lt;br /&gt;
&lt;br /&gt;
==== Test leds et boutons ====&lt;br /&gt;
Afin de vérifier que notre carte fonctionne correctement après sa brasure, on code un programme permettant d'allumer une LED lorsqu'un bouton poussoir est pressé. Chaque bouton est associé à une LED. &lt;br /&gt;
&lt;br /&gt;
===== Modification de l'horloge =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void setupClock(void)&lt;br /&gt;
{&lt;br /&gt;
	CLKSEL0 = 0b00010101;   // sélection de l'horloge externe&lt;br /&gt;
	CLKSEL1 = 0b00001111;   // minimum de 8Mhz&lt;br /&gt;
	CLKPR = 0b10000000;     // modification du diviseur d'horloge (CLKPCE=1)&lt;br /&gt;
	CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation  des pins ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupPin(volatile uint8_t* PORTx, volatile uint8_t* DDRx, uint8_t pin, pinmode mode) {&lt;br /&gt;
    switch (mode)&lt;br /&gt;
    {&lt;br /&gt;
    case INPUT: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case INPUT_PULL_UP: // Forcage pin à 0&lt;br /&gt;
        *DDRx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    case OUTPUT: // Forcage pin à 1&lt;br /&gt;
        *DDRx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction initialisation de l'ensemble des boutons et LEDs ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction de lecture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int readPin(volatile uint8_t* PINx, uint8_t pin) {&lt;br /&gt;
    return (*PINx &amp;amp; (1 &amp;lt;&amp;lt; pin));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction d'écriture de pin ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void writePin(volatile uint8_t* PORTx, uint8_t pin, write level) {&lt;br /&gt;
    if (level == LOW)&lt;br /&gt;
        *PORTx |= (1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
    else&lt;br /&gt;
        *PORTx &amp;amp;= ~(1 &amp;lt;&amp;lt; pin);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Fonction pour initialiser les composantes de la cartes ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------ Boutons ------------------ //&lt;br /&gt;
#define BTNsUp_Left_PORT &amp;amp;PORTC&lt;br /&gt;
#define BTNsUp_Left_DDR &amp;amp;DDRC&lt;br /&gt;
#define BTNsUp_Left_PIN &amp;amp;PINC&lt;br /&gt;
&lt;br /&gt;
#define BTNsDown_Right_PORT &amp;amp;PORTB&lt;br /&gt;
#define BTNsDown_Right_DDR &amp;amp;DDRB&lt;br /&gt;
#define BTNsDown_Right_PIN &amp;amp;PINB&lt;br /&gt;
&lt;br /&gt;
#define BTN_Up_PIN PC4 &lt;br /&gt;
#define BTN_Down_PIN PB5 &lt;br /&gt;
#define BTN_Left_PIN PC6 &lt;br /&gt;
#define BTN_Right_PIN PB6 &lt;br /&gt;
&lt;br /&gt;
// ------------------ LEDs ------------------ //&lt;br /&gt;
#define LEDs_PORT &amp;amp;PORTD&lt;br /&gt;
#define LEDs_DDR &amp;amp;DDRD&lt;br /&gt;
#define LEDs_PIN &amp;amp;PIND&lt;br /&gt;
&lt;br /&gt;
#define LED1_PIN PD0&lt;br /&gt;
#define LED2_PIN PD1&lt;br /&gt;
#define LED3_PIN PD2&lt;br /&gt;
#define LED4_PIN PD3&lt;br /&gt;
&lt;br /&gt;
// ------------------ Enum ------------------ //&lt;br /&gt;
typedef enum {&lt;br /&gt;
    INPUT,&lt;br /&gt;
    INPUT_PULL_UP,&lt;br /&gt;
    OUTPUT,&lt;br /&gt;
} pinmode;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setupHardware() {&lt;br /&gt;
    setupClock();&lt;br /&gt;
&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED1_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED2_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED3_PIN, OUTPUT);&lt;br /&gt;
    setupPin(LEDs_PORT, LEDs_DDR, LED4_PIN, OUTPUT);&lt;br /&gt;
&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Up_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Down_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsUp_Left_PORT, BTNsUp_Left_DDR, BTN_Left_PIN, INPUT_PULL_UP);&lt;br /&gt;
    setupPin(BTNsDown_Right_PORT, BTNsDown_Right_DDR, BTN_Right_PIN, INPUT_PULL_UP);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LUFA ====&lt;br /&gt;
Afin de pouvoir faire de notre carte un périphérique USB, nous allons utiliser la LUFA (Lightweight USB Framefork for AVRs).&lt;br /&gt;
&lt;br /&gt;
* Tout d'abord, nous avons codé un programme permettant de voir si la lufa détecte bien les boutons-poussoirs de notre carte comme des points d'accès d'entrées en affichant sur le minicom quel bouton-poussoir était pressé par l'utilisateur. Notre code se trouve dans le répertoire se ci-dessous :  [https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/02%20-%20Programmation/lufa-LUFA-210130-NSI/se/VirtualSerial https://gitea.plil.fr/ahouduss/se3_2024_B2/src/branch/main/01%20-%20Programmateur%20AVR/programmation/lufa-LUFA-210130-NSI/se/VirtualSerial]&lt;br /&gt;
Pour écrire notre code, nous avons réutilisé l'exemple VirtualSerial récupéré au repertoire suivant : &amp;lt;code&amp;gt;LUFA/Demos/Device/LowLevel/VirtualSerial&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le code exemple du VirtualSerial permet d'écrire des données à notre pc (visible via minicom).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de mettre le programme sur notre carte, on vérifie au préalable que notre carte est bien reconnue en tant que périphérique USB à l'aide de la commande lsusb.&lt;br /&gt;
[[Fichier:Terminal - cmd lsusb avec carte.png|droite|sans_cadre|712x712px]]&lt;br /&gt;
[[Fichier:Terminal - Cmd lsusb.png|gauche|sans_cadre|756x756px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour plus de détail sur notre périphérique usb branché on doit faire la commande suivante :&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
cedricagathe@computer:~$ lsusb -s 003:008 -v&lt;br /&gt;
&lt;br /&gt;
Bus 003 Device 011: ID 03eb:2044 Atmel Corp. LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
Device Descriptor:&lt;br /&gt;
  bLength                18&lt;br /&gt;
  bDescriptorType         1&lt;br /&gt;
[...]&lt;br /&gt;
  bDeviceClass            2 Communications&lt;br /&gt;
  bDeviceSubClass         0 &lt;br /&gt;
  bDeviceProtocol         0 &lt;br /&gt;
[...]&lt;br /&gt;
  idVendor           0x03eb Atmel Corp.&lt;br /&gt;
  idProduct          0x2044 LUFA CDC Demo Application&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        0&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           1&lt;br /&gt;
      bInterfaceClass         2 Communications&lt;br /&gt;
      bInterfaceSubClass      2 Abstract (modem)&lt;br /&gt;
      bInterfaceProtocol      1 AT-commands (v.25ter)&lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x82  EP 2 IN&lt;br /&gt;
[...]&lt;br /&gt;
    Interface Descriptor:&lt;br /&gt;
      bLength                 9&lt;br /&gt;
      bDescriptorType         4&lt;br /&gt;
      bInterfaceNumber        1&lt;br /&gt;
      bAlternateSetting       0&lt;br /&gt;
      bNumEndpoints           2&lt;br /&gt;
      bInterfaceClass        10 CDC Data&lt;br /&gt;
      bInterfaceSubClass      0 &lt;br /&gt;
      bInterfaceProtocol      0 &lt;br /&gt;
[...]&lt;br /&gt;
      Endpoint Descriptor:&lt;br /&gt;
        bLength                 7&lt;br /&gt;
        bDescriptorType         5&lt;br /&gt;
        bEndpointAddress     0x83  EP 3 IN&lt;br /&gt;
 [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vu en cours, on revoit nos descripteurs ainsi que la description des interfaces de l'usb ainsi que d'autres données. Une fois notre carte détectée, on appuie en continue sur le bouton HWB puis une seule impulsion sur le bouton RESET afin de mettre notre carte en mode DFU et ensuite on relache HWB. &lt;br /&gt;
&lt;br /&gt;
On pourra alors téléverser notre programme sur la carte à l'aide d'un makefile préalablement codé en tapant la commande &amp;lt;code&amp;gt;make dfu&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Led carte.mp4|500px|left|led_carte]]&lt;br /&gt;
[[Fichier:Bouton carte.mp4|500px|right|bouton_carte]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On place le programme dans notre carte à l'aide de la commande &amp;lt;code&amp;gt;make upload&amp;lt;/code&amp;gt;. Pour ce faire, il faut modifier le makefile original par ceci :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
MCU          = atmega8u2&lt;br /&gt;
ARCH         = AVR8&lt;br /&gt;
BOARD        = NONE&lt;br /&gt;
F_CPU        = 16000000&lt;br /&gt;
F_USB        = $(F_CPU)&lt;br /&gt;
OPTIMIZATION = s&lt;br /&gt;
TARGET       = VirtualSerial&lt;br /&gt;
SRC          = $(TARGET).c Descriptors.c spi.c $(LUFA_SRC_USB)&lt;br /&gt;
LUFA_PATH    = ../../LUFA&lt;br /&gt;
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig/&lt;br /&gt;
LD_FLAGS     =&lt;br /&gt;
PROGRAMMER  = dfu-programmer&lt;br /&gt;
&lt;br /&gt;
# Include LUFA-specific DMBS extension modules&lt;br /&gt;
DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-sources.mk&lt;br /&gt;
include $(DMBS_LUFA_PATH)/lufa-gcc.mk&lt;br /&gt;
&lt;br /&gt;
# Include common DMBS build system modules&lt;br /&gt;
DMBS_PATH      ?= $(LUFA_PATH)/Build/DMBS/DMBS&lt;br /&gt;
include $(DMBS_PATH)/core.mk&lt;br /&gt;
include $(DMBS_PATH)/cppcheck.mk&lt;br /&gt;
include $(DMBS_PATH)/doxygen.mk&lt;br /&gt;
include $(DMBS_PATH)/dfu.mk&lt;br /&gt;
include $(DMBS_PATH)/gcc.mk&lt;br /&gt;
include $(DMBS_PATH)/hid.mk&lt;br /&gt;
include $(DMBS_PATH)/avrdude.mk&lt;br /&gt;
include $(DMBS_PATH)/atprogram.mk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le MCU a été changé afin de correspondre au nôtre. Une commande clean à été ajouté et la fréquence CPU augmentée à 16 MHz.&lt;br /&gt;
&lt;br /&gt;
* On lance la commande minicom afin de voir si le bouton pressé est bien affiché via la liaison série. On configure le minicom au préalable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;terminfo&amp;quot;&amp;gt;&lt;br /&gt;
sudo minicom -os&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ensuite nous entrons dans Serial port setup :[[Fichier:Image terminal apres commande minicom -os.png|gauche|sans_cadre|361x361px]]&lt;br /&gt;
[[Fichier:Menu serial port ssetup minicom.png|centré|sans_cadre|395x395px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
Il va falloir procéder à 3 changements : &lt;br /&gt;
&lt;br /&gt;
# '''''Appuyer sur A''''' et changer modem par l'emplacement de notre carte (ici /ttyACM0) puis ENTRER&lt;br /&gt;
# '''''Appuyer sur E''''' puis appuyer sur C pour configurer la vitesse de notre minicom puis ENTRER&lt;br /&gt;
# '''''Appuyer sur F''''' puis ENTRER&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Appuyer de nouveau sur ENTRER pour enregistrer la configuration et ensuite faite Exit. &lt;br /&gt;
&lt;br /&gt;
On est maintenant prêt à recevoir les messages USB qu'on envoie lors d'un appui :&lt;br /&gt;
[[Fichier:Interface minicom vierge.png|gauche|sans_cadre|620x620px]]&lt;br /&gt;
[[Fichier:Interface minicom apres reception message.png|centré|sans_cadre]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Nous avons ensuite implémenté un programme permettant d'allumer une led différente à chaque bouton-poussoir pressé.&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:Lufa boutons.mp4|vignette|Demonstration vidéo de notre programme utilisant la LUFA|centré]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;big&amp;gt;Modification du fichier virtual.c&amp;lt;/big&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
====== Afficher sur le minicom lorsqu'un bouton est pressé et test leds ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void CDC_Task(void) {&lt;br /&gt;
	static char ReportBuffer[64]; // Buffer pour stocker le message à envoyer&lt;br /&gt;
	static bool ActionSent = false; // Pour éviter d'envoyer plusieurs fois le même message&lt;br /&gt;
&lt;br /&gt;
	bool hasMessage = false;  // Indique si un bouton a été pressé&lt;br /&gt;
&lt;br /&gt;
	// Vérifie que l’appareil est connecté et configuré&lt;br /&gt;
	if (USB_DeviceState != DEVICE_STATE_Configured)&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton haut&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Up_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du haut\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED1_PIN);&lt;br /&gt;
		_delay_ms(300); // Anti-rebond&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton bas&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Down_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton du bas\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED2_PIN);&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton gauche&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsUp_Left_PIN, BTN_Left_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		strcpy(ReportBuffer, &amp;quot;bouton de gauche\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED3_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Détection du bouton droit&lt;br /&gt;
	if (!readPin_HardwareProgAVR(BTNsDown_Right_PIN, BTN_Right_PIN)) {&lt;br /&gt;
		hasMessage = true;&lt;br /&gt;
		ReportString = &amp;quot;bouton de droite\r\n&amp;quot;;&lt;br /&gt;
		// spi_test_octet(ReportBuffer);&lt;br /&gt;
		// getIDspi(ReportBuffer);&lt;br /&gt;
&lt;br /&gt;
		toggleLed(LEDs_PORT, LEDs_PIN, LED4_PIN);&lt;br /&gt;
		_delay_ms(300);&lt;br /&gt;
	}&lt;br /&gt;
	else ActionSent = false;&lt;br /&gt;
&lt;br /&gt;
	// Si un bouton a été pressé et qu'aucune action n’a encore été envoyée&lt;br /&gt;
	if (hasMessage &amp;amp;&amp;amp; (ActionSent == false) &amp;amp;&amp;amp; LineEncoding.BaudRateBPS) {&lt;br /&gt;
		ActionSent = true;&lt;br /&gt;
&lt;br /&gt;
		// Envoie le message via USB&lt;br /&gt;
		Endpoint_SelectEndpoint(CDC_TX_EPADDR);&lt;br /&gt;
		Endpoint_Write_Stream_LE(ReportBuffer, strlen(ReportBuffer), NULL);&lt;br /&gt;
&lt;br /&gt;
		bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);&lt;br /&gt;
		Endpoint_ClearIN();&lt;br /&gt;
&lt;br /&gt;
		if (IsFull) {&lt;br /&gt;
			Endpoint_WaitUntilReady();&lt;br /&gt;
&lt;br /&gt;
			Endpoint_ClearIN();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Nettoie le buffer de réception (inutile ici mais bonne pratique)&lt;br /&gt;
	Endpoint_SelectEndpoint(CDC_RX_EPADDR);&lt;br /&gt;
	if (Endpoint_IsOUTReceived())&lt;br /&gt;
		Endpoint_ClearOUT();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;L’ajout d’un délai de 300 ms est nécessaire pour éviter les rebonds des boutons mécaniques. Sans cela, une pression peut être détectée plusieurs fois à cause des oscillations électriques rapides à l’activation.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Ici on voit que le code peut être factorisé. Cela n'a pas été fais pour tester individuellement des fonctions différentes sur chaque bouton (cf prochaine section).&lt;br /&gt;
&lt;br /&gt;
====== '''Récupération ID microcontroleur''' ======&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Il y a des tentatives afin de récuprer un identifiant d'un microcontroleur via la fonction getIDspi(ReportBuffer). &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;../spi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define SPI_DDR         DDRB&lt;br /&gt;
#define SPI_PORT        PORTB&lt;br /&gt;
#define SPI_SS          PB0&lt;br /&gt;
#define SPI_SCK         PB1&lt;br /&gt;
#define SPI_MOSI        PB2&lt;br /&gt;
#define SPI_MISO        PB3&lt;br /&gt;
&lt;br /&gt;
void spi_init(void) {                                 // Initialisation du bus SPI&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_MOSI) | (1 &amp;lt;&amp;lt; SPI_SCK) | (1 &amp;lt;&amp;lt; SPI_SS);   // Définition des sorties&lt;br /&gt;
    SPI_DDR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_MISO);                           // Définition de l'entrée&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Désactivation du périphérique&lt;br /&gt;
    SPCR = (1 &amp;lt;&amp;lt; SPE) | (1 &amp;lt;&amp;lt; MSTR) | (1 &amp;lt;&amp;lt; SPR1) | (1 &amp;lt;&amp;lt; SPR0);       // Activation SPI (SPE) en état maître (MSTR)&lt;br /&gt;
    SPSR &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI2X);                                // horloge F_CPU/128 (SPI2X=0, SPR1=1,SPR0=1)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_activer(void) {                              // Activer le périphérique&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);                            // Ligne SS à l'état bas&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void spi_desactiver(void) {                           // Désactiver le périphérique&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);                             // Ligne SS à l'état haut&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_echange(uint8_t envoi) {    // Communication sur le bus SPI&lt;br /&gt;
    SPDR = envoi;                                                   // Octet a envoyer&lt;br /&gt;
    while (!(SPSR &amp;amp; (1 &amp;lt;&amp;lt; SPIF)));                                     // Attente fin envoi (drapeau SPIF du statut)&lt;br /&gt;
    return SPDR;                                                    // Octet reçu&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {&lt;br /&gt;
    spi_echange(a);&lt;br /&gt;
    spi_echange(b);&lt;br /&gt;
    spi_echange(c);&lt;br /&gt;
    return spi_echange(d);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end_pmode(void) {&lt;br /&gt;
    PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define nibble2char(n)  (((n)&amp;lt;10)?'0'+(n):'a'+(n)-10)&lt;br /&gt;
&lt;br /&gt;
void convert(unsigned char byte, char* string) {&lt;br /&gt;
    string[0] = nibble2char(byte &amp;gt;&amp;gt; 4);&lt;br /&gt;
    string[1] = nibble2char(byte &amp;amp; 0x0f);&lt;br /&gt;
    string[2] = '\0';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void getIDspi(char* ReportString) {&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
&lt;br /&gt;
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); //cf p8 Device Code de la DS AVR_ISP&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(high, format + 5);  // &amp;quot;xx&amp;quot; remplacé par la valeur convertie&lt;br /&gt;
&lt;br /&gt;
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    _delay_ms(10); // délai optionnel pour stabilité&lt;br /&gt;
    // convert(middle, format + 14);  // &amp;quot;yy&amp;quot; remplacé par middle&lt;br /&gt;
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
    // convert(low, format + 22);     // &amp;quot;zz&amp;quot; remplacé par low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    char highHex[3], middleHex[3], lowHex[3];&lt;br /&gt;
    convert(high, highHex);&lt;br /&gt;
    convert(middle, middleHex);&lt;br /&gt;
    convert(low, lowHex);&lt;br /&gt;
&lt;br /&gt;
    // Construire la chaîne manuellement&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;RAW SPI ID: H=0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = highHex[0];&lt;br /&gt;
    *ptr++ = highHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* midStr = &amp;quot; M=0x&amp;quot;;&lt;br /&gt;
    while (*midStr) *ptr++ = *midStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = middleHex[0];&lt;br /&gt;
    *ptr++ = middleHex[1];&lt;br /&gt;
&lt;br /&gt;
    const char* lowStr = &amp;quot; L=0x&amp;quot;;&lt;br /&gt;
    while (*lowStr) *ptr++ = *lowStr++;&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = lowHex[0];&lt;br /&gt;
    *ptr++ = lowHex[1];&lt;br /&gt;
&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Avec l’aide de M. Redon, nous avons tenté de récupérer l’identifiant unique d’un microcontrôleur AVR en utilisant le protocole SPI. Pour cela, nous avons développé un programme basé sur une communication SPI bas-niveau, avec les fonctions d’initialisation, d’activation/désactivation du bus, et d’échange de données via SPI.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Le principe était d’envoyer des commandes spécifiques conformes au protocole ISP (In-System Programming) d’Atmel, notamment l’envoi de la commande &amp;lt;code&amp;gt;0x30&amp;lt;/code&amp;gt; suivie d’adresses pour lire les octets composant l’ID (haut, milieu, bas). La fonction &amp;lt;code&amp;gt;getIDspi()&amp;lt;/code&amp;gt; réalise ces transactions successives et convertit les valeurs reçues en chaîne hexadécimale lisible.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;Nous avons également implémenté une fonction de test simple, &amp;lt;code&amp;gt;spi_test_octet()&amp;lt;/code&amp;gt;, qui envoie un octet &amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt; et devrait recevoir la même valeur en retour si le périphérique répond correctement.&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void spi_test_octet(char* ReportString) {&lt;br /&gt;
    // Configuration de RESET en sortie&lt;br /&gt;
    SPI_DDR |= (1 &amp;lt;&amp;lt; SPI_SS);          // RESET = PB0 en sortie&lt;br /&gt;
    SPI_PORT &amp;amp;= ~(1 &amp;lt;&amp;lt; SPI_SS);        // RESET à 0 : mode programmation&lt;br /&gt;
    // _delay_ms(30);                     // Attente suffisante (20ms minimum)&lt;br /&gt;
&lt;br /&gt;
    // Envoie de la commande ISP pour vérifier si la cible répond&lt;br /&gt;
    // spi_activer();                     // SS SPI actif (LOW)&lt;br /&gt;
    // uint8_t check = spi_transaction(0xAC, 0x53, 0x00, 0x00);  // Activation ISP&lt;br /&gt;
    // spi_desactiver();                 // SS SPI inactif (HIGH)&lt;br /&gt;
&lt;br /&gt;
    // if (check != 0x53) {&lt;br /&gt;
    //     strcpy(ReportString, &amp;quot;Erreur: mode ISP non actif\r\n&amp;quot;);&lt;br /&gt;
    //     SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);      // RESET à 1 : fin prog&lt;br /&gt;
    //     return;&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Si la cible est bien en mode programmation, test SPI&lt;br /&gt;
    spi_activer();&lt;br /&gt;
    uint8_t response = spi_echange(0x55);&lt;br /&gt;
    spi_desactiver();&lt;br /&gt;
&lt;br /&gt;
    SPI_PORT |= (1 &amp;lt;&amp;lt; SPI_SS);  // Fin du mode ISP (RESET à 1)&lt;br /&gt;
&lt;br /&gt;
    // Construction de la chaîne&lt;br /&gt;
    char hexStr[3];&lt;br /&gt;
    convert(response, hexStr);&lt;br /&gt;
&lt;br /&gt;
    char* ptr = ReportString;&lt;br /&gt;
    const char* prefix = &amp;quot;SPI test response: 0x&amp;quot;;&lt;br /&gt;
    while (*prefix) *ptr++ = *prefix++;&lt;br /&gt;
    *ptr++ = hexStr[0];&lt;br /&gt;
    *ptr++ = hexStr[1];&lt;br /&gt;
    *ptr++ = '\r';&lt;br /&gt;
    *ptr++ = '\n';&lt;br /&gt;
    *ptr = '\0';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;'''Cependant, malgré plusieurs essais, la récupération stable de l’ID n’a pas été réussie :'''&lt;br /&gt;
* Le signal SPI semble fonctionner de manière intermittente.&lt;br /&gt;
* La réponse attendue (&amp;lt;code&amp;gt;0x55&amp;lt;/code&amp;gt;) est parfois reçue, mais de façon instable.&lt;br /&gt;
* Cela pourrait venir d’un problème matériel (câblage, niveau des signaux) ou d’un timing non respecté dans le protocole SPI/ISP.&lt;br /&gt;
&lt;br /&gt;
* '''Fonctions SPI implémentées''' :&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_init()&amp;lt;/code&amp;gt; : configure les broches et paramètres SPI maître.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_activer()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spi_desactiver()&amp;lt;/code&amp;gt; : contrôle de la ligne SS (Slave Select).&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_echange()&amp;lt;/code&amp;gt; : envoie et réception d’un octet SPI.&lt;br /&gt;
** &amp;lt;code&amp;gt;spi_transaction()&amp;lt;/code&amp;gt; : envoie une séquence de quatre octets en SPI, utile pour la commande ISP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jusqu'ici, je n'ai pas de piste pour pouvoir continuer, sachant que le câblage n'est pas le problème.&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Dashboard.png&amp;diff=8050</id>
		<title>Fichier:Dashboard.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Dashboard.png&amp;diff=8050"/>
		<updated>2025-06-15T20:18:45Z</updated>

		<summary type="html">&lt;p&gt;Cpayet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dashboard&lt;/div&gt;</summary>
		<author><name>Cpayet</name></author>
	</entry>
</feed>