Développement d'un driver de news avec VxWorks
Par Bayoumi Chaïmaa et Comte-Gaz Quentin (encadré par M. Kessal)
 Tout Structures de données Fichiers Fonctions Variables Pages
Différentes étapes du développement d'un driver de news avec VxWorks

Partie 1 : Apprentissage de Workbench, et utilisation de tâches et sémaphores


1) Tâche qui s'endort periodiquement (dans notre cas : toute les 3 secondes):


int IDtache1;
int systick_freq; // Nombre de ticks par seconde
void init(void){
systick_freq=sysClkRateGet();
IDtache_news=taskSpawn("test",101,0,5000,(FUNCPTR)test,10,0,0,0,0,0,0,0,0,0);
taskDelete(MOI);
}
void test(int variable_quelquonque){
while(1){
printf("Variable quelquonque : %d\nZzZzZzZ\n",variable_quelquonque);
taskDelay(3*systick_freq); // Attente de 3 sec
}
}


2) Deux tâches dont une (tperiodique) synchronisant la deuxième (tesclave) :


Dans cet exemple, la priorité n'a pas un rôle primordial (cela risque uniquement de changer l'ordre des tâches, en aucun cas elles ne bloqueront (pas de famine) !

int IDtache1;
int IDtache2;
void init(void){
// Récupération de la fréquence des ticks
systick_freq=sysClkRateGet();
// Initialisation des taches
IDtache1=taskSpawn("tperiodique",102,0,5000,(FUNCPTR)tperiodique,0,0,0,0,0,0,0,0,0,0);
IDtache2=taskSpawn("tesclave",101,0,5000,(FUNCPTR)tesclave,0,0,0,0,0,0,0,0,0,0);
taskDelete(MOI);
}
void tperiodique(void){
while(1){
printf("tperiodique (id %d)\n",IDtache1);
taskDelay(3*systick_freq); // Attendre 3sec
taskResume(IDtache2);
}
}
void tesclave(void){
while(1){
printf("tesclave (id %d)\n",IDtache2);
taskSuspend(IDtache2);
}
}

3) Etude du fichier partie1-3.c :


Le code de l'application fourni dans le fichier "partie1-3.c" ne gérait pas le cas où le scheduler donnait la main à une autre tâche pendant que celle-ci édite des ressources critiques (var1 et var2).
Il faut donc proteger ces ressources à l'aide d'un sémaphore :

SEM_ID semaphore1;
void init(void)
{
semaphore1=semBCreate(SEM_Q_PRIORITY,SEM_EMPTY);
...
}
void codeTache1(void)
{
while(TRUE)
{
semTake(semaphore1,WAIT_FOREVER);
var1=compteur;
var2=compteur;
semGive(semaphore1);
compteur++;
}
}
void codeTache2(void)
{
while(TRUE)
{
semTake(semaphore1,WAIT_FOREVER);
if ((var1==var2)) printf("var1 = %d var2=%d\n",var1,var2);
semGive(semaphore1);
taskDelay(1); // Attente d'un tick de l'OS
}
}


4) Image de VxWorks :

Etant donné que des fichiers .c et .h du noyau VxWorks sont manquant sur notre ordinateur, il n'a pas été possible de générer l'image de VxWorks...
Heureusement qu'il y a deux binômes par carte !

Partie 2 : gestion des interruptions UART 2 :

Dans cette partie, nous allons concevoir un code permettant de recevoir via l'UART2 des caractères (interruption).
Nous les mettrons dans un buffer afin de recevoir un paquet complet (67 octets).
Quand celui-ci est plein, nous regardons le 'numéro du canal' de ce paquet afin de l'envoyer (avec une mailbox) dans la tâche correspondante.

Voici le code correspondant :

// ------ Tâche active ------
#define MOI 0
// ------ Adresses des registres de l'UART 2 ------
#define adr_base_uart2 0xFF400000+0x4600
#define URBR2 ((volatile unsigned char*)(adr_base_uart2+0x00))
#define UTHR2 ((volatile unsigned char*)(adr_base_uart2+0x00))
#define UDLB2 ((volatile unsigned char*)(adr_base_uart2+0x00))
#define UDMB2 ((volatile unsigned char*)(adr_base_uart2+0x01))
#define UIER2 ((volatile unsigned char*)(adr_base_uart2+0x01))
#define ULCR2 ((volatile unsigned char*)(adr_base_uart2+0x03))
#define ULSR2 ((volatile unsigned char*)(adr_base_uart2+0x05))
// ------ Include ------
#include <stdio.h>
#include <taskLib.h>
#include <semLib.h>
#include <logLib.h>
#include <sysLib.h>
#include <intLib.h>
#include <iv.h>
// ------ Variables globales ------
int position_dans_buffer; // Si -1 : nous n'avons toujours pas trouvé le 1er élément
char buffer[67]; // Buffer temp (reception d'un paquet complet avant d'envoyer via mailbox)
// ------ Déclaration des tâches ------
void serveurIT(int);
void init_global(void);
void init(void);
void init_irq(void);
void news(void);
void news2(void);
// ------ Déclaration de l'ID des tâches ------
int IDtache1;
int IDtache2;
int IDtache_news;
int IDtache_news2;
// ------ Déclaration des mailbox ------
MSG_Q_ID news_box;
MSG_Q_ID news_box2;
// Intialisation des tâches ET de l'interruption permettant de récupérer
void init_global(void){
init();
init_irq();
taskDelete(MOI);
}
// Fonction INTERNE permettant uniquement l'initialisation des tâches
void init(void){
// Initialisation des deux mailboxes (l'une pour l'id 0 et l'autre pour l'id 1)
// Nous avons 2 mailbox qui contiennent chacune au maximum 1 message de 67 char
// On aurait pu mettre plus de messages par mailbox afin d'eviter d'avoir des pertes.
news_box=msgQCreate(1,67,MSG_Q_FIFO);
news_box2=msgQCreate(1,67,MSG_Q_FIFO);
// Initialisation des tâches
IDtache_news=taskSpawn("news",101,0,5000,(FUNCPTR)news,0,0,0,0,0,0,0,0,0,0);
IDtache_news2=taskSpawn("news2",101,0,5000,(FUNCPTR)news2,0,0,0,0,0,0,0,0,0,0);
printf("OS initialisé\n");
}
// Fonction INTERNE permettant uniquement l'initialisation de l'UART2 (avec IRQ RX)
void init_irq(void){
STATUS erreur;
position_dans_buffer=-1;
// Acceder aux registres de configuration
*ULCR2=0x80;
// Baudrate de 9600 bits/sec
*UDLB2=0xB7;
*UDMB2=0x06;
// Parité
*ULCR2=0x03;
// Activation UART2 RX IRQ
erreur=intConnect(10,serveurIT,0);
if(erreur!=OK)
printf("ERREUR intConnect\n");
*UIER2=0x01;
intEnable(10);
}
// Interruption de l'UART2
void serveurIT(int x){
// Récupération du char recu
char valeur = *URBR2;
if(position_dans_buffer==-1){ // 1er élément trouvé ? Est-ce le début de la trame ?
if(valeur==0x7F){
position_dans_buffer=0;
buffer[0]=0x7F;
}
}
else{ // Le premier élément a déjà été trouvé
position_dans_buffer++;
buffer[position_dans_buffer]=valeur;
if(position_dans_buffer==66){ // Fin de la trame
position_dans_buffer=-1;
if(buffer[1]==0){ // Si numéro canal = 0 --> tache1
if(msgQSend(news_box,buffer,sizeof(buffer),NO_WAIT,MSG_PRI_NORMAL)==ERROR)// Envoyer la trame complète dans la mailbox associée
logMsg("Error send mailbox (canal 0)\n",0,0,0,0,0,0);
else
logMsg("OK send mailbox (canal 0)\n",0,0,0,0,0,0);
}
else if(buffer[1]==1){ // Si numéro canal = 1 --> tache1
if(msgQSend(news_box2,buffer,sizeof(buffer),NO_WAIT,MSG_PRI_NORMAL)==ERROR)// Envoyer la trame complète dans la mailbox associée
logMsg("Error send mailbox (canal 1)\n",0,0,0,0,0,0);
else
logMsg("OK send mailbox (canal 1)\n",0,0,0,0,0,0);
}
else
logMsg("Probleme : mauvais numero de canal\n",0,0,0,0,0,0);
}
}
//logMsg("%c",valeur,0,0,0,0,0);
}
// Tâches affichant les trames de news (tâche news--> canal 0, tâche news2--> canal 1) :
void news(void){
int nb_octets_recus;
char buffer2[67];
while(1){
nb_octets_recus=msgQReceive(news_box,buffer2,67,WAIT_FOREVER);
if(nb_octets_recus==67){
printf("TACHE 1 (canal 0) :\nTrame de news du canal %d :\nNuméro du paquet : %c%c%c\nNews : %s\n",buffer2[1],buffer2[2],buffer2[3],buffer2[4],buffer2+6);
}
else{
printf("Pas assez d'éléments dans le msgQueue de\n");
}
}
}
void news2(void){
int nb_octets_recus;
char buffer2[67];
while(1){
nb_octets_recus=msgQReceive(news_box2,buffer2,67,WAIT_FOREVER);
if(nb_octets_recus==67){
printf("TACHE 2 (canal 1) :\nTrame de news du canal %d :\nNuméro du paquet : %c%c%c\nNews : %s\n",buffer2[1],buffer2[2],buffer2[3],buffer2[4],buffer2+6);
}
else{
printf("Pas assez d'éléments dans le msgQueue\n");
}
}
}

Partie 3 : Développement d'un driver de news UART

Nous allons maintenant développer un driver permettant de gérer les canaux de news afin de les associer à une tâche.
Par exemple, si nous souhaitons écouter uniquement ce qu'il se passe dans le canal 0, il faudra initialiser le service complet de news puis activer le canal 0 (et l'associer à une tâche).

...................................


Vous trouverez ici la description des différentes fonctions crées :
Fonctions du driver

Vous trouverez ici la description des différentes structures crées :
Structures du driver


Voici maintenant le code complet du driver :

//code à mettre ici