Vamos falar um pouco sobre Drivers?

Estamos de volta pessoal! ✌
 
Nossa próxima etapa no projeto Inter e Intra será sobre drivers e, assim, acho que vale a pena conversarmos um pouco sobre eles, não só porque é um assunto totalmente novo para muitos de vocês mas também quero alinhar com os mais experientes o que quero dizer quando falo de driver. Vamos para um bate papo?

Andre, o que são drivers?

Todos os microcontroladores possuem periféricos, que são os blocos que fazem a interface com o mundo exterior.
 
É através dos periféricos que atuamos sobre os pinos, contamos tempo, usamos interfaces de comunicação, etc.
 
Os drivers são módulos que ficam responsáveis por usar esses periféricos, ‘escondendo’ os detalhes de como eles precisam ser usados a fim de que o pedido de um módulo usuário seja atendido.
 
A operação dos periféricos pode diferir bastante quando mudamos de microcontrolador, especialmente se trocamos para um de outro fabricante. Os drivers ficam responsáveis por cuidar dessas diferenças, permitindo que, se trocarmos de dispositivo, precisamos apenas trocar os drivers, deixando todo o resto igual! 👍
Diferentes microcontroladores, diferentes drivers 💭

Quais são os tipos de drivers mais comuns?

Vamos listar os drivers utilizados na maioria dos produtos? São eles:
  • GPIO: Leitura e escrita digital de pinos.
  • Timer: Contagem simples de tempo.
  • ADC: Leitura analógica de pinos, lendo a tensão deles.
  • RTC: Relógio, data e hora.
  • UART, SPI, I2C: Comunicações seriais.
  • PWM: Ondas quadradas e pulsos.
  • System: Controle do microcontrolador (clock, power…)
  • Flash: Leitura e escrita em memórias não voláteis Flash.
  • Wdog: Watchdog, o cão de guarda do dispositivo 🐶

Tá... e quais são os menos comuns?

Também existem outros que alguns produtos precisam e vamos trombar neles só de vez em quando. Alguns exemplos:
  • DAC: Saída analógica, controlando a tensão de pinos.
  • CDC: Comunicação serial através de USB, usando do device class CDC.
  • EEPROM: Leitura e escrita em memórias não voláteis EEPROM.
  • Pulse: Contagem de pulsos.
  • CAN, LIN: Comunicações seriais utilizadas em meios industriais, automotivos, etc.
  • I2S: Conexão de dispositivos de áudio.

Ahh, o nome do driver é o nome do periférico!

Calma! O nome do driver é relacionado ao que ele oferece para os seus clientes, não ao que ele utiliza para fazer o seu trabalho. Em muitos casos… sim, os nomes batem, mas não tome isso como regra, pois:

O nome do periférico pode variar muito de fabricante para fabricante

Pegue por exemplo… timers. No STM32L0 vamos implementar esse driver atuando nos periféricos chamados TIM, enquanto que no KL25 vamos utilizar dos TPM.

O driver pode utilizar de mais de um periférico

Além do periférico ‘principal’ para a função do driver, ele frequentemente vai ter que atuar em outros para:
  • Habilitar a operação.
  • Ajustar o clock da atividade.
  • Controle de interrupções.
  • Utilizar DMA.
  • Etc.

O driver pode utilizar periféricos... 'exóticos' 👀

Alguns exemplos:
  • Drivers de timers podem ser implementados usando RTC.
  • SPI e I2C masters podem ser feitas com GPIOs, através do bom e velho bit-banging.
  • Drivers de memórias podem apenas alocar buffers na RAM e usar deles na escrita e leitura de dados.

O driver pode operar sem nenhum periférico!

Muitas vezes vamos precisar que o nosso driver só ‘exista’, apenas para compilar a aplicação e deixar os seus clientes felizes – o bom e velho driver dummy. Fazemos isso frequentemente quando começamos o desenvolvimento dos drivers para um novo microcontrolador, por exemplo.

O driver sempre atua escrevendo diretamente nos periféricos?

Nope

Hoje em dia todo fabricante de microcontroladores fornece uma API para te auxiliar a utilizar dos periféricos. Essa API abstrai muitos detalhes de como os periféricos devem ser utilizados, simplificando bastante o desenvolvimento.
 
Outra vantagem também é que a API simplifica a criação dos testes unitários do driver, porque podemos assim fazer o mock da API.
 
As APIs geralmente atendem 99% dos casos, então atue diretamente nos periféricos somente se for realmente necessário!

Se o fabricante já fornece uma API, porque eu já não uso ela direto como driver?

Em primeiro lugar, essas APIs geralmente não oferecem o nível de abstração que é interessante para as nossas aplicações. Elas facilitam o nosso trabalho para lidar com os periféricos, mas elas ainda são muito voltadas para as funções do microcontrolador.
 
Em segundo lugar, mesmo se essa API prover a abstração que você precisa você ainda ficará amarrado à essa abstração, que é voltada para um microcontrolador especifico! Você terá problemas de interface quando precisar trocar de dispositivo…
 
O que eu recomendo é que, se você quer se preocupar menos com driver e quer utilizar do que já está pronto – o que é uma boa ideia, by the way 👀 – então sugiro que você utilize de middlewares que não são atrelados à nenhum microcontrolador ou fabricante em específico. Dois grandes exemplos é a plataforma Conera™ e o projeto Zephyr.

Drivers e testes unitários combinam?

Eles combinam, mas testar drivers é beeeem mais difícil. 🙈

Desenvolver drivers usando TDD não funciona bem porque sempre encontramos surpresas durante o desenvolvimento deles. Nós frequentemente vamos ‘descobrindo’ o que precisamos implementar conforme vamos trabalhando neles e para o TDD funcionar bem precisamos saber já desde o princípio o que precisa ser feito. Implementar drivers envolve muita tentativa e erro, fazendo testes práticos no microcontrolador.
 
Assim, para elaborar drivers, eu recomendo que:
  1. A sua implementação completa seja feita e testada na prática.
  2. Uma rede de testes unitários seja feita para ‘travar’ a funcionalidade implementada.
 
Finalmente, fazer o mock de periféricos do dispositivo é difícil. Por isso que recomendei lá em cima a utilizar da API do fabricante!

Éééé Andre... tem muitos detalhes...

Sim, tem… o universo dos drivers é gigante e complexo mas todo desenvolvedor embarcado precisa lidar com ele. Alguns amam, outros odeiam! 😅

Engenheiros tentando entender porque o driver não está funcionando 🤣
Por causa disto tudo eu tentei simplificar ao máximo os drivers para o nosso projeto Inter e Intra, porque o nosso foco lá não são eles, queremos trabalhar nas arquiteturas e nos módulos da aplicação.
 
No nosso próximo artigo começamos com o nosso primeiro driver, o de GPIO. Até lá! 👊