O acompanhamento remoto de ambientes fundamenta-se primordialmente no paradigma da Internet das Coisas (IoT). A transformação introduzida pelo mercado de IoT está reconfigurando a economia global de maneira extraordinária, ao proporcionar maior eficiência, estimular inovações e impulsionar melhorias significativas em diversos setores. De acordo com Perles et al. (2018), a conservação preventiva do patrimônio cultural, como obras de arte e artefatos históricos, é aprimorada hoje através da gestão de dados coletados por sensores eletrônicos
Com o crescimento do mercado de Internet das Coisas (IoT), uma empresa que deseja ingressar nesse setor solicitou aos estudantes de Engenharia da Computação da UEFS o desenvolvimento de um protótipo de um sensor digital para o monitoramento de ambientes. O objetivo do protótipo é realizar o monitoramento de um ambiente através da detecção de temperatura e umidade relativa coletadas por sensores DHT11, utilizando para recebimento e envio de comandos o protocolo de transmissão serial UART.
A parte de coleta de dados dos sensores DHT11 já foi desenvolvida em Verilog (linguagem de descrição de Hardware) e programada na FPGA Cyclone IV, e pode ser analisada no repositório repAntigo. Neste antigo repositório, a interface com o usuário foi desenvolvida na linguagem C e era dada por meio de dois terminais Linux, um para visualização e outro para envio de comandos.
Para este projeto, foi solicitado o desenvolvimento de uma IHM (Interface Homem-Máquina) que apresente em um display LCD as informações do sensor desenvolvido. A interface deve substituir a que foi desenvolvida em linguagem C, atendendo aos mesmos requisitos. O protótipo dessa interface será embutido em um computador de placa única (SBC), mas especificamente na Orange Pi, e seu código deve ser escrito em Assembly para arquitetura ARMv7.
Este projeto resulta em um sistema capaz de receber e enviar comandos de requisições do usuário por meio de uma interface com um display LCD e botões e monitorar o ambiente, fornecendo dados como temperatura e umidade atual. As próximas seções abordarão em mais detalhes as metodologias empregadas, a descrição do projeto, resultados e testes.
Para a construção de todo o projeto solicitado, foram necessárias algumas ferramentas, assim como a leitura e análise dos datasheets de algumas delas. Como conseguinte, para a construção do projeto, foram estabelecidas a divisão em partes, a fim de modularizar e, assim, facilitar a manutenção do código, a legibilidade e também a escalabilidade, onde a base do projeto possui instruções básicas para qualquer nova adição futura.
As ferramentas utilizadas no desenvolvimento do projeto foram:
- ORANGE PI PC PLUS
- Display LCD HD44780
- FPGA CYCLONE IV
- Sensor(es) DHT11
- Editor de Texto para escrita do código em Assembly e Terminal Linux para conexão SSH com a Orange Pi, compilação e execução do código.
- Lucidchart, para construção de fluxogramas.
Após o conhecimento e análise das ferramentas, o projeto foi divididao nas seguintes etapas, em ordem cronológica:
-
Chamadas de Sistema (SysCall)
- Pesquisa das chamadas de sistemas do sistema operacional Raspbian.
- Utilização das mesmas como forma de auxílio na construção do projeto.
-
Abertura e Mapeamento da Memória
- Abertura da "dev/mem" por meio da chamada de sistema para obter acesso à memória física da placa.
- Construção de um código em Assembly para mapear a "dev/mem" em endereços lógicos por meio de outra chamada de sistema.
- Desenvolvimento de um código em Assembly capaz de acessar esses endereços e realizar alterações pontuais, assim como a obtenção de alguns valores.
-
Configuração do GPIO (General Purpose Input/Output)
- Obtenção do endereço base referente ao GPIO.
- Utilização do código de mapeamento para mapear o GPIO e ter acesso aos pinos físicos da placa.
- Obtenção dos desvios padrões dos pinos na memória.
- Alocação de direções padrões para os pinos essenciais na construção do projeto.
-
Configuração da UART na Orange Pi
- Obtenção do endereço base referente ao CCU (Clock Control Unit) e também à UART.
- Utilização do código de mapeamento para mapear a CCU e, em seguida, a UART.
- Obtenção dos desvios padrões referentes à configuração da CCU e UART.
- Configuração da CCU para liberação do clock para UART.
- Configuração da UART para definição da baudrate e também do FIFO.
-
Utilizando o Display LCD
- Utilização dos pinos já configurados e mapeados para enviar sinais para o display.
- Necessário inicializar o display antes de qualquer operação.
- Configuração para o modo de 4 bits a fim de sincronizar com a disposição de pinos na placa.
- Criação de algumas macros/funções para funcionalidades básicas do controlador.
Para desenvolvimento do projeto, foi seguido esta ordem de prioridade no código:
Para manipulação de memória de dispositivos em sistemas Linux, como é o caso da Orange Pi, precisamos liberar as operações de leitura e gravação em arquivos especiais no diretório "/dev". Depois, fazemos os mapeamentos dos Pinos que utilizamos no protótipo que são os GPIOs, CCU e UART. Mapeados os pinos, podemos configurá-los e setar suas direções para OUTPUT ou INPUT. Depois é configurado o CCU e a UART e inicializado o display. Essas etapas serão explicadas detalhadamente nas seções posteriores.
O fluxo do programa pode ser visto na figura abaixo:
A tela inicial é a de escolha do sensor e comando, nela o usuário escolhe qual sensor vai ser requisitado e qual comando escolhido. Após essa escolha, ele será direcionado para tela das respostas imediatas, como Status do Sensor e Temperatura/Umidade atual. Se não foi requisitado nenhuma resposta imediata, aparece uma tela de Aguardando. Se na tela dos imediatos for pressionado o botão esquerdo, o usuário irá para a última tela das respostas contínuas; se o botão for o central, irá para a primeira tela dos contínuos; e sempre que apertar o botão direito, voltará para escolha do sensor e comando.
Na tela das temperaturas e umidades contínuas, sempre que o usuário estiver na última tela e apertar o botão central, ele retorna para a tela das respostas imediatas, e sempre que estiver na primeira tela e apertar o botão esquerdo, ele também retorna para a tela das respostas imediatas. Só retorna para a escolha do sensor e comando se apertar o botão direito.
Nas telas dos contínuos, ao apertar o botão central, o usuário caminha para “frente”, visualizando dos sensores 0 ao 32; ao apertar o botão esquerdo, ele segue o caminho contrário, do sensor 32 ao 0.
O mapeamento constitui um dos pontos-chave do projeto, uma vez que é aplicado em todas as outras funcionalidades necessárias e adicionais. Com esse propósito em mente, para iniciar a compreensão da construção desta parte do projeto, tornou-se imperativo consultar um manual sobre como começar no Raspbian, utilizando o raspberry pi assembly language, a fim de compreender conceitos básicos, como as chamadas de sistema para atingir o objetivo proposto. Aqui entra outro detalhe: como o sistema operacional estava instalado em uma Orange Pi, cujo hardware e funcionamento diferem um pouco do Raspberry Pi, foi necessário também consultar o Datasheet da OrangePi Pc Plus para obter informações mais detalhadas sobre os endereços base e desvios na memória.
Com base nesse conhecimento adquirido, tornou-se evidente a necessidade de desenvolver um arquivo em assembly com o único objetivo de lidar com essa parte específica do projeto. Essa abordagem, naturalmente, busca a modularização, permitindo atender a todas as necessidades sem a constante criação de novos códigos ou alterações nos já existentes. A partir dessa concepção e da leitura dos arquivos mencionados anteriormente, foi essencial a construção de funções e macros. Essas estruturas foram criadas para modificar a direção dos pinos físicos, obter o valor que o pino está recebendo e enviar um valor através do pino, seja em nível lógico alto ou baixo, garantindo assim uma abordagem abrangente e flexível. Tudo isso para o mapeamento do GPIO.
Essas alterações no direcionamento serão empregadas para configurar os pinos no modo UART TX e RX, assim como no modo de entrada (input) para capturar informações quando um botão for pressionado. Além disso, será necessário direcionar os pinos como saída para enviar sinais para o LCD. Mais adiante, será abordado detalhadamente o uso de outros mapeamentos, como o da CCU e também da UART, com o propósito de liberar o clock para UART e configurá-la, utilizando um endereço base e seus desvios correspondentes.
A comunicação entre a FPGA e a Orange Pi se dá por meio do envio e recebimento de comandos e respostas, essa transmissão é feita através do protocolo UART (Universal Asynchronous Receiver Transmitter. O detalhamento do protocolo implementado, bem como o significado dos códigos utilizados podem ser vistos em Sensor Query ReadMe.
A configuração da UART na Orange Pi é feita a partir do acesso de endereços de memória, endereços esses que estão descritos no datasheet citado previamente. Existem dois endereços base que foram utilizados para configurar a UART, o primeiro é o da CCU (Clock Control Unit), que fica responsável por gerar e controlar o clock que é utilizado na UART, e o segundo é o da própria UART, onde são setadas as preferências de transmissão de dados.
Na CCU, os seguintes endereços foram modificados:
Nome do Offset | Nome da Função | Descrição |
---|---|---|
Bus Clock Gating Reg3 | UART3_GATING | Libera o clock para a UART 3 |
PLL_PERIPH0 Control | PLL_ENABLE | Gera o clock de 600mhz |
APB2 Configuration | APB2_CLK_SRC_SEL | Seleciona o PLL_PERIPH0 como fonte do clock da UART |
Bus Software Reset Reg4 | UART3_RST | Liga ou desliga a UART |
Após configurar a geração de clock da UART, bem como resetá-la para garantir o pleno funcionamento, a configuração segue para o endereço base da própria UART:
Nome do Offset | Nome da Função | Descrição |
---|---|---|
UART FIFO Control | FIFOE | Ativa o modo FIFO |
UART FIFO Control | RFIFOR | Reseta o receiver da FIFO |
UART FIFO Control | XFIFOR | Reseta o transmitter da FIFO |
UART Line Control | DLAB | Permite a configuração de Baud Rate (desliga a UART se ativado) |
UART Divisor Latch Low | DLL | Menores 8 bits do resultado da fórmula que determina a Baud Rate |
UART Divisor Latch High | DLH | Maiores 8 bits do resultado da fórmula que determina a Baud Rate |
UART Line Control | DLS | Seta o modo de operação para 8 bits por vez |
UART Reciever Buffer | RBR | Buffer que recebe os valores da UART |
UART Transmit Holding | THR | Registrador que envia os valores pela UART |
UART Line Status | DR | Indica se existe algum dado para ser lido no RBR |
Após a configuração , o DLAB é setado em 0 novamente, para permitir o uso da UART.
O envio de dados no código é bem simples, o valor é carregado para o THR e enviado automaticamente. Já o recebimento requer um tratamento mais complexo.
Existem quatro vetores de words no .data responsáveis por controlar e guardar os valores contínuos que são recebidos pela UART, além de três words que guardam a última resposta recebida. Já que o comando de resposta de um valor contínuo é o mesmo de um não contínuo, um dos vetores fica responsável por guardar a informação de que um determinado sensor foi setado como contínuo anteriormente, seja para umidade ou temperatura, para que a informação seja impressa na tela de contínuos, e não na de valor atual.
A leitura do RBR é feita constantemente, sempre que o DR (Data Ready) está em 1, indicando que existe algum dado para ser lido, dado esse que sempre será primeiramente um endereço, depois um comando e logo em seguida um valor recebido.
Foi utilizado o controlador de display de cristal líquido (LCD) HD44780U, que consegue exibir caracteres alfanuméricos e símbolos. Ele pode ser configurado para controlar o LCD com um processador de 4 ou 8 bits. O HD44780U possui compatibilidade de função de pino com o HD44780S, possui ROM de gerador de caracteres estendida para um total de 240 fontes de caracteres. Além disso, pode exibir até uma linha de 8 caracteres ou duas linhas de 8 caracteres.
Para protótipo, o display LCD utilizado deve ser configurado para uma interface de 4 bits e com duas linhas de 8 caracteres cada.
Após o mapeamento, conseguimos assim configurar os pinos do LCD e setar as suas direções (INPUT ou OUTPUT). No caso dos pinos usados no projeto, só foram de OUTPUT.
A pinagem do HD44780U é a seguinte:
- VSS - Terra
- VDD - Alimentação
- V0 - Tensão de contraste
- RS - Seleção de registro (0 para instruções, 1 para dados)
- R/W - Leitura/Gravação (0 para gravação, 1 para leitura)
- E - Habilitação do display
- DB0 - Dado de barramento de 8 bits
- DB1 - Dado de barramento de 8 bits
- DB2 - Dado de barramento de 8 bits
- DB3 - Dado de barramento de 8 bits
- DB4 - Dado de barramento de 8 bits
- DB5 - Dado de barramento de 8 bits
- DB6 - Dado de barramento de 8 bits
- DB7 - Dado de barramento de 8 bits
Os pinos 1, 2 e 3 são usados para alimentação e contraste, enquanto os pinos 4 a 14 são usados para comunicação. O pino R/W está sempre recebendo nível lógico alto, ou seja, só conseguimos fazer escrita no display. Como a configuração é de 4 bits de dados, o LCD utilizado não tem os pinos DB0, DB1, DB2 e DB3, somente do DB4 ao DB7.
Para enviar uma instrução ao display, precisa-se setar o valor de RS para 0, setar os bits de dados para os dados de barramento e ativar o pino de enable. Da mesma forma acontece para enviar um dado ao display, como um caractere ou dígito, só muda o valor de RS que deve ser 1.
O pino de Enable é utilizado para sinalizar a transmissão de dados dos outros pinos para o display. A cada envio de dados, devemos acionar o Enable.
Para usar o Enable, deve-se esperar um tempo mínimo de 60 ns (tAS) entre a setagem do pino RS e a subida de sinal do Enable. Depois o sinal de Enable é mantido em alta por um tempo mínimo de 450 ns (PWEH); após este tempo, o sinal pode retomar ao valor 0 (LOW). Entre uma borda de subida do Enable e outra, deve-se esperar um tempo mínimo de 1000 ns (tcycE).
Dentro do arquivo codesLCD.s, tem uma macro que aciona o Enable. Ela é invocada a cada vez que queremos enviar os dados.
Entendo o funcionamento, as pinagens e os tempos de Enable, podemos assim partir para a parte de inicialização do display. Para utilizar o display devemos antes inicializá-lo, que é basicamente configurá-lo para o nosso propósito de uso.
O processo de inicialização pode ser visto na imagem abaixo:
O processo de inicialização pode ser feito em duas etapas: a primeira etapa é a inicialização de hardware, que envolve a alimentação do controlador e a espera de um tempo mínimo para que ele se estabilize. Essa etapa envolve os três blocos de bits iniciais, juntamente com os tempos de 15 ms, 4.1 ms e 100 us.
A segunda etapa é a inicialização de software, que envolve a configuração dos parâmetros do controlador, como o número de linhas do display, o tamanho dos caracteres e a posição do cursor.
Dentro do arquivo codesLCD.s, há uma macro chamada “initialize’ que deve ser chamada uma única vez no código antes de usar o display. Ela que realiza a inicialização e configuração do LCD.
Para a escrita de um caractere no display, basta enviarmos seu código binário em ASCII. A tabela ASCII é usada pelo HD44780U para mapear os códigos dos caracteres para os valores correspondentes em sua memória interna, permitindo que o controlador exiba caracteres alfanuméricos e símbolos no display, como pode ser visto na imagem abaixo.
No arquivo codesLCD.s, há uma função “instructionCode” que recebe como parâmetro os 9 bits correspondentes aos pinos RS e os 8 de dados. Essa função envia inicialmente para o pino RS um bit indicando se vai ser uma instrução (mover cursor/display, limpar display, etc…) ou se vai ser uma transmissão de 1 byte ASCII para escrita. Depois ele parte para o envio dos nibbles (4 bits). Envia 4 bits mais significativos, ativa o Enable, depois envia os outros 4 bits menos significativos. Resumidamente, essa função serve para setar algumas funcionalidades do display e para escrever caracteres ASCII.
No mesmo arquivo, há uma função “writeString” que realiza a escrita de strings na tela do display. A cada chamada dessa função, ela limpa o display e escreve uma nova tela nele. Para usá-la, deve ser escrita na seção .data do arquivo main.s uma label com o nome da tela e duas linhas de Strings, como no exemplo abaixo:
Deve-se escrever .ascii na primeira linha, porque ao passar um char para um registrador, ele interpreta o seu binário em ASCII. O “\n” é utilizado para identificar que acabou a primeira linha. Deve-se escrever .asciz na segunda linha, pelos mesmos motivos da primeira linha, só que o Z indica que aquela String tem o caractere “\0” que indica o término de String.
O algoritmo do “writeString” funciona da seguinte forma: Passe para o registrador r5 o endereço da tela no .data; O algoritmo vai pegar o endereço base da tela (primeiro caractere) e chamar a “intructionCode” para imprimi-lo no display; Depois vai percorrer o próximo endereço (próximo caractere) e imprimí-lo na próxima posição do display e assim sucessivamente até encontrar o “\n”; Assim, pula a linha do display e continua a escrever até encontrar o “\0”.
No arquivo control.s tem a lógica de controle da tela de envio de comando e sensor, ela utiliza as funções de “writeString” e “instructionCode”.
Já no arquivo windowsLCD.s tem a lógica das telas de respostas imediatas e das telas das respostas contínuas.
Exemplos de telas com respostas imediatas:
Exemplo de tela com resposta contínua:
O projeto cumpriu todas as funções requisitadas, e consegue mostrar as temperaturas e umidades contínuas de diversos sensores, ao mesmo tempo que recebe novas requisições para os demais. Vários desafios foram encontrados e resolvidos durante o desenvolvimento e testes do projeto, em especial os que envolviam a interpretação dos datasheets. A inicialização do display LCD e da UART necessitou ampla leitura e diversos testes, já que vários detalhes são necessários para que ambos funcionem corretamente.
A utilização de funções para melhor execução do código também trouxe desafios, já que o uso de registradores sem o devido tratamento para que as informações previamente guardadas neles fossem perdidas trouxe diversos problemas, que foram solucionados com o uso correto das instruções de "push" e "pop".
Um ponto de melhoria que fica evidente é a possibilidade de informar os comandos para o usuário de forma mais intuitiva, como usar a sigla "TC" para indicar que será enviado um comando de "Ativar Temperatura Constante".
Algumas telas do projeto em funcionamento:
- Angel Perles, Eva Pérez-Marín, Ricardo Mercado, J. Damian Segrelles, Ignacio Blanquer, Manuel Zarzo, Fernando J. Garcia-Diego, An energy-efficient internet of things (IoT) architecture for preventive conservation of cultural heritage, Future Generation Computer Systems, 2018, Volume 81, Pages 566-581.
- Tutores, Problema #1 – Interfaces de E/S, 2023.
- Tutores, Problema #2 – Assembly, 2023.
- Allwinner H3 Datasheet Quad-Core OTT Box Processor. [s.l: s.n.]. Disponível em: https://linux-sunxi.org/images/4/4b/Allwinner_H3_Datasheet_V1.2.pdf. Acesso em: 27 dez. 2023.
- HD44780U (LCD-II) (Dot Matrix Liquid Crystal Display Controller/Driver). [s.l: s.n.]. Disponível em: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf.
- SMITH, S. Raspberry Pi Assembly language programming : ARM processor coding. Berkeley, California: Apress, 2019.
- José Gabriel de Almeida Pontes
- Pedro Mendes
- Thiago Pinto Pereira Sena