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.





terça-feira, 17 de junho de 2025

Lendo o ADC do STM32 na IDE arduino

Olá, neste exemplo vou demonstrar como ler 2 entradas analógicas e imprimir pela serial do microcontrolador STM32F103C6T6 . O pino PA10 é o RX (ligado ao TX do PC) e o pino PA9 o TX (ligado ao RX do PC).Veja que o código é bem simples:


#define LED    PC13

  //#define BOTAO  PA7  não sera usado
  #define A0  PA0
  #define A1  PA1

 bool status_led=0;

void setup() {
Serial.begin(9600);
  pinMode(BOTAO, INPUT);  // botao
  pinMode(LED, OUTPUT);  // led de status
  pinMode(A0, INPUT);  // configura entradas analogicas
  pinMode(A1, INPUT);
}

void loop() {
  // variavei para ler AN0 e AN1
  int canal0 = analogRead(A0);
  int canal1 = analogRead(A1);
  // imprime os valores
  Serial.print("canal_A0:");
  Serial.print(canal0);
  Serial.println(",");
  Serial.print("canal_A1:");
  Serial.println(canal1);

    if(status_led == 0) // pisca led
    {
        digitalWrite(LED, HIGH);
        status_led = 1;
    }
    else
    {
        digitalWrite(LED, LOW);
        status_led = 0;
    }  
  delay(300);

}

segunda-feira, 16 de junho de 2025

Comunicação serial 232 modo DMA usando o CUBE IDE no STM32F103C6T6

     Olá, veja a demonstração como usar o recurso DMA do microcontrolador da STM32, pois este recurso permite que a CPU do microcontrolador não pare de funcionar  a cada recepção de caracteres que chegam pela serial. Vale lembrar que também é possível configurar este recuso para transmissão, ou seja pode se colocar um vetor(array) para transmissão e então habilitar (ou não) para que ao finalizar gere uma interrupção de envio de todos os dados. aqui será usado a interface gráfica do CUBE IDE.

    Aqui estou usando o microcontrolador STM32F103C6T6 da placa de desenvolvimento blue pill.

configurando o CUBE IDE :

    Basta seguir as imagens para configurar corretamente. Na primeira imagem da aba "PINOUT & CONFIGURATION -> NVIC Setting"  deve estar habilitado as 2 interrupção de DMA e global, pois uma e para coletar os caractere e a outra é acionada quando o DMA atingir o numero de caracteres configurado, observe que na interface gráfica os pinos configurados são destacados.

    Na aba DMA Settings habilita USART_RX, mas se quiser habilitar pra transmissão use o USART_TX

    Na aba Parameter Settings configure a porta serial. 

    Neste exemplo usei o cristal externo na aba RCC, porem se quiser usar o clock interno fique a vontade, pois a propria IDE faz os ajuste automaticamente neste parâmetros, quando se seta na aba "CLOCK CONFIGURATION" .

Pronto ! agora é codificar

O código fonte :


    Pra quem usa o CUBE IDE sabe que ele irá gerar automaticamente o código da estrutura "main.c" quando se salva as configurações portanto é necessário acrescentar apenas algumas variáveis e as chamadas de interrupções, neste caso utilizei a de recepção completa(HAL_UART_RxCpltCallback) mas existe a opção de avaliar quando se chega metade dos dados(HAL_UART_RxHalfCpltCallback), caso precise validar um pacote grande pode ser util. 
    Na rotina principal não precisaria ter nada, porém coloquei uma variável para demonstrar caso seja necessário fazer alguma ação depois que recebe o vetor com os dados recebidos.

#include "main.h"

#include <stdio.h>

UART_HandleTypeDef huart1;

DMA_HandleTypeDef hdma_usart1_rx;


/* USER CODE BEGIN PV */

/* USER CODE END PV */


/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

static void MX_GPIO_Init(void);

static void MX_DMA_Init(void);

static void MX_USART1_UART_Init(void);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */


/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

#define TX_BUFFER_SIZE 128 // Tamanho do buffer de transmissão


uint8_t RxData[20]; //20 bytes de recepção

uint8_t tem_dados = '0';


char txBuffer[TX_BUFFER_SIZE]; // Buffer para enviar mensagens de debug/resposta


void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)

{ //chegou a metade dos dados pode ser util em alguma aplicação

//HAL_UART_Transmit(&huart1,RxData, 10, 50); //imprime a metade coletada

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{ //chegou a quantidade de dados escolhidos

HAL_UART_Receive_DMA(&huart1,RxData,10); // solicita novamente mais 10 bytes

tem_dados = '1';

HAL_UART_Transmit(&huart1,RxData, 10, 50);

int apaga=0;

while(apaga<20)//limpa pacote

{

RxData[apaga] =' ';

apaga++;

}


}


/* USER CODE END 0 */


/**

* @brief The application entry point.

* @retval int

*/

int main(void)

{


/* USER CODE BEGIN 1 */

/* USER CODE END 1 */


/* MCU Configuration--------------------------------------------------------*/


/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();


/* USER CODE BEGIN Init */

/* USER CODE END Init */


/* Configure the system clock */

SystemClock_Config();


/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */


/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_USART1_UART_Init();

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

HAL_UART_Receive_DMA(&huart1,RxData,10);

/* Infinite loop */

/* USER CODE BEGIN WHILE */

/* USER CODE END WHILE */

HAL_UART_Transmit(&huart1, (uint8_t*)"Sistema Pronto. Aguardando dados...\n\r", strlen("Sistema Pronto. Aguardando dados...\n\r"), HAL_MAX_DELAY);

for(;;)

{

if(tem_dados == '1')

{

tem_dados = '0';

HAL_UART_Transmit(&huart1, (uint8_t*)"chegou dados!", strlen("chegou dados!"), HAL_MAX_DELAY);

}

}

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */

}


/**

* @brief System Clock Configuration

* @retval None

*/

void SystemClock_Config(void)

{

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


/** Initializes the RCC Oscillators according to the specified parameters

* in the RCC_OscInitTypeDef structure.

*/

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

RCC_OscInitStruct.HSEState = RCC_HSE_ON;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

{

Error_Handler();

}


/** Initializes the CPU, AHB and APB buses clocks

*/

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

{

Error_Handler();

}

}


/**

* @brief USART1 Initialization Function

* @param None

* @retval None

*/

static void MX_USART1_UART_Init(void)

{


/* USER CODE BEGIN USART1_Init 0 */

/* USER CODE END USART1_Init 0 */


/* USER CODE BEGIN USART1_Init 1 */

/* USER CODE END USART1_Init 1 */

huart1.Instance = USART1;

huart1.Init.BaudRate = 9600;

huart1.Init.WordLength = UART_WORDLENGTH_8B;

huart1.Init.StopBits = UART_STOPBITS_1;

huart1.Init.Parity = UART_PARITY_NONE;

huart1.Init.Mode = UART_MODE_TX_RX;

huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart1.Init.OverSampling = UART_OVERSAMPLING_16;

if (HAL_UART_Init(&huart1) != HAL_OK)

{

Error_Handler();

}

/* USER CODE BEGIN USART1_Init 2 */

/* USER CODE END USART1_Init 2 */


}


/**

* Enable DMA controller clock

*/

static void MX_DMA_Init(void)

{


/* DMA controller clock enable */

__HAL_RCC_DMA1_CLK_ENABLE();


/* DMA interrupt init */

/* DMA1_Channel5_IRQn interrupt configuration */

HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);


}


/**

* @brief GPIO Initialization Function

* @param None

* @retval None

*/

static void MX_GPIO_Init(void)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* USER CODE BEGIN MX_GPIO_Init_1 */

/* USER CODE END MX_GPIO_Init_1 */


/* GPIO Ports Clock Enable */

__HAL_RCC_GPIOC_CLK_ENABLE();

__HAL_RCC_GPIOD_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();


/*Configure GPIO pin Output Level */

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);


/*Configure GPIO pin : LED_Pin */

GPIO_InitStruct.Pin = LED_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);


/* USER CODE BEGIN MX_GPIO_Init_2 */

/* USER CODE END MX_GPIO_Init_2 */

}


/* USER CODE BEGIN 4 */

/* USER CODE END 4 */


/**

* @brief This function is executed in case of error occurrence.

* @retval None

*/

void Error_Handler(void)

{

/* USER CODE BEGIN Error_Handler_Debug */

/* USER CODE END Error_Handler_Debug */

}


#ifdef USE_FULL_ASSERT

/**

* @brief Reports the name of the source file and the source line number

* where the assert_param error has occurred.

* @param file: pointer to the source file name

* @param line: assert_param error line source number

* @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/* USER CODE BEGIN 6 */

/* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */


Obrigado pela atenção! e até a próxima postagem. Data: 16/06/2025         








           


terça-feira, 3 de junho de 2025

Exemplo de código com comunicação serial para placa BluePill (STM32F103C6T6) no Arduino. (método polling)

 // Código Exemplo para Comunicação Serial com STM32F1 (ex: Blue Pill) usando Arduino IDE

// Autor: Aguivone M.Fogia
// Data: 03/06/2025
// Descrição: Demonstrar envio e recebimento de dados via porta serial (USART1), lendo um botao e acionando led.
//    modo polling

/*
  material necessario
  1. Placa STM32F103C8T6 ou C6T6 (Blue Pill ou similar).
  2. Programador ST-Link V2 ou conversor USB-Serial (para upload e/ou comunicação).
  3. Core STM32 para Arduino IDE instalado (veja instruções no exemplo blink anterior).
  4. Configuração da Placa no Arduino IDE:
     - Ferramentas > Placa: "Generic STM32F103C series" ou similar.
     - Ferramentas > Variant: "STM32F103C6T6"
     - Ferramentas > Upload method: "STLink" ou "Serial".
     - Ferramentas > Porta: Selecione a porta COM correspondente ao seu conversor USB-Serial ou ST-Link (se aplicável para comunicação).

  --- Conexão Física (para USART1) ---
  - Conecte o pino PA9 (TX1) do STM32 ao pino RX do seu conversor USB-Serial.
  - Conecte o pino PA10 (RX1) do STM32 ao pino TX do seu conversor USB-Serial.
  - Conecte o GND do STM32 ao GND do conversor USB-Serial.
  - Alimente a placa STM32 (via ST-Link, USB ou fonte externa).

  --- Nota sobre Portas Seriais ---
  - O objeto `Serial` no core STM32 para Arduino geralmente se refere à USART1 (pinos PA9/PA10).
  - Algumas configurações de placa/core podem mapear `Serial` para a porta USB CDC (Comunicação via cabo USB direto, se a placa tiver bootloader compatível ou hardware USB nativo configurado).
  - Se você precisar usar outras portas seriais (USART2, USART3), use os objetos `Serial1`, `Serial2`, `Serial3`, etc., e conecte aos pinos correspondentes (verifique o pinout da sua placa e a documentação do core).
    - Serial1: PA9 (TX), PA10 (RX)
    - Serial2: PA2 (TX), PA3 (RX)
    - Serial3: PB10 (TX), PB11 (RX)
*/

// --- Configurações ---
  #define LED    PC13
  #define BOTAO  PA7
  long           baudRate = 9600; // Taxa de comunicação serial (bits por segundo)
  // --- Variáveis Globais ---
  String         pacote = ""; // String para armazenar dados recebidos
  unsigned long  tempo_atual;
  unsigned long  tempo_anterior;
  unsigned long  tempo_led = 100;
  int           comando_botao=0;
 
// --- Função Setup: Executada uma vez ---
void setup()
{
  // Inicializa a comunicação serial (USART1 por padrão)
  Serial.begin(baudRate);
  pinMode(LED, OUTPUT); //LED
  pinMode(BOTAO, INPUT);
  // Espera um pouco para a porta serial estabilizar (opcional, mas útil)
  delay(100);
  Serial.println("Comunicação serial com STM32F103C6T6 \n\r");
  Serial.print("Usando botão pra mudar velocidade do led ou a serial\n\r");
}

// --- Função Loop: Executada repetidamente ---
void loop()
{
  // --- Leitura de Dados da Serial ---
  if (Serial.available() > 0) { // Verifica se há dados chegando
    char caracter = Serial.read(); // Lê um caractere
       if(caracter == 'A')//vel1
       {
          Serial.println("velocidade 1 = 100 \n\r");
          tempo_led = 100;
          comando_botao = 0;
          delay(100);//debounce para botao
       }
       if(caracter == 'B')//vel2
       {
          Serial.println("velocidade 2 = 500 \n\r");
          tempo_led = 500;
          comando_botao = 1;
          delay(100);//debounce para botao
       }
       if(caracter == 'C')//vel3
       {
          Serial.println("velocidade 3 = 1000 \n\r");
          tempo_led = 1000;
          comando_botao = 2;
          delay(100);//debounce para botao
       }
     
  }
  else
  {
    if(digitalRead(BOTAO)== 1)
    {
        comando_botao++;
        if(comando_botao > 2)
        {
            comando_botao = 0;
        }
        if(comando_botao == 0)//vel1
       {
          Serial.println("velocidade 1 = 100 \n\r");
          tempo_led = 100;
       }
       if(comando_botao == 1)//vel2
       {
          Serial.println("velocidade 2 = 500 \n\r");
          tempo_led = 500;
       }
       if(comando_botao == 2)//vel3
       {
          Serial.println("velocidade 3 = 1000 \n\r");
          tempo_led = 1000;
       }
       delay(500);//debounce para botao
    }

    tempo_atual = millis();
    if(tempo_atual - tempo_anterior >= tempo_led)
        {
          digitalWrite(LED, !digitalRead(LED));
          tempo_anterior = tempo_atual;
        }
  }

}

quinta-feira, 10 de abril de 2025

Frequencímetro via usb com o ESP32 (com precisão de nanosegundos) - Arduino

     Olá pessoal depois de um bom tempo sem postar nada trago um exemplo de como usar o pino de interrupção do ESP32 para medir frequência com uma precisa até maior do que as de muitos osciloscópios comercias visto que a frequência interna no ESP está com 240MHZ(não precisei de mudar nada pois é padrão), só foi utilizada a biblioteca esp_timer.h.

       Não vou postar aqui o oscilador que foi conectado ao ESP32 pois ele foi apenas para demonstração não sendo o intuito do desse artigo. Só foi usado os pinos GPIO 14(para capturar a interrupção)  e GPIO 13( para o led), o restante já vem pronto na placa ESP32 DEV KIT.

  O código fonte é este:

#include <esp_timer.h>

#define INT1 14  
#define LED 13 
  // led de status

 
uint64_t tempo_anterior = 0;
uint64_t tempo_atual = 0;
uint64_t periodo1 = 0;
uint16_t  numero_amostras = 0;

//interrupção do pino1 
void IRAM_ATTR interrupt1() 
{  
   //tempo_atual = esp_timer_get_time();//micros(); //pega tempo em microsegundos
   tempo_atual = xthal_get_ccount();  // pegar tempo em nanosengundos
   if(tempo_atual>tempo_anterior)
   {
      periodo1 = periodo1 + (tempo_atual - tempo_anterior);
      numero_amostras++;
      if(numero_amostras > 4000)
      {
        // 13us estava dando 3160 aprox logo 243(ticks(clock)) ou seja cada clock = 0,243 ns  
    
        uint64_t periodo_buffer = periodo1/4000;
        Serial.print("periodo(ciclos)= ");
        Serial.print(periodo_buffer);
        Serial.print(" | periodo(us)= ");
        Serial.print(periodo_buffer/226);
        Serial.print(" | frequencia(hz) = ");
        Serial.println(240600000/periodo_buffer);
        periodo1 = 0;
        numero_amostras=0;
      }
   }
   /*else
   { //nem vai precisar e só descartar essa amostra 
      periodo1 = periodo1 + ((0XFFFFFFFFFFFFFFFF - tempo_anterior)+ tempo_atual); // como é de 64 bits fica grande
   }*/
   
  tempo_anterior = tempo_atual;
}

void setup() {
  Serial.begin(115200);  // Inicializa a comunicação serial que comunica com o arduino 
  pinMode(INT1, INPUT);  // Configura o primeiro pino de interrupção
  pinMode(LED, OUTPUT);  // Configura o primeiro pino de interrupção
  attachInterrupt(digitalPinToInterrupt(INT1), interrupt1, FALLING);  // Interrupção a cada  descida do sinal
  Serial.println("inicializado");
}

void loop() { 
   digitalWrite(LED, LOW);
   delay(300);
   digitalWrite(LED, HIGH);
   delay(300);
}

O vídeo de demonstração é este :

Veja a comparação com um multímetro comercial como ficou muito preciso.

formatado por : http://hilite.me/  acessando em 10/04/2025.