Aceleração do temporizador AVR no ATmega328

9

Ao executar no relógio prescaler de 64 no ATmega328, um dos meus timers acelera por motivos desconhecidos em um momento específico da execução.

Estou usando dois temporizadores no ATmega328 para gerar o clock necessário para o TLC5940 (veja abaixo o porquê; isso é irrelevante para a pergunta). TIMER0gera um sinal de relógio usando o Fast PWM ativado OC0Be é configurado da seguinte maneira:

TCCR0A = 0
    |(0<<COM0A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM0A0)    // 
    |(1<<COM0B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM0B0)
    |(1<<WGM01)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(1<<WGM00)
    ;
TCCR0B = 0
    |(0<<FOC0A)     // Force Output Compare A
    |(0<<FOC0B)     // Force Output Compare B
    |(1<<WGM02)     // Bit 3 – WGM02: Waveform Generation Mode
    |(0<<CS02)      // Bits 2:0 – CS02:0: Clock Select
    |(1<<CS01)
    |(0<<CS00)      // 010 = clock/8
    ;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;

TIMER2ajusta uma linha de dados para gerar um pulso de apagamento a cada 256 TIMER0ciclos e é configurada da seguinte maneira:

ASSR = 0;
TCCR2A = 0
    |(0<<COM2A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM2A0)    // 
    |(0<<COM2B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM2B0)
    |(0<<WGM21)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(0<<WGM20)
    ;
TCCR2B = 0
    |(0<<FOC2A)     // Force Output Compare A
    |(0<<FOC2B)     // Force Output Compare B
    |(0<<WGM22)     // Bit 3 – WGM02: Waveform Generation Mode
    |(1<<CS22)      // Bits 2:0 – CS02:0: Clock Select
    |(0<<CS21)
    |(0<<CS20)      // 100 = 64
    ;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
    |(1<<TOIE2);    // Timer/Counter0 Overflow Interrupt Enable

TIMER2chama um ISR em excesso (a cada 256 ciclos). O ISR gera manualmente um pulso de apagamento e um pulso de travamento, se necessário:

volatile uint8_t fLatch;

ISR(TIMER2_OVF_vect) {
    if (fLatch) {
        fLatch = 0;
        TLC5940_XLAT_PORT |=  (1<<TLC5940_XLAT_BIT);        // XLAT -> high
        for (int i=0;i<10;i++)
            nop();
        TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT);        // XLAT -> high
    }
    // Blank
    TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
    for (int i=0;i<10;i++)
        nop();
    TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}

O nop()atraso no código acima é apenas para tornar o pulso mais aparente no rastreamento do analisador lógico. Aqui está a main()aparência do loop na função: envie alguns dados seriais, aguarde o ISR cuidar da trava e faça novamente:

for (;;) {
    if (!fLatch) {
        sendSerial();
        fLatch = 1;
        _delay_ms(1);
    }
    nop();
}

sendSerial()faz alguns envios SPI ( código em pastebin por uma questão de brevidade ). Meu problema é que, após a sendSerial()conclusão, enquanto espera para fLatchser definido como baixo (processado), o temporizador acelera. Aqui está o rastreio do analisador lógico (recortei as áreas em que o mesmo sinal continua a diminuir o gráfico):

insira a descrição da imagem aqui

No lado esquerdo, os canais 0 e 1 mostram o final dos dados SPI sendo enviados. Também à esquerda, no canal 4, você pode ver um pulso em branco. No canal 2, o pulso do relógio toca conforme o esperado. Bem ao redor de onde está a lacuna na imagem, fLatché definido como 1dentro da main()rotina. E logo depois TIMER0acelera cerca de um fator de 4. Eventualmente, o pulso de apagamento e o pulso de travamento são executados (canais 3 e 4, terço direito da imagem), e agora o pulso de clock retoma sua frequência regular e os dados seriais são enviado novamente. Tentei eliminar a delay_ms(1);linha main(), mas os mesmos resultados são obtidos. O que está acontecendo? Devo observar que o ATmega é cronometrado em um cristal de 20Mhz e depois diminuído de 64x usando o seguinte código:

CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);

Para que serve: Estou experimentando o controle do driver de LED TLC5940 : esses chips exigem um relógio externo mais uma redefinição no final do ciclo de clock.

angelatlarge
fonte
Se você tiver um depurador, tente interromper seu código quando o timer estiver muito rápido e leia novamente o registro de configuração desse timer. Depois de encontrar aquele com o valor errado, acione um ponto de interrupção nessa alteração do registro e veja qual parte do seu código está executando errado. Eu acho que o problema está localizado em uma biblioteca externa que você pode usar e que usa esse timer para coisas internas, como atrasos.
precisa saber é o seguinte
Dois problemas: a) eu não tenho um programador JTAG, portanto não tenho como depurar o chip; b) nunca altero o valor do registro do timer após a configuração mostrada acima; portanto, não espero que os valores do registro do timer sejam realmente mudar. Isso é ingênuo?
precisa saber é o seguinte
11
Na verdade, uma biblioteca que você usa pode alterar as configurações do UART. Vejo que você usa uma função sendSerial (). Faz parte do seu código ou é uma biblioteca externa? Pode não ser você quem altera as configurações, mas um pedaço de código dentro de uma biblioteca chamada. Sugiro que você use sua porta serial para produzir os parâmetros de configuração e tente descobrir o que foi alterado. Você também pode procurar a fonte das bibliotecas usadas (se houver) e garantir que elas também não usem esse timer.
precisa saber é o seguinte
11
Além do que o @ Blup1980 sugeriu, outra coisa que vale a pena tentar é remover o TLC5940 para garantir que ele não esteja fazendo nada estranho com a linha do relógio.
11133 PeterJ
@ Blup1980 Não tenho certeza de ver a relevância do UART: não estou usando o USART for SPI, apenas as instalações "regulares" do SPI. sendSerial()é o meu código que envia dados via SPI: ele não toca nos TCCRregistros (controle do temporizador).
angelatlarge

Respostas:

1

Para uma depuração rápida, tentaria fazer o mesmo usando a Biblioteca do Arduino para TLC5940 e ver se está ficando rápido ou não. Se funcionar com a biblioteca, você pode verificar sua fonte e comparar com a sua. Como você está familiarizado com o AVR, você deve converter facilmente a fonte do Arduino em AVR nativo.

Para o caso de você não saber como fazer upload de esboços compilados do Arduino no AVR: Quando você compila seu esboço, ele cria um arquivo hexadecimal (você pode ver a localização exata do arquivo ativando o modo detalhado nas configurações). Você pode enviar esse hex para o seu AVR com o seu programador favorito.

Espero que ajude

superkeci
fonte