sexta-feira, 29 de agosto de 2025

Acionando fita de leds (WS2812) endereçável sem nenhuma biblioteca externa.

Olá amigos! aqui vou demonstrar como acionar a fita de leds WS2812 com o ESP32, embora este exemplo possa também ser utilizado pelo ES8266, e usando o mesmo raciocínio é possível portar este código pra qualquer microcontrolador que consiga rodar numa frequência rápida o suficiente para gerar os sinais. 

Como sempre recomendo a ler o datasheet da fita que você quer acionar pois estes tempos dos bits podem variar conforme o modelo de fita utilizado. Claro que você também pode usar uma biblioteca pronta como a "FASTLED" porem o intuito aqui é desmistificar o pensamento de que é difícil controlar os leds.  

// CLIENTE: microcontroladores-c

// AUTOR: Aguivone M. Fógia
// HARDWARE: ESP32
// CONFIG = FREQUENCIA CPU ->240MHZ  e FREQUENCIA FLASH -> 80MHZ
////////////////////////////////////////////////////
//acionando fita de led WS2812 sem usar nenhuma biblioteca
/////////////////////////////////////////////////////
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void BIT_0()//bit 0
{   // para outros modelos de fitas consultar datasheet
   for(int a=0 ; a<8; a++)//aprox.400ns
   {//só repete
     REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
   }
    for(int a=0; a<16; a++)//aprox.850ns
   {
     REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
   }
}
void BIT_1()//bit 1
{// para outros modelos de fitas consultar datasheet  
   for(int a=0 ; a<17; a++)//aprox.800ns
   {//só repete
     REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
   }
    for(int a=0; a<7; a++)//aprox.450ns
   {
     REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
   }
}
void pacote(char BLUE,char RED,char GREEN)//1 pacote de 24bits
{
    uint32_t dados = (GREEN<<16) + (RED<<8) + BLUE;//verificar sequencia  
    uint32_t aux = 1;
    while(aux<0X1000000)
    {
        if((dados & aux)>1)
        {//bit 1
          BIT_1();
        }
        else
        {
          BIT_0();
        }
        aux = aux<<1;
    }
}
void apagar_fita(long num_leds)
{
  long aux = 0;
  num_leds = num_leds*24; // 24bits por led
   while(aux<num_leds)
    {
      BIT_0();
      aux++;
    }
}
void preencher_fita(long num_leds)
{//com as 3 cores é mais rapido assim // cor branca
  long aux = 0;
  num_leds = num_leds*24; // 24bits por led
   while(aux<num_leds)
    {
      BIT_1();
      aux++;
    }
}
void preencher_cor(char GREEN,char RED,char BLUE,long num_leds)
{//preenche com a cor desejada
  long aux = 0;
   num_leds++;
   while(aux<num_leds)
    {
      pacote(GREEN,RED,BLUE);//só repete o pacote
      aux++;
    }
     
}
void preencher_cor_tempo(char GREEN,char RED,char BLUE,long num_leds,long milisegundos)
{//preenche com a cor desejada de tempo em tempo
  long aux = 1;
   while(aux<num_leds)
    {
      preencher_cor(GREEN,RED,BLUE,aux);//só repete o pacote
      aux++;
      delay(milisegundos);
    }
     
}
void move_ponto(char GREEN,char RED,char BLUE,long Pos)
{
   uint32_t dados = (GREEN<<16) + (RED<<8) + BLUE;//verificar sequencia  
   apagar_fita(Pos-1);    
    uint32_t aux = 1;
    while(aux<0X1000000)
    {
        if((dados & aux)>1)
        {//bit 1
          BIT_1();
        }
        else
        {
          BIT_0();
        }
        aux = aux<<1;
    }
}

void loop()
{
  apagar_fita(500);//limpa fita toda(ver numero de leds totais)
  for(;;)
  {
  // a barra vai sendo preencida ate um determinado led e depois apaga e começa novamente
    preencher_cor_tempo(0,25,0,49,200);
    apagar_fita(100);//se quiser apagar toda a fita colocar o numero total de leds
    delay(500);
    move_ponto(0,25,0,5);
    delay(100);
    move_ponto(0,25,0,10);
    delay(100);
    move_ponto(0,25,0,20);
    delay(100);
    move_ponto(0,25,0,30);
    delay(100);
    move_ponto(0,25,0,40);
    delay(100);
    move_ponto(0,25,0,50);
    delay(100);
  }
}


