Cada frame é definido por três palavras de 16 bits, cada uma controlando os 9 LEDs de um plano:
// Esta tabela define a animação // Cade frame determina o estado dos LEDs nos três planos uint16_t const frameTab[][3] PROGMEM = { { 0b100000000, 0b000000000, 0b000000000 }, { 0b010000000, 0b000000000, 0b000000000 }, { 0b001000000, 0b000000000, 0b000000000 }, { 0b000100000, 0b000000000, 0b000000000 }, { 0b000010000, 0b000000000, 0b000000000 }, { 0b000001000, 0b000000000, 0b000000000 }, { 0b000000100, 0b000000000, 0b000000000 }, { 0b000000010, 0b000000000, 0b000000000 }, { 0b000000001, 0b000000000, 0b000000000 }, { 0b000000000, 0b000000000, 0b000000000 }, { 0b111111111, 0b000000000, 0b000000000 }, { 0b000000000, 0b111111111, 0b000000000 }, { 0b000000000, 0b000000000, 0b111111111 }, { 0b000000000, 0b000000000, 0b000000000 }, { 0b111000000, 0b111000000, 0b111000000 }, { 0b000111000, 0b000111000, 0b000111000 }, { 0b000000111, 0b000000111, 0b000000111 }, { 0b000000000, 0b000000000, 0b000000000 }, { 0b100100100, 0b100100100, 0b100100100 }, { 0b010010010, 0b010010010, 0b010010010 }, { 0b001001001, 0b001001001, 0b001001001 }, { 0b000000000, 0b000000000, 0b000000000 } }; #define NUM_FRAMES (sizeof(frameTab)/sizeof(frameTab[0]))A rotina setup() inicia as portas e o Timer1 do ATmega; a rotina loop() não precisa fazer nada:
// Rotinas locais static void portInit(void); static void startTimer1(void); // Iniciação do sketch void setup () { portInit(); startTimer1(); }; // Laço principal do sketch void loop () { // Nada a fazer - toda a ação ocorre na interrupção };As rotinas de iniciação das portas e do Timer1 são as mesmas da biblioteca padrão:
/* * Copyright (c) 2011 - Rolf van Widenfelt, Mitch Altman. Some rights reserved. * * Note: This source code is licensed under a Creative Commons License, CC-by-nc-sa. * (attribution, non-commercial, share-alike) * see http://creativecommons.org/licenses/by-nc-sa/3.0/ for details. */ // Inicia os ports de E/S static void portInit(void) { // note: these MUST be in sync with actual hardware! // note: DDR pins are set to "1" to be an output, "0" for input. // 76543210 //PORTB = 0b00000001; // initial: set to enable pullups on inputs: BUT1 pullup //DDRB = 0b00100000; // inputs: BUT1 (PB0); outputs: SCK (PB5); reserved (PB6, PB7) PORTB = 0x01; // (see above) DDRB = 0x30; // (see above) // 76543210 //PORTC = 0b00000000; // initial: no pullups on inputs //DDRC = 0b00111111; // outputs: CC33-CC12 (PC0-PC5) PORTC = 0x00; // (see above) DDRC = 0x3F; // (see above) // 76543210 //PORTD = 0b00000000; // initial: no pullups on inputs //DDRD = 0b11111100; // outputs: PP3-PP1 (PD2-PD4), CC31-CC11 (PD5-PD7); reserved (PD0/RxD,PD1/TxD) PORTD = 0x00; // (see above) DDRD = 0xFC; // (see above) } // // set up timer1 in "fast PWM" mode 14 (see waveform generation, pg 137 of atmega168/328 doc - doc8271). // // note: this causes TIMER1 to interrupt every 1ms! (TIMER1_OVF) // note: it's super important to call this AFTER arduino environment does it's good (and bad) // things to various TIMER settings. // static void startTimer1(void) { // make sure interrupts are off cli(); // initialize ICR1, which sets the "TOP" value for the counter to interrupt and start over // note: value of 125-1 ==> 1khz (assumes 8mhz clock, prescaled by 1/64) ICR1 = 125-1; OCR1A = 0; // needed?? (no) // // timer1 config: // fast pwm (mode 14) - set WGM13,WGM12,WGM11 bits // prescaler clock/64 - set CS11,CS10 bits // TCCR1A = _BV(WGM11); TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); TCCR1C = 0; // not needed? TIMSK1 = _BV(TOIE1); // enable timer1 overflow interrupt.. now were running! sei(); }A lógica toda está na rotina de interrupção do timer:
// Rotina de interrupção do timer1 // Executada a cada milisegundo ISR(TIMER1_OVF_vect) { static int iRep = 0; // repetições do frame static int iPlane = 0; // plano atual no frame static int iFrame = 0; // frame atual uint16_t plane; uint8_t dbits, cbits; // Pega na Flash o padrão do plano corrente do frame atual plane = pgm_read_word (&frameTab[iFrame][iPlane]); // Acende os LEDs // // pixels 0,1,2 go to IO pins D7,D6,D5 // also, add in 3 bits to turn off all the planes (PP1-PP3) by pulling them high // dbits = ((plane & 0x001) << 7) | ((plane & 0x002) << 5) | ((plane & 0x004) << 3) | (0x7 << 2); PORTD = dbits; // // pixels 3-8 go to IO pins C5-C0 // cbits = ((plane & 0x008) << 2) | ((plane & 0x010)) | ((plane & 0x020) >> 2) | ((plane & 0x040) >> 4) | ((plane & 0x080) >> 6) | ((plane & 0x100) >> 8); PORTC = cbits; // Seleciona o Plano if (iPlane == 0) { PORTD &= ~0x10; } else if (iPlane == 1) { PORTD &= ~0x08; } else if (iPlane == 2) { PORTD &= ~0x04; } // Vai para o próximo plano if (++iPlane == 3) { // Repete o frame iPlane = 0; if (++iRep == (TIME_FRAME/3)) { // Próximo frame iRep = 0; if (++iFrame == NUM_FRAMES) { // De volta ao primeiro frame iFrame = 0; } } } }O primeiro passo é pegar na Flash a palavra de 16bits correspondente ao plano e frame atuais. A rotina pgm_read_word da biblioteca avr-libc faz o trabalho pesado. O passo seguinte é mover os bits desta palavra para os bits que controlam os LEDs nos ports C e D. No port D está também a seleção do plano a energizar. A rotina repete em sequência os planos 0, 1 e 2 até completar o tempo de apresentação do frame, quando passa para o frame seguinte. Ao chegar ao último frame, volta ao primeiro.
Com esta programação a animação é controlada pela estrutura de dados (frameTab), não é necessário alterar o código para mostrar uma animação diferente. Uma evolução (que fica como exercício ao leitor) é pegar os frames da E2Prom ao invés da Flash e acrescentar uma forma de receber a animação pela serial e gravá-la na E2Prom.
O vídeo abaixo mostra o programa em ação.
O sketch está nos arquivos do blog em LedCube2pt.zip.
Um comentário:
Olá gosto muito desse Blog ,
poderia me ajudar, vou começa meu TCC 1.Porém não tenho ideias, pode me dar uma ideia? que esteja dentro de eletrônica e automação
Postar um comentário