LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

[STemWIN] Cyfrowy magnetometr na STM32 z graficzną prezentacją wyników

Po uruchomieniu układu wykonywanie programu rozpoczyna się od głównej funkcji main(), w której konfigurowane są peryferia (przycisk, przetwornik A/C), tworzone jest nowe zadanie Magnetometer oraz wywoływany jest planista (scheduler) systemu FreeRTOS.

int main(void){
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

  /* Configure USER button */
  STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_GPIO);

  /* Configure ADC */
  ADC_Config();

  /* Create main task */
  xTaskCreate(Main_Task,
              (signed char const*)"Magnetometer",
              Main_Task_STACK,
              NULL,
              Main_Task_PRIO,
              &Task_Handle);

  /* Launch Touchscreen Timer */
  TouchScreenTimer = xTimerCreate ((signed char *)"Timer", 50, pdTRUE, (void*) 1, vTimerCallback);

  if( TouchScreenTimer != NULL )
  {
    if( xTimerStart( TouchScreenTimer, 0 ) != pdPASS )
    {
      /* The timer could not be set into the Active state. */
    }
  }

  /* Start the FreeRTOS scheduler */
  vTaskStartScheduler();

  return 0;
}

Przetwornik A/C jest skonfigurowany do pracy z 13 kanałem (nóżka PC3), a pojedynczy pomiar wyzwalany jest programowo w funkcji adc_convert():

static void ADC_Config(void){
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  ADC_InitTypeDef       ADC_InitStructure;
  GPIO_InitTypeDef      GPIO_InitStructure;

  /* Enable ADC interface clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

  /* Enable GPIOC clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

  /* Configure ADC pin */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Reset ADC */
  ADC_DeInit();

  /* Configure ADC - common settings */
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);

  /* Configure ADC - settings for ADC1 */
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = 0;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* Activate ADC */
  ADC_Cmd(ADC1, ENABLE);

  /* Select channel ADC123_IN13 (PC3 pin) */
  ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_144Cycles);
}

uint16_t adc_convert(){
  // Start conversion of regular channel
  ADC_SoftwareStartConv(ADC1);
  // Wait until end of conversion
  while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
  // Return converted data
  return ADC_GetConversionValue(ADC1);
}

Zadanie o nazwie Magnetometer realizuje funkcja Main_Task(). Pierwsza rzecz jaka jest wykonywana w tym zadaniu to inicjalizacja warstwy BSP oraz biblioteki STemWin, a dopiero po tym tworzone są widżety FRAMEWIN, GRAPH i TEXT.

static void Main_Task(void * pvParameters){
  unsigned int ticks = 0;

  // Deklaracja uchwytów
  FRAMEWIN_Handle    hFrame, hClientWin;
  GRAPH_Handle       hGraph;
  GRAPH_DATA_Handle  hGraphData;
  GRAPH_SCALE_Handle hGraphScale;
  TEXT_Handle        hText_1, hText_2, hText_3, hText_4;

  uint16_t conversion_value;

  double voltage_mV, voltage_V;
  double magneticField_G, magneticField_mT;

  char text[100];

  // Inicjalizacja warstwy BSP
  LowLevel_Init();

  // Inicjalizacja biblioteki STemWin GUI
  GUI_Init();

  // Ustawienie koloru tła na jasnoniebieski
  WM_SetDesktopColor(GUI_LIGHTBLUE);

  // Ustawienie stylu FLEX dla widżetu FRAMEWIN
  FRAMEWIN_SetDefaultSkin(FRAMEWIN_SKIN_FLEX);

  // Utworzenie widżetu FRAMEWIN
  hFrame = FRAMEWIN_CreateEx(10, 30, 220, 230, WM_HBKWIN, WM_CF_SHOW, 0, 0, "Magnetometer", NULL);
  hClientWin = WM_GetClientWindow(hFrame);        // Pobranie uchwytu do obszaru klienta
  FRAMEWIN_SetFont(hFrame, &GUI_Font16_ASCII);    // Ustawienie czcionki tekstu na pasku tytułowym
  FRAMEWIN_SetTextAlign(hFrame, GUI_TA_HCENTER);  // Wyśrodkowanie tekstu na pasku tytułowym

  // Utworzenie i konfiguracja widżetu GRAPH
  hGraph = GRAPH_CreateEx(5, 5, 200, 120, hClientWin, WM_CF_SHOW, 0, GUI_ID_GRAPH0);
  GRAPH_SetBorder(hGraph, 40, 0, 0, 0);           // Ustawienie szerokości 40 px lewego obramowania
  GRAPH_SetGridDistY(hGraph, 15);                 // Odstęp 15 px między poziomymi liniami siatki
  GRAPH_SetGridOffY(hGraph, 10);                  // Przesunięcie siatki o 10 px w górę
  GRAPH_SetGridVis(hGraph, 1);                    // Uwidocznienie siatki
  GRAPH_SetLineStyleH(hGraph, GUI_LS_DOT);        // Poziome linie siatki w stylu kropkowanym
  GRAPH_SetLineStyleV(hGraph, GUI_LS_DOT);        // Pionowe linie siatki w stylu kropkowanym
  GRAPH_SetUserDraw(hGraph, _UserDraw);           // Wskazanie funkcji rysującej dodatkowe elementy na wykresie
  // Utworzenie zbioru danych o pojemności do 200 próbek
  hGraphData = GRAPH_DATA_YT_Create(GUI_GREEN, 200, NULL, 0);
  GRAPH_DATA_YT_SetOffY(hGraphData, 10);          // Przesunięcie danych na wykresie o 10 px w górę
  // Dołączenie zbioru danych do widżetu GRAPH
  GRAPH_AttachData(hGraph, hGraphData);
  // Utworzenie skali osi pionowej z krokiem co 30 px i tekstem wyrównanym do prawej
  hGraphScale = GRAPH_SCALE_Create(38, GUI_TA_RIGHT, GRAPH_SCALE_CF_VERTICAL, 30);
  GRAPH_SCALE_SetFactor(hGraphScale, 0.033);      // Przeliczenie wartości na skali z [px] na [V]
  GRAPH_SCALE_SetNumDecs(hGraphScale, 1);         // Ustawienie liczby cyfr wyświetlanych po przecinku
  GRAPH_SCALE_SetOff(hGraphScale, 10);            // Przesunięcie skali na wykresie o 10 px w górę
  GRAPH_SCALE_SetTextColor(hGraphScale, GUI_BLACK); // Wybranie koloru tekstu na skali
  // Dołączenie skali do widżetu GRAPH
  GRAPH_AttachScale(hGraph, hGraphScale);

  // Utworzenie widżetów TEXT
  hText_1 = TEXT_CreateEx(10, 140, 100, 20, hClientWin, WM_CF_SHOW, TEXT_CF_LEFT, GUI_ID_TEXT0, "[V]");
  hText_2 = TEXT_CreateEx(10, 160, 100, 20, hClientWin, WM_CF_SHOW, TEXT_CF_LEFT, GUI_ID_TEXT1, "[G]");
  hText_3 = TEXT_CreateEx(10, 180, 100, 20, hClientWin, WM_CF_SHOW, TEXT_CF_LEFT, GUI_ID_TEXT2, "[mT]");
  hText_4 = TEXT_CreateEx(160, 160, 20, 20, hClientWin, WM_CF_SHOW, TEXT_CF_LEFT, GUI_ID_TEXT3, " ");
  TEXT_SetFont(hText_4, &GUI_Font20_ASCII);

  // Wybranie/Zaznaczenie okna głównego
  WM_SetFocus(hFrame);

  // Wykonanie zadania głównego
  while(1){
    if(ticks++ > 5){
      ticks = 0;

      // Inwersja LED3 co 50ms
      STM_EVAL_LEDToggle(LED3);

      // Odczyt nowych danych z czujnika
      conversion_value = adc_convert();
      // Konwersja 12-bitowej wartości na [V]
      voltage_mV = (double)conversion_value * 0.7324;
      voltage_V = voltage_mV / 1000.0;
      // Konwersja na gaussy [G] oraz minitesle [mT]
      magneticField_G = (voltage_mV - 1500) / A1395_SENSITIVITY;
      magneticField_mT = magneticField_G / 10.0;

      // Przeskalowanie i dodanie nowej próbki do zbioru danych widżetu GRAPH
      GRAPH_DATA_YT_AddValue(hGraphData, 30*voltage_V);

      // Aktualizacja tekstu widżetów TEXT
      sprintf(text,"V = %.3f [V]", voltage_V);
      TEXT_SetText(hText_1, text);
      sprintf(text,"B = %.3f [G]", magneticField_G);
      TEXT_SetText(hText_2, text);
      sprintf(text,"B = %.3f [mT]", magneticField_mT);
      TEXT_SetText(hText_3, text);

      // Wyświetlenie biegunowości pola magnetycznego
      if(magneticField_G > 5.0){
        TEXT_SetText(hText_4, "N");
        TEXT_SetTextColor(hText_4, GUI_BLUE);
      }
      else if(magneticField_G < -5.0){
        TEXT_SetText(hText_4, "S");
        TEXT_SetTextColor(hText_4, GUI_RED);
      }
      else{
        TEXT_SetText(hText_4, " ");
      }
    }
    GUI_Delay(10);
  }

  // Usunięcie widżetów
  WM_DeleteWindow(hGraph);
  WM_DeleteWindow(hFrame);
}
Autor: Jan Szemiet