Como visto na parte 1, optei pelo microcontrolador MSP430G2231. O desenvolvimento foi feito com o IAR Kickstart. Para gravar o software na Flash do MSP430 usei a Launchpad.
O programa principal consiste em parar o watchdog (que é ativado automaticamente após um reset), programar as entradas e saídas digitais (pinos não usados são programados como saída para reduzir o consumo), ativar o clock auxiliar (ACLK) de 32KHz dividido por 8 (o que inclui selecionar os capacitores apropriados), programar o Timer A (mais sobre isto adiante) e colocar o microcontrolador no modo LPM3
#define POT BIT0 #define LED BIT2 #define DISPARO BIT3 #define NAO_USADO (BIT4 | BIT5 | BIT6 | BIT7) #define POT_ADC INCH_1; int main( void ) { // Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; // inicia led P1DIR = LED | POT | DISPARO | NAO_USADO; P1OUT = 0; // Ativa o clock de 32KHz BCSCTL1 |= DIVA_3; // ACLK /8 BCSCTL3 |= XCAP_3; // 12.5pF // init timer A CCTL0 = CCIE; // CCR0 interrupt enabled CCR0 = 512; TACTL = TASSEL_1 + MC_1; // ACLK upmode _BIS_SR(LPM3_bits + GIE); // Enter LPM3 w/ interrupt }Para economizar energia, o microcontrolador ficará a maior parte do tempo "dormindo" no modo LPM3, apenas o clock auxiliar e o timer estarão ativos. É isto que faz a instrução _BIS_SR(LPM3_bits + GIE) no final do programa principal. Ao ocorrer uma interrupção o processador entrará no modo ativo, com todos os clocks ativos. Ao final da interrupção, salvo procedimentos especiais, o microcontrolador restaurará o modo anterior (LPM3).
O timer A tem por entrada um clock de 32768/8 Hz. Programado para o upmode com uma contagem máxima de 512, ele gera uma interrupção a cada 1/8 de segundo. A rotina de interrupção é uma máquina de estados:
#define T_LED 2 // tempo de LED aceso em 1/8 seg #define T_PAUSA 8 // tempo entre apagar o LED e disparar a foto em 1/8 seg #define T_DISPARO 2 // tempo que mantem ativo o disparo em 1/8 seg static volatile unsigned int pot; // Timer A interrupt service routine // Ocorre a cada 32768/8/512 = 1/8 segundo #pragma vector=TIMERA0_VECTOR __interrupt void Timer( void ) { static byte estado = 0; static unsigned int cnt = 1; static unsigned int tempo = 10; static unsigned int pot_ant = 0; if (estado == 0) { if (--cnt == 0) { // Inicio de um novo ciclo // solta o disparo P1OUT &= ~DISPARO; // ler o potenciômetro P1OUT |= POT; // alimenta o potenciômetro ADC10CTL0 &= ~ENC; ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; ADC10CTL1 = ADC10SSEL_3 + POT_ADC; ADC10CTL0 |= ENC + ADC10SC; estado = 1; } } else if (estado == 1) { // Leu o potenciômetro, determina o novo tempo int dif = (pot > pot_ant) ? (pot - pot_ant) : (pot_ant - pot); if (dif > 16) { // mudou significativamente a posição unsigned int aux = pot >> 4; if (aux < 10) tempo = 10 + 2*aux; // 10 a 28seg, passo = 2 seg else if (aux < 20) tempo = 30 + 10*(aux-10); // 30 a 120 seg, passo = 10 seg else if (aux < 35) tempo = 150 + 30*(aux-20); // 150 a 570 seg, passo = 30 seg else if (aux < 55) tempo = 600 + 60*(aux-35); // 10 a 29 min, passo = 1 min else tempo = 1800 + 10*(aux-55); // 30 a 75 min, passo = 5 min pot_ant = pot; } cnt = tempo << 3; // converte p/ oitavos de segundo cnt -= T_LED + T_PAUSA + T_DISPARO; estado = 2; } else if (estado == 2) { // Aguardando tempo para acender o LED if (--cnt == 0) { // acende o led P1OUT |= LED; cnt = T_LED; estado = 3; } } else if (estado == 3) { // Aguardando tempo para apagar o LED if (--cnt == 0) { // apaga o LED P1OUT &= ~LED; cnt = T_PAUSA; estado = 4; } } else if (estado == 4) { // Aguardando tempo para disparar foto if (--cnt == 0) { // dispara a foto P1OUT |= DISPARO; cnt = T_DISPARO; estado = 0; } } }A operação é dividida em cinco estados:
- 0: aguardando o inicio de um novo ciclo
- 1: aguardando a leitura da posição do potenciômetro
- 2: aguardando para acender o LED
- 3: aguardando para apagar o LED
- 4: aguardando para disparar a foto
Após alguns testes, decidi que o LED ficaria aceso por 2/8 de segundo, o disparo seria comandado 1 segundo após apagar o LED e que o sinal de disparo ficaria ativo por 2/8 de segundo. Este último parâmetro é importante, já que o acoplador ótico é quem mais consome energia. O valor adotado pressupõe que não será usado o foco automático.
O aguardo da leitura do potenciômetro é feito com o microcontrolador dormindo. A interrupção do ADC (conversor analógico digital) salva o valor lido e retira a alimentação do potenciômetro.
// Interrupção de fim da conversão #pragma vector=ADC10_VECTOR __interrupt void Adc_isr( void ) { pot = ADC10MEM; P1OUT &= ~POT; }A interpretação da leitura do potenciômetro toma alguns cuidados. O ADC é programado para usar o seu clock interno e trabalhar com a escala de 0 a Vcc. O valor lido é entre 0 a 1023 (10 bits), o código ignora pequenas variações na leitura. O valor lido é dividido por 16, ficando entre 0 e 63. Organizei os tempos em faixas contínuas mas com inclinação crescente, para permitir escolher facilmente tempos entre 10 segundos e 75 minutos:
- 10 a 28 segundos, passo de 2 segundos
- 30 a 120 segundos, passo de 10 segundos
- 150 a 570 segundos, passo de 30 segundos
- 10 a 29 minutos, passo de 1 minuto
- 30 a 75 minutos, passo de 5 minutos
Nenhum comentário:
Postar um comentário