« I2L 2022 Groupe4 » : différence entre les versions
Aucun résumé des modifications |
m (Correction des dernières fautes d'orthographe) |
||
(60 versions intermédiaires par 3 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
= Sujet du projet = | |||
Clavier de raccourcis programmables - par Axel Lebas & Wassim Djamaa & Maxime Vitse | Clavier de raccourcis programmables - par Axel Lebas & Wassim Djamaa & Maxime Vitse | ||
= Proposition de système = | = Proposition de système = | ||
Une carte avec | Une carte avec 6 boutons programmables via un fichier de configuration qui est fourni par l'utilisateur. Accompagnée d'une LED. | ||
= Contre-proposition = | = Contre-proposition = | ||
Bonne idée. Votre périphérique USB sera programmé avec une interface comportant un point d'accès OUT pour envoyer les chaînes de caractères à envoyer sur pression des boutons. Il serait intéressant d'écrire ces chaînes dans l'EEPROM de l'ATMega16u2 pour qu'elles persistent une fois le périphérique débranché. Les LED du périphérique doivent permettre d'indiquer quelles touches sont programmées. Le programme de configuration sera écrit avec la bibliothèque libusb-1.0. | Bonne idée. Votre périphérique USB sera programmé avec une interface comportant un point d'accès OUT pour envoyer les chaînes de caractères à envoyer sur pression des boutons. Il serait intéressant d'écrire ces chaînes dans l'EEPROM de l'ATMega16u2 pour qu'elles persistent une fois le périphérique débranché. Les LED du périphérique doivent permettre d'indiquer quelles touches sont programmées. Le programme de configuration sera écrit avec la bibliothèque libusb-1.0. | ||
= Carte = | |||
[[Fichier:I2L-2022-G4-schema.pdf|thumb|400px|left|Schéma]] | |||
[[Fichier:I2L-2022-G4-PCB.pdf|thumb|400px|right|Carte]] | |||
<div style="clear: both;" /> | |||
Carte soudée : | |||
[[Fichier:I2L-2022-G4-carte-soudée.jpg|thumb|500px|gauche]] | |||
= Fichiers = | = Fichiers = | ||
Le circuit imprimé à utiliser est le même que celui du groupe | Le circuit imprimé à utiliser est le même que celui du groupe 3 avec le clavier de la carte du groupe 2. | ||
Après quelques problèmes avec le connecteur USB C, une variante de la carte du groupe 3 est proposée avec un connecteur USB A. | |||
- | Projet KiCAD : [[File:I2L-2022-CLIPET-MATHON-BOUKELLAL_version_USBA.zip]] | ||
== Trois parties à faire == | |||
* Prog SE: | |||
** Jouer avec les LEDs | |||
* Prog USB: | * Prog USB: | ||
** Avec LUFA, faire agir le contrôleur comme un clavier avec des raccourcis qui sont codé en dur dans le code LUFA. | |||
* Prog PC: | * Prog PC: | ||
** Avec libusb, créer un programme pour la machine (le PC), qui va permettre d'envoyer à travers une nouvelle interface usb à partir code précédent | |||
** Bien évidemment, le code LUFA sera a adapté pour recevoir les raccourcis et la touche à binder. | |||
== '''Prog SE :''' == | |||
== Makefile pour automatiser le processus suivant == | == Makefile pour automatiser le processus suivant == | ||
Par défaut le micro processeur a un bootloader qui permet de faire de la programmation (sans passer par le SPI) , l'utilitaire que nous utilisons est DFU/USB. | Par défaut, le micro processeur a un bootloader qui permet de faire de la programmation (sans passer par le SPI) , l'utilitaire que nous utilisons est DFU/USB. | ||
"5.2 ATMega328p: Chaîne de compilation (2/2)" de https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme.html<syntaxhighlight lang="shell"> | "5.2 ATMega328p: Chaîne de compilation (2/2)" de https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme.html<syntaxhighlight lang="shell"> | ||
$ apt install gcc-avr avr-libc | $ apt install gcc-avr avr-libc dfu-programmer // DFU/USB dfu 6 programmer | ||
$ avr-gcc -mmcu=atmega328p -DF_CPU=8000000UL -c -Wall -I. -Os timer.c -o timer.o // compilation source en un . objet | $ avr-gcc -mmcu=atmega328p -DF_CPU=8000000UL -c -Wall -I. -Os timer.c -o timer.o // compilation source en un . objet | ||
Ligne 41 : | Ligne 77 : | ||
$ avr-objcopy -j .text -j .data -O ihex timer.elf timer.hex // nécessaire pour les utilitaires | $ avr-objcopy -j .text -j .data -O ihex timer.elf timer.hex // nécessaire pour les utilitaires | ||
$ | $ dfu-programmer atmega16u2 erase | ||
$ dfu-programmer atmega16u2 flash timer.hex | |||
$ dfu-programmer atmega16u2 reset | |||
</syntaxhighlight>Exemple de makefile<syntaxhighlight lang="makefile"> | |||
CC = avr-gcc | |||
OBJCOPY = avr-objcopy | |||
DFU_PROGRAMMER = dfu-programmer | |||
CFLAGS = -mmcu=atmega328p -DF_CPU=8000000UL -c -Wall -I. -Os | |||
LDFLAGS = -mmcu=atmega328p -g -lm -Wl,--gc-sections | |||
all: timer.hex | |||
timer.o: timer.c | |||
$(CC) $(CFLAGS) $< -o $@ | |||
$ | timer.elf: timer.o | ||
$(CC) $(LDFLAGS) $< -o $@ | |||
timer.hex: timer.elf | |||
$(OBJCOPY) -j .text -j .data -O ihex $< $@ | |||
erase: | |||
$(DFU_PROGRAMMER) atmega16u2 erase | |||
flash: timer.hex | |||
$(DFU_PROGRAMMER) atmega16u2 flash $< | |||
reset: | |||
$(DFU_PROGRAMMER) atmega16u2 reset | |||
clean: | |||
rm -f timer.o timer.elf timer.hex | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Gestion des entrées-sorties numériques avec avr-gcc == | == Gestion des entrées-sorties numériques avec avr-gcc == | ||
<syntaxhighlight lang="c"> | Code : https://github.com/Weamix/shortcut/blob/master/timer.c | ||
1ère version :<syntaxhighlight lang="c"> | |||
#include <avr/io.h> | #include <avr/io.h> | ||
#include <util/delay.h> | |||
int main(void){ | |||
CLKSEL0 = 0b00010101; // sélection de l'horloge externe | |||
CLKSEL1 = 0b00001111; // minimum de 8Mhz | |||
CLKPR = 0b10000000; // modification du diviseur d'horloge (CLKPCE=1) | |||
CLKPR = 0; // 0 pour pas de diviseur (diviseur de 1) | |||
DDRB |= 0x01; // direction sortie pour PB0 | |||
PORTB &= ~0x01; | |||
PORTD &= ~0x78; // entrees | |||
PORTD |= ~0x78; | |||
while(1){ | |||
if ((PIND & 0x40)) | |||
PORTB &= ~0x80; // LED éteinte | |||
else | |||
PORTB |= 0x80; // LED allumée | |||
if ((PIND & 0x20)) | |||
// open led on pb6 | |||
PORTB &= ~0x40; // LED éteinte | |||
else | |||
PORTB |= 0x40; // LED allumée | |||
if ((PIND & 0x10)) | |||
// open led on pb5 | |||
PORTB &= ~0x20; // LED éteinte | |||
else | |||
PORTB |= 0x20; // LED allumée | |||
if ((PIND & 0x08)) | |||
// open led on pb4 | |||
PORTB &= ~0x10; // LED éteinte | |||
else | |||
PORTB |= 0x10; // LED allumée | |||
} | |||
} | |||
</syntaxhighlight> | |||
2nde version : | |||
<syntaxhighlight lang="c"> | |||
int main(void){ | int main(void){ | ||
CLKSEL0 = 0b00010101; // sélection de l'horloge externe | |||
CLKSEL1 = 0b00001111; // minimum de 8Mhz | |||
CLKPR = 0b10000000; // modification du diviseur d'horloge (CLKPCE=1) | |||
CLKPR = 0; // 0 pour pas de diviseur (diviseur de 1) | |||
while(1){ | #if 0 | ||
DDRB |= 0x01; // direction sortie pour PB0 | |||
PORTB &= ~0x01; | |||
PORTD &= ~0x78; // entrees | |||
PORTD |= ~0x78; | |||
#endif | |||
DDRB |= 0xf0; | |||
PORTB &= ~0xf0; | |||
DDRD &= ~0x78; | |||
PORTD |= 0x78; | |||
// lire pin d sur les bits 40 20 10 et 08 | |||
//DDRB &= ~0b00000011; // Entrée pour le bouton | |||
//PORTB |= 0x02; // Configuration de la résistance de tirage | |||
while(1){ | |||
if(!(PIND & 0x40)) PORTB |= 0x80; else PORTB &= ~0x80; | |||
} | |||
while(1){ | |||
if ((PIND & 0x40)) | |||
PORTB &= ~0x80; // LED éteinte | |||
else | |||
PORTB |= 0x80; // LED allumée | |||
if ((PIND & 0x20)) | |||
// open led on pb6 | |||
PORTB &= ~0x40; // LED éteinte | |||
else | |||
PORTB |= 0x40; // LED allumée | |||
if ((PIND & 0x10)) | |||
// open led on pb5 | |||
PORTB &= ~0x20; // LED éteinte | |||
else | |||
PORTB |= 0x20; // LED allumée | |||
if ((PIND & 0x08)) | |||
// open led on pb4 | |||
PORTB &= ~0x10; // LED éteinte | |||
else | |||
PORTB |= 0x10; // LED allumée | |||
} | |||
} | |||
</syntaxhighlight>'''Ports''' | |||
B: 8 sorties | |||
C: > 8 sorties | |||
D: 8 sorties | |||
En général 8 fois 3 entrées, ici une vingtaine de sorties | |||
'''Registre DDR (data direction register) : sens de la LED''' | |||
Pour chaque bit du port si le bit correspond à une entrée ou une sortie | |||
Voir PB0 / PB1 sur le schéma KICAD | |||
- Par défaut, BIT à 0 en entrée | |||
- 1 pour sortie | |||
Il faut toucher au bit avec le poids le plus faible. | |||
'''Rappels sur les opérandes en C''' | |||
& | sont des opérandes bit à bit pas comme les opérandes logiques dans les if && ou || | |||
tilde is NOT | |||
Le OU exclusif s'écrit PORTB '''^ =''' 0X01 | |||
le & avec le tilde permet un bit à 0 | |||
le | sans le tilde permet un bit à 1 | |||
'''2 types de registres''' | |||
Port B -> sortie | |||
PINB -> entrée | |||
== '''Prog Lufa :''' == | |||
=== Bibliothèque LUFA === | |||
* boucler sur la gestion des événements USB USB_USBTask ; | |||
* éventuellement appeler aussi la gestion d’une classe (e.g. HID_Task) ; | |||
* écrire les fonctions de rappel (callback) pour implanter les fonctionnalités souhaitées. | |||
* Modifier le Makefile de LUFA en mettant MCU en atmega16u2 4 premières lignes à modifier seulement (une à none). | |||
* Modifier dans descriptor.c et descriptor.h | |||
=== Base pour le projet === | |||
Comme base pour le programme LUFA nous avons utilisé le projet LUFA "écho" disponible sur la page Wiki du groupe 2. | |||
=== Code simple avec des shortcuts mis en dur dans le code === | |||
<syntaxhighlight lang="c"> | |||
void SetupHardware(void) | |||
{ | |||
... | |||
// LEDS : PB7 à PB4 | |||
/* Hardware Initialization */ | |||
USB_Init(); | |||
DDRB |= 0x01; // direction sortie pour PB0 | |||
PORTB &= ~0x01; | |||
PORTD &= ~0x78; // entrees | |||
PORTD |= ~0x78; | |||
} | |||
void CreateKeyboardReport(USB_KeyboardReport_Data_t* const ReportData) | |||
{ | |||
uint8_t UsedKeyCodes = 0; | |||
/* Clear the report contents */ | |||
memset(ReportData, 0, sizeof(USB_KeyboardReport_Data_t)); | |||
// Add shortcuts for copy, paste, and delete | |||
// PB3 à PB0 - PD3 à PD6: touches | |||
if (!(PIND & 0x40)) | |||
{ | |||
// Copy shortcut: Ctrl + C | |||
ReportData->Modifier = HID_KEYBOARD_MODIFIER_LEFTGUI; | |||
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_C; | |||
} | |||
if (!(PIND & 0x20)) | |||
{ | |||
// Paste shortcut: Ctrl + V | |||
ReportData->Modifier = HID_KEYBOARD_MODIFIER_LEFTGUI; | |||
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_V; | |||
} | |||
if (!(PIND & 0x10)) | |||
{ | |||
// letters | |||
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_F; | |||
} | |||
} | |||
</syntaxhighlight> | |||
[[Fichier:Schémas pinb pind.png|centré|vignette]] | |||
<center> | |||
{| class="wikitable" | |||
|+ | |||
!Port | |||
!Raccourcis | |||
! rowspan="4" | | |||
|- | |||
|PB0 | |||
|CTRL + V | |||
|- | |||
|PB1 | |||
|CTRL + C | |||
|- | |||
|PB2; PB3 et PD3; PD6 | |||
|Bouton programmable avec l'interface PC grâce aux scanf | |||
|} | |||
</center> | |||
Schémas PINB/PIND pour le biding des touches (avec les if (!(PIND & 0x20))) : | |||
== '''Prog PC (lib USB) :''' == | |||
Comme base pour le programme PC nous avons utilisé le programme libusb disponible sur la page Wiki du groupe 2. | |||
=== Bibliothèque LUFA-210230 === | |||
- Télécharger la bibliothèque ([http://www.fourwalledcubicle.com/LUFA.php ici]) | |||
- Ajouter l'archive dans le projet et compiler (make et make dfu) afin de tester son bon fonctionnement | |||
=== Installation de la libusb et son kit de développement sur Mac === | |||
- Télécharger [https://www.macports.org/install.php MacPorts] avant d'installer la libus | |||
- Voici la [https://ports.macports.org/port/libusb-devel/ libusb] qui est libusb-devel à utiliser sur Mac | |||
=== Le programme PC: === | |||
Le programme PC va demander au user d'envoyer la touche à programmer par l'user, puis le shortcut à utiliser (une suite de touches).<syntaxhighlight lang="c"> | |||
unsigned char prompt_user_for_key(void) | |||
{ | |||
int c; | |||
printf("Press the key to configure...\n"); | |||
scanf("%d", &c); | |||
return c; | |||
} | |||
unsigned char *prompt_user_for_string(void) | |||
{ | |||
char *s = malloc(15 * sizeof(char)); | |||
printf("Enter a shortcut to bind to the key...\n"); | |||
scanf("%s", s); | |||
for (int i = strlen(s); i < 15; i++) | |||
{ | |||
s[i] = 0; | |||
} | } | ||
return s; | |||
} | } | ||
int test_echo_v2(void) { | |||
char key = prompt_user_for_key(); | |||
char *s = prompt_user_for_string(); | |||
if (strlen(s) > 15) { | |||
printf("Error: string too long\n"); | |||
return -1; | |||
} | |||
unsigned char token[EP_OUT_SIZE] = {key, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], | |||
s[12], s[13], s[14]}; | |||
unsigned char bytes[EP_IN_SIZE]; | |||
printf("nb %d \n", nb_devices); | |||
if (nb_devices > 0) { | |||
if (devices[0].in[0].type != LIBUSB_TRANSFER_TYPE_INTERRUPT) | |||
return -2; | |||
int in = devices[0].in[0].address; | |||
if (devices[0].out[0].type != LIBUSB_TRANSFER_TYPE_INTERRUPT) | |||
return -3; | |||
int out = devices[0].out[0].address; | |||
int size = 0, ressnd, resrcv, i; | |||
ressnd = libusb_interrupt_transfer(devices[0].handle, out, token, EP_OUT_SIZE, &size, DEFAULT_TIMEOUT); | |||
sleep(1); | |||
resrcv = libusb_interrupt_transfer(devices[0].handle, in | LIBUSB_ENDPOINT_IN, bytes, EP_IN_SIZE, &size, | |||
DEFAULT_TIMEOUT); | |||
return 0; | |||
} | |||
return -1; | |||
} | |||
</syntaxhighlight>Du côté du code LUFA: | |||
On va venir mapper la suite de touches (récupérées au préalable dans le Handle_EP_IN() en lisant l'endpoint défini) grâce à cette fonction:<syntaxhighlight lang="c"> | |||
unsigned char convert(unsigned char ascii){ | |||
switch(ascii){ | |||
case 'a': return HID_KEYBOARD_SC_A; | |||
case 'b': return HID_KEYBOARD_SC_B; | |||
case 'c': return HID_KEYBOARD_SC_C; | |||
case 'd': return HID_KEYBOARD_SC_D; | |||
case 'e': return HID_KEYBOARD_SC_E; | |||
// ... | |||
} | |||
} | |||
</syntaxhighlight>Puis on vient récupérer les shortcuts dans la matrice qui a été construite pour les touches:<syntaxhighlight lang="c"> | |||
if (!(PIND & 0x40)) // PD6 | |||
{ | |||
for (int i = 0; i < 4; i++) { | |||
ReportData->KeyCode[UsedKeyCodes++] = EP_DataShortcutsMatrix[0][i]; | |||
} | |||
} | |||
</syntaxhighlight> | |||
== '''Difficultés rencontrées''' == | |||
Problème de compatibilité avec l'OS Mac, souvent nous avons eu des problèmes de liaison entre la carte programmable et nos ordinateurs surtout liés à l'USB-C. Pour contrer certaines problématiques et après beaucoup de recherches. Nous avons réussi à faire évoluer notre système afin d'atteindre notre objectif final (l'installation de LUFA-210230 et lsusb) | |||
== '''Démo finale''' == | |||
[[Fichier:Video.mov|vignette|centré]]Démo des touches (PB1/PB0) qui sont rattachées au ctrl/c et ctrl/v et exemple de programmation d'une touche. | |||
Le how to run est disponible dans le readme sur le git : https://github.com/Weamix/shortcut | |||
== '''Conclusion''' == | |||
Le code final fonctionne avec 2 parties : | |||
1) lib usb (la programmation pc) où le code intéressant se trouve dans : https://github.com/Weamix/shortcut/blob/master/USBprogI2L/proj_echo.c | |||
C'est le programme Host pour envoyer des shortcuts sur la carte | |||
2) la partie lufa keyboard dans : https://github.com/Weamix/shortcut/blob/master/lufa-copy-paste/Demos/Device/LowLevel/Keyboard/Keyboard.c | |||
C'est le programme LUFA qui reçoit les shortcuts et les exécute en tant que clavier | |||
Ce module nous a permis d'appréhender la programmation d'un système embarqué en nous familiarisant avec les concepts de base, tels que la gestion des entrées/sorties, la manipulation des actionneurs, ainsi que la communication avec d'autres composants du système. Nous avons également acquis des compétences pratiques en développant des applications embarquées.De plus, nous avons également appris à lire un schéma avec Kicad ce qui était très important pour le paramétrage des boutons poussoirs ainsi que pour la configuration des entrées/sorties. |
Version actuelle datée du 18 juin 2023 à 14:49
Sujet du projet
Clavier de raccourcis programmables - par Axel Lebas & Wassim Djamaa & Maxime Vitse
Proposition de système
Une carte avec 6 boutons programmables via un fichier de configuration qui est fourni par l'utilisateur. Accompagnée d'une LED.
Contre-proposition
Bonne idée. Votre périphérique USB sera programmé avec une interface comportant un point d'accès OUT pour envoyer les chaînes de caractères à envoyer sur pression des boutons. Il serait intéressant d'écrire ces chaînes dans l'EEPROM de l'ATMega16u2 pour qu'elles persistent une fois le périphérique débranché. Les LED du périphérique doivent permettre d'indiquer quelles touches sont programmées. Le programme de configuration sera écrit avec la bibliothèque libusb-1.0.
Carte
Carte soudée :
Fichiers
Le circuit imprimé à utiliser est le même que celui du groupe 3 avec le clavier de la carte du groupe 2.
Après quelques problèmes avec le connecteur USB C, une variante de la carte du groupe 3 est proposée avec un connecteur USB A.
Projet KiCAD : Fichier:I2L-2022-CLIPET-MATHON-BOUKELLAL version USBA.zip
Trois parties à faire
- Prog SE:
- Jouer avec les LEDs
- Prog USB:
- Avec LUFA, faire agir le contrôleur comme un clavier avec des raccourcis qui sont codé en dur dans le code LUFA.
- Prog PC:
- Avec libusb, créer un programme pour la machine (le PC), qui va permettre d'envoyer à travers une nouvelle interface usb à partir code précédent
- Bien évidemment, le code LUFA sera a adapté pour recevoir les raccourcis et la touche à binder.
Prog SE :
Makefile pour automatiser le processus suivant
Par défaut, le micro processeur a un bootloader qui permet de faire de la programmation (sans passer par le SPI) , l'utilitaire que nous utilisons est DFU/USB.
"5.2 ATMega328p: Chaîne de compilation (2/2)" de https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme.html$ apt install gcc-avr avr-libc dfu-programmer // DFU/USB dfu 6 programmer
$ avr-gcc -mmcu=atmega328p -DF_CPU=8000000UL -c -Wall -I. -Os timer.c -o timer.o // compilation source en un . objet
$ avr-gcc -mmcu=atmega328p -g -lm -Wl,--gc-sections -o timer.elf timer.o // édition des liens (afin d'obtenir un exécutable)
$ avr-objcopy -j .text -j .data -O ihex timer.elf timer.hex // nécessaire pour les utilitaires
$ dfu-programmer atmega16u2 erase
$ dfu-programmer atmega16u2 flash timer.hex
$ dfu-programmer atmega16u2 reset
CC = avr-gcc
OBJCOPY = avr-objcopy
DFU_PROGRAMMER = dfu-programmer
CFLAGS = -mmcu=atmega328p -DF_CPU=8000000UL -c -Wall -I. -Os
LDFLAGS = -mmcu=atmega328p -g -lm -Wl,--gc-sections
all: timer.hex
timer.o: timer.c
$(CC) $(CFLAGS) $< -o $@
timer.elf: timer.o
$(CC) $(LDFLAGS) $< -o $@
timer.hex: timer.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $@
erase:
$(DFU_PROGRAMMER) atmega16u2 erase
flash: timer.hex
$(DFU_PROGRAMMER) atmega16u2 flash $<
reset:
$(DFU_PROGRAMMER) atmega16u2 reset
clean:
rm -f timer.o timer.elf timer.hex
Gestion des entrées-sorties numériques avec avr-gcc
Code : https://github.com/Weamix/shortcut/blob/master/timer.c
1ère version :#include <avr/io.h>
#include <util/delay.h>
int main(void){
CLKSEL0 = 0b00010101; // sélection de l'horloge externe
CLKSEL1 = 0b00001111; // minimum de 8Mhz
CLKPR = 0b10000000; // modification du diviseur d'horloge (CLKPCE=1)
CLKPR = 0; // 0 pour pas de diviseur (diviseur de 1)
DDRB |= 0x01; // direction sortie pour PB0
PORTB &= ~0x01;
PORTD &= ~0x78; // entrees
PORTD |= ~0x78;
while(1){
if ((PIND & 0x40))
PORTB &= ~0x80; // LED éteinte
else
PORTB |= 0x80; // LED allumée
if ((PIND & 0x20))
// open led on pb6
PORTB &= ~0x40; // LED éteinte
else
PORTB |= 0x40; // LED allumée
if ((PIND & 0x10))
// open led on pb5
PORTB &= ~0x20; // LED éteinte
else
PORTB |= 0x20; // LED allumée
if ((PIND & 0x08))
// open led on pb4
PORTB &= ~0x10; // LED éteinte
else
PORTB |= 0x10; // LED allumée
}
}
2nde version :
int main(void){
CLKSEL0 = 0b00010101; // sélection de l'horloge externe
CLKSEL1 = 0b00001111; // minimum de 8Mhz
CLKPR = 0b10000000; // modification du diviseur d'horloge (CLKPCE=1)
CLKPR = 0; // 0 pour pas de diviseur (diviseur de 1)
#if 0
DDRB |= 0x01; // direction sortie pour PB0
PORTB &= ~0x01;
PORTD &= ~0x78; // entrees
PORTD |= ~0x78;
#endif
DDRB |= 0xf0;
PORTB &= ~0xf0;
DDRD &= ~0x78;
PORTD |= 0x78;
// lire pin d sur les bits 40 20 10 et 08
//DDRB &= ~0b00000011; // Entrée pour le bouton
//PORTB |= 0x02; // Configuration de la résistance de tirage
while(1){
if(!(PIND & 0x40)) PORTB |= 0x80; else PORTB &= ~0x80;
}
while(1){
if ((PIND & 0x40))
PORTB &= ~0x80; // LED éteinte
else
PORTB |= 0x80; // LED allumée
if ((PIND & 0x20))
// open led on pb6
PORTB &= ~0x40; // LED éteinte
else
PORTB |= 0x40; // LED allumée
if ((PIND & 0x10))
// open led on pb5
PORTB &= ~0x20; // LED éteinte
else
PORTB |= 0x20; // LED allumée
if ((PIND & 0x08))
// open led on pb4
PORTB &= ~0x10; // LED éteinte
else
PORTB |= 0x10; // LED allumée
}
}
B: 8 sorties
C: > 8 sorties
D: 8 sorties
En général 8 fois 3 entrées, ici une vingtaine de sorties
Registre DDR (data direction register) : sens de la LED
Pour chaque bit du port si le bit correspond à une entrée ou une sortie
Voir PB0 / PB1 sur le schéma KICAD
- Par défaut, BIT à 0 en entrée
- 1 pour sortie
Il faut toucher au bit avec le poids le plus faible.
Rappels sur les opérandes en C
& | sont des opérandes bit à bit pas comme les opérandes logiques dans les if && ou ||
tilde is NOT
Le OU exclusif s'écrit PORTB ^ = 0X01
le & avec le tilde permet un bit à 0
le | sans le tilde permet un bit à 1
2 types de registres
Port B -> sortie
PINB -> entrée
Prog Lufa :
Bibliothèque LUFA
- boucler sur la gestion des événements USB USB_USBTask ;
- éventuellement appeler aussi la gestion d’une classe (e.g. HID_Task) ;
- écrire les fonctions de rappel (callback) pour implanter les fonctionnalités souhaitées.
- Modifier le Makefile de LUFA en mettant MCU en atmega16u2 4 premières lignes à modifier seulement (une à none).
- Modifier dans descriptor.c et descriptor.h
Base pour le projet
Comme base pour le programme LUFA nous avons utilisé le projet LUFA "écho" disponible sur la page Wiki du groupe 2.
Code simple avec des shortcuts mis en dur dans le code
void SetupHardware(void)
{
...
// LEDS : PB7 à PB4
/* Hardware Initialization */
USB_Init();
DDRB |= 0x01; // direction sortie pour PB0
PORTB &= ~0x01;
PORTD &= ~0x78; // entrees
PORTD |= ~0x78;
}
void CreateKeyboardReport(USB_KeyboardReport_Data_t* const ReportData)
{
uint8_t UsedKeyCodes = 0;
/* Clear the report contents */
memset(ReportData, 0, sizeof(USB_KeyboardReport_Data_t));
// Add shortcuts for copy, paste, and delete
// PB3 à PB0 - PD3 à PD6: touches
if (!(PIND & 0x40))
{
// Copy shortcut: Ctrl + C
ReportData->Modifier = HID_KEYBOARD_MODIFIER_LEFTGUI;
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_C;
}
if (!(PIND & 0x20))
{
// Paste shortcut: Ctrl + V
ReportData->Modifier = HID_KEYBOARD_MODIFIER_LEFTGUI;
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_V;
}
if (!(PIND & 0x10))
{
// letters
ReportData->KeyCode[UsedKeyCodes++] = HID_KEYBOARD_SC_F;
}
}
Port | Raccourcis | |
---|---|---|
PB0 | CTRL + V | |
PB1 | CTRL + C | |
PB2; PB3 et PD3; PD6 | Bouton programmable avec l'interface PC grâce aux scanf |
Schémas PINB/PIND pour le biding des touches (avec les if (!(PIND & 0x20))) :
Prog PC (lib USB) :
Comme base pour le programme PC nous avons utilisé le programme libusb disponible sur la page Wiki du groupe 2.
Bibliothèque LUFA-210230
- Télécharger la bibliothèque (ici)
- Ajouter l'archive dans le projet et compiler (make et make dfu) afin de tester son bon fonctionnement
Installation de la libusb et son kit de développement sur Mac
- Télécharger MacPorts avant d'installer la libus
- Voici la libusb qui est libusb-devel à utiliser sur Mac
Le programme PC:
Le programme PC va demander au user d'envoyer la touche à programmer par l'user, puis le shortcut à utiliser (une suite de touches).unsigned char prompt_user_for_key(void)
{
int c;
printf("Press the key to configure...\n");
scanf("%d", &c);
return c;
}
unsigned char *prompt_user_for_string(void)
{
char *s = malloc(15 * sizeof(char));
printf("Enter a shortcut to bind to the key...\n");
scanf("%s", s);
for (int i = strlen(s); i < 15; i++)
{
s[i] = 0;
}
return s;
}
int test_echo_v2(void) {
char key = prompt_user_for_key();
char *s = prompt_user_for_string();
if (strlen(s) > 15) {
printf("Error: string too long\n");
return -1;
}
unsigned char token[EP_OUT_SIZE] = {key, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14]};
unsigned char bytes[EP_IN_SIZE];
printf("nb %d \n", nb_devices);
if (nb_devices > 0) {
if (devices[0].in[0].type != LIBUSB_TRANSFER_TYPE_INTERRUPT)
return -2;
int in = devices[0].in[0].address;
if (devices[0].out[0].type != LIBUSB_TRANSFER_TYPE_INTERRUPT)
return -3;
int out = devices[0].out[0].address;
int size = 0, ressnd, resrcv, i;
ressnd = libusb_interrupt_transfer(devices[0].handle, out, token, EP_OUT_SIZE, &size, DEFAULT_TIMEOUT);
sleep(1);
resrcv = libusb_interrupt_transfer(devices[0].handle, in | LIBUSB_ENDPOINT_IN, bytes, EP_IN_SIZE, &size,
DEFAULT_TIMEOUT);
return 0;
}
return -1;
}
unsigned char convert(unsigned char ascii){
switch(ascii){
case 'a': return HID_KEYBOARD_SC_A;
case 'b': return HID_KEYBOARD_SC_B;
case 'c': return HID_KEYBOARD_SC_C;
case 'd': return HID_KEYBOARD_SC_D;
case 'e': return HID_KEYBOARD_SC_E;
// ...
}
}
if (!(PIND & 0x40)) // PD6
{
for (int i = 0; i < 4; i++) {
ReportData->KeyCode[UsedKeyCodes++] = EP_DataShortcutsMatrix[0][i];
}
}
Difficultés rencontrées
Problème de compatibilité avec l'OS Mac, souvent nous avons eu des problèmes de liaison entre la carte programmable et nos ordinateurs surtout liés à l'USB-C. Pour contrer certaines problématiques et après beaucoup de recherches. Nous avons réussi à faire évoluer notre système afin d'atteindre notre objectif final (l'installation de LUFA-210230 et lsusb)
Démo finale
Le how to run est disponible dans le readme sur le git : https://github.com/Weamix/shortcut
Conclusion
Le code final fonctionne avec 2 parties :
1) lib usb (la programmation pc) où le code intéressant se trouve dans : https://github.com/Weamix/shortcut/blob/master/USBprogI2L/proj_echo.c
C'est le programme Host pour envoyer des shortcuts sur la carte
2) la partie lufa keyboard dans : https://github.com/Weamix/shortcut/blob/master/lufa-copy-paste/Demos/Device/LowLevel/Keyboard/Keyboard.c
C'est le programme LUFA qui reçoit les shortcuts et les exécute en tant que clavier
Ce module nous a permis d'appréhender la programmation d'un système embarqué en nous familiarisant avec les concepts de base, tels que la gestion des entrées/sorties, la manipulation des actionneurs, ainsi que la communication avec d'autres composants du système. Nous avons également acquis des compétences pratiques en développant des applications embarquées.De plus, nous avons également appris à lire un schéma avec Kicad ce qui était très important pour le paramétrage des boutons poussoirs ainsi que pour la configuration des entrées/sorties.