bom projetos pra vocês!

quarta-feira, 27 de agosto de 2025

Conseguindo melhorar a velocidade dos pinos do ESP32 e ESP8266 - análise de desempenho.

 Olá quando se precisa de tempos na escalas de nanosegundos se torna um problema para muitas placas e módulos utilizados no ARDUINO, então se recorre a módulos mais velozes como o ESP32 ou ESP8266. Porem temos a surpresa de mesmo sendo tão velozes não conseguimos acionar de forma pulsada na escala de nanosegundos ou até 20 microssegundos, pois abaixo disso nestes módulos começa a fazer diferenças os pequenos atrasos de tempo nas rotinas de tempo e com a função "digitalWrite".

configuração do módulo, a 240Mhz,logo cada ciclos de máquina levaria 4,16ns, o que :

Vamos a nosso plano de análise com um trecho de código fonte simples, onde apenas vamos
ligar de desligar o pino 13(D13) do módulo ESP32, a mesma analise vale para o ESP8266.
Note que em vez de colocar pino D0,D1,D2 ... podemos usar os define de sistema BIT0,BIT1,
BIT2 ... ou seja não precisa memorizar qual o valor em hexadecimal da posição de cada
pino. porém o tempo será diferente.

Veja o trecho de código:
//demontrando a diferença entre usar digitalWrite e REG_WRITE
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void loop()
{
  digitalWrite(13, HIGH);
  delayMicroseconds(1);
  digitalWrite(13, LOW);
  delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TS_REG, BIT13); // é um define do arduino que vale 0X2000
  delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
  delayMicroseconds(1);

}

Para o primeiro teste:

//demontrando a diferença entre usar digitalWrite e REG_WRITE
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void loop()
{
  digitalWrite(13, HIGH);
  delayMicroseconds(1);
  digitalWrite(13, LOW);
  delayMicroseconds(1);
  /*REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
  delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
  delayMicroseconds(1);*/

}

Para o segundo teste:

//demontrando a diferença entre usar digitalWrite e REG_WRITE
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void loop()
{
  /*digitalWrite(13, HIGH);
  delayMicroseconds(1);
  digitalWrite(13, LOW);
  delayMicroseconds(1);*/
  REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
  delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
  delayMicroseconds(1);
}

Para o terceiro teste:

//demontrando a diferença entre usar digitalWrite e REG_WRITE
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void loop()
{
  //digitalWrite(13, HIGH);
  delayMicroseconds(1);
  //digitalWrite(13, LOW);
  delayMicroseconds(1);
  /*REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
  delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
  delayMicroseconds(1);*/
}

Para o quarto teste:

//demontrando a diferença entre usar digitalWrite e REG_WRITE
void setup()
{
  pinMode(13, OUTPUT); //pino D13
}
void loop()
{
  /*digitalWrite(13, HIGH);
  delayMicroseconds(1);
  digitalWrite(13, LOW);
  delayMicroseconds(1);*/
  REG_WRITE(GPIO_OUT_W1TS_REG, BIT13);
 // delayMicroseconds(1);
  REG_WRITE(GPIO_OUT_W1TC_REG, BIT13);
 // delayMicroseconds(1);
}
Agora vamos ao resultado dos testes, usando a rotina "delayMicroseconds(1)" :

digitalWrite -> 3,27us em nivel alto e 4,19us em nivel baixo.
REG_WRITE -> 1,54us em nivel alto e 1,54us em nivel baixo.

sem rotinas de tempo :

digitalWrite -> 529ns em nivel alto e 778ns em nivel baixo.
REG_WRITE -> 58ns em nivel alto e 252ns em nivel baixo.

vídeo de demonstração:




Conclusão:
    Veja a diferença que tivemos ao usar "REG_WRITE" em vez de "digitalWrite", dando até 10x
mais rapido em nivel alto e 3X mais rapido em nivel baixo, claro que se pode descer um
pouco mais e fazer em assembly conseguindo algo proximo dos 4,16ns esperado, porém fica
o alerta para aplicações criticas. um dos motivos dessas diferenças entre nível alto e baixo
é devido aos codigos que rodam por baixo dessas funções.
Espero que possa ajudar alguém essa simples e importante análise.