Skocz do zawartości

[C] Przetwornik ADC w połączeniu z USART na STM32F411RE


Pomocna odpowiedź

Napisano

Witam,

napisałem sobie kod z wykorzystaniem bibliotek STM32 peripheral, w którym robię pomiar na potencjometrze za pomocą przetwornika ADC. Po zakończeniu każdego pomiaru mikrokontroler wchodzi w przerwanie, w którym chcę przesyłać wynik pomiaru na PC za pomocą USART. Niestety coś idzie nie tak, gdyż program wysyła co najwyżej 2 pomiary i potem się zawiesza. Kod zaimplementowałem na płytce NUCLEO-F411RE z mikrokontrolerem STM32F411RE.

Niżej przedstawiam kod mojego programu:

#include "stm32f4xx.h"
void send_char(char c)
{
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, c);
}

int __io_putchar(int c)
{
  if (c=='\n')
send_char('\r');

send_char(c);
return c;
}

void ADC_IRQHandler() {

  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
     uint16_t conv_result;
     conv_result = ADC_GetConversionValue(ADC1);
     printf("Adc = %d V_conv = %f\n",conv_result,conv_result * 3.3f / 4096.0f);

}
int main(void)
{

// podłączenie zegara taktującego na przetwornik ADC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
// zegar dla UART
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// zegar dla portu A
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);


USART_InitTypeDef uart;
GPIO_InitTypeDef gpio;
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
GPIO_StructInit(&gpio);
gpio.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_0;
gpio.GPIO_Mode = GPIO_Mode_AN;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio);

USART_StructInit(&uart);
uart.USART_BaudRate = 115200;
uart.USART_Mode = USART_Mode_Tx;
uart.USART_Parity = USART_Parity_No;
uart.USART_StopBits = USART_StopBits_1;
uart.USART_WordLength = USART_WordLength_8b;
uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2, &uart);
USART_Cmd(USART2, ENABLE);

// inicjalizacja przetwornika ADC
ADC_InitTypeDef adc;
// Struktura, która zawiera informacje o wspólnej konfiguracji dla paru przetworników ADC, ale
// na płytce F411RE jest tylko jeden przetwornik
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_StructInit(&adc);
ADC_CommonStructInit(&ADC_CommonInitStructure);
NVIC_InitTypeDef NVIC_InitStructure;


// ustawienie częstotliwości przetwornika na 25 MHZ
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
// wyłączenie DMA
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
// opóźnienie pomiędzy dwoma konwersjami w liczbie cykli
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_16Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
// tryb ciągły działania przetwornika
adc.ADC_ContinuousConvMode = ENABLE;
adc.ADC_ScanConvMode = DISABLE;
adc.ADC_Resolution = ADC_Resolution_12b;
adc.ADC_DataAlign = ADC_DataAlign_Right;
// The total number of channels to be converted in sequence is specified by:
adc.ADC_NbrOfConversion= 1;
adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
adc.ADC_ExternalTrigConv = 0;
ADC_Init(ADC1, &adc);

//ADC_RegularChannelConfig(ADC1,ADC_Channel_17,1,ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_480Cycles);
// This call enables the end-of-conversion flag after each channel
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);

/* Enable ADC interrupts */
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

/* Configure NVIC */
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
// uruchomienie przetwornika
ADC_Cmd(ADC1,ENABLE);
ADC_TempSensorVrefintCmd(ENABLE);
ADC_SoftwareStartConv(ADC1);
for(;;){

}
}

Przesłanie pomiary w terminalu:

Jak widać przesłane są zaledwie dwa pomiary, później rekacjii nie ma. Jestem pewien co do tego, że przetwornik jest skonfigurowany dobrze i działa w trybie ciągłym(ang. continuous mode). Na potwierdzenie, że tak jest pokazuje poniżej co dostaję, jak zmodyfikuję kod przerwania ADC_IRQHandler() do postaci:

uint16_t conv_buffer[5];
int index = 0;
void ADC_IRQHandler() {

  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
     uint16_t conv_result;
     conv_result = ADC_GetConversionValue(ADC1);
     conv_buffer[index] = conv_result;
     index++;
     if(index == 5){

   	  int i;
   	  for(i = 0; i < 5; i++){

   		  printf("Adc = %d V_conv = %f\n",conv_buffer[i],conv_buffer[i] * 3.3f / 4096.0f);

   	  }
   	  index = 0;
     }

}

I wtedy mam 5 przesłanych próbek, jak widać w terminalu poniżej:

Stąd wyciągnąłem wniosek, że przetwornik ADC działa dobrze, tylko wszystko się zawiesza przy wysyłaniu przez USART. Nie wiem jak to rozwiązać, brakuję mi już pomysłów.

Obecnie wydaje mi się, że może zanim informacja się prześlę, to przetwornik kończy pomiar i chcę wejść znowu w przerwanie i wszystko wysiada? Nie wiem. Próbowałem też kombinować z ustawieniem sprzętowej kontroli przepływu(ang. hardware flow control), ale nic to nie zmieniło.

Gdyby ktoś mógł mi pomóc, miałby jakiś pomysł co tu może być nie tak, to byłbym bardzo wdzięczny.

Pozdrawiam 🙂

Jedną z zasad stosowanych w oprogramowaniu do systemów wbudowanych jest realizacja przerwań możliwie najszybciej. Wysyłanie danych przez port szeregowy w przerwaniu na pewno nie jest dobrym pomysłem.

Najprawdopodobniej Twoje przerwanie trwa tak długo, że kolejne jest zgłoszone jeszcze w trakcie trwania poprzedniego. Podejrzewałbym, że pojawia się jakieś przerwanie, dla którego nie masz napisanego handlera, więc wykonywany jest domyślnych handler w postaci while(1);

Wykorzystaj debugger do sprawdzenia jaki jest przebieg działania kodu.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...