Registradores – ESP32
Com o sucesso gigantesco do ESP32 que veio para bater de frente dos PIC32 e STM32, ganhando em alguns aspectos como velocidade, não podemos deixar de lado a manipulação de registradores que não estavam presentes, de maneira fácil, no ESP8266. Neste tutorial, vamos aprender a manipular os registradores de porta (GPIO) do ESP32 de forma simples para efetuar um Blink extremamente compacto e o mais rápido possível.
[toc]
O que são registradores em um microcontrolador?
São pequenos endereços de memória que nos permitem, via software, controlar pinos, processos, interrupções e coisas do tipo de forma extremamente eficiente. Podemos dizer que isso se assemelha ao assembly, visto que é um dos itens de mais baixo nível que conseguimos controlar diretamente pelo código e com isso temos uma eficiente forma de programação em questão de velocidade e confiança.
O ESP32 conta com centenas de registradores de 32bits para que possamos manipular com eficiência todo o hardware disponível, todas as informações estão disponíveis no Datasheet que estará ao fim deste tutorial.
Observação importante: em alguns casos de registradores como o correspondente ao famoso “pinMode()” que declara um pino como saída por exemplo, há mais de um registrador que faz essa mesma tarefa e também mais formas de escrever ou ler registradores, entretanto, sobre os registradores, usaremos os registradores atômicos, que nos garantem a escrita ordenada em um ambiente Triple-Core.
Mãos a obra – Piscando um LED através da manipulação de registradores
Componentes necessários
- 1x – ESP32.
- 1x – LED (usaremos o LED Onboard).
Código do projeto
void setup() { REG_WRITE(GPIO_ENABLE_REG, BIT2);//Define o GPIO2 como saída } void loop() { REG_WRITE(GPIO_OUT_W1TS_REG, BIT2);//GPIO2 HIGH (set) delay(250); REG_WRITE(GPIO_OUT_W1TC_REG, BIT2);//GPIO2 LOW (clear) delay(250); }
Colocando para funcionar
Entendendo a fundo
Software
Quem esta acostumado com registradores pode estranhar esse jeito de manipula-los, parecendo muito mais fácil do que em outros microcontroladores. A verdade é que “REG_WRITE” é uma macro para facilitar a manipulação dos registradores que é definida no arquivo “soc.h”, lá você também encontra macros como REG_READ que é usada para leitura de registradores, REG_SET_FIELD e etc.
Os três registradores usados são:
- GPIO_ENABLE_REG (Figura 1): Registrador que habilita o GPIO(0-31) como saída.
- GPIO_OUT_W1TS_REG (Figura 2): Registrador que define o GPIO(0-31) em HIGH (Vcc). (SET)
- GPIO_OUT_W1TC_REG (Figura 3): Registrador que define o GPIO(0-31) em LOW (GND). (CLEAR)
Uma forma bem comum de se utilizar registradores para pinos, é a manipulação direta (nesse caso há a 32 bits, logo, 32 pinos) de uma única vez em uma linha! Com isso é possível economizar varias linhas e também deixar o código mais rápido. Se você pretende definir dois pinos como saída, a sentença ficará dessa forma (serve para os outros registradores também):
REG_WRITE(GPIO_ENABLE_REG, BIT2 + BIT4);//Define o GPIO2 e GPIO4 como saída
Esses são os registradores básicos para manipular um pino de saída, se você pretende ler pinos de entrada, terá que usar outros registradores que estão detalhados no Datasheet.
Observação: alguns registradores não estão com os nomes definidos nos arquivos do ESP32, logo, você não conseguirá manipular o registrador pelo nome (igual feito acima com GPIO_ENABLE_OUT e etc). Para manipular os registradores que não estão definidos, é necessário pegar o endereço do registrador na memória que se encontra no Datasheet. Veja como ficaria a manipulação sem o nome definido:
REG_WRITE(0x3ff44020, BIT2);//Define o GPIO2 como saída while (1) { REG_WRITE(0x3ff44008, BIT2);//GPIO2 = HIGH delay(250); REG_WRITE(0x3ff4400C, BIT2);//GPIO2 = LOW delay(250); }
Considerações finais
Manipulando diretamente os registradores, conseguimos fazer tarefas com uma extrema velocidade e confiança, sendo necessária para vários projetos. Leia bem o Datasheet se você pretende dominar este incrível microcontrolador e boa sorte com os registradores.
Referências
Todos os registradores, informações e detalhes sobre o ESP32 se encontram nesse Datasheet:
http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
Estudante de Engenharia da Computação pela USC, pretende se aprimorar e fazer a diferença nesta imensa área da tecnologia. Apaixonado por IoT, sistemas embarcados, microcontroladores e integração da computação nos mais diversos fins práticos e didáticos.
17 Comments
Deixe uma pergunta, sugestão ou elogio! Estamos ansiosos para ter ouvir!Cancelar resposta
Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.
Bom dia Jose!!
Estou tentando implementar seu código, mais estou com dificuldades em achar o header soc.h poderia me dizer qual o path?
Hey Jose, great article. Could you please also tell how to define an analog input?
Hi,
Found some info on the internet how to read from an input register.
It seems I have to enable the bits in the register (only need 3 bits) by writing a clear (for INPUT) in the Enable register. What happens to the other bits in the ENABLE register.
Henk
If you just set reg. like 0x3FFxxxx = 1, you are writing “0” to all other 31 bits, it’s just a logic. If you need to keep last value in bit of reg, use more logic (OR) with pointers! like this:
(*(volatile uint32_t *)(0x3FFxxxx)) |= (1<<24);
Mesma duvida com relação ao timer.
Quando eu compilo no arduino IDE da erro falando que TIMGn_T0CONFIG_REG não existe (n = 0 ou n = 1)
Olá José Morais.
Sua ajuda foi muito boa e eu já tenho a configuração de status GPIO. Foi uma boa ajuda também me ajudou, que eu também posso verificar os estados nas saídas do GPIO.
Dê o código talvez alguém vai tirar vantagem disso.
#define stepPinR ((GPIO_REG_READ (GPIO_IN_ADDRESS) & (BIT (5)))! = 0) // leia o estado HIGH / LOW pin 5 z – sinal do comparador. Gostaria de pedir ajuda para definir o timer usando o registro. Eu gostaria de fazer isso:
1 – ajuste TIMER usando o registrador (TIMGn_T0CONFIG_REG).
2 – TIMER iniciar usando o registrador (TIMGn_T0LO_REG).
3 – TIMER parar de usar a lista
4 – reiniciando o timer por meio de um registrador.
Eu entendo que o TIMER é um ciclo de clock de 1 CPU e eu conectei que é o suficiente para eu medir um temporizador baixo.
Eu fiz muitos testes, mas o ARUINO IDE ainda me mostra que estou cometendo erros usando a lista de documentos do dokumuntacja.
Por favor ajude.
Futek
Valeu José Morais ! Eu vou dar uma lida melhor no datasheet e no set de instruções também. Minhas placas com ESP32 estão quase chegando. Assim que chegarem vou começar a colocar a mão na massa. Mas primeiro vou seguir os passos de exemplos prontos como os seus. Só depois vou partir para os meus próprios programas.
Eu tenho que corrigir porque não mostra o que é importante:
void Time_Echo(){
stopTimeEcho = ESP.getCycleCount();
if (stopTimeEcho 0 )
{
Serial.println(” “);
Serial.print(” meserTimeEcho : “);
Serial.print(meserTimeEcho);
Serial.println(” uS”);
}
To jest treraz dobrze
Olá José Morais.
Obrigado por explicar o tópico.
Mas quanta neblina para obter a precisão do tempo quando um pulso de eco aparece?
Eu só uso o IDE ARDUINO e o que você me recomenda é que eu tenha que aprender quanto menos interessado.
Mas talvez você tenha alguma outra maneira simples de medir o tempo com o timer nos registradores, o programa é acionado e o pulso é encerrado com o comparador TLV3501.
Eu estou pedindo ajuda, tanto quanto possível.
Peço desculpas pela minha fraca língua portuguesa, mas estou usando o tradutor do GOOGLE.
Atenciosamente
Futek
Primeiramente, pode usar inglês se preferir ao invés do tradutor para PT.
Normalmente é um erro fatal não querer aprender algo quando se pretende fazer algo mais complexo, mas você pode tentar utilizar os registradores para manipular os HW TMR, ainda sim creio que não funcione tão bem para 1us.
Se você usar a manipulação do HW TMR através de uma ISR (o mais rápido que podemos fazer), ainda sim isso é lento no ESP32. As interrupções utilizadas nele com C, são chamas de interrupções de alta latência e são bem lentas infelizmente. Talvez você precise aprender sobre as interrupções de baixa latência que são utilizadas apenas em Assembly, veja nesse link: https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/hlinterrupts.html
José Morais, parabéns pela matéria ! Essa parte de manipulação direta dos IO’s me interessa muito. Eu já havia aprendido a lidar com elas nos Atmega, e estava curioso a respeito do funcionamento no ESP32. Seu artigo foi de enorme ajuda para eu ter um ponto de partida. Eu sei das implicações de falta de portabilidade, ao manipular diretamente os registradores, mas eu gosto de fazer isso assim mesmo.
Ahhh se você tivesse colocado aqui os registradores para leitura das portas também !
Depois sobre PWM, Timers, ADC, etc…
Curso super avançado, né ?
A ideia das coisas que escrevo é apenas dar um norte, “um ponto de partida”. Gosto que vocês aprendam o resto, entretanto, citei sobre a macro REG_READ(_r). Com ela você pode ler registradores como de input (GPIO_IN_REG e …_IN1_…). O jeito mais simples de testar a leitura de um registrador, é ler o registrador de números aleatórios. Procure no datasheet sobre isso e lá é citado o valor de memória que se deve ler, mostre esse valor no Serial monitor e veja funcionar :D
Obrigado pelos elogios e boa diversão com o Super ESP32!
Olá José Morais.
Obrigado pela informação.
Preciso que demore algum tempo após o qual o sinal ECHA aparecerá.
Porque no ESP32 ele cria uma caneta TDR simples.
https://forum.arduino.cc/index.php?topic=183770.msg1365676#msg1365676
Mas o projeto está em ARDUINO UNO e isso não garante boa precisão, apesar do fato de que o autor usou TIMER e REGISTER.
O código do programa já funciona no ESP32. mas eu tenho um problema com esta medição de tempo aparecendo.
O sinal de interrupção no GPIO25 vem do comparador TLV3501 1 ou 0.
Eu já escrevi:
const int TLV3501_pin = 25;
STUP()
{
pinMode(TLV3501_pin, INPUT_PULLUP); // input DIGITAL from TLV 3501
attachInterrupt(TLV3501_pin, Time_Echo, CHANGE);
}
E assim por diante
void Time_Echo(){
stopTimeEcho = ESP.getCycleCount();
if (stopTimeEcho 0 )
{
Serial.println(” “);
Serial.print(” meserTimeEcho : “);
Serial.print(meserTimeEcho);
Serial.println(” uS”);
}
Claro, depois de cada medição em outro lugar é:
startTimeEcho = ESP.getCycleCount();
Você, com muita experiência nos registros, pediria uma atualização para tornar esse tempo mais preciso. É o que eu já fiz já é bom o suficiente. Porque uma operação como o atraso 1 micro sedunka nos dá uma medida de 280 relógios e não é um stabline porque uma vez é 250 e na próxima vez 360 ciclos de clock.
Estou a pedir ajuda porque não fiz nada nos registos do ESP32 e também estou geralmente fraca com o ARDUINO.
Ele escreve para você da Polônia.
lembranças
Futek
Se o atraso de 1us realmente for inaceitável ao projeto, creio que você precise sair da Arduino IDE e utilizar diretamente a ESP-IDF (no mínimo). Ainda sim, depois de utilizar a IDF, você pode precisar utilizar as interrupções de baixa latência que são programadas apenas em Assembly. Você pode encontrar mais informações sobre as interrupções de baixa latência no próprio site de ajuda da IDF (readthedocs).