TikiWiki CMS/Groupware v1.9.11 -Sirius- © 2002–2008 par la communauté Tiki Sep 08, 2010 [00:40]
CodeFR
Pin ups
thumbnail
Mars 2010
Menu [cacher]
  Wiki
  Blogues
  Forums
  QCMs
Mégaphone
hixcod, 12:10, Jan 09, 2010: Happy New year !
MooZ, 09:41, Jan 05, 2010: Bonne année! (erm...)
MooZ, 13:51, Jan 05, 2009: Bonne année!
MooZ, 07:19, Jan 04, 2008: Diantre! Je me suis fait doublé moi aussi. Bonne année quand même (et il faut que je trouve une pinup).
Axioplase, 15:39, Jan 03, 2008: Merde, Froggy m'a devancé sur 2008 !
Froggy, 22:21, Jan 01, 2008: Super mega cool
MooZ, 12:34, Oct 27, 2007: Yeah! Seven!
Seven, 16:41, Oct 18, 2007: Prout !
MooZ, 09:47, Mars 05, 2007: Feed the wiki comme dirait l'autre!
hixcod, 19:26, Mars 04, 2007: et moi donc
pages pointant cette page imprimer pdf
similairediapositivescommentaire
Programmation et systèmes embarqués

1. Introduction

On retrouve des logiciels embarqués a peu prêt dans tous les matériels électroniques; que ce soit des matériels médicaux, assistants numériques, imprimantes, four micro-ondes, téléphones portables, voitures, missiles, sondes spatiales, etc. Tous ces matériels sont spécifiques, souvent uniques, spécialisés dans un domaine particulier, et demandent parfois beaucoup de temps, de connaissances, et d’expérience pour pouvoir en tirer le meilleur parti.
Les langages les plus répandus pour faire leurs logiciels embarqués sont et seront encore pour quelques temps les langages C et C++. Les systèmes embarqués existent depuis l’invention du premier microprocesseur dans les années 70 et leur nombre est en constante augmentation, ils font déjà parti de notre quotidien, sans même parfois que vous vous en aperceviez. Fort est à parier que de plus en plus de personnes travailleront dans ce domaine, peut-être vous même, qui sait…

2. Qu’est ce qu’un système embarqué ?

Un système embarqué est une combinaison de hardware, software, et souvent de parties mécaniques (comme pour une imprimante par exemple) créées pour réaliser une fonction spécifique et connue. C’est pour cette dernière raison que l’on ne considère pas un ordinateur personnel comme un système embarqué et ce malgré ses parties mécaniques, hardware et software. En effet l’ordinateur personnel a une utilisation générique, on ne sait pas à l’avance ce que va en faire l’utilisateur, c’est sa généricité qui le fait sortir de la catégorie.
Dans un système embarqué on ne doit pas remarquer la présence de processeurs et de logiciel, le système a pour but d’être transparent et de simplifier la vie, exemple le four micro-onde ou les imprimantes.
Un système embarqué n’est généralement pas seul, c’est souvent qu’une partie d’un ensemble; dans un téléphone mobile par exemple, on retrouve souvent un système embarqué applicatif, responsable des applications et contenant ou non un système d’exploitation temps réel et un système embarqué spécialisé dans la communication, les 2 parties communicants par un bus quelconque ou une zone de mémoire partagée.
Notez qu’il existe 2 grandes familles de systèmes embarqués, les systèmes embarqués temps réel et ceux qui ne le sont pas.

2.1 Les systèmes embarqués non temps réels

Ces systèmes sont tout simplement des systèmes sans OS temps réels, c’est à dire des systèmes qui n’ont pas de contraintes temporelles significatives. On retrouve ces systèmes dans des matériels qui ont de grosses contraintes de taille mémoire et/ou peu de nécessité de protection mémoire ou des systèmes dit non critiques comme une calculatrice, des logiciels de tests bas niveau ou certains firmwares.

2.2 Les systèmes embarqués temps réels

Ceux là ont des contraintes de temps fortes, la nécessité de répondre à un événement en un temps connu est souvent due à :

• La sauvegarde de l’intégrité matérielle du système : si le temps n’est pas respecté le système est endommagé, exemple: un gestionnaire de pompe pressurisée, si l’événement qui dit que la pression doit être relâchée n’est pas traité dans les 10 secondes qui suivent la pompe peut être dégradée par la trop forte pression.

• La sauvegarde des personnes dépendantes du système embarqué : comme le système ESP/ABS/airbag des voitures. (Nécessité de réaction rapide, donc due une contrainte relative à la fonctionnalité attendue du système : vous sauver la vie… on alors dit que le système à des « hard deadlines ».)

• La fonctionnalité attendue : dans le cas d’un téléphone portable la nécessité de traiter rapidement les signaux GSM reçus pour pouvoir gérer une communication.

Pour informations il existe plusieurs OS temps réels utilisés dans l’embarqué : VRTX, RTXC, PSOS, RTAI, RTLinux, OSEK, uCOS, Nucleus, QNX, etc. Chacun avec leurs spécificités propres et leur « footprint » (utilisation mémoire) associée.

Le développeur d’un système embarqué doit donc être minutieux dans son travail et prévoir tous les cas d’utilisation possibles, un système embarqué ne se relance pas toutes les heures ou tous les mois, il peut ne pas s’arrêter pendant des années et la vie de personnes peut dépendre de la bonne exécution du programme. C’est pour ça que l’on retrouve souvent des méthodes de spécification, de développement et de tests très stricts dans le monde de l’embarqué.

3. Composition, fonctionnement et contraintes

Contrairement a un PC, ordinateur a tout faire, un logiciel embarqué ne peut en general pas tourner sur un autre systemes embarqués sans grandes modifications, tout ca parce que le hardware sousjacent n'est jamais le même. Le hardware dans chaque systeme embarqué est adapté aux fonctions de l'application, et tout le hardware superflu est elliminé.
D'un façon générale un systeme embarqué contient un processeur et un logiciel embarqué, bien sur pour faire tourner ce software on va aussi trouver de la ROM (pour garder le tout, tout le temps en memoire, l'equivalent d'un disque dur), et de la RAM (parcekilenfo), quelle soit interne au processeur ou externe selon les besoins.
En plus de ces grand classiques on va retrouver tout un ensemble de peripheriques liés a la fonction que le systemes embarqué doit remplir. Ainsi on peut retrouver un ecran LCD, un keypad, un systemes GPS, un capteur de temperature, un lecteur de SD card, un bras articulé, un emeteur radio accessible via un bus SPI (Serial Peripheral Interface), etc...

Chaque système embarqué est le resultat d'un ensemble de contraintes, et est donc en général unique. Une de ces contraintes peut être le coût, si le système doit avoir un coût inferieur a 50 euros alors les choix technologiques, de consommation de puissance ou encore de fiabilité peuvent en patir pour addresser ce besoin.
Voici quelques elements qui peuvent aussi influer sur le design:

- La puissance de calcul:
La puissance nécéssaire pour que le système embarqué fonctionne comme il se doit, 8, 16 ou 32 bits, avec telle ou telle quantité de MIPS. En general une fois choisi la technologie de la decoule directement la quantité de mémoire que l'on peut inclure, si on a choisi un processeur 8 bits, et bien a premiere vue on n'aura que 256 zones memoires uniques.
En general les systemés embarqués sont souvent construit avec de vieux processeurs qui sont bon marché.

- Le memoire:
La quantité de memoire necessaire pour contenir le logiciel embarqué et ses données. Il faut estimer le mieux possible ces quantité. La quantité de memoire necessaire peut influer sur le choix du type de processeur puisque c'est lui qui defini la limite haute de memoire accessible.

- Le cout de developpement:
Le coup de design du hardware et du software. C'est un coup fixe, on n'a a le faire qu'une seule fois, et il peut etre derisoire par rapport au volume de vente et/ou de fabrication.

- Le nombre d'unité:
Le nombre d'unité produite. Il est generalement pas necessaire de developper son propre hardware pour de petite quantité, le coup de developpement etant alors plus important que les gains.

- Le temps de vie:
Combien de temps va vivre le systeme ? un mois, un an, des années ? Cela affecte aussi les choix dans le design, de la selection des composants hardware a combien rique de couter le systeme a developper et produire.

- La fiabilité:
Quelle fiabilité doit avoir le produit ? Si c'est un pauvre jouet pour enfant, le systeme n'a pas besoin de marcher toujours parfaitement, mais si c'est pour une fusée, une voiture, c'est autre chose, le systeme a plutot interet a faire parfaitement ce qu'il est cencé faire. Forcement plus la fiabilité doit etre grande plus le cout de developpement va etre grand.

4. Exemple de système embarqué: Motorola MX1 ADS

Cette board ADS est orienté PDA, mais on le retrouve aussi dans des baladeurs MP3, une console (Zodiac), et moult autres bidules electroniques. Voici a quoi ressemble l'ADS iMX1 et le connecteur pour debugger:

img/wiki_up//Picture 1.jpg img/wiki_up//Picture 2.jpg

Et voici son block diagram, c'est a dire en gros un schema vu de trés haut :) Vous pouvez voir ainsi comment sont reliés les elements par rapport aux autres.

img/wiki_up//dbmx_block.JPG

Voici quelques specificités du chip :

• ARM920T Microprocessor Core
• AHB to IP Bus Interfaces (AIPIs)
• External Interface Module (EIM)
• SDRAM Controller (SDRAMC)
• Clock Generation Module (CGM) and Power Control Module
• Three Universal Asynchronous Receiver/Transmitters (UART 1, UART 2 and UART 3)
• Two Serial Peripheral Interfaces (SPI)
• Two General-Purpose? 32-bit Counters/Timers
• Watchdog Timer
• Real-Time? Clock/Sampling Timer (RTC)
• LCD Controller (LCDC)
• Pulse-Width? Modulation (PWM) Module
• Universal Serial Bus (USB) Device
• Multimedia Card and Secure Digital (MMC/SD) Host Controller
• Memory Stick® Host Controller (MSHC)
• SmartCard? Interface Module (SIM)
• Direct Memory Access Controller (DMAC)
• Two Synchronous Serial Interfaces and Inter-IC Sound (SSI/I2S) Module
• Inter-IC (I2C) Bus Module
• Video Port
• General-Purpose? I/O (GPIO) Ports
• Bootstrap Mode
• Analog Signal Processing (ASP) Module
• Bluetooth Accelerator (BTA)
• Multimedia Accelerator (MMA)
• Power Management Features
• Operating Voltage Range: I/O voltage 1.7 V to 3.3 V, core voltage 1.7 V to 2.0 V
• Packaging: 256-pin MAPBGA PBGA

Pour plus d'info : http://e-www.motorola.com/webapp/sps/site/prod_summary.jsp?code=i.MX1&nodeId=018rH3ZrDR

Voici le memory mapping du chip, le memory mapping est specifique a chaque chip, il permet de savoir ou sont placés en memoires les registres des peripheriques et des bus, et aussi ou est mappé la ram, la rom et la flash.

img/wiki_up//dbmx_mmap.JPG

5. Programmation embarquée et langage C

Maintenant que vous avez tout compris a comment est architecturé un systeme embarqué, on peut faire un premier programme tout bête.
Bon on n'a pas de stdout par defaut, ca se saurait, donc on va eviter le simple printf hello world, mais pourquoi pas afficher une image sur l'ecran LCD.

img/wiki_up//Picture 3.jpg

/*****************************************************************
*
*   MX1 LCD Demo code for Sharp TFT 640x480 for ADS
*
*   Display a 640x480 16bpp background
*
*****************************************************************/

#include <rt_misc.h>
#include <stdio.h>

/*****************/
/*****Defines*****/
/*****************/

/*****LCD Characteristics*****/

#define LCD_WIDTH 640
#define LCD_HEIGHT 480

/*****Image*****/

// Background image start location and characteristics
// the image can be created using the bmp2bin.exe or bmp24to16.exe
// utilities available on the MX1 website
#define IMAGE_ADDRESS 0x0C100000
#define IMAGE_WIDTH 640
#define IMAGE_HEIGHT 480
#define IMAGE_BPP 16


/*****Registers*****/
// System
#define CSCR    (*(volatile unsigned int *)(0x0021B000))
#define PCDR    (*(volatile unsigned int *)(0x0021B020))
// GPIO
#define DDIR_C  (*(volatile unsigned int *)(0x0021C200))
#define DDIR_D  (*(volatile unsigned int *)(0x0021C300))
#define OCR2_C  (*(volatile unsigned int *)(0x0021C208))
#define DR_C    (*(volatile unsigned int *)(0x0021C21C))
#define GIUS_C  (*(volatile unsigned int *)(0x0021C220))
#define GIUS_D  (*(volatile unsigned int *)(0x0021C320))
#define GPR_C   (*(volatile unsigned int *)(0x0021C238))
#define GPR_D   (*(volatile unsigned int *)(0x0021C338))
// LCDC
#define SSA     (*(volatile U32 *) (0x00205000)) // Screen Start Address Register
#define SIZE    (*(volatile U32 *) (0x00205004)) // Size Register
#define VPW     (*(volatile U32 *) (0x00205008)) // Virtual Page Width Register
#define CPOS    (*(volatile U32 *) (0x0020500C)) // LCD Cursor Position Register
#define LCWHB   (*(volatile U32 *) (0x00205010)) // LCD Cursor Width Height and Blink Register
#define LCHCC   (*(volatile U32 *) (0x00205014)) // LCD Color Cursor Mapping Register
#define PCR     (*(volatile U32 *) (0x00205018)) // Panel Configuration Register
#define HCR     (*(volatile U32 *) (0x0020501C)) // Horizontal Configuration Register
#define VCR     (*(volatile U32 *) (0x00205020)) // Vertical Configuration Register
#define POS     (*(volatile U32 *) (0x00205024)) // Panning Offset Register
#define LGPMR   (*(volatile U32 *) (0x00205028)) // LCD Gray Palette Mapping Register
#define LSCR1   (*(volatile U32 *) (0x00205028)) // Sharp Register
#define PWMR    (*(volatile U32 *) (0x0020502C)) // PWM Contrast Control Register
#define DMACR   (*(volatile U32 *) (0x00205030)) // DMA Control Register
#define RMCR    (*(volatile U32 *) (0x00205034)) // Refresh Mode Control Register
#define LCDICR  (*(volatile U32 *) (0x00205038)) // Interrupt Configuration Register
#define LCDISR  (*(volatile U32 *) (0x00205040)) // Interrupt Status Register

/*****Macros*****/
// On/Off using Port C bit 16
#define LCD_ON DR_C |= 0x00010000
#define LCD_OFF DR_C &= 0xFFFEFFFF


int main ()
{

    /************************/
    /*****Initialization*****/
    /************************/

    /*****System Clock*****/

    // CSCR ### Change BCLK (CPUCLK) to 96MHz (Div = 1)
    // BCLK Divider = [13:10] + 1
    CSCR = 0x2F008003;

    // PCDR ### Change PerCLK2 (LCDCLK) to 48MHz (Div = 2)
    // PerCLK2 Divider = [7:4] + 1
    PCDR = 0x000B001B;


    /****************************/
    /*****GPIO Configuration*****/
    /****************************/

    /** Set Port C bit 16 to function as on/off (set = LCD on) **/
    GIUS_C = 0x00010000;
    OCR2_C = 0x00000003;
    DDIR_C = 0x00010000;
    GPR_C  = 0x00010000;
    LCD_OFF;

    /** Configure Port D pins [30:6] to LCD signals **/
    DDIR_D |= 0x7FFFFFC0;
    GIUS_D &= 0x8000003F;
    GPR_D  &= 0x8000003F;


    /****************************/
    /*****LCD Initialization*****/
    /****************************/

    // Disable LCDC
    RMCR = 0x00000000;

    // Screen Start Address Register - "top left corner" of the image
    SSA = IMAGE_ADDRESS;

    // Configure dimensions of panel: [23:20] = Width / 16 , [8:0] = Height
    SIZE = ((LCD_WIDTH / 16) << 20)|
            (LCD_HEIGHT << 0));

    // Virtual Page Width: image_width * bpp / 32
    VPW = IMAGE_WIDTH * IMAGE_BPP / 32;


    //Cursor Controls
    /*
    CPOS = 0x40010001;
    LCWHB = 0x1F1F0000;
    LCHCC = 0x0000f800;
    /**/

    // Panel Control Register ### tft ,color, 16bpp, PCD = 1 (+1)
    PCR = ( (1 << 31)|  // TFT enable
            (1 << 30)|  // Color enable (set for TFT)
            (3 << 28)|  // Panel Bus Width, 0=1 bit,1=2,2=4,3=8 (does not matter for TFT)
            (4 << 25)|  // BPP, 0=1bpp,1=2,2=4,3=8,4= 12 or 16
            (0 << 24)|  // Pixel Polarity, 0 = Active High, 1 = Active Low
            (1 << 23)|  // First Line Marker Polarity, "
            (1 << 22)|  // Line Pulse Polarity, "
            (1 << 21)|  // LCD Shift Clock, 0 = Active Falling, 1 = Active Rising (reverse for TFT)
            (0 << 20)|  // Output Enable Polarity, 0= Active High, 1 = Active Low
            (0 << 19)|  // LSCLK Idle Enable (when VSYNC is idle in TFT mode)
            (0 << 18)|  // Endian Select, 0 = Little, 1 = Big
            (0 << 17)|  // Swap Select, 0 = 16bpp, 1 = else
            (0 << 16)|  // Reverse Vertical Scan
            (0 << 8)|   // ACD Stuff
            (1 << 7)|   // LSCLK Select, 0 = Disable OE & LSCLK when no data output in TFT mode for power savings, 1 = Always on
            (1 << 6)|   // Sharp Panel Signals Enable
            (1 << 0));  // Pixel CLK Divider, actual divider is PCD value + 1

    // HCR ### Controls HSYNC signal, for other LCDs you will need to determine these values based off of spec
    //  note- H_WAIT2 derived experimentally due to lack of documentation, other values taken from previous demo code
    HCR =   ((1 << 26)|     // H_WIDTH + 1 = width of Horizontal Sync Pulse in Pixel CLK periods
             (16 << 8)|     // H_WAIT1 + 1 = delay in Pixel CLK periods between OE & HSYNC
             (102 << 0));   // H_WAIT2 + 3 = delay between HSYNC & first data of next line


    // VCR ###
    VCR =   ((1 << 26)|     // V_WIDTH = Vertical Sync pulse width in HSYNC periods
             (0 << 8)|      // V_WAIT1 = delay between OE and VSYNC (in TFT)
             (34 << 0));    // V_WAIT2 = delay between VSYNC and OE of the first line of next frame

    /****************/
    /*****LCD ON*****/
    /****************/

    // LCDC Enabled, self-refresh mode disabled
    RMCR =  ((1 << 1)|  // LCDC Enable
             (0 << 0)); // Self-refresh mode enable
    // Power On
    LCD_ON;

    return;
}


Chiant ? Ca c'était que l'écran... et une image uniquement fixe.

6. Compilation, liage, téléchargement et déboguage

Je vais passer sur la compilation, vous savez tous ce que c'est. Le link est beaucoup plus interressant. bon le but du link c'est de resoudre les symboles, jusque la tout va bien, les addresses sont associé grace a un fichier de link, le linker file (ou scatter file) qui specifie ou se trouve les sections en memoire.
Voici un exemple de linker file pour ARM:

FLASH 0x24000000 0x4000000
{
    FLASH 0x24000000 0x4000000
    {
        init.o (Init, +First)
        * (+RO)
    }

    32bitRAM 0x0000
    {
        vectors.o (Vect, +First)
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }

    STACKS 0x40000 UNINIT
    {
        stack.o (+ZI)
    }
}


mais il peut y en avoir de beaucoup plus complexes:

//CodeWarrior linker command file for Rainbow MCore.

MEMORY {
    ESRAM (RWX) : ORIGIN = 0x00100000 , LENGTH = 0x00000800
    ERAM  (RWX) : ORIGIN = 0x07800000 , LENGTH = 0x00014000
    RAM   (RWX) : ORIGIN = 0x08000000 , LENGTH = 0x00800000
    ROM1  (RX)  : ORIGIN = 0x10000000 , LENGTH = 0x00020000
    ROM2  (RX)  : ORIGIN = 0x10020000 , LENGTH = 0x007E0000
    ROM3  (RX)  : ORIGIN = AFTER(ROM2), LENGTH = 0x00000000
}

KEEP_SECTION { .INT_vectors }

SECTIONS {

    _STACK_SIZE_    = 0x00004000; # 16 KB
    _HEAP_SIZE_     = 0x00100000; # 1  MB

    .start :
    {
        WRITEW _start;
        WRITEW 0x00000000;
        WRITEW 0x00000000;
        WRITEW 0x00000000;
        WRITEW 0x00000000;
    } > ROM1

    .text :
    {
        . = ALIGN(0x400);
        * (.INT_vectors)

        #####################
        # main_app
        #####################
        *(.text)

        _NEXT_GROUP_ADDR_ = .;

    } > ROM2

    .data : AT( _NEXT_GROUP_ADDR_ )
    {
        #####################
        # main_app_data
        #####################
        * (.rodata)

        . = ALIGN(0x4);
        __data_ROM_begin = .;
        * (.data)
        __data_ROM_end = .;

        * (.vtables)
        * (.exception)

        . = ALIGN(0x8);
        __exception_table_start__ = .;
        EXCEPTION
        __exception_table_end__ = .;

        ___sinit__ = .;
        STATICINIT

        #####################
        # main_app_bss
        #####################
        __bss_begin = .;
        * (.bss)
        __bss_end =.;

    } > ERAM


    __stack_end     = .;
    __stack_begin   = __stack_end + _STACK_SIZE_;
    __stack_begin   = (__stack_begin + 7) & ~7;

    __heap_addr     = __stack_begin;                # see MSL alloc.c
    __heap_end      = __heap_addr + _HEAP_SIZE_;
    __heap_size     = __heap_end - __heap_addr;

    __data_begin    = __data_ROM_begin;             # set to shadow RAM address
    __data_size     = __data_ROM_end - __data_ROM_begin;
}


Le linker file file permet de specifier ou se trouve le stack (call, push pop and co.), ou se trouve le heap (malloc and co.) et ou sont stocké les differents données, de .text (code) a .data (donnée) en passant par .bss (static, init).
Ainsi on peut faire des programmes qui tournent uniquement en ram interne, en ram externe ou pour une flash.

La plupart des outils de developpement, que ca soit GNU ou autre, rajoutent lors du link ce que l'on appelle un startup code. Le startup code est un petit bout de code assembleur qui permet de preparer l'excution du programme. L'espace pour le stack doit etre alloué et initialisé avant qu'un programme en langage de plus haut niveau soit executé, de meme les copies ROM/RAM doivent etre effectué a cet endroit pour pouvoir placer le code au bon endroit en memoire avant execution. La plupart des startup code effectue les operations suivantes dans cet ordre :
- Desactiver des interruptions
- Copier ROM to RAM
- Mettre a zero la zone uninitialized data
- Allouer l'espace pour le stack
- Initialiser le stack pointer
- Creer et initialiser le heap
- Executer les constructeurs et initialiser les variables globales
- Activer les interruptions
- Appeller main

Voici un exemple de startup code pour DSP Starcore SC140

Un startup code peut donc etre assez complexe, et il est indispensable, la plupart du temps un exemple ou un startup code par defaut est fourni.

Pour ce qui est du telechargement et du debug, lors du debug on telecharge le soft sur la board via le debugger, donc via le device de debug connecté a la board. Le moyen de connection est soit par port serie avec une interface debugger software embarqué en plus sur la board, soit directement sur un connecteur JTAG (protocole de debug) via un Multi ICE pour ARM ou un module JTAG.
Lorsque l'application est fini, le seul moyen de l'envoyer sur le board est en general via un mode bootstrap sur la board c'est a dire un mode ou tout ce qui est envoyé sur l'uart est placé en memoire (moyennant un protocole de bootstrap pour placer tout ca ou on veut) ou alors on peut utiliser un programme de flashing, fourni avec la board, qui se bootstrap mais permet de stocker son programme en flash.
Dans l'embarqué les environnement de developpement sont nombreux, ceux de debug un peu moins, un outils trés connu dans le monde de l'embarqué est CodeWarrior? de Metrowerks, qui fournit un environnement de developpement pour beaucoup de processeurs embarqués et de consoles.

img/wiki_up//cdw.JPG

7. Conclusion

Voila, j'espere que vous avez maintenant un meilleure vision du monde l'embarqué. Ce fut un survol rapide, un exemple trés simple, et peut etre dans un prochain article je vous exposerai comment marche un OS embarqué et comment adapter/porter un OS embarqué sur une board.
D'ici là, salut et bon code.

Créé par: Seven dernière modification: Lundi 08 of Mars, 2004 [21:27:57 UTC] par Seven


Utilisateurs connectés
Il y a 3 utilisateurs connectés
Hack a Day
    Clubic news
      Utilise Tikiwiki Utilise PHP Utilise Smarty Utilise ADOdb Utilise CSS Utilise RDF
      RSS Wiki RSS Blogues rss Articles RSS Galeries d'images RSS Galeries de fichiers RSS Forums
      [ Temps d'exécution: 0.69 secs ]   [ Mémoire utilisée: 30.04MB ]   [ GZIP Enabled ]   [ Charge du serveur: 0.00 ]