« SE4Binome2023-10 » : différence entre les versions
Ligne 526 : | Ligne 526 : | ||
Nous avons couper la piste du pin 14 et le relier avec le "via" connecté à CS avec un fil. Puis relier le pin 10 avec B7 de l'écran LCD. | Nous avons couper la piste du pin 14 et le relier avec le "via" connecté à CS avec un fil. Puis relier le pin 10 avec B7 de l'écran LCD. | ||
[[Fichier:Carte resoudée.jpg|néant|vignette|Carte avec inter-changement des pins B7 et CS]]Malheureusement on a pris beaucoup de temps pour constater l'erreur, donc on a perdu du temps en pensant que l'erreur était dans le code. | [[Fichier:Carte resoudée.jpg|néant|vignette|Carte avec inter-changement des pins B7 et CS]]Malheureusement on a pris beaucoup de temps pour constater l'erreur, donc on a perdu du temps en pensant que l'erreur était dans le code. | ||
==== Programmation du microcontrôleur de la carte LCD ==== | |||
Pour traiter les commandes reçues via l'interface SPI, on a ajouté la fonction commandes() : | |||
<syntaxhighlight lang="c"> | |||
void commandes(void) | |||
{ | |||
char cmd = SPDR; //spi_wait_cmd(); | |||
char data = spi_wait_cmd(); | |||
char tmp; | |||
if(cmd == 0x00) | |||
{ | |||
lcdPutc('o'); //test | |||
lcdPutc('k'); | |||
if(data == INFO) | |||
{ | |||
tmp = data; | |||
if(tmp != 0xff) | |||
{ | |||
lcdPutc(data); | |||
} | |||
else if(tmp==0x00) | |||
{ | |||
SPDR = 0xA2; //Pour Affichage, Envoi de la donnée 0xA2 via SPI si la donnée reçue est égale à 0x00 | |||
} | |||
} | |||
} | |||
if(cmd == 0xff) | |||
{ | |||
if(data == 0xff) | |||
{ | |||
SPDR = 0x55; // Envoi de la donnée 0x55 via SPI si la commande est 0xff et la donnée reçue est 0xff | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== GIT == | == GIT == | ||
<p style="clear: both;" />Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git | <p style="clear: both;" />Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git |
Version du 18 janvier 2024 à 09:59
Ordonnanceur / Système d'exploitation
Réalisation du Shield
Séance 13/09 : Parmi les activités disponibles, nous avons soudé le régulateur de tension, le lecteur SD, les résistances liées aux LEDs. Nous avons réalisé 2 câbles pour les cartes filles et un adaptateur.
Séance 19/09 : Durant cette séance nous avons soudé les autres résistances et les connecteurs. Et nous avons testé le bon fonctionnement de la carte Shield.
Voici la carte Shield soudé :
Nous avons soudé cette carte :
Pour le test nous avons utilisé le code qui est disponible sur le git. Voici ci-dessous une vidéo montrant le clignotement des LEDs.
Clignotement des LEDs avec ordonnanceur
Au cours de la fabrication de notre carte, nous avons commencé la réalisation de l'ordonnanceur.
Pour réaliser l'ordonnanceur, nous avons besoin d'une fonction de gestion d'interruption ISR qui sera déclenché par l'interruption.
ISR(TIMER1_COMPA_vect, ISR_NAKED) {
SAVE_REGISTERS();
taches[current].pointer_pile = SP;
// Appel à l'ordonnanceur
scheduler();
SP = taches[current].pointer_pile;
RESTORE_REGISTERS();
asm volatile ( "reti" );
}
Le principe c'est de créer deux tâches pour faire clignoter deux LEDs du bouclier avec des périodes différentes. La première tache allume et éteint une LED toutes les 200ms, la deuxième tache réalise la même opération sur une LED différente toutes les 1000ms.
// Tâche 1
void task_LED_1() {
DDRD |= (1 << LED4_PIN);
PORTD &= ~(1 << LED4_PIN);
while (1) {
PORTD ^= (1 << LED4_PIN);
_delay_ms(200); // Fréquence 200 ms
}
}
// Tâche 2
void task_LED_2() {
DDRC |= (1 << LED1_PIN);
PORTC &= ~(1 << LED1_PIN);
while (1) {
PORTC ^= (1 << LED1_PIN);
_delay_ms(1000); // Fréquence 1000 ms
}
}
Et voici la fonction permettant d'initialiser le contexte de chaque tâche :
void init_task(int id)
{
uint16_t save_sp = SP;
SP= taches[id].pointer_pile;
uint16_t adresse=(uint16_t)taches[id].task_func;
asm volatile("push %0" : : "r" (adresse & 0x00ff) );
asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
SAVE_REGISTERS();
taches[id].pointer_pile = SP;
SP = save_sp;
}
Afficheur 7 segments
Pour l'afficheur 7 segments, nous l'avons dans un premier temps fait sans ordonnancement pour connaitre sa fonctionnalité. Pour le tester on a juste affiché "1 2 3 4" sur l'afficheur comme indiqué sur l'image ci-dessous :
void afficheur(void)
{
spi_activer();
spi_exch(0x76);
spi_desactiver();
char seg[4] = "1234";
while(1){
spi_activer();
for(uint8_t i = 0; i < 4 ; i++)
{
spi_exch(seg[i]);
}
_delay_ms(1000);
spi_desactiver();
}
}
Pour l'ordonnancement, nous l'avons testé sur l'afficheur 7 segments avec lecture/écriture sur le port série :
// fonction pour afficheur 7 segments
void afficheur(void)
{
pause_smp(SEMAPHORE_SPI);
spi_activer(SS_3);
spi_exch(0x76);
spi_desactiver(SS_3);
while(1){
for(int i = 0; i < 4 ; i++)
{
spi_activer(SS_3);
spi_exch(caractere);
}
spi_desactiver(SS_3);
_wait_ms(5);
liber_smp(SEMAPHORE_SPI);
}
}
Carte FPGA / VHDL
Controleur VGA
Un contrôleur VGA sert à générer des signaux de synchronisation horizontale (HS) et verticale (VS), ainsi que des signaux de couleur (r, g, b) pour un écran VGA. L’horloge d'entrée est à 100 MHz et génère une horloge de sortie à 65 MHz généré par le wizard (clk_wiz_0).
Le code ci-dessous permet de générer des signaux VGA pour afficher des images colorées sur un écran VGA et un Basys3
- Génération des signaux HS et VS pour indiquer les périodes de synchronisation horizontale et verticale
process1 : process(clock_65Mhz)
begin
if clock_65Mhz'event and clock_65Mhz = '1' then
if compteur_vert < 774 then
if compteur_hori < 1048 then
HS <= '1';
compteur_hori <= compteur_hori + 1;
elsif compteur_hori >= 1048 and compteur_hori <1183 then
HS <= '0';
compteur_hori <= compteur_hori + 1;
elsif compteur_hori >= 1183 and compteur_hori < 1343 then
HS <= '1';
compteur_hori <= compteur_hori + 1;
else
HS <= '1';
compteur_hori <= 0;
compteur_vert <= compteur_vert + 1;
end if;
end if;
if compteur_hori < 1048 then
if compteur_vert < 774 then
VS <= '1';
compteur_vert <= compteur_vert + 1;
elsif compteur_vert <= 774 and compteur_vert < 780 then
VS <= '0';
compteur_vert <= compteur_vert + 1;
elsif compteur_vert <=780 and compteur_vert < 805 then
VS <='1';
compteur_vert <= compteur_vert + 1;
else
VS<= '1';
compteur_vert <= 0;
compteur_hori <= compteur_hori + 1;
end if;
end if;
- Génération des Signaux de Couleur (r, g, b) en utilisant les boutons b1, b2 et b3
if b1 = '1' then
--couleur rouge
if compteur_hori > 300 and compteur_hori < 600 then
if compteur_vert > 300 and compteur_vert < 600 then
r <= "1111";
g <= "0000";
b <= "0000";
end if;
end if;
end if;
--couleur verte
if b2 = '1' then
if compteur_hori > 360 and compteur_hori < 1080 then
if compteur_vert > 201 and compteur_vert < 604 then
r <= "0000";
g <= "1111";
b <= "0000";
end if;
end if;
end if;
--couleur bleue
if b3 = '1' then
if compteur_hori > 360 and compteur_hori < 1080 then
if compteur_vert > 201 and compteur_vert < 604 then
r <= "0000";
g <= "0000";
b <= "1111";
end if;
end if;
end if;
end if;
Controleur bus SPI
Les dispositifs SPI communiquent en utilisant une architecture maître-esclave avec un seul maître.Un ou plusieurs dispositifs esclaves SPI sont pris en charge par la sélection d'esclaves individuels SS
Avant de démarrer une communication avec un esclave particulier, le maître active la ligne SS
associée à cet esclave en la faisant passer de l'état haut à l'état bas.
Une fois que l'esclave est sélectionné, le maître peut commencer à envoyer des données sur la ligne MOSI et recevoir des données sur la ligne MISO.
Nous devons illustrer une implémentation d'un émetteur SPI synchrone. Dans notre contexte, on gère la transmission séquentielle d'octets sur la ligne MOSI, tout en signalant la fin de chaque transmission par le signal complete
. Lorsqu'il est déclenché par un front montant d'horloge, le code gère différents états, tels que l'état IDLE
où le système est en attente, et l'état TRANSFER
où les données sont envoyées bit par bit.
Voici le code pour tester chaque état
- Etat IDLE
Dans l'état IDLE, le système est en attente d'une nouvelle transmission SPI, et la transition vers l'état de transfert est conditionnée par la détection d'un signal de démarrage. Lorsque ce signal est activé, l'état passe à TRANSFER, initialisant la communication en activant la sélection d'esclave et préparant la transmission des données.
when IDLE =>
if start = '1' then
-- Début d'une nouvelle transmission
state <= TRANSFER;
ss <= '0'; -- Activer la sélection d'esclave
bit_counter <= 7; -- Initialiser le compteur de bits
mosi <= data_buffer(7);
end if;
- Etat TRANSFER
L'état TRANSFER gère la transmission séquentielle des bits sur la ligne MOSI.
when TRANSFER =>
if bit_counter > 0 then
-- Transfert des bits
bit_counter <= bit_counter - 1;
mosi <= data_buffer(bit_counter);
else
-- Fin de la transmission
state <= DONE;
end if;
- Etat DONE
Dans l'état DONE, après la fin de la transmission SPI, le système désactive la sélection d'esclave et peut lire les données reçues sur la ligne MISO
when DONE =>
-- Désactiver la sélection d'esclave et générer une impulsion d'horloge
ss <= '1';
--sclk <= not sclk;
complete <= '1';
-- Lecture des données reçues sur la ligne MISO
data_in <= data_in (6 downto 0) & miso;
Carte électronique numérique
Type de carte choisie
La carte fille écran LCD.
Elle se compose :
- d'un ATMega328p
- d'un écran LCD à base de contrôleur HD44780
- d'un connecteur HE10
- d'un connecteur AVR ISP pour programmer le microcontrôleur
- d'un potentiomètre pour régler la luminosité des cristaux liquides
- d'un bouton switch pour le reset
Réalisation de la carte
Séance 22/09 : Durant cette séance nous avons commencé la schématique de notre carte.
Voici le schéma électronique de notre carte fille LCD :
Séance 03/10 : nous avons commencé le routage.
Séance 17/10 : routage terminé
Après le routage nous avons obtenu :
Séance 24/10 :
Le routage terminé, le prof a vérifié le routage final et nous avons envoyé la carte afin de l'imprimer.
Durant cette même séance nous avons commencer la programmation de l'ordonnancement.
Voici une vue 3D de ce qui va se trouver au dessus de notre carte. Notre écran se trouvera donc en dessous.
Présentation de la carte fille LCD
Séance 14/11 :
Nous avons reçu la carte, nous avons commencé la soudure des composants nécessaires pour un premier test de la carte.
- Le microprossesseur
- une led
- L'horloge
Pour vérifier le bon fonctionnement de la carte, nous avons mis une led afin de savoir la fonctionnalité de la carte.
Pour cela, on a testé avec le code exemple présent Arduino IDE.
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(7, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(7, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(7, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Nous avons soudé le reste des composants ainsi que l'écran LCD.
- Le potentiomètre
- le bouton reset
- les connecteurs
- l'écran LCD
- Les capacités
Voici tous la présentation de tous les composants de notre carte fille :
Test de l'ecran avec Aduino IDE
Nous avons testé le lcd avec le code qui est présent sur Arduino IDE. Voici le code utilisé:
// include the library code:
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = A0, en = A2, d4 = 4, d5 = 5, d6 = 9, d7 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
pinMode(A1,OUTPUT);
digitalWrite(A1,LOW);
delay(1000);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
pinMode(7,OUTPUT);
digitalWrite(7,HIGH);
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
// print the number of seconds since reset:
lcd.print(millis() / 1000);
}
Programmation de la carte fille LCD
Les broches nécessaire à la programmation de l'écran LCD :
- RS (Register Select)
- EN (Enable)
- R/W (Read/Write)
- DB4 à DB7
Ecriture sur la carte LCD
La fonction lcdWrite4bits
est conçue pour écrire quatre bits de données sur l’écran LCD en utilisant une interface 4 bits.
void lcdWrite4bits(uint8_t data, uint8_t rs){
if(rs)
LCD_RS_ON();
else
LCD_RS_OFF();
LCD_RW_OFF();
LCD_EN_ON();
_delay_us(1);
if(data & 1)
LCD_DB4_ON();
else
LCD_DB4_OFF();
if(data & 2)
LCD_DB5_ON();
else
LCD_DB5_OFF();
if(data & 4)
LCD_DB6_ON();
else
LCD_DB6_OFF();
if(data & 8)
LCD_DB7_ON();
else
LCD_DB7_OFF();
LCD_EN_OFF();
_delay_us(1);
}
- rs
indique si les données à écrire sont des données :
- d'instruction « 0 »
- des données à afficher « 1 »
- Les blocs de if
testent chaque bit dans data
. Ils activent ou désactivent les lignes de données (DB4 à DB7).
Gestion des caractères de contrôle et initialisation LCD
Pour initialiser l'écran LCD, nous avons écris la fonction suivante
void lcdInit(){
LCD_RS_INIT();
LCD_EN_INIT();
LCD_RW_INIT();
LCD_DB4_INIT();
LCD_DB5_INIT();
LCD_DB6_INIT();
LCD_DB7_INIT();
_delay_ms(1);
lcdWrite8bits(0x28,0); //4bits, 2 lignes
lcdWrite8bits(0x08,0); //LCD off
lcdWrite8bits(0x01,0); //Clear LCD
_delay_ms(2);
lcdWrite8bits(0x06,0); //incrementation
lcdWrite8bits(0x0c,0);
}
lcdWrite8bits(0x0c, 0)
: Allume l'écran LCD sans afficher le curseur.
La fonction lcdWrite8bits est conçue pour écrire une valeur de 8 bits de data sur l'écran LCD.
Pour la gestion du retour à la ligne et le retour chariot il faut vérifier si le caractère data est égal à un retour à la ligne \n
ou un retour chariot \r
.
if ((data == '\n') || (data == '\r')) {
pos.current_x = 0;
pos.current_y = (pos.current_y + 1) % NB_LIGNE;
lcdMoveCursor(pos.current_y, 0);
return;
}
La mise à jour du curseur dépend de la valeur de data et si elle vaut l'un des caractère de contrôle, le curseur se mettra à jour sur la ligne suivante grâce à la fonction lcdMoveCursor(pos.current_y, 0)
Pour le retour à la ligne automatique, faut vérifier la position de x, pour cela on a ajouté la condition suivante :
if (pos.current_x == NB_COL) {
pos.current_x = 0;
pos.current_y = (pos.current_y + 1) % NB_LIGNE;
lcdMoveCursor(pos.current_y, 0);}
Pour déplacer le curseur sur l'écran LCD :
void lcdMoveCursor(uint8_t row, uint8_t col) {
if (row == 0) {
// Pour la première ligne
lcdWrite8bits(0x80 + col, 0); // Adresse de début pour la première ligne + colonne
} else if (row == 1) {
// Pour la deuxième ligne
lcdWrite8bits(0xC0 + col, 0); // Adresse de début pour la deuxième ligne + colonne
}
}
Pour effacer le contenu affiché sur un écran LCD. on a ajouté la fonction lcdClear qui envoie une commande à l'écran LCD pour effacer son contenu, puis introduit un délai de 2 millisecondes pour permettre à l'écran de s'effacer correctement avant de poursuivre l'exécution du programme.
void lcdClear() {
lcdWrite8bits(0x01, 0);
_delay_ms(2);
}
Affichage des caractères
Pour afficher chaque caractère on a écris la fonction lcdPutc
void lcdPutc(char c){
busy = 1;
lcdWrite8bits(c,1);
busy = 0;
}
busy
à 1 est utilisé pour indiquer l'état de l'écran LCD s'il est en train de traiter une opération.
Voici la vidéo montrant le bon fonctionnement de notre carte :
le code source complet est dans le git.
LCD avec SPI
Lors de la programmation de la carte fille, nous avons constaté qu'il y'a une erreur sur la carte: le pin CS de l'Atmega est liée au PD6 alors qu'il doit être connecté au PB2. Donc on a du interchanger CS et B7 sinon la communication SPI ne sera pas fonctionnelle.
Nous avons donc procéder avec monsieur Redon la modification des pins.
Nous avons couper la piste du pin 14 et le relier avec le "via" connecté à CS avec un fil. Puis relier le pin 10 avec B7 de l'écran LCD.
Malheureusement on a pris beaucoup de temps pour constater l'erreur, donc on a perdu du temps en pensant que l'erreur était dans le code.
Programmation du microcontrôleur de la carte LCD
Pour traiter les commandes reçues via l'interface SPI, on a ajouté la fonction commandes() :
void commandes(void)
{
char cmd = SPDR; //spi_wait_cmd();
char data = spi_wait_cmd();
char tmp;
if(cmd == 0x00)
{
lcdPutc('o'); //test
lcdPutc('k');
if(data == INFO)
{
tmp = data;
if(tmp != 0xff)
{
lcdPutc(data);
}
else if(tmp==0x00)
{
SPDR = 0xA2; //Pour Affichage, Envoi de la donnée 0xA2 via SPI si la donnée reçue est égale à 0x00
}
}
}
if(cmd == 0xff)
{
if(data == 0xff)
{
SPDR = 0x55; // Envoi de la donnée 0x55 via SPI si la commande est 0xff et la donnée reçue est 0xff
}
}
}
GIT
Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git