Os temporizadores são um dos recursos de hardware comuns em todos os microcontroladores. Timers e interrupções de timer são de grande utilidade em aplicações de microcontroladores. As interrupções do temporizador são frequentemente usadas quando um tempo preciso é necessário sem uma fração de erro. Todas as portas MicroPython possuem um ou mais temporizadores. Alguns desses temporizadores podem ser reservados para funções específicas, como rede ou Wi-Fi, enquanto os temporizadores restantes podem ser utilizados no aplicativo do usuário. Este artigo explorará temporizadores e a implementação de interrupções de temporizador no MicroPython. A seguir, examinaremos os temporizadores disponíveis no ESP8266 e ESP32. Por fim, usaremos os conceitos mencionados acima para projetar um ticker ESP8266.
Funções relacionadas ao tempo do MicroPython
MicroPython fornece um tempo módulo para atrasos de tempo, intervalos e manutenção de data e hora. Este módulo é uma reimplementação de um módulo CPython de mesmo nome. As funções relacionadas ao tempo do MicroPython são compatíveis com portas incorporadas, que usam a época de 2000-01-01 00:00:00 UTC em vez da época dos sistemas POSIX de 1970-01-01 00:00:00 UTC. É importante observar que a configuração e manutenção do horário do calendário depende do sistema operacional ou RTOS instalado na porta suportada como no Raspberry Pico. Um RTC integrado pode gerenciar isso no caso de portas de microcontroladores. As funções relacionadas ao tempo do MicroPython consultam o OS/RTOS ou RTC para manter ou definir data e hora. A configuração da hora pode ser feita manualmente através de um protocolo de rede ou bateria reserva.
O tempo O módulo fornece as seguintes funções relacionadas a hora, data e atraso.
tempo.sleep(segundos): Este método de bloqueio fornece um atraso em segundos. Algumas portas permitem especificar o tempo de atraso como um número de ponto flutuante. Uma vez chamado o método, o controlador interrompe a execução do programa do usuário pelo número definido de segundos.
tempo.sleep_ms(ms): Este é outro método de bloqueio que fornece um atraso em milissegundos. Comparado com time.sleep , este método é mais preciso. Um número positivo ou zero pode ser passado como argumento para este método. Se for usado para uma interrupção por temporizador, o atraso pode se estender até que a execução da rotina de serviço de interrupção seja concluída. Se 0 for passado como argumento, o atraso será igual ao tempo gasto na execução da rotina de serviço de interrupção.
time.sleep_us(nós): Este é mais um método de bloqueio que fornece um atraso em microssegundos. Um número positivo ou zero pode ser passado como argumento. Se usado para uma interrupção de temporizador, o atraso pode se estender até que a execução da rotina de serviço de interrupção seja concluída. Se 0 for passado como argumento, o atraso será igual ao tempo gasto na execução da rotina de serviço de interrupção.
hora.ticks_ms : este método retorna o número de milissegundos passados em uma referência arbitrária. É um pouco semelhante à função millis do Arduino. Embora a função millis no Arduino retorne um intervalo de tempo em milissegundos a partir da inicialização da placa Arduino, time.ticks_ms assume um ponto de tempo arbitrário para referência. O valor envolvente usado como intervalo máximo é sempre na potência de 2, de modo que permanece o mesmo em toda a implementação do MicroPython, independentemente da porta. É denominado TICKS_PERIOD, que é igual a um a mais que TICKS_MAX. Portanto, uma chamada para esta função sempre retorna um valor não negativo variando de 0 a TICKS_MAX inclusive, o que é indicativo dos milissegundos no intervalo mencionado passados em um ponto arbitrário no tempo. É importante notar que, diferentemente do Arduino, os operadores matemáticos padrão ou operadores relacionais, diretamente ou como argumentos para ticks_diff ou ticks_add métodos nos valores retornados por este método ou a marcação conforme os métodos mencionados acima não são permitidos. Tais operações podem levar a um erro de sintaxe (dependendo do IDE) ou a resultados errados.
time.ticks_us : Este método é semelhante a hora.ticks_ms exceto que ele retorna o tempo passado em microssegundos a partir de um ponto arbitrário no tempo.
tempo.ticks_cpu : Este método é semelhante a hora.ticks_ms e time.ticks_us exceto que ele retorna os ciclos de clock da CPU passados a partir de um ponto arbitrário no tempo. Ele fornece a resolução mais alta possível. Em vez de ciclos de clock, outra unidade de resolução mais alta fornecida por um temporizador integrado pode ser retornada em algumas portas. Deve-se observar que este método não está disponível para todas as portas MicroPython. Portanto, a documentação específica da porta deve ser verificada antes de usar este método em um script MicroPython.
time.ticks_add(carrapatos, delta): Este método calcula prazos para eventos e tarefas do microcontrolador. Dependendo do tempo, ele retorna um prazo em milissegundos, microssegundos ou ciclo de CPU.ticks_ms , time.ticks_us ou tempo.ticks_cpu é usado como parâmetro de ticks, respectivamente. O argumento delta pode ser um argumento inteiro ou numérico. É igual ao número de ticks, onde os ticks podem ser milissegundos, microssegundos ou ciclos de CPU definidos como prazo. Por exemplo, a instrução a seguir retorna um prazo de 200 milissegundos.
prazo = ticks_add(time.ticks_ms , 200)
A instrução a seguir retorna um prazo de 100 microssegundos.
prazo = ticks_add(time.ticks_us , 100)
A instrução a seguir retorna um prazo de 20 ciclos de CPU.
prazo = ticks_add(time.ticks_cpu , 20)
time.ticks_diff(ticks1, ticks2): Este método retorna a diferença entre dois ticks. A diferença de ticks pode ser em milissegundos, microssegundos ou ciclos de CPU, dependendo do valor retornado por hora.ticks_ms , time.ticks_us ou tempo.ticks_cpu funções são usadas como ticks respectivamente. O valor retornado é ticks1-ticks2, que pode variar de -TICKS_PERIOD/2 a TICKS_PERIOD/2 – 1. Time.ticks_diff é útil para pesquisar um tempo limite, agendar uma tarefa incorporada ou calcular um prazo.
hora.hora : Este método retorna o número de segundos desde a época, desde que o RTC de uma determinada porta seja definido e mantido. A época é compatível com incorporação e o tempo retornado é o número de segundos desde 01/01/2000 às 00:00:00 UTC. A época pode ser uma referência específica da porta, como o tempo desde a inicialização ou reinicialização em algumas portas.
hora.time_ns : este método retorna o número de microssegundos desde a época. É muito útil para determinar o tempo absoluto. O valor retornado por este método é um número inteiro.
hora.mktime : Este método retorna um número de segundos passados entre a época (ou seja, 2000-01-01 00:00:00 UTC) e uma hora local passada como argumento. O método considera uma tupla completa de 8 como hora local, onde a tupla está no seguinte formato – (ano, mês, dia, hora, minuto, segundo, dia da semana, dia do ano) onde os valores da tupla devem estar no seguinte intervalo.
Ano | ano em DC |
mês | 1~12 |
hoje | 1~31 |
hora | 0~23 |
minuto | 0~59 |
segundo | 0~59 |
dia da semana | 0~6 para Seg~Dom |
dia do ano | 1~366 |
time.gmtime((segs)): Este método retorna data e hora em UTC desde os segundos especificados como argumento. A hora retornada é uma tupla no formato (ano, mês, dia, hora, minuto, segundo, dia da semana, dia do ano).
hora.localtime((segs)): Este método retorna data e hora na hora local, pois os segundos são especificados como argumento. A hora retornada é uma tupla no formato (ano, mês, dia, hora, minuto, segundo, dia da semana, dia do ano). A hora local pode ser definida de acordo com OS/RTOS ou RTC.
Usando funções de ticking
As funções de marcação são úteis quando um tempo preciso é necessário em um programa de usuário MicroPython. As funções de marcação podem calcular o tempo gasto na execução de uma tarefa incorporada, definir um prazo para uma tarefa incorporada, definir tempos limite e agendar tarefas incorporadas.
A seguir está um exemplo válido de cálculo do tempo gasto na execução de uma parte do script MicroPython.
hora de importação
início = time.ticks_us
…#Declarações MicroPython para teste de tempo
imprimir(time.ticks_diff(time.ticks_us , início))
A seguir está um exemplo válido de localização de TICKS_MAX de uma determinada porta.
imprimir(ticks_add(0, -1))
A seguir está um exemplo válido de definição de prazo para uma tarefa incorporada.
prazo = ticks_add(time.ticks_ms , 200)
enquanto ticks_diff(prazo, time.ticks_ms ) > 0:
faça_um_pouco_de_algumacoisa
A seguir está um exemplo válido de pesquisa de um evento com tempo limite.
início = time.ticks_us
enquanto pin.value == 0:
se time.ticks_diff(time.ticks_us , start) > 500:
aumentar TimeoutError
A seguir está um exemplo válido de agendamento de tarefas incorporadas usando funções de ticking.
agora = time.ticks_ms
horário_agendado = tarefa.hora_agendada
se ticks_diff(hora_agendada, agora) == 0:
print(“hora de executar a tarefa!”)
tarefa.run
Problema com funções relacionadas ao tempo
As funções de ticking são bastante precisas. As funções de marcação são úteis para calcular intervalos de tempo, definir tempos limite para eventos e até mesmo agendar tarefas. Embora não bloqueiem, essas funções não são frequentemente usadas para fornecer atrasos ou agendar tarefas. A principal razão por trás disso é a dependência do módulo de tempo do OS/RTOS ou RTC. Em segundo lugar, estes métodos podem ser interrompidos por outros eventos de maior prioridade do microcontrolador.
Por outro lado, as funções relacionadas ao atraso do módulo de tempo, como time.sleep , time.sleep_ms e time.sleep_us , têm dois problemas dignos de nota. Primeiro, esses métodos são de natureza bloqueadora e interrompem o script quando chamados. Em segundo lugar, estes métodos não proporcionam um atraso preciso. Por exemplo, um atraso de alguns segundos no método time.sleep pode apresentar um erro de alguns milissegundos. Esses erros podem chegar a 1 ou 2 por cento.
Nessas situações, os cronômetros chegam ao resort. Os temporizadores têm interrupções de maior prioridade que muitas vezes não podem ser substituídas, exceto com um reset. Suas interrupções usam hardware subjacente, ou seja, os registros do temporizador que não deixam margem para qualquer erro. Para definir tempos limite ou agendar tarefas incorporadas de tempo crítico, as interrupções do temporizador são as melhores opções. Ao mesmo tempo, as funções de ticking podem ser utilizadas para definir prazos ou calcular o tempo gasto na execução de partes críticas do script MicroPython.
O que é um cronômetro?
Cada microcontrolador possui alguns recursos de hardware integrados. O temporizador/contador é um dos importantes periféricos integrados quase presentes em todos os microcontroladores. Um temporizador/contador é usado para medir eventos de tempo ou operar como contador. Um temporizador está vinculado ao relógio do sistema do microcontrolador, o que permite rastrear o tempo com alta precisão e precisão. Pode haver vários temporizadores em um microcontrolador. Cada temporizador é configurado, rastreado e controlado por um conjunto de registros internos.
O que é uma interrupção do temporizador?
Uma das funções importantes dos temporizadores é cronometrar eventos. Isso é feito com a ajuda de interrupções de temporizador. Um evento nada mais é do que a execução de um bloco de código específico em um microcontrolador. Este bloco de código está incluído dentro Função Rotina de Serviço de Interrupção (ISR). Um ISR é executado quando uma interrupção é gerada.
Normalmente, o microcontrolador executa instruções de maneira sequencial. Quando uma interrupção é gerada, o microcontrolador pula a execução atual do código e executa o ISR primeiro. Assim que o ISR for concluído, ele retoma a execução normal do código.
As interrupções do temporizador são acionadas quando o temporizador atinge uma contagem definida. Um registro atualiza a contagem do temporizador, geralmente chamado de registro do temporizador. Existe outro registro onde o usuário define a contagem de referência. Isso geralmente é chamado de registro de comparação e correspondência. Pode haver um ou mais registros associados à definição das configurações do temporizador. Existe um registro que mantém valores de diversas máscaras de interrupção. Sempre que uma interrupção do temporizador é acionada, seu bit de máscara correspondente é alternado no registro da máscara de interrupção. Ao rastrear o bit da máscara de interrupção, uma interrupção do temporizador é detectada. Isso pode fornecer um atraso, definir um tempo limite ou agendar tarefas em uma rotina de serviço de interrupção.
Classe de temporizador MicroPython
MicroPython fornece uma classe de timer para lidar com timers e interrupções de timer das portas suportadas. A classe timer faz parte do módulo da máquina. Ele é importado em um script MicroPython usando a instrução a seguir.
da importação da máquina Temporizador
Caso a porta seja WiPy, deve-se utilizar a seguinte instrução.
da importação da máquina TimerWiPy
É importante observar que se forem gerados erros na execução de uma rotina de serviço de interrupção, o MicroPython não produz um relatório de erros, a menos que um buffer especial seja criado para isso. Portanto, um buffer deve ser criado para depuração quando o temporizador interrompe ou outras interrupções são usadas em um script MicroPython. O buffer pode ser criado usando as instruções a seguir.
importar micropython
micropython.alloc_emergency_exception_buf(100)
É importante observar que o buffer armazena apenas o rastreamento de pilha de exceção mais recente aqui. Se uma segunda exceção for lançada enquanto o heap estiver bloqueado, o rastreamento de pilha da segunda exceção substituirá o rastreamento original.
Após importar a classe timer, um objeto timer deve ser criado. Isso é feito chamando o método construtor. O método construtor possui o seguinte protótipo.
classe máquina.Timer(id, /,…)
O método construtor usa o id do temporizador como argumento. Pode ser um número positivo 0, 1, 2, etc., para um temporizador de hardware ou -1 para um temporizador virtual, desde que a porta o suporte. Os outros parâmetros de configuração também podem ser definidos na chamada ao método construtor. Caso contrário, o objeto timer pode ser configurado explicitamente usando o comando temporizador.init método. O método timer.init possui o seguinte protótipo.
Timer.init(*, modo=Timer.PERIODIC, período=- 1, retorno de chamada=Nenhum)
O modo pode ser definido como timer.ONE_SHOT ou Timer.PERIODIC. Se definido como timer.ONE_SHOT, o cronômetro será executado apenas uma vez até que o período especificado em milissegundos seja passado. Se definido como timer.PERIODIC, o cronômetro é executado periodicamente em um intervalo em milissegundos passado como argumento período. O argumento período é o período de tempo em milissegundos usado como tempo limite para executar a função de retorno de chamada uma vez ou periodicamente, dependendo do modo definido. O ligar de volta é uma chamada executada no término do período de tempo. A rotina de serviço de interrupção é chamada para executar as tarefas incorporadas desejadas ao aumentar a interrupção do temporizador. O chamável pode ser uma função ou mesmo um método pertencente a um objeto de classe.
A classe timer permite parar o timer e desabilitar o periférico do timer. Isso é feito chamando o método timer.deinit . Uma chamada para esse método interrompe imediatamente o cronômetro se estiver em execução, desinicializa o objeto do cronômetro e desativa o periférico do cronômetro. Possui o seguinte protótipo.
Temporizador.deinit
Se um temporizador desativado tiver que ser reativado, um objeto temporizador com o mesmo ID deverá ser criado novamente no programa do usuário.
Manipuladores de interrupção MicroPython
A função de retorno de chamada especificada como argumento na inicialização ou criação de um objeto temporizador é a rotina de serviço de interrupção executada quando a interrupção do temporizador é acionada. Curiosamente, o MicroPython não expõe a programação em nível de registro para temporizadores. Em vez disso, ele usa interrupções de temporizador para fornecer um tempo limite em milissegundos. Os métodos estão disponíveis através da máquina. Os temporizadores são normalmente aplicáveis a todas as portas suportadas.
A função de retorno de chamada ou rotina de serviço de interrupção para objetos temporizadores requer certas precauções de programação para evitar falhas de tempo de execução. Já discutimos um desses cuidados: definir um objeto buffer para armazenar o último erro de exceção. Vamos discutir mais algumas recomendações para escrever manipuladores de interrupção no MicroPython.
O corpo de uma rotina de serviço de interrupção deve ser o mais curto e direto possível. As rotinas de serviço de interrupção destinam-se a executar ações de tempo crítico. Estes não devem ser utilizados indevidamente para o agendamento regular de tarefas incorporadas. Se qualquer agendamento de tarefas incorporadas for necessário em um script MicroPython, isso deverá ser feito usando micropython.schedule. Não deve haver nenhum tipo de alocação de memória dentro de uma rotina de serviço de interrupção. Evite valores de ponto flutuante, inserindo-os em dicionários ou acrescentando itens em listas. No entanto, as variáveis globais podem ser atualizadas nas rotinas de serviço de interrupção.
A maioria das plataformas de microcontroladores não permite que rotinas de serviço de interrupção retornem valores. Porém, o MicroPython permite interromper rotinas de serviço e retornar um ou vários valores. O ISR pode retornar um único valor atualizando uma variável global. Se vários valores precisarem ser retornados, uma matriz de bytes pré-alocada deverá ser atualizada pela rotina. Um objeto de matriz pré-alocado deverá ser atualizado se a rotina retornar vários valores inteiros. Entretanto, esse compartilhamento de variáveis e objetos entre o ISR e o loop principal pode causar uma condição de corrida, onde tanto o loop do programa principal quanto a corrida do ISR alteram o valor da variável. Portanto, atualizar o valor destas variáveis no loop principal do programa requer cuidado extra. Antes de atualizar a variável compartilhada/matriz de bytes/matriz no loop principal do programa, as interrupções devem ser desabilitadas chamando o comando pyb.disable_irq método. Depois de atualizar a variável compartilhada/matriz de bytes/matriz no programa principal, as interrupções podem ser reativadas chamando o comando pyb.enable_irq método.
Temporizadores em ESP8266
No ESP8266, existem dois temporizadores – timer0 e timer1. O timer0 é usado para as funções de rede. O timer1 está disponível para uso no ESP8266, mas o MicroPython fornece acesso aos timers do ESP8266. Em vez disso, ESP8266 fornece uma API para um temporizador virtual. Este temporizador virtual baseado em RTOS tem um id de -1. A seguir está um exemplo válido de interrupção de temporizador no ESP8266.
da importação da máquina Temporizador
tim = Temporizador(-1)
tim.init(período=500, modo=Timer.ONE_SHOT, retorno de chamada=lambda t:print(1))
tim.init(período=200, modo=Timer.PERIODIC, callback=lambda t:print(2))
Temporizadores no ESP32
ESP32 possui quatro temporizadores de hardware com id 0 a 3. Todos os temporizadores estão disponíveis para o usuário. A seguir está um exemplo válido de interrupções de temporizador no ESP32.
da importação da máquina Temporizador
tim0 = Temporizador(0)
tim0.init(período=2000, modo=Timer.ONE_SHOT, retorno de chamada=lambda t:print(0))
tim1 = Temporizador(1)
tim1.init(período=1000, modo=Timer.PERIODIC, retorno de chamada=lambda t:print(1))
Usando o temporizador ESP8266 para ticker LED
Vamos agora usar temporizadores MicroPython para alternar o estado de um LED.
Componentes necessários
- ESP8266/ESP32 x1
- LED de 5mm x1
- Resistência de 330Ω x1
- Tábua de ensaio
- Conectando fios/fios de jumper
Conexões de circuito
Conecte o ânodo do LED com GPIO14 do ESP8266 ou ESP32. Conecte um resistor de 330Ω ao cátodo do LED e aterre a outra extremidade do resistor.
Script MicroPython
Como funciona
O LED é conectado ao GPIO14 do ESP8266 de forma que brilha quando a placa fornece corrente para ele, enquanto é desligado quando há um sinal baixo do pino. O script MicroPython implementa uma interrupção de temporizador a cada 1 segundo, onde o estado do LED é alternado e o número de piscadas é atualizado no console.
Conclusão
Da discussão acima, podemos concluir que as funções de ticking do MicroPython são úteis no cálculo de tempos limite e na definição de prazos para a execução de partes do script. As interrupções do temporizador são úteis quando tarefas incorporadas de tempo crítico devem ser agendadas com precisão dentro do script, independentemente de sua execução ser necessária uma vez ou periódica. As interrupções do temporizador são muito mais eficientes na produção de atrasos e intervalos exatos.