Aumentar a resolução de bits PWM

9

Gostaria de aumentar a resolução de bits PWM do Arduino Uno. Neste momento, são 8 bits que considero muito baixos. Isso é possível sem perder a capacidade de interrupções e atrasos?

Koen

EDIT Esta configuração oferece uma resolução de 16 bits

void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS11);                    /* prescaler: clock / 8 */
    ICR1 = 0xffff;                      /* TOP counter value (freeing OCR1A*/
}
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> (16000000/8)/(65535+1)=30.5175Hz
*/

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}
KoenR
fonte

Respostas:

15

O Arduino Uno é baseado em um microcontrolador ATmega382P. Este chip possui dois temporizadores de 8 bits, controlando dois canais PWM cada, e um temporizador de 16 bits, controlando os dois últimos canais.

Você não pode aumentar a resolução dos temporizadores de 8 bits. No entanto, você pode colocar o timer de 16 bits no modo de 16 bits, em vez do modo de 8 bits usado pela biblioteca principal do Arduino. Isso fornecerá dois canais PWM de 16 bits, com uma frequência reduzida de 244 Hz (máximo). Você provavelmente terá que configurar o temporizador por conta própria e não se beneficiará da função fácil de usar analogWrite(). Para obter detalhes, consulte a seção Timer 1 na folha de dados do ATmega328P .

Atualização : Aqui está uma implementação de 16 bits analogWrite(). Funciona apenas nos pinos 9 e 10, pois esses são os únicos pinos conectados ao timer de 16 bits.

/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                    /* no prescaling */
    ICR1 = 0xffff;                      /* TOP counter value */
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

Você pode perceber que a parte superior da sequência do contador está configurada explicitamente. Você pode alterar isso para um valor menor para acelerar o PWM, com o custo da resolução reduzida.

E aqui está um exemplo de esboço ilustrando seu uso:

void setup() {
    setupPWM16();
}

/* Test: send very slow sawtooth waves. */
void loop() {
    static uint16_t i;
    analogWrite16(9, i);
    analogWrite16(10, 0xffff - i);
    i++;
    delay(1);
}
Edgar Bonet
fonte
Uau, muito obrigado, é exatamente isso que eu preciso. Quero que minha resolução PWM seja igual à resolução do meu sensor. Se eu alterar o seu código para <look at my edit>, seria uma resolução de 13 bits? Em caso afirmativo, qual seria a frequência? Eu vou estar dirigindo um motor DC com ele até 244Hz será um pouco menos eu acho
KoenR
@KoenR: Não, o pré-calibrador não afeta a resolução, seu objetivo é diminuir a contagem. Definir o prescaler para 8 fornecerá uma frequência PWM de 30,5 Hz. Se você quiser resolução de 13 bits, conjunto ICR1para 0x1fff, em seguida, sua frequência será 1953 Hz (F_CPU / (TOP + 1)) com um prescaler em 1.
Edgar Bonet
Obrigado pela explicação. Editou minha pergunta para que cubra esses erros. Para que outras pessoas possam vê-lo diretamente. Obrigado!
KoenR
11
@ Edgar Bonet Isso é ótimo, mas não consigo desligar completamente um LED. Estou usando ICR1 = 0x03FFe em 0 estou vendo um pulso minúsculo no osciloscópio o suficiente para acender o led. Alguma ideia?
Davivid 07/03
11
@ David: Sim, você não pode ter um ciclo de trabalho zero. analogWrite16(pin, val)fornece um ciclo de trabalho de (val + 1) / ICR1. Como solução alternativa, o Arduino analogWrite()faz if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. Mas então você não pode obter um ciclo de trabalho de 1 / ICR1 ...
Edgar Bonet
3

Com alguma calibração, você pode somar as saídas de dois canais PWM com diferentes resistores de ponderação. No extremo, você pode usar uma saída para fornecer 8 bits de resolução e escalar a outra para 1/25 de nível e adicioná-los para que o segundo canal cubra um bit de alcance e você (novamente) tenha 16 bits de resolução. Sem imenso cuidado e ajuste, tudo o que você conseguiria seria uma bagunça.
No entanto, dividindo o segundo canal por 16 ou 32, você pode adicionar vários bits extras de resolução PWM. Apenas adicionando 2 canais PWM às saídas analógicas filtradas, você adiciona um bit extra (já que o alcance potencial é dobrado para mV / bit inalterado).
Obviamente (novamente) para cada divisão adicional por 2, você obtém um pouco mais de resolução, mas isso só pode ser realizado por talvez 4, 5 ou 6 bits extras, com requisitos de precisão crescente de resistores de escala e calibração e propensão a erros mais difíceis .

Breve exemplo.
Se um PWM for reduzido para dar 0 - 255 mV em uma etapa de 1 mV, a soma de duas PWMs com amplitude igual daria uma faixa de 0 - 510 mV em etapas de 1 mV.
Se um PWM é reduzido por um fator de 32, em vez de adicionar 255 mV ao intervalo inicial de PWMs, ele adicionaria apenas 8 mV ao extremo superior (0,256.32 = 8 mV, mas a resolução seria 0,03125 (1/32) ) etapas de mV.

Embora isso possa ser alcançado puramente com a soma de resistores e a filtragem RC, o uso de um verão de ampères melhoraria bastante os resultados.

Além disso, a ondulação do PWM pode ser filtrada com um filtro RC simples, mas o uso de um opamp como buffer (ou mesmo apenas um único transistor como seguidor de emissor) forneceria a você 3 ou 5 pólos de filtragem passa-baixo e muito mais chances de obter PWM extra resolução. Eu não inspecionei a "coerência de fase" das saídas do PWM, mas espero que elas se movam em relativa trava de modo que você não obtenha a vantagem de suavizar a adição de duas formas de onda não correlacionadas.

Mais comentários podem ser feitos, se necessário. Pergunte se estiver interessado.

Russell McMahon
fonte
Isso é inteligente! Parece que a biblioteca de síntese de som Mozzi usa esse truque para o chamado modo "HIFI".
Edgar Bonet
Essa é uma grande nós do PWM. Mas isso não suavizaria a forma de onda? Estou perguntando isso, já que você está usando um filtro RC. Não mencionei isso na minha pergunta, mas estou dirigindo um motor DC com <sentimento de vergonha>. Obrigado pela contribuição!
KoenR
@KoenR (fwiw: não vejo nada para me envergonhar.) Não sei de que frequência de resposta / taxa de alteração você deseja na sua saída da ADC. Ou por que você quer N bits ou quão grande é suficiente. Os motores geralmente não serão utilmente controlados por mais de 8 bits - depende da precisão de um aplicativo que você possui. O motor atua como parte de um filtro de suavização devido à indutância. Você precisa dizer que tipo de motor e como é conduzido. E um diagrama de circuitos é essencial. A menos que o motor seja pequeno, você tem um motorista. Um PWM alimentado por motor escovado deve ter um diodo de captura para passar a corrente do motor quando o PWM estiver desligado. Adicionando dois ...
Russell McMahon
... Os PWMs aqui são totalmente factíveis, mas os detalhes do circuito precisam ser conhecidos.
Russell McMahon
Cuidado! Em alguns casos, não é desejável suavizar o PWM com um RC de passa-baixo. Por exemplo, se você conectar a saída do Arduino na porta de um MOSFET, o MOSFET permanecerá frio enquanto for acionado por um PWM limpo. Mas se você suavizar, o MOSFET começará a dissipar muito mais calor. Às vezes isso não é uma coisa boa.
Florin Andrei