SE4Binome2024-3
Description
L'objectif de ce projet est de réaliser un pico ordinateur. Nous allons traiter le développement de la carte mère, un shield arduino ainsi que la programmation d'un ordonnanceur.
Notre carte mère sera alimenté via une alimentation secteur en 5v et le code sera implanté par USB.
Choix technologique
Pour alimenter les cartes filles, nous avons fait le choix de rendre disponible à la fois le 3v3 et le 5v. Pour ce faire nous allons utiliser des connecteurs HE -0 10 pins et non 8 pins dont voici les références :
-
Réalisation d'un shield arduino
Schématique
Routage
PINOUT
LED1 | PC0 |
LED2 | PC3 |
LED3 | PD1 |
LED4 | PD4 |
LED5 | PD7 |
Brasage du shield
shield nu
shield fini
Test du shield
Réalisation carte mère
Schématique
Routage
Programmation ordonnanceur
Initialisation de la pile
void init_pile(int N){
uint16_t tempSP = SP;
SP = task[N].stackPointer;
uint16_t adresse=(uint16_t)task[N].addressFunction;
asm volatile("push %0" : : "r" (adresse & 0x00ff) );
asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
SAVE_REGISTERS();
task[N].stackPointer = SP;
SP = tempSP;
}
La fonction init_pile() sert à charger un processus dans la pile. Cette fonction est appelée dans une boucle au début du main pour initialiser tous les processus.
Interruption de l'ordonnanceur
L'ordonnanceur est appelé à un interval de temps régulier grâce à des interruptions du Timer1.
Avant de passer d'une tâche à une autre (mode Round Robin) il faut d'abord effectuer quelques opérations sur la mémoire du micro-processeur.
ISR(TIMER1_COMPA_vect, ISR_NAKED){ // Procédure d'interruption
/* Sauvegarde du contexte de la tâche interrompue */
SAVE_REGISTERS();
task[currentTask].stackPointer = SP;
/* Appel à l'ordonnanceur */
scheduler();
/* Récupération du contexte de la tâche ré-activée */
SP = task[currentTask].stackPointer;
RESTORE_REGISTERS();
asm volatile ( "reti" );
}
TODO : explication des interruptions (pourquoi en NAKED, chaque fonctions, le reti ...)
Structure d'une tâche
enum States{
AWAKE,
IDLE
};
enum IDLE_TYPE{
IDLE_TYPE_DELAY,
IDLE_TYPE_STABLE
};
typedef union {
int sleeping_time;
} Time;
typedef struct {
enum IDLE_TYPE type;
Time time;
} Etat ;
typedef struct{
uint16_t stackPointer; // stack pointer, défini le début du process
void (*addressFunction)(void); // adresse de l'instruction en cours
enum States state; // état de la fonction (Awake ou Idle)
Etat etat;
}process;
TODO : explication de Etat qui est utile pour la mise en veille des processus
Code de l'ordonnanceur
void scheduler(){
// décrémentation de l'attente de toutes les tâches en IDLE
for(int i=0;i<NB_TASKS;i++){
if(task[i].state==IDLE && (task[i].etat.type==IDLE_TYPE_DELAY)){
task[i].etat.time.sleeping_time -= PERIODE;
if(task[i].etat.time.sleeping_time <= 0){
task[i].etat.time.sleeping_time = 0;
task[i].state = AWAKE;
}
}
}
do{
currentTask ++;
if(currentTask >= NB_TASKS) currentTask = 0; // ordonnanceur en mode Round Robin
}while(task[currentTask].state == IDLE);
}
TODO : explication de l'ordonnanceur : Round robin, explication du IDLE et de l'intérêt de ne pas faire tourner des tâches qui sont en veille
Fonction wait
void wait(int time_ms){
task[currentTask].state = IDLE;
task[currentTask].etat.type=IDLE_TYPE_DELAY;
task[currentTask].etat.time.sleeping_time = time_ms;
TCNT1 = 0;
TIMER1_COMPA_vect();
}
Permet d'endormir un processus pendant un temps donné en ms.
Exemple de tâches
void USART_Init(unsigned int ubrr)
{
/*Set baud rate */
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
/*Enable receiver and transmitter */
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 2stop bit */
UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
void LED_init(){
DDRC |= (1<<LED1) | (1<<LED2);
DDRD |= (1<<LED3) | (1<<LED4) | (1<<LED5);
PORTC |= (1<<LED1)|(1<<LED2);
PORTD |= (1<<LED3)|(1<<LED4)|(1<<LED5);
}
void LED1_blink(){
while(1){
PORTC ^= (1<<LED1);
_delay_ms(1000);
}
}
void LED2_blink(){
while(1){
PORTC ^= (1<<LED2);
wait(100);
}
}
void Serial_Message(){
unsigned char data;
while(1){
data = USART_Receive();
USART_Transmit(data);
}
}
void USART_Transmit(unsigned char data)
{
/* Wait for empty transmit buffer */
while (!(UCSR0A & (1<<UDRE0)))
;
/* Put data into buffer, sends the data */
UDR0 = data;
}
unsigned char USART_Receive(void)
{
/* Wait for data to be received */
while (!(UCSR0A & (1<<RXC0)))
;
/* Get and return received data from buffer */
return UDR0;
}
void spi_activer(void){ // Activer le périphérique
PORTD &= ~(1<<SPI_SS); // Ligne SS à l'état bas
}
void spi_desactiver(void){ // Désactiver le périphérique
PORTD |= (1<<SPI_SS); // Ligne SS à l'état haut
}
uint8_t spi_echange(uint8_t envoi){ // Communication sur le bus SPI
SPDR = envoi; // Octet a envoyer
while(!(SPSR & (1<<SPIF))); // Attente fin envoi (drapeau SPIF du statut)
return SPDR; // Octet reçu
}
void seven_seg(){
while(1){
spi_activer();
spi_echange(0x01);
spi_desactiver();
}
}
Exemple d'utilisation
Clignotement de 5 Leds asynchrones
Affichage clavier avec minicom
Bilan de puissance
Composant | Courant | Puissance |
---|---|---|
74LVC125 | 40 uA | 0.2mW |
Carte SD | 100 mA | 330 mW |
TOTAL | 100,04 mA | 330.2 mW |
Composant | Courant | Puissance |
---|---|---|
AtMega328p | 14mA | 70mW |
ATMega8U2 | 21mA | 105mW |
5xLED | 5*5mA | 5*25 mW |
TOTAL | 60mA | 300 mW |
CARTE | PUISSANCE (maximale) |
---|---|
Mère | 0,63W |
Clavier | 0,17W |
Réseau | 0,15W |
Ecran | 1,735W |
TOTAL | 2,685W |
Liens
Lien du git : https://gitea.plil.fr/vdetrez/SE4_PICO_DETREZ_CART