collapse

* Posts Recentes

Autor Tópico: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%  (Lida 623 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Este tópico vem na sequência de outro tópico em que este assunto estava a ficar off-topic.

N é igual ao acima,  OCRnx pode ser no minimo zero, e tens 16000000/2 = 8Mhz, mas tens uma onda quadrada com um duty cycle de 50%.

É, acho que percebi mesmo mal o que li. Os nomes dos registos causam-me confusão, ao fim do segundo começo a misturar tudo. Eu sei que está tudo na datasheet. O problema é que está espalhado por toda a datasheet.

Olha que nem por isso... há um capítulo só sobre cada contador e um minúsculo sobre os prescalers. Os nomes parecem esquisitos mas têm uma lógica subjacente.

Uma pergunta objetiva, considerando um uno ou um nano, qual é a mais alta frequência possível para o pwm e ainda conseguir variar o duty cycle entre 0 e 100% ?

O senso foi buscar a informação à secção "Fast PWM Mode" do capítulo sobre o Timer/Counter0 (TC0) da datasheet.

Neste modo o contador (TCNT0) conta sempre de BOTTOM a TOP e depois volta a BOTTOM e repete. BOTTOM é sempre 0 (secção "Definitions") e TOP pode ser 0xFF (se WGM0[2:0] = 011b) ou o valor do registo OCR0A (se WGM0[2:0] = 111b).

Para teres 0 a 100% tens que usar o registo OCR0A como TOP, colocando o valor do duty cycle pretendido no registo OCR0B. Isto obriga a usar o pino OC0B (PD5, ou Arduino pino 5) e não funciona com o OC0A (PD6), pois o registo associado, OCR0A, neste modo está ocupado a servir de TOP. Quando o contador (TCNT0) chega a 0 (BOTTOM) o pino vai a 1 e quando chega a OCR0B (ocorrer o "compare match") vai a 0; depois quando o contador chega a OCR0A o contador volta a 0 (BOTTOM) no clock seguinte levando a saída novamente a 1. E tudo se repete.

Acho que a expressão da datasheet é para o caso do TOP ser 0xFF (256 = TOP + 1). O "TOP + 1" tem a ver com o facto do zero também representar uma fatia de tempo. A expressão geral é

fOCnx_PWM = fclk_IO / (N * (TOP + 1))

Incompletude da datasheet. Portanto quando se usa o registo OCR0A como TOP (definido com 99 para termos 0..100%) e N = 1

fOC0B_PWM = fclk_IO / (1 * (99 + 1)) = 160 KHz

O menor prescaler dá a maior frequência de contagem; o prescaler é um valor pelo qual o clock é dividido (scaled down) antes (pre) de ser usado.
fclk_IO é basicamente o clock de sistema, 16MHz no Arduino. Há um capítulo na datasheet sobre os clocks, e o capítulo do timer/counter também tem uma secção sobre o clock.
1% de duty cycle gera uma onda com impulsos de 62.5ns (1 / 160 KHz / 100) repetidos a cada 6.25us (1/160KHz).

Se meteres o ATmega a 20MHz chegas aos 200 KHz (1% -> 50ns).

Ainda segundo a datasheet, na mesma secção sobre "Fast PWM Mode", não é possível chegar a 0% (pino sempre a zero), pelo que para ter 0% é preciso desligar o PWM e meter o pino a zero. No sketch demonstrativo em anexo faço isso usando o campo "Compare Output Mode" no TCCR0A, que permite desligar o Timer/Counter0 do pino do chip.

Fica ainda a nota de que o Arduino usa o Timer/Counter0 para manter as funções relacionadas com tempo (como a millis()), portanto no programa exemplo essas funções não funcionam. Pode ser usado o Timer/Counter2 que é muito semelhante ao 0. Alterar o programa para funcionar no Timer/Counter2 fica como exercício para o leitor :D
« Última modificação: 14 de Junho de 2018, 18:52 por Njay »

Offline vasco

  • Mini Robot
  • *
  • Mensagens: 331
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #1 em: 21 de Maio de 2018, 11:23 »
Obrigado pela explicação.
Estou mesmo a tentar compreender isto, mas não está a ser fácil.

Sobre o timer0, continua a ser possível usar a função _ms_delay() em vez do delay(), certo ?

Mas não percebo muito bem onde dizem

The maximal possible delay is 262.14 ms / F_CPU in MHz.

Isto quer dizer que o delay máximo dado pelo _ms_delay(nnnnn) são aprox 16.4 ms ?

Stupid men are often capable of things the clever wouldn't dare to contemplate.

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #2 em: 21 de Maio de 2018, 16:00 »
Os timers também me faziam muita confusão, até que um dia tive que me sentar com calma e ler o capítulo do inicio ao fim. Depois ficaram muito fáceis  :D

Sim, a _delay_ms() e _dely_us() continuam a poder ser usadas pois não dependem de timers nem de nenhum hardware excepto do CPU, são implementadas como ciclos só a comer tempo, como isto:

for (int ctr = 100; ctr; ctr--);

Como a implementação usa uma variável  contadora de tamanho limitado (não sei se 16, 24 ou 32 bits), o tempo máximo de delay também é limitado. A divisão pela velocidade do CPU vem mesmo disso, quanto mais rápido menor é o tempo máximo de delay. Sim, com o CPU a 16MHz como no Arduino UNO o máximo da _delay_ms() é 16.38375ms. Mas facilmente dás a volta a isso, esta dá até ~65s mesmo num AVR a 20MHz:

static void DelayMs (uint16_t ms)
{
    while (ms--)
    {
        _delay_ms(0.999);
    }
}

Isto pode ser melhorado para eliminar parte do overhead do controle do while, mas para um delay é meio irrelevante. De qualquer forma não queres fazer delays muito grandes com espera activa, consome CPU.
« Última modificação: 21 de Maio de 2018, 16:04 por Njay »

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.586
  • Helpdesk do sitio
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #3 em: 21 de Maio de 2018, 17:46 »
Essa coisa do delay faz-me confusão.
Já li isso, já usei delays de 500ms e até de 1000ms, e funcionam perfeitamente.

Faz toogle a um led com _delay_ms(1000), vais ver que funciona como é esperado, é só definir o F_CPU antes do #include, o _delay_ms() usa floats(ou pelo menos usava, incluir _delay_ms incorre em 2-3KB extra de flash utilizada.
Avr fanboy

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #4 em: 21 de Maio de 2018, 20:27 »
Já experimentei no passado e não se portava bem se o limite fosse excedido...
Ele só inclui código de floats se o argumento não for uma constante; se for constante ele calcula o valor do contador em compile-time e faz um loop optimizado.

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.586
  • Helpdesk do sitio
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #5 em: 21 de Maio de 2018, 20:35 »
Com O0(O zero) usava floats mesmo com uma constante, mas tenho ideia de nunca ter falhado com valores grandes, mas a primeira coisa que faço em qualquer micro já á muitos anos é meter um timer a correr e ter bases de tempo de 1 e 10ms pelo menos para usar á lá milis do arduino e deixei de usar delays, tento fazer sempre código não blocante(errrr, blocking).

EDIT:

Posso estar a fazer confusão e efectivamente ele chegar a um ponto que se passa, já foi á uns anos.
« Última modificação: 21 de Maio de 2018, 22:27 por senso »
Avr fanboy

Offline vasco

  • Mini Robot
  • *
  • Mensagens: 331
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #6 em: 22 de Maio de 2018, 20:04 »
Provavelmente a maneira mais certa de fazer um loop desses é em assembly, com as otimizações do compilador deve ser muito difícil chegar a um valor exato. No spectrum (z80) era só meter uns NOPs ou outra instrução inocula em sequência. Desde que se faça o disable dos interrupts deve ser estável.

Ainda tenho muita coisa a aprender dos AVR antes sequer de poder pensar em assembly.
Stupid men are often capable of things the clever wouldn't dare to contemplate.

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #7 em: 22 de Maio de 2018, 21:05 »
bloqueante :)

Não substimes o poder de optimização do GCC, Vasco :) Eu comecei nos AVR em assembly mas fiz 1 projecto e passei para o C, sendo extremamente raro ter que recorrer a assembly, porque o GCC é muito bom e o AVR muito rápido na gama dele. Diga-se de passagem que assembly de Z80 também não me é estranho, até tenho publicado um compilador de um sub-set do C para Z80 (para a TI-86) que até nem é muito mau a optimizar (sendo prova disso que cheguei a ser contactado por um developer do SDCC que queria o meu optimizador :), a que chamo um peephole optimizer on steroids).
Bom, mas vejamos:

void test ()
{
    _delay_us(1.5);
    _delay_ms(1.5);
}


Compilado com -Os (optimizar para tamanho reduzido; mesmo rsultado com -O2) para um AVR a 5 MHz resulta em

00000126 <test>:
 126:   82 e0          ldi   r24, 0x02   ; 2
 128:   8a 95          dec   r24
 12a:   f1 f7          brne   .-4         ; 0x128 <test+0x2>

 12c:   83 e5          ldi   r24, 0x53   ; 83
 12e:   97 e0          ldi   r25, 0x07   ; 7
 130:   01 97          sbiw   r24, 0x01   ; 1
 132:   f1 f7          brne   .-4         ; 0x130 <test+0xa>

 134:   08 95          ret


As 1ªas 3 instruções são um _delay_us e as seguintes 4 o _delay_ms. Estes são os ciclos mais compactos que consegues fazer. Por exemplo no 1º caso, para 1.5us, o gajo gasta

  1 clock no load inicial do r24 com 2
  depois faz 3 clocks na 1ª iteração, 1 do dec e 2 do brne (jump para instrução anterior)
  mais 2 clocks na 2ª e última iteração, pois quando não há salto o brne (branch if not equal/zero) gasta 1 clock
  1 + 3 + 2 = 6 clocks @ 5MHz -> 1.2us

Por acaso esperava que ele optasse sempre por demorar um pouco mais do que o valor pedido, quando não dá para ter o valor exacto com um ciclo e a frequência de clock, mas está lá perto (mais uma iteração daria 1.8us, 1.5 fica mesmo no meio entre 1.2 e 1.8 :D).

No caso do _delay_ms são precisos mais de 256 iterações e então o ciclo usa um contador de 16 bits (r24 e r25).

De qualquer forma acho que as _delay_*s foram implementados para serem executados sempre desta forma, num ciclo vazio.
« Última modificação: 22 de Maio de 2018, 21:22 por Njay »

Offline vasco

  • Mini Robot
  • *
  • Mensagens: 331
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #8 em: 22 de Maio de 2018, 22:49 »
Acho que me expliquei mal, não estou a dizer que o gcc é bom ou mau a otimizar, apenas que é difícil para o comum dos mortais fazer código que depois de otimizado pelo gcc dê exatamente o delay que se quer.
É mais fácil (ou pelo menos mais linear) fazer o tunning juntando ou tirando uma instrução ao ciclo.

Os meus devaneios pelo z80 estão a anos-luz desse tipo de coisa, e nunca tive que fazer nada que me levasse a estudar teoria de compiladores.
Stupid men are often capable of things the clever wouldn't dare to contemplate.

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #9 em: 23 de Maio de 2018, 00:29 »
Acho que me expliquei mal, não estou a dizer que o gcc é bom ou mau a otimizar, apenas que é difícil para o comum dos mortais fazer código que depois de otimizado pelo gcc dê exatamente o delay que se quer

Fazer em C, ou em qualquer outra linguagem acima do assembly, um delay por "consumo de ciclos", não me faz sentido nenhum, quer o compilador optimize ou não...

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.586
  • Helpdesk do sitio
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #10 em: 23 de Maio de 2018, 01:22 »
Acho que me expliquei mal, não estou a dizer que o gcc é bom ou mau a otimizar, apenas que é difícil para o comum dos mortais fazer código que depois de otimizado pelo gcc dê exatamente o delay que se quer.
É mais fácil (ou pelo menos mais linear) fazer o tunning juntando ou tirando uma instrução ao ciclo.

Os meus devaneios pelo z80 estão a anos-luz desse tipo de coisa, e nunca tive que fazer nada que me levasse a estudar teoria de compiladores.

Usas um timer para coisas mais exactas, mas erro existe sempre.
Avr fanboy

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #11 em: 24 de Maio de 2018, 18:53 »
Vasco, podes sempre forçar uma instrução assembly no meio do C. Em GCC é mais ou menos assim para instruções sem argumentos:

asm volatile ("sleep");

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.396
    • Tróniquices
Re: Frequência máxima de pwm com controlo de duty cycle de 0 a 100%
« Responder #12 em: 24 de Maio de 2018, 19:02 »
Se pegares num dos AVR com um PLL interno, como os ATtinyxxx (não me lembro se todos), consegues frequências mais altas.
Este PLL gera um clock até 64MHz a partir do clock de sistema, e podes usar esse clock para um contador. Daí que...

fOCxy_PWM = 64 MHz / (1 * (99 + 1)) = 640 KHz
  (untested)

Em todos os casos podes obter maior frequência abdicando de resolução.
« Última modificação: 24 de Maio de 2018, 19:04 por Njay »