[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); }