poniedziałek, 14 marca 2016

[2] STM32F4 - Discovery - CubeMx - ADC

W tym poście chciałbym zaprezentować obsługę ADC z wykorzystaniem biblioteki HAL.

CubeMx


W tym celu po otwarciu odpowiedniego pliku można odrazu przejść do odpowiedniej zakładki w Peripheral. Opisywany mikrokontroler posiada trzy przetworniki ADC.

Rys. 1. Okno programu CubeMx


Następnie należy rozwinąć jedną z gałęzi przetwornika. Jak widać na rysunku 2, niektóre z wyjść są oznaczone na czerwono. Oznacza to, że piny do których są już przyporządkowane mają podłączone inne elementy, które przeszkadzają w jego obsłudze.

Rys. 2. Zakładka ADC

Aby wybrać piny które nas interesują należy kliknąć na ikonę zaznaczania przy wejściach przetwornika lub bezpośrednio na pin, z którego byśmy korzystali. Dla przykładu zaznaczę wejście IN1 i IN2. Po ich wybraniu kolory przy wyprowadzeniach PA1 oraz PA2 zmieniły się na zielony. 

Rys. 3. Wybranie wyprowadzeń

Po wybraniu potrzebnych wyprowadzeń należy przejść do zakładki Clock Configuration w celu ustawienia zegara. Po wcześniejszym wybraniu źródła w Peripherals zakładka RCC. Ja wybrałem maksymalną częstotliwość taktowania, szczegóły jak ją ustawić znajdują się w poście [1] dla tej sekcji.

Gdy już to zostało ustawione należy przejść do zakładki Configuration i odpowiednio skonfigurować przetwornik.

Rys. 4. Zakładka configuration

Dalej należy kliknąć w przycisk ADC1 i przejść do ustawień.

Rys. 5. Ustawienie ADC

U samej górze znajduje się tryb pracy przetwornika. Poniżej należy wybrać wartość dzielnika dla zegara taktującego 2, 4, 6 lub 8. W kolejnym kroku do ustawienia jest rozdzielczość przetwornika. Następnie wyrównanie danych oraz tryby konwersji danych czy włączenie DMA. Dalej liczba konwersji, oraz ustawienie trybu wyzwalania zewnętrznego.

W kolejnych zakładkach znajduje się ustawienie przerwań, DMA czy ustawienie wybranych pinów dla konwertera.

Programowanie


Wygenerowany fragment kodu odpowiedzialny za ustawienie przetwornika:

  1. /* ADC1 init function */
  2. void MX_ADC1_Init(void)
  3. {
  4.   ADC_ChannelConfTypeDef sConfig;
  5.     /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  6.     */
  7.   hadc1.Instance = ADC1;
  8.   hadc1.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2;
  9.   hadc1.Init.Resolution = ADC_RESOLUTION12b;
  10.   hadc1.Init.ScanConvMode = DISABLE;
  11.   hadc1.Init.ContinuousConvMode = DISABLE;
  12.   hadc1.Init.DiscontinuousConvMode = DISABLE;
  13.   hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  14.   hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  15.   hadc1.Init.NbrOfConversion = 1;
  16.   hadc1.Init.DMAContinuousRequests = DISABLE;
  17.   hadc1.Init.EOCSelection = EOC_SINGLE_CONV;
  18.   HAL_ADC_Init(&hadc1);
  19.     /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  20.     */
  21.   sConfig.Channel = ADC_CHANNEL_1;
  22.   sConfig.Rank = 1;
  23.   sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  24.   HAL_ADC_ConfigChannel(&hadc1, &sConfig);
  25. }

Następnym elementem jest ustawienie pinów oraz włączenie taktowania. Ten fragment kodu został umieszczony w pliku stm32f4xx_hal_msp.c:

  1. void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  2. {
  3.   GPIO_InitTypeDef GPIO_InitStruct;
  4.   if(hadc->Instance==ADC1)
  5.   {
  6.   /* USER CODE BEGIN ADC1_MspInit 0 */
  7.   /* USER CODE END ADC1_MspInit 0 */
  8.     /* Peripheral clock enable */
  9.     __ADC1_CLK_ENABLE();
  10.     /**ADC1 GPIO Configuration    
  11.     PA1     ------> ADC1_IN1
  12.     PA2     ------> ADC1_IN2
  13.     */
  14.     GPIO_InitStruct.Pin = ADC1_1_Pin|ADC1_2_Pin;
  15.     GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  16.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  17.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  18.   /* USER CODE BEGIN ADC1_MspInit 1 */
  19.   /* USER CODE END ADC1_MspInit 1 */
  20.   }
  21. }

W celu dokonania pomiarów należy w głównej pętli programu wprowadzić następujący kod.

  1. int main(void)
  2. {
  3.   /* USER CODE BEGIN 1 */
  4.    
  5.     int ADC_Val = 0;
  6.    
  7.   /* USER CODE END 1 */
  8.   /* MCU Configuration----------------------------------------------------------*/
  9.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  10.   HAL_Init();
  11.   /* Configure the system clock */
  12.   SystemClock_Config();
  13.   /* Initialize all configured peripherals */
  14.   MX_GPIO_Init();
  15.   MX_ADC1_Init();
  16.     //Wywolanie funkcji wlaczajacej piny i zegar dla ADC
  17.     HAL_ADC_MspInit(&hadc1);  
  18.      HAL_ADC_Start(&hadc1);
  19.     ADC_Val = HAL_ADC_GetValue(&hadc1);
  20.   /* USER CODE BEGIN 2 */
  21.   /* USER CODE END 2 */
  22.   /* Infinite loop */
  23.   /* USER CODE BEGIN WHILE */
  24.   while (1)
  25.   {
  26.   /* USER CODE END WHILE */
  27.   /* USER CODE BEGIN 3 */
  28.   }
  29.   /* USER CODE END 3 */
  30. }

Powyżej funkcja główna wykonująca prosty pojedynczy pomiar.

Jeśli chce się wykonywać pomiary ciągiem, to należy wywołać następującą funkcje. Dodatkowo w funkcji policzone zostanie napięcie oraz temperatura. Dane zostaną przesłane przez UART do komputera.

  1. void adcConversion()
  2. {
  3.     uint16_t ADC_Val = 0;
  4.     float temp;
  5.     float vsense;
  6.     char buffer[50];
  7.     const float V25 = 0.76;
  8.     const float Avg_Slope=0.0025;
  9.     const float SupplyVoltage = 3.0;
  10.     const float ADCResolution = 4095.0;
  11.  
  12.     HAL_ADC_Start(&hadc1);
  13.     ADC_Val = HAL_ADC_GetValue(&hadc1);
  14.     vsense = (SupplyVoltage*ADC_Val)/ADCResolution;// Przeliczenie wartosci zmierzonej na napiecie
  15.     temp = ((vsense-V25)/SupplyVoltage)+25;// Obliczenie temperatury
  16.  
  17.     sprintf((char*)buffer,"ADC Value1: %u; Vsense: %f Temperature: %f\r\n", ADC_Val, vsense, temp);
  18.     HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  19. }