czwartek, 26 stycznia 2017

[10] STM32F7 - ADC

Ten dosyć krótki post chciałbym poświęcić na krótki opis sposobu inicjalizacji oraz przygotowania ADC do zbierania pomiarów z linii.

Na dostępne wyjścia układu wyprowadzono piny ADC o numerach:
  • PA0 - ADC kanał 0
  • PF10 - ADC kanał 1
  • PF9 - ADC kanał 2
  • PF8 - ADC kanał 3
  • PF7 - ADC kanał 4
  • PF6 - ADC kanał 5

Inicjalizacja portu GPIO w trybie analogowym:

  1. void ADC_PA0_GPIO_Init(void)
  2. {
  3.   GPIO_InitTypeDef GPIO_InitStruct;
  4.   __GPIOA_CLK_ENABLE();
  5.   //Konfiguracja GPIO PA0
  6.   GPIO_InitStruct.Pin = GPIO_PIN_0;
  7.   GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  8.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  9.   GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  10.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  11. }

Włączenie układu ADC:

  1. ADC_HandleTypeDef    Adc_Handle_Conf;
  2. void ADC1s_InitADC(void)
  3. {
  4.   __HAL_RCC_ADC1_CLK_ENABLE();
  5.   Adc_Handle_Conf.Instance                   = ADC1;
  6.   Adc_Handle_Conf.Init.ClockPrescaler        = ADC_CLOCKPRESCALER_PCLK_DIV4; //ustawienie 25MHz
  7.     /*
  8.     *   Dostepna konfiguracja prescalera dla taktowania linii 100MHz
  9.     *   #define ADC_CLOCKPRESCALER_PCLK_DIV1    ADC_CLOCK_SYNC_PCLK_DIV1 //100Mhz
  10.     *   #define ADC_CLOCKPRESCALER_PCLK_DIV2    ADC_CLOCK_SYNC_PCLK_DIV2 //50Mhz
  11.     *   #define ADC_CLOCKPRESCALER_PCLK_DIV4    ADC_CLOCK_SYNC_PCLK_DIV4 //25Mhz
  12.     *   #define ADC_CLOCKPRESCALER_PCLK_DIV6    ADC_CLOCK_SYNC_PCLK_DIV6 //16,66MHz
  13.     *   #define ADC_CLOCKPRESCALER_PCLK_DIV8    ADC_CLOCK_SYNC_PCLK_DIV8 //12.5MHz
  14.     */
  15.   Adc_Handle_Conf.Init.Resolution            = ADC_RESOLUTION_12B;
  16.   Adc_Handle_Conf.Init.ScanConvMode          = DISABLE;
  17.   Adc_Handle_Conf.Init.ContinuousConvMode    = DISABLE;
  18.   Adc_Handle_Conf.Init.DiscontinuousConvMode = DISABLE;
  19.   Adc_Handle_Conf.Init.NbrOfDiscConversion   = 0;
  20.   Adc_Handle_Conf.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;
  21.   Adc_Handle_Conf.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_CC1;
  22.   Adc_Handle_Conf.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
  23.   Adc_Handle_Conf.Init.NbrOfConversion       = 1;   //Pojedyncza konwersja
  24.   Adc_Handle_Conf.Init.DMAContinuousRequests = DISABLE;
  25.   Adc_Handle_Conf.Init.EOCSelection          = DISABLE;
  26.   HAL_ADC_Init(&Adc_Handle_Conf);
  27. }

Funkcja odczytująca pojedynczy wynik pomiaru:

  1. uint16_t ADC1s_Read_Data(ADC1s_NAME_t adc_name)
  2. {
  3.     uint16_t wartosc_ADC=0;
  4.     ADC_ChannelConfTypeDef ADC_Channel_Conf;
  5.     ADC_Channel_Conf.Channel      = ADC_CHANNEL_0;
  6.     ADC_Channel_Conf.Rank         = 1;
  7.     ADC_Channel_Conf.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  8.     ADC_Channel_Conf.Offset       = 0;
  9.     if (HAL_ADC_ConfigChannel(&Adc_Handle_Conf, &ADC_Channel_Conf) != HAL_OK) { return 0; }
  10.     if (HAL_ADC_Start(&Adc_Handle_Conf) != HAL_OK) { return 0; }
  11.     HAL_ADC_PollForConversion(&Adc_Handle_Conf, 10);
  12.     if(HAL_IS_BIT_CLR(HAL_ADC_GetState(&Adc_Handle_Conf), HAL_ADC_STATE_REG_EOC)) { return 0; }
  13.     wartosc_ADC=HAL_ADC_GetValue(&Adc_Handle_Conf);
  14.     return(wartosc_ADC);
  15. }


Tą funkcje można zmodyfikować tak aby były odczytywane większe ilości danych. Wykonuje się to poprzez zapętlenie pomiaru pojedynczego np. za pomocą pętli for. Dane po każdej konwersji zbiera się do wspólnej zmiennej (najbezpieczniej 32 bitowej uint32_t). Po przejściu zadanej liczby pomiarów wykonuje się dzielenie przez ilość pomiarów. Można też wykonać przesunięcia bitowe w lewo przy stosowaniu regularnych zakresów. Schemat poniżej:

Liczba pomiarów - 2; Przesunięcie - 1;
Liczba pomiarów - 4; Przesunięcie - 2;
Liczba pomiarów - 8; Przesunięcie - 3;
Liczba pomiarów - 16; Przesunięcie - 4;
Liczba pomiarów - 32 Przesunięcie - 5;
Liczba pomiarów - 64; Przesunięcie - 6;
Liczba pomiarów - 128; Przesunięcie - 7;
Przesuniecie będzie wyglądało następująco: zmienna_32_bit = (zmienna_32_bit >> przesuniecie);