Leitura analógica do ADXL337 através do periférico ADC em FreeRTOS
Introdução
Grandezas como temperatura, pressão, nível, vazão entre outras são comumente encontradas no quotidiano do ser humano, sendo atualmente inviável manter o cenário industrial sem a perfeita aquisição de tais grandezas. Com isso, o presente trabalho visa estabelecer o monitoramento do sensor ADXL337 através da placa de desenvolvimento STM32 Primer1 com um Sistema Operativo em Tempo Real, freeRTOS, capaz de tanto realizar a converção analógico-digital por seu periférico ADC embebido, quanto gerênciar as tarefas necessárias para o seu correto funcionamento.
Sensor ADXL337
O sensor ADXL337 fabricado pela SparkFun, trata-se de um sensor de aceleração, ou acelerômetro, sensível a variações mínimas de ± 3 g nos eixos x, y e z. Sua fonte de alimentação pode variar de 1.8 V a 3.6 V. Os dados de saída são no formato analógico. Ele pode medir a aceleração da gravidade estática em aplicações de detecção de inclinação , bem como aceleração dinâmica resultante do movimento , choque ou vibração [1].
Periférico ADC
O STM32 Primer1, possui dois periféricos ADC embebidos, o ADC1 e ADC2, cada um com 16 canais de entrada, sendo estas multiplexadas por um seletor. A resolução de conversão é de 12 bits [2]. O procedimento primário para que o ADC possa funcionar é sua configuração. A estrutura do ADC do STM32 possui o seguinte formato:
typedef struct {
u32 ADC_Mode ;
FunctionalState ADC_ScanConvMode ;
FunctionalState ADC_ContinuousConvMode ;
u32 ADC_ExternalTrigConv ;
u32 ADC_DataAlign ;
u8 ADC_NbrOfChannel ;
} ADC_InitTypeDef
O ADC Mode é o modo em que o ADC irá trabalhar, visto que o microcontrolador possui mais de um ADC, o ADC ScanConvMode especifica se a conversão será realizada em multi-canais ou apensa um único canal, o ADC ContinuousConvMode especifica se a conversão será realizada de forma contínua ou de forma única, o ADC ExternalTrigConv determina se será utilizado um trigger externo para dar início ao processo de conversão AD, o ADC DataAlign determina se os dados serão alinhados para esquerda ou direita e o ADC N brOfChannel determina o número de canais que serão utilizados. Para realizar o processo de leitura dos dados do ADXL337, o periférico ADC1 foi configurado da seguinte forma:
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA , ENABLE ) ; // Habilita o clock do GPIOA para os canais do ADC
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_ADC1 , ENABLE ) ; // Habilita o clock para o ADC1
ADC_InitTypeDef ADC_InitStructure ;
ADC_InitStructure . ADC_Mode = ADC_Mode_Independent ;
ADC_InitStructure . ADC_ScanConvMode = DISABLE ;
ADC_InitStructure . ADC_ContinuousConvMode = DISABLE ;
ADC_InitStructure . ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;
ADC_InitStructure . ADC_DataAlign = ADC_DataAlign_Right ;
ADC_InitStructure . ADC_NbrOfChannel = 3;
ADC_Init ( ADC1 , & ADC_InitStructure ) ;
ADC_Cmd ( ADC1 , ENABLE ) ; // Habilita o ADC1
ADC_ResetCalibration ( ADC1 ) ; // Habilita o RESET do registo de calibracao
while ( ADC_GetResetCalibrationStatus ( ADC1 ) ) ; // Verificar o fim do RESET da calibracao
ADC_StartCalibration ( ADC1 ) ; // Inicia a calibracao
while ( ADC_GetCalibrationStatus ( ADC1 ) ) ; // Verificar o fim da calibracao
Posteriormente é realizado um processo de calibração do ADC1. Já o processo de amostragem dos sinais para a conversão digital é controlado por vários comandos realizados por software, de acordo com o seu respetivo datasheet. Sendo assim, segue-se abaixo a sequência correta de execução do código:
ADC_RegularChannelConfig ( ADC1 , ADC_Channel_4 , 1 , ADC_SampleTime_41Cycles5 ) ; // Seleciona o canal a ser lido
ADC_SoftwareStartConvCmd ( ADC1 , ENABLE ) ; // Inicia a conversao AD do periferico ADCx
while (! ADC_GetFlagStatus ( ADC1 , ADC_FLAG_EOC ) ) ; // Aguarda a conversao finalizar
ADCx_value = ADC_GetConversionValue ( ADC1 ) ; // Recolhe e armazena os dados em uma determinada variavel
FreeRTOS
O FreeRTOS é um sistema operativo em tempo real livre e apoiado, mesmo quando utilizado em aplicações comerciais [3]. Os RTOS permitem o desenvolvimento de aplicações através de tarefas independentes (thread e recursos), baseados num algoritmo de sequenciamento que vai executando alternadamente os “threads” concorrentes, onde cada tarefa tem seu contexto. Assim é possível separar todo o código em diferentes tarefas, cada uma dedicada a apenas um determinado processo. O código completo será formado pelas seguintes tarefas:
- static void prvreadADC( void *pvParameters );
- static void prvLcdTask( void *pvParameters );
A tarefa prvreadADC() será responsável por selecionar o canal do ADC, inicializar a conversão e armazenar o valor lido em uma variável. Já a tarefa prvLCD() será responsável por exibir os dados convertidos do ADC no display LCD do STM32 Primer1. O código também irá utilizar funções comuns com o intuito de auxiliar no processo de configuração do Clock, GPIO, ADC e USART e conversão dos valores obtidos do ADC para valores de aceleração. Como forma de aproveitar, ou libertar, o processamento do microcontrolador, será utilizado o conceito de filas de mensagens. Assim em vez do código ficar executando polling de uma flag, como é o caso da linha do código acima, em que o microcontrolador permanece a espera da flag de fim de conversão (EOC) ir a ”1”. Portanto com o uso de filas de mensagens, basta configurar o ADC como interrupção, através do NVIC, e assim que houver uma interrupção, fim da conversão, na rotina de interrupção é inserido o valor lido pelo ADC na fila, e na tarefa prvreadADC() ter uma fila no modo bloqueante a espera dos dados. Como o código também enviará os dados pela USART, o mesmo procedimento das filas foi implementado. De forma a associar cada leitura do sensor com seu respetivo instante de leitura, foi criada uma estrutura de dados ”Accelerometer”ao qual possui dois campos de dados, o acel e o tick, onde o acel recebe o valor do ADC e o tick recebe o valor do tickcount atual, como é mostrado no código abaixo:
struct Accelerometer {
uint16_t acel ; // recebe o valor do ADC
TickType_t tick ; // recebe o valor do tickcount
} recebe ;
Quanto à ordem de prioridades das tarefas, foi definido que a tarefa responsável por fazer a leitura dos dados do ADC possui maior prioridade, já a tarefa responsável por exibir os dados no display LCD ficou com prioridade inferior a do ADC. Em relação à ordem de prioridade das interrupções presentes, ADC e USART2, foi defindo novamente a maior prioridade a ISR do ADC e posteriormente a da USART2. Por fim, de forma a otimizar o aproveitamento da memória RAM do microcontrolador, foi desenvolvido durante a etapa de depurção uma função que determina o tamanho que determinada tarefa possui na Stack. De posse dos resultados, foi realizado um ajuste do tamanho da Stack da tarefa no momento de sua criação, como é mostrado abaixo:
xTaskCreate ( prvreadADC , " ADC", configMINIMAL_STACK_SIZE -30 , NULL , mainADC_TASK_PRIORITY , & HandleTask1 ) ;
xTaskCreate ( prvLcdTask , " LCD", configMINIMAL_STACK_SIZE -30 , NULL , mainLCD_TASK_PRIORITY , & HandleTask2 ) ;
Conclusão
Portanto com a utilização de um sistema operativo em tempo real para gerenciar diversas tarefas concorrentes de um microcontrolador, há uma ampliação significativa dos recursos e horizontes de aplicações destes no cenário tecnológico atual.
Referências
[1] SparkFun. ADXL337, 2016.
[2] STMicroelectronics. ARM-based 32-bit MCU STM32F101xx and STM32F103xx firmware library, 2015.
[3] FreeRTOS. FreeRTOS, 2016.