/* Programme de chenillard illustrant l'utilisation de la ligne série. Il est basé sur LED_TIMER pour ce qui est de la manière de faire défiler les patterns d'allumage, avec une petite différence cependant concernant les I/O utilsées pour les LEDs. En effet, PD0 et PD1 correspondent à RXD et TXD. Ces deux LEDs ont donc été transférées sur PC0 et PC1 respectivement. Cible : ATMega8 Quartz : 8MHz Compilateur : avr-gcc (WInAVR) */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> // compteur du nombre d'interruptions timer volatile int tmr_cnt = 0 ; // vu le prescaling et l'étendue du compteur, un intervalle de 100ms correspond à 3 interruptions #define _100_MS 3 // les différentes séquences de patterns d'allumage des LEDs unsigned char seq_simple[] = { 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000 } ; unsigned char seq_2x[] = { 0b10000000, 0b00100000, 0b00001000, 0b00000010, 0b00000001, 0b00000100, 0b00010000, 0b01000000, 0b00000000 } ; unsigned char seq_fill[] = { 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110, 0b11111111, 0b01111111, 0b00111111, 0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000 } ; unsigned char seq_alternate[] = { 0b10101010, 0b10101010, 0b01010101, 0b01010101, 0b10101010, 0b10101010, 0b01010101, 0b01010101, 0b10101010, 0b10101010, 0b01010101, 0b01010101 } ; unsigned char seq_flash[] = { 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000 } ; typedef struct { int size ; unsigned char* patterns; } SEQUENCE_DESC ; SEQUENCE_DESC sequences[] = { {sizeof(seq_simple), seq_simple}, {sizeof(seq_2x), seq_2x}, {sizeof(seq_fill), seq_fill}, {sizeof(seq_alternate), seq_alternate} , {sizeof(seq_flash), seq_flash} } ; // index de la séquence courante volatile unsigned char seq_num = 0 ; // séquence courante volatile unsigned char* sequence ; // index du pattern courant volatile unsigned char pattern_num = 0 ; // echaînement de toutes les séquences ? unsigned char run_all = 1 ; // défilement en pause ? volatile char paused = 0 ; // prototypes des fonctions référencées static void next_sequence(void) ; static void previous_sequence(void) ; /* Interrupt handler pour l'overflow du timer 0 */ SIGNAL (SIG_OVERFLOW0) { unsigned char pattern ; if (paused) return ; // mise à jour du compteur d'interruptions tmr_cnt++ ; // si on est arrivé au nombre nécessaire à l'obtention d'un délai de 0.2s, // il est temps de changer de motif if (tmr_cnt == 2 *_100_MS) { // remise à 0 du compteur d'interruptions tmr_cnt = 0 ; // récupération du pattern courant pattern = sequence[pattern_num] ; // on modifie l'état des sorties avec le pattern courant PORTD = (PORTD & 0x03) | (pattern & 0xFC) ; PORTC = (PORTC & 0xFC) | (pattern & 0x03) ; // avance de l'index du pattern, avec passage à la séquece suivante ou // rebouclage si en fin de séquence if (++pattern_num == sequences[seq_num].size) { // si en mode enchaînement des séquences, on passe à la suivante if (run_all) { next_sequence(); } else { pattern_num = 0 ; } } } } // descripteur de buffer circulaire pour les communications série typedef struct { volatile unsigned char head ; // pointeur d'écriture volatile unsigned char tail ; // pointeur de lecture volatile unsigned char nbchars ; // nombre de caractères en attente volatile unsigned char size ; // taille du buffer } BUFFER_DESC ; // buffer de réception char rxbuff[32] ; volatile BUFFER_DESC rxdesc ; // buffer d'émission char txbuff[32] ; volatile BUFFER_DESC txdesc ; /* Interrupt handler USART Rx complete Déclenchée dès qu'un caractère est disponible dans le registre de réception */ SIGNAL (SIG_UART_RECV) { //void RXC_handler(void) { // lecture du caractère reçu char c = UDR ; // si buffer pas plein, on le stocke if (rxdesc.nbchars < rxdesc.size) { // stockage du caractère rxbuff[rxdesc.head] = c ; // avance du pointeur de tête avec gestion du rebouclage if (++rxdesc.head == rxdesc.size) rxdesc.head = 0 ; // actualisation du nombre de caractères en attente rxdesc.nbchars++ ; } } /* Interrupt handler USART Data Register Empty Déclenchée tant que le registre de transmission est vide */ SIGNAL (SIG_UART_DATA) { char c ; // extrait le caractère à envoyer depuis le buffer c = txbuff[txdesc.tail] ; // met à jour le nombre de caractères en attente dans le buffer --txdesc.nbchars ; // s'il n'y en a plus d'autre, on désactive l'interruption if (txdesc.nbchars == 0) UCSRB &= ~(1 << UDRIE) ; // met à jour le pointeur d'extraction, en gérant le rebouclage if (++txdesc.tail == txdesc.size) txdesc.tail = 0 ; // et envoie le caractère UDR = c ; } /* Initialisation des ports */ void port_init(void) { // configuration du port D // - PD2..PD7 en sortie DDRD = 0xFC ; // - toutes les LEDs éteintes PORTD = 0x00 ; // configuration du port C // - PC0 et PC1 en sortie DDRC = DDRC | 0x03 ; // - toutes les LEDs éteintes (en laissant /RESET à 1) PORTC = 0x40 ; } /* Initialisation de l'USART */ void usart_init(void) { // vitesse : 9600 bauds => UBRR = 51 pour une horloge à 8MHz (cf p 158) #define BAUD 51 UBRRH = (unsigned char)(BAUD >> 8) ; UBRRL = (unsigned char)BAUD ; // format de frame : 8bits, pas de parité, 1 stop bit UCSRC = (1 << URSEL) // sélection de UCSRC (cf DS p 149 & 153) | (1 << UCSZ1) | (1 << UCSZ0) // 8-bits ; // enable Rx et Tx UCSRB = (1 << RXEN) // enable receive | (1 << TXEN) // enable transmit | (1 << RXCIE) // enable Receive Complete interrupt ; rxdesc.head = 0 ; rxdesc.tail = 0 ; rxdesc.nbchars = 0 ; rxdesc.size = sizeof(rxbuff) ; txdesc.head = 0 ; txdesc.tail = 0 ; txdesc.nbchars = 0 ; txdesc.size = sizeof(txbuff) ; } /* Lecture d'un caractère sur l'USART */ unsigned char usart_getc(void) { unsigned char c ; // attente de disponibilité d'un caractère while (rxdesc.nbchars == 0) ; // masquage des interruptions pour éviter une altération du buffer pendant // le traitement cli() ; // extraction du caractère c = rxbuff[rxdesc.tail] ; // mise à jour du pointeur d'extraction, avec rebouclage éventuel if (++rxdesc.tail == rxdesc.size) rxdesc.tail = 0 ; // ... et du nombre de caractères en attente rxdesc.nbchars-- ; // rétablissement des interruptions sei() ; return c ; } /* Envoi d'un caractère sur l'USART */ void usart_putc(unsigned char c) { // si le buffer de transmission est plein, on attend qu'il y ait de la place while (txdesc.nbchars == txdesc.size) ; // masquage des interruptions pendant l'accès au buffer, pour être certain // que personne d'autre ne va le modifier cli() ; // ajout du caractère txbuff[txdesc.head] = c ; // mise à jour du pointeur d'injection, avec rebouclage éventuel if (++txdesc.head == txdesc.size) txdesc.head = 0 ; // si c'est le premier caractère dans le buffer, on active l'interruption // Data Register Empty de l'USART pour commencer à "pomper" le buffer via // le handler de l'interruption if (txdesc.nbchars == 0) UCSRB |= (1 << UDRIE) ; // mise à jour du nombre de caractères en attente txdesc.nbchars++ ; // autorisation des interruptions pour continuer le travail sei() ; } /* Envoi d'une chaîne de caractères sur l'USART */ void usart_puts(char *s) { unsigned char c ; while ((c = *s++) != 0) { usart_putc(c) ; } } /* Initialisation des périphériques */ void init_devices(void) { cli(); // inhibition des interruptions (pour avoir la paix) port_init(); // initialisation de prts usart_init() ; // initialisation de l'UART // MCU Control Register // - interruptions externes sur niveau bas de la ligne // - sleep mode désactivé MCUCR = 0x00; // General Interrupt Control // - INT0 et INT1 inactives GICR = 0x00; // Timer/Counter Interrupt Mask // - activation de l'interrupt d'overflow du timer 0 TIMSK = 0x01; // Timer/Counter 0 Control Register // - pre-scaling réglé sur 1024 TCCR0 = 0x05; sei(); // autorisation des interruptions } /* Passage à la séquence suivante, avec rebouclage en fin de liste */ static void next_sequence(void) { // masquage des interruptions car modification du contexte utilisé par // la handler de TMR0 intertupt cli() ; if (++seq_num == sizeof(sequences) / sizeof (SEQUENCE_DESC)) seq_num = 0 ; sequence = sequences[seq_num].patterns ; pattern_num = 0 ; sei() ; } /* Passage à la séquence précédente, avec rebouclage en début de liste */ static void previous_sequence(void) { cli() ; if (seq_num > 0) { --seq_num ; } else { seq_num = sizeof(sequences) / sizeof (SEQUENCE_DESC) - 1 ; } sequence = sequences[seq_num].patterns ; pattern_num = 0 ; sei() ; } //- -------------------------------------------------------------------------------- int main(void) { seq_num = 0 ; sequence = sequences[seq_num].patterns ; init_devices(); usart_puts( "\fLED chaser started...\r\n" "Commands : <space>, +, -, *\r\n\n" ) ; while (1) { // attente de le prochaine commande unsigned char cmde = usart_getc() ; // analyse et traitement de la commande reçue switch (cmde) { // pause du défilement case ' ' : paused = ~paused ; if (paused) { usart_puts("Paused\r\n") ; } else { usart_puts("Resumed\r\n") ; } break ; // séquence suivante case '+' : cli() ; // on ne doit pas être interrompu pendant le changement run_all = 0 ; // passage en séquence simple next_sequence() ; sei() ; usart_puts("Next sequence selected\r\n") ; break ; // séquence précédente case '-' : cli() ; run_all = 0 ; previous_sequence() ; sei() ; usart_puts("Previous sequence selected\r\n") ; break ; // enchaînement de toutes les séquences case '*' : run_all = 1 ; usart_puts("Playing all sequences\r\n") ; break ; } } }