Atividade 08¶
Nota
O define CONFIG_FXOS8700_MODE_ACCEL utilizado para configurar o projeto no arquivo prj.conf ativa o sensor acelerômetro da placa, porém ele apresenta um nome diferente do sensor presente (o correto deveria se mma8451q e não fxos8700).
O chip mma8451q que está na sua placa é apenas um acelerômetro. Algum tempo depois, a Freescale/NXP lançou um outro chip chamado fxos8700, presente na placa frdm-k64f. Esse chip mais novo é composto por um magnetômetro e um acelerômetro.
O Zephyr tinha um driver separado só para o mma8451q. Porém, os desenvolvedores do sistema operacional removeram o drive já que o código do acelerômetro no driver do fxos8700 é idêntico e oferece mais funcionalidades, removendo código duplicado, restando apenas as configurações do Device Tree para a placa frdm-kl25z.
Commit da remoção
3ad8431f ("Revert "samples: sensors: mma8451q: Add accelerometer sample"")
Driver fxos8700 can also be used for the MMA8451 accelerometer and offers more functionality. Revert the commit to avoid duplicate code.
Análise do programa¶
O funcionamento se divide em quatro partes principais: a aquisição de dados na função accel_thread_data_acquisition(), transmissão dos dados via UART na função accel_thread_communication(), implementação do filtro FIR na função filter_fir() e a implementação de um script para realizar a leitura e visualização em tempo real dos dados.
Aquisição de dados
A função
accel_thread_data_acquisition()representa o entry point da thread de aquisição de dados. Inicialmente é feita a configuração doODR(Output Data Rate) para definir a taxa de aquisição do sensor (valor máximo de 800 Hz) e a configuração de umtimerutilizado para limitar a taxa de busca de amostras entre o microcontrolador e o sensor, feito na funçãosensor_sample_fetch(). Isso evita a leitura de dados antigos e consequentemente repetidos. O valor de atraso do timer é dado em microsegundos (1250 \(\mu s\), o que equivale a 800 Hz), por isso foi definidoCONFIG_SYS_CLOCK_TICKS_PER_SEC=10000no arquivoprj.conf, aumentando a precisão do relógio.A rotina contínua de aquisição dos dados é feita após as configuções, ela é composta de uma função que faz a sincronização das aquisições para limitar em 800 Hz,
k_timer_status_sync(), configurada pelotimer, que trava o laço na frequência correta. Em seguida, os dados são buscados porsensor_sample_fetch()e, em caso de sucesso, extraídos pela funçãosensor_channel_get(). Com os dados obtidos, o filtro FIR é aplicado (ou não) e os valores empacotado em umstructdo tipodata_packet_t. Por fim, o pacote é adicionado em uma fila (se a fila estiver cheia, ela é limpa antes da inserção) para que a próxima thread realize o envio.Transmissão dos dados via uart
A função
accel_thread_communication()representa o entry point da thread de transmissão de dados. Ela possui uma prioridade inferior à thread de aquisição. Sua função é retirar os itens da fila de dados empacotados obtidos na aquisição e transmiti-los em formato binário, byte a byte, usando a funçãouart_poll_out().Os pacotes são compostos por um header para validar o início transmissão ($A), um número da sequência (para permitir a detecção de pacotes perdidos, isso iria ser feito de forma automática caso se utilizasse o sistema de logs ou
printk()para transmitir os dados, já que iria apresentar um erro caso perdesse algum dado), um timestamp e os valores dos eixos x, y e z.struct __attribute__((packed)) data_packet_t { uint8_t header[2]; // Marcador de início: '$' e 'A' (0x24, 0x41) uint32_t sequence; // 4 bytes int64_t ts; // 8 bytes int32_t x_val1; // 4 bytes int32_t x_val2; // 4 bytes int32_t y_val1; // 4 bytes int32_t y_val2; // 4 bytes int32_t z_val1; // 4 bytes int32_t z_val2; // 4 bytes }; // Total: 2 + 4 + 8 + (6 * 4) = 38 bytes
Implementação do filtro FIR
A função que aplica o filtro FIR,
filter_fir(), processa os dados no formato de ponto fixo utilizando inteiros (int32_t), visto que o microcontrolador não possui hardware dedicado para cálculos em ponto flutuante (float). O sistema de ponto fixo utilizado segue o padrão do subsistema de sensores do Zephyr:struct sensor_value { /** Integer part of the value. */ int32_t val1; /** Fractional part of the value (in one-millionth parts). */ int32_t val2; };
Para manter os coeficientes do filtro no mesmo formato inteiro dos dados lidos, eles são previamente multiplicados por 1024. Dessa forma, após a etapa de acumulação das multiplicações no filtro, basta deslocar os bits do resultado em 10 posições para a direita (\(\gg 10\)), o que equivale a uma divisão rápida e eficiente por 1024.
Atualmente, o filtro está sendo aplicado apenas no eixo X, servindo como uma base de referência cruzada para comparar o sinal limpo com os resultados brutos dos outros eixos. Os coeficientes utilizados no código
Cforam gerados externamente através de um script auxiliar em Python (get_filter.py).- Implementação do script
Para validar e analisar o comportamento do sistema, foi desenvolvido um script em Python utilizando as bibliotecas
pyserial,matplotlibenumpy. Ele atua em duas frentes processando os pacotes binários recebidos via UART em tempo real:Diagnóstico de Comunicação: O script monitora os identificadores de sequência dos pacotes e os valores literais das medições para diferenciar a Taxa da UART (quantos pacotes o microcontrolador envia por segundo) da Taxa Real do Sensor (quantas vezes o acelerômetro efetivamente atualizou o dado físico), além de acusar imediatamente a porcentagem de pacotes perdidos no barramento.
Análise de Sinais (Domínio do Tempo e Frequência): O sistema exibe gráficos dinâmicos das acelerações (X, Y e Z) no domínio do tempo e aplica a Transformada Rápida de Fourier (FFT) com um janelamento de Hanning. Isso permite identificar visualmente os picos de ruído e as frequências de vibração dominantes em cada eixo, validando a eficácia do filtro FIR implementado no firmware.
Análise do funcionamento¶
A análise de funcionamento do sistema foi baseada em uma metodologia de testes dividida em três etapas principais.
Variação da Taxa de Aquisição
Para avaliar a estabilidade do sensor e do barramento I2C, a taxa de aquisição (
ODR) foi variada enquanto a transmissão serial foi mantida com folga em 460.800 bps. O filtro FIR permaneceu desativado.ODR Configurado (Hz)
Taxa UART Lida (Hz)
Taxa Sensor (Hz)
Pacotes Perdidos (%)
50
770
65
0.00%
100
770
125
0.00%
400
770
513
0.00%
800
770
769
0.00%
Análise:
A taxa de aquisição do sensor conseguiu acompanhar o
ODRconfigurado nos valores mais altos sem ter perdar de pacotes na transmissão.Variação da Taxa de Transmissão
Neste teste, a taxa de aquisição do sensor foi fixada em 400 Hz, porém o programa faz aquisições em 800 Hz por conta do _timer_, com isso são 800 pacotes de 380 bits por segundo (38 bytes utilizando o padrão de 1 start bit, 8 de dados, 1 stop bit, cada byte custa 10 bits no barramento), logo exige no mínimo 304.000 bps, e o baud rate foi reduzido gradativamente para observar o ponto de colapso da comunicação. O filtro FIR permaneceu desativado.
Baud Rate (bps)
Taxa UART Lida (Hz)
Taxa Sensor (Hz)
Pacotes Perdidos (%)
1.000.000
770
510
0.00%
500.000
770
510
0.00%
230.400
566
375
28.44%
115.200
304
186
63.02%
Análise:
A baixo do limite mínimo de 304.000 bps a transmissão de dados começou a apresentar perdas de pacotes, apresentando o gargalo da transmissão nessa taxa de 800 Hz.
Impacto do Filtro FIR
Com a comunicação estabilizada (ODR em 400 Hz, porém aquisições em 800 Hz e UART em 1.000.000 bps), o filtro FIR foi ativado. O número de coeficientes foi variado para analisar o impacto das operações matemáticas na CPU.
Coeficientes
Taxa UART Lida (Hz)
Taxa Sensor (Hz)
Pacotes Perdidos (%)
31
766
769
0.00%
63
769
769
0.00%
127
737
737
0.00%
255
605
606
0.00%
Análise:
Após 127 coeficientes a placa começou a não conseguir entregar os 800 pacotes por segundo devido ao tempo gasto com os calculos, isso fica evidente quando se utiliza 255 coeficientes
Imagem exemplo para 63 coeficientes: