Baixe o app para aproveitar ainda mais
Prévia do material em texto
USANDO O CONVERSOR A/D DO CORTEX M0 Vamos fazer um passo a passo de como utilizar o conversor A/D do Cortex M0. Inicialmente vamos recordar como é o conversor A/D e suas principais características. A figura 1 mostra a arquitetura do A/D, o qual é composto de um conversor do tipo SAR de 12 bits, com taxa máxima de amostragem de 1MSP/s (um milhão de amostras por segundo). O usuário dispõe de até 16 entradas analógicas espalhadas pelas portas A, B, C, D e F do microcontrolador. Figura 1 – Arquitetura do conversor A/D utilizado O conversor A/D conta com um circuito de amostragem/retenção (S/H), garantindo estabilidade na entrada do conversor A/D. O A/D pode ser utilizado também para medir a Temperatura (Temp) do microcontrolador fazendo do mesmo um sensor de temperatura. Também é possível medir a tensão de referência do A/D (Vref) e a tensão da bateria que alimenta o circuito de RTC (Vbat). Para obter a velocidade máxima de aquisição, um oscilador interno chamado HSI14 de 14MHz pode ser utilizado. Para a conclusão de uma conversão são necessários 12 pulsos de clock. Outros dois são necessários para sincronização do circuito de S/H. Ou seja, utilizando um clock de 14MHz é possível obter uma taxa de aquisição de 1MSp/s. Como exemplo vamos montar um circuito em que na entrada analógica Ain0 será conectado um potenciômetro (ou trimpot) entre o Vcc (3,3 Volts) e o GND (0 Volts). Conectado ao µC temos um display de duas linhas por 16 colunas ligado em uma interface de 4 bits. Imprima a leitura do conversor (valor absoluto) na primeira linha. Faça a conversão deste valor e imprima o mesmo na segunda linha, ambos os valores no canto esquerdo. A figura 2 ilustra o problema. O processo de leitura e impressão se repete indefinidamente em intervalos de um segundo! Figura 2 – Circuito de teste do Conversor A/D Iniciamos por configurar os pinos do microcontrolador através da ferramenta STM32 CubeMX. A figura 3 mostra a tela de abertura. Clique no menu File e selecione a opção New Project. Figura 3 – Tela de Abertura – Selecionando a placa do Cortex M0 Uma janela de diálogo irá surgir. Selecione a aba Board Selector (1). Em Part Number Search (2) digite NUCLEO-F072. Na parte inferior da janela irá surgir a placa que você quer utilizar (3). Confirme sua escolha clicando duas vezes na figura! Uma nova janela de diálogo irá surgir, perguntando se você deseja inicializar (de forma automática) os pinos da placa (ver figura 4). Responda que sim! Figura 4 – Confirmando a Inicialização. Então a IDE para configuração do Cortex será aberta, com o desenho da pinagem do µC bem no centro (ver figura 5). Os pinos desenhados na cor cinza estão disponíveis para o usuário utilizar. Eles estão configurados como GPIO-INPUT do tipo float (condição de Reset - Reset State). Os pinos em destaque na cor verde são pinos alocados para a placa NUCLEO e não devem ser nem utilizados e nem modificados. Os pinos destacados em laranja são pinos que estão alocados mas não estão configurados. Será preciso que o usuário faça a configuração manualmente. Estes pinos (PF0 e PF1) são do oscilador externo de alta frequência (>1MHz), cujo cristal não vem usualmente soldado na placa. Este oscilador é utilizado no controle da CPU determinando os tempos das instruções. Na ausência deste cristal, o oscilador interno HSI toma conta do µC. O ideal é soldar um cristal de 8Mhz na placa para exatidão e estabilidade em frequência. Figura 5 – IDE de configuração do STM32F072. Vamos agora escolher o pino do conversor A/D Ain0. Ele está mapeado no pino PA0. Clique no pino PA0 e escolha a opção ADC_IN0. O pino muda para cor verde. Precisamos configurar o conversor A/D. Clique no menu Analog no canto esquerdo da figura 5. Ele abre um novo menu sobre a barra cinza que divide a imagem (ver figura 6). Figura 6 – Configurando o canal de entrada Ain0. Lista de canais usados e disponíveis! Menu de configuração Na lista de canais usados e disponíveis pode-se observar que In0 está ocupado (pino PA0). Então no menu de configuração vamos configurar este canal analógico. Verifique os seguintes parâmetros: Clock Prescaler: Asynchronous clock mode Resolution: ADC 12-bit Data Alignment: Right Continous Conversion Mode: Enabled End Of Conversion Selection: End of single conversion Sampling Time: 1.5 cycles External Trigger Source: Launched by software Com isto o conversor A/D e a respectiva entrada analógica foram configurados. Agora é necessário ir no menu Project Manager (ver figura 7) para ajustar o nome projeto e a IDE que será utilizada (Keil uVision5) para editar o programa em linguagem C. Figura 7 – Definições de Projeto. Defina então o nome do seu projeto (o meu defini como F072_AD), onde o mesmo será armazenado (no meu caso D:\Workspace\Cube\) e qual a IDE utilizada – MDK-ARMV5. Feito isto clique em Generate Code (canto superior direito da tela) para gerar as bibliotecas e o programa main.c. O programa leva um certo tempo para gerar toda a estrutura de arquivos. Ao completar a tarefa o programa lança a seguinte janela de diálogo (ver figura 8). Figura 8 – Codificação completada Se este processo está sendo feito pela primeira vez ou você abriu apenas o programa Cube (a IDE da Keil está fechada), selecione a opção Open Project. Se este processo não está sendo feito pela primeira vez (talvez porque você esqueceu de alguma coisa e está corrigindo ou alterando alguma configuração) e a IDE da Keil já está aberta, então clique na opção Close. Neste caso, ao retornar para a IDE da Keil, uma outra janela de diálogo irá aparecer (ver figura 9). Figura 9 – Sobrescrever arquivo fonte Clique na opção Sim. Duas vezes! Agora podemos editar o programa. A IDE da Keil inicia abrindo a Tela de edição vazia, apenas indicando no canto esquerdo da janela, a arvore de diretórios criada. Clique no [+] do nome do arquivo definido para o projeto. Então o programa desce um nível na arvore de diretórios e mostra agora as opções de 4 grandes diretórios. Clique no [+] do diretório Application/User. Com isto deve aparecer uma lista de arquivos .c. Selecione o arquivo main.c . Veja na figura 10 como ficam estes passos. Figura 10 – Aspecto da IDE da Keil. Como o assunto é sobre o conversor A/D, assume-se que as funções do display LCD já foram escritas e estão incluídas no arquivo main.c. Logo será discutido apenas as funções do conversor A/D. As funções mais básicas são: HAL_ADC_Init(&hadc); HAL_ADC_Start(&hadc); HAL_ADC_PollForConversion(&hadc, 1); HAL_ADC_GetValue(&hadc); HAL_ADC_Stop(&hadc); O argumento hadc é uma variável criada pelo Cube para armazenar toda a configuração e funcionamento do conversor A/D. A função Init inicializa o conversor A/D. A função Start dispara a função de S/H e depois de conversão. A função PollForConversion faz o processador aguardar (sincronizar) a resposta do processador com a resposta do A/D. O argumento que segue a Janela de Edição da IDE variável hadc é o argumento de timeout (em ms), ou seja, o quanto o processador deve aguardar até retornar, mesmo que o conversor A/D não tenha dado resposta. Isto é necessário para não fazer com que o processador fique travado indefinidamente se o conversor A/D por exemplo, apresentar algum tipo de erro. No exemplo, o valor “1” corresponde a 1ms. A função GetValue retorna um inteiro que corresponde a resposta do conversor frente a um sinal de entrada. Por fim a função Stop encerra o processo de conversão A/D e desativa o circuito. Logo, para o exercício proposto, uma solução possível seria da seguinte forma: int main(void) { /* USER CODE BEGIN 1 */ int leitura; float x; /* USER CODE END 1 */ /* MCU Configuration-----------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface */ HAL_Init(); /* USER CODE BEGIN Init*/ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); MX_ADC_Init(); /* USER CODE BEGIN 2 */ HAL_ADC_Init(&hadc); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_ADC_Start(&hadc); HAL_ADC_PollForConversion(&hadc, 1); leitura = HAL_ADC_GetValue(&hadc); HAL_ADC_Stop(&hadc); x = 0.0008059*leitura; lcd_goto(0, 0); printf("%4d", leitura); lcd_goto(0, 1); printf("%.1f Volts", x); HAL_Delay(925); // Para fazer a leitura a cada 1000 ms... } /* USER CODE END 3 */ } Outro exemplo. Vamos montar um termômetro, conectando na entrada analógica Ain0 um sensor de temperatura do tipo LM35, cuja característica de componente é gerar uma saída de 10mV/°C com offset de 35mV. Conectado ao µC temos um display de duas linhas por 16 colunas ligado em uma interface de 4 bits. Imprima seu nome na primeira linha do display. Faça a conversão do valor para °C e imprima este valor no canto inferior esquerdo. No canto inferior direito imprima a temperatura em °F. A figura 11 ilustra o problema. O termômetro vai ler temperaturas entre 0 e 50°C. O processo de leitura e impressão se repete indefinidamente em intervalos de 200ms! Imprima o símbolo de grau. Isto pode ser feito imprimindo o caracter 248. Figura 11 – Termômetro utilizando o Cortex M0. SOLUÇÃO int main(void) { /* USER CODE BEGIN 1 */ int leitura; float Xc, Xf; /* USER CODE END 1 */ /* MCU Configuration-----------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); MX_ADC_Init(); /* USER CODE BEGIN 2 */ HAL_ADC_Init(&hadc); printf("Seu Nome"); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_ADC_Start(&hadc); HAL_ADC_PollForConversion(&hadc, 1); leitura = HAL_ADC_GetValue(&hadc); HAL_ADC_Stop(&hadc); Xc = 0.0806*(leitura-43); Xf = 1.8*Xc + 32; lcd_goto(0, 1); printf("%.1f%cC", Xc); lcd_goto(9, 1); printf("%.1f%cF", Xf); HAL_Delay(125); // Para fazer a leitura a cada 200 ms... } /* USER CODE END 3 */ } EXERCÍCIO 1. Para o problema 2, determine o número efetivo de bits utilizados devido a amplitude do sinal fornecida pelo sensor. Projete então um circuito de condicionamento de sinal de modo a explorar a totalidade (ou quase a totalidade) dos bits do conversor. Mostre os cálculos envolvidos no circuito de condicionamento e no de conversão para a faixa de valores que se quer medir (0 a 50°C). Uma vez que o sinal explora mais bits do conversor A/D, determine quantas casas decimais podem ser impressas na indicação de °C. 2. Um sensor de temperatura industrial tem saída de 4 a 20mA para a faixa de temperatura de -300°C à 850°C. Um resistor de 165Ω é usado para converter a corrente em tensão na entrada Ain0. Escreva um programa que imprima na primeira linha o valor absoluto e na segunda linha a temperatura em °C. Imprima o resultado com uma casa decimal. Quantas casas decimais são válidas nesta solução? 3. Repita o problema 2 indicando quantos bits estão sendo efetivamente utilizados. Projete um circuito de condicionamento de sinal que maximize o uso de bits do conversor. USANDO O CONVERSOR D/A DO CORTEX M0 Vamos fazer um passo a passo de como utilizar o conversor D/A do Cortex M0. Inicialmente vamos recordar como é o conversor A/D e suas principais características. A figura 12 mostra a arquitetura do conversor D/A, de 12 bits. Os Cortex da família M0 podem ter até dois conversores independentes, localizados na porta PA4 (DAC1) e PA5 (DAC2) do microcontrolador. Figura 12 – Conversor D/A A tensão de referência é de 3,3Volts. Sabendo isto, utilizando o conversor D/A escreva um programa que gere uma tensão contínua de 2,5Volts na porta PA5, usando: a) Configuração de 8 bits. Calcule o erro esperado para a saída. b) Configuração de 12 bits (a direita). Calcule o erro esperado para a saída. c) Configuração de 12 bits (a esquerda). Calcule o erro esperado para a saída. Iniciamos por configurar os pinos do microcontrolador através da ferramenta STM32 CubeMX. A figura 13 mostra a tela de abertura. Clique no menu File e selecione a opção New Project. Ao abrir a janela de diálogo, digite F071 em Part Number Search. Figura 13 - Tela de Abertura – Selecionando o Cortex STM32F071C8 Na parte inferior da janela irá surgir o desenho do microcontrolador F071. No canto superior direito da janela, confirme sua escolha clicando em Get Start Project. Então a IDE será aberta, com o desenho da pinagem do µC bem no centro (ver figura 14). Todos os pinos (exceto os de alimentação/sistema) estão desenhados na cor cinza e disponíveis para o usuário utilizar. Eles estão configurados como GPIO-INPUT do tipo float (Reset State). Reserve os pinos PC14, PC15, PF0, PF1 para os cristais osciladores e PA13 e PA14 para a gravação do dispositivo (Porta DAP). Agora vamos configurar as portas do conversor D/A! Figura 14 – Reservando os pinos do F071 Clique no menu Analog (em destaque na figura). Ali aparecem as opções ADC, COMP1, COMP2 e DAC. Clique em DAC. Um novo menu é mostrado, como ilustra a figura 15. Figura 15 – Configurando os pinos do DAC. Clique nos “check boxes” OUT1 e OUT2 (em destaque na figura). Veja que ao fazer isto, os pinos PA4 (DAC1) e PA5 (DAC2) foram reservados (mudaram de cor para verde). Na parte debaixo do menu Analog ficam as configurações dos DACs. Na opção Trigger escolha a opção software. Com isto, ao escrever no registrador do DAC, o valor correspondente em tensão vai aparecer no pino de saída deste conversor. Agora você deve preencher os dados da aba Project Manager (nome do projeto, onde vai ser salvo e que você vai utilizar a ferramenta da Keil – opção MDK-ARMV5). Feito isto clique em Generate Code (canto superior direito da janela) para gerar o código e poder editar o programa em C. Ao surgir a janela de diálogo, escolha a opção Open Project. Vamos resolver o problema então. Para gerar 2,5Volts, determine o valor que deve ser escrito em 8bits e 12bits. Para 8 bits, o equacionamento se dá da seguinte forma: 𝑣𝐷𝐴𝐶 = 𝑉𝑟𝑒𝑓 𝑛 255 → 𝑛 = 255 𝑣𝐷𝐴𝐶 𝑉𝑟𝑒𝑓 → 𝑛 = 193 O erro para 8 bits equivale a um step do conversor (∆), o qual vale: ∆ = 𝑉𝑟𝑒𝑓 28 = ± 6,4𝑚𝑉 Logo 𝑣𝐷𝐴𝐶 = 2,5𝑉 ± 6,4𝑚𝑉 Para 12 bits, o equacionamento se dá da seguinte forma: 𝑣𝐷𝐴𝐶 = 𝑉𝑟𝑒𝑓 𝑛 4095 → 𝑛 = 4095 𝑣𝐷𝐴𝐶 𝑉𝑟𝑒𝑓 → 𝑛 = 3102 O erro para 12 bits equivale a um step do conversor (∆), o qual vale: ∆ = 𝑉𝑟𝑒𝑓 212 = ± 0,4𝑚𝑉 Logo 𝑣𝐷𝐴𝐶 = 2,5𝑉 ± 0,4𝑚𝑉 SOLUÇÃO int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration-----------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); MX_DAC_Init(); /* USER CODE BEGIN 2 */HAL_DAC_Init(&hadc); // Solução em 8bits... HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_8B_R, 193); HAL_DAC_Stop(&hdac, CHANNEL_2); // Solução em 12bits... HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12_R, 3102); HAL_DAC_Stop(&hdac, CHANNEL_2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } EXERCÍCIO Gere um sinal, conforme ilustra a figura 16, na porta PA4, com resolução de 12 bits. O sinal deve ser amostrado no tempo com 101 amostras. O resultado da amostragem deve resultar em um sinal de 1kHz. Figura 16 – Gerador de Sinal dente de serra 0,000 0,200 0,400 0,600 0,800 Gerador de Sinais 0,5V 0,741V O valor mais baixo é dado por: 𝑣𝐷𝐴𝐶 = 3,3 𝑛 4095 = 0,5𝑉 → 𝑛 = 4095 0,5𝑉 3,3𝑉 → 𝑛 = 620 O valor mais alto é dado por: 𝑣𝐷𝐴𝐶 = 3,3 𝑛 4095 = 0,741𝑉 → 𝑛 = 4095 0,741𝑉 3,3𝑉 → 𝑛 = 920 Como a sequência vai ter 101 passos a mesma começa com x=0 e termina e termina com x=100. Como n começa em 620 e termina em 920, a relação entre x e n pode ser escrita como: 𝑛 = 620 + 3𝑥 Para que o sinal tenha frequência de 1kHz (T=1ms), deve-se calcular o tempo de amostragem (Ta), o qual depende do número de amostras, ou seja: 𝑇𝑎 = 𝑇 101 = 1𝑚𝑠 101 = 9,9𝑢𝑠 Como não é possível escolher um delay fracionário, devemos fazer uma aproximação. Se escolhermos Ta=10us, então: 𝑇 = 101𝑇𝑎 → 𝑓 = 1 𝑇 ≈ 990𝐻𝑧 Se escolhermos Ta=9us, então: 𝑇 = 101𝑇𝑎 → 𝑓 = 1 𝑇 ≈ 1100𝐻𝑧 Logo, a melhor opção será um tempo de amostragem (Ta) de 10us. Segue a solução em C. SOLUÇÃO int main(void) { /* USER CODE BEGIN 1 */ int n, x; /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash & the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DAC_Init(); /* USER CODE BEGIN 2 */ HAL_DAC_Init(&hdac); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ for (x = 0; x < 101; x++) { n = 620 + 3 * x; HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, n); HAL_DAC_Stop(&hdac, DAC_CHANNEL_1); delayus(10); } } /* USER CODE END 3 */ } EXERCÍCIO Gere um sinal senoidal, conforme ilustra a figura 17, na porta PA5, com resolução de 12 bits. O sinal deve ser amostrado no tempo com 256 amostras. O resultado da amostragem deve resultar em um sinal de 1kHz. Figura 17 – Gerador de Sinal dente de serra O sinal senoidal apresentado na figura 17 pode ser escrito como: 𝑠(𝑛) = (0,5(1 + sin (2𝜋 𝑛 256 )))( 4095 3,3 ) Como a sequência vai ter 256 passos para que o sinal tenha frequência de 1kHz (T=1ms), deve- se calcular o tempo de amostragem (Ta), ou seja: 0 200 400 600 800 1000 1200 1400 Gerador Senoidal 0V 1V 𝑇𝑎 = 𝑇 256 = 1𝑚𝑠 256 = 3,9𝑢𝑠 Como não é possível escolher um delay fracionário, devemos fazer uma aproximação. Se escolhermos Ta=4us, então: 𝑇 = 256𝑇𝑎 → 𝑓 = 1 𝑇 ≈ 976𝐻𝑧 Se escolhermos Ta=3us, então: 𝑇 = 256𝑇𝑎 → 𝑓 = 1 𝑇 ≈ 1302𝐻𝑧 Logo, a melhor opção será um tempo de amostragem (Ta) de 4us. Segue a solução em C. SOLUÇÃO /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ // Tabela do sinal senoidal feita em Excel const short seno[256] = { 620, 636, 651, 666, 681, 696, 711, 726, 741, 756, 771, 786, 800, 815, 829, 844 // e assim por diante }; /* USER CODE END PD */ int main(void) { /* USER CODE BEGIN 1 */ int n; /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash & the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DAC_Init(); /* USER CODE BEGIN 2 */ HAL_DAC_Init(&hdac); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ for (n = 0; n < 256; n++) { HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, seno[n]); HAL_DAC_Stop(&hdac, DAC_CHANNEL_2); delayus(4); } } /* USER CODE END 3 */ }
Compartilhar