Prostřednictvím vývojové desky vhodně zvoleného mikrokontroléru s architekturou ARM realizujte řízení mechanického rotačního systému, skládajícího se z DC motorku a inkrementálního enkodéru. Implementujte funkce otáčení zvolenou rychlostí, natočení o požadovaný úhel a ukládání aktuální pozice v závislosti na synchronizačním signálu.
Uvedený projekt je součástí diplomové práce na téma Modul pro verifikaci rotačních pozičních senzorů, jejímž výstupem je návrh řídícího modulu včetně komunikačního rozhraní pro mechanický rotační systém. Cílem tohoto projektu je vytvořit program pro vhodně zvolenou vývojovou desku mikrokontroléru (dále MCU kit) umožňující jak řízení systému tak i časově synchronizovaný záznam velkého množství aktuálních pozic.
Mechanický rotační systém se skládá ze stejnosměrného motorku vybaveného planetovou převodovkou s převodem do pomala. Dále z inkrementálního kvadraturního rotačního kodéru s rozlišením 100 000 PPR (pulzů na otáčku) a lineárního čtyřkvadrantového servozesilovače, který udržuje nastavené otáčky konstantní díky zpětné vazbě z tachodynama. Systém je uveden na následující fotografii.
Primární aplikací modulu je testování při vývoji bezkontaktních indukčních pozičních senzorů, kdy se vyhodnocuje přesnost měřeného úhlu natočení vůči referenční hodnotě zaznamenané právě touto jednotkou. Aplikační zapojení si lze prohlédnout na následujícím obrázku, kde A, B tvoří kvadraturní signál kodéru (vzájemný fázový posuv 90°) a M je tzv. index signál informující svou nástupnou hranou o počáteční pozici rotoru. Řídící signály STEP a DIR slouží k řízení otočení o předem definovaný krok. REQB je synchronizační signál jehož sestupná hrana definuje časové okamžiky zápisu aktuální pozice rotoru. Otáčky motorku se řídí velikosti napětí signálu SPD na vstupu lineárního zesilovače, kdy kladné napětí znamená otáčení ve směru hodinových ručiček a záporné napětí směr opačný. Digitální signál STOP nastavuje nulový točivý moment, aby se rotor neotáčel díky napěťovému ofsetu.
Volba MCU závisí na požadavcích aplikace modulu, mezi které patří dostatečná kapacita datové paměti, schopnost komunikovat s počítačem v dnešní době především přes USB, vysoký taktovací kmitočet pro dostatečně rychlou reakci na synchronizační signál, hardwarová podpora pro inkrementální kodér a digitálně analogový převodník (DAC) s vyhovujícím rozlišením. Těmto požadavkům vyhovuje vývojový kit STM32F429 Discovery od výrobce STMicroelectronics osazený 32 bitovým MCU STM32F429ZI s jádrem Cortex-M4 architektury ARM. Tento kit disponuje mimo jiné datovou pamětí SDRAM s kapacitou 8 MB.
Vybrané parametry STM32F429ZI:
Blokové zapojení řídící jednotky se zvoleným vývojovým kitem si lze prohlédnout na obrázku:
Další obrázek ukazuje volbu vstupně/výstupních vývodů pro řídící signály a rotační kodér včetně analogového výstupu z převodníku.
Z důvodů přímo připojeného gyroskopu ST MEMS L3GD20 a uživatelského tlačítka (také slouží jako wake-up) nelze použít ani jeden ze dvou 32 bitových časovačů TIM2 a TIM5 ve funkci čítače pulsů z inkrementálního rotačního kodéru. Proto byl pro tuto funkci vybrán 16 bitový časovač TIM8 s nastavenou periodou 50 000. Pro analogový výstup je k dispozici pouze druhý kanál DAC. Pro emulaci kvadraturního signálu z kodéru byly vybrány kanály 2 a 3 časovače TIM1. Ostatní vývody byly voleny s ohledem na maximální počet linek externího přerušení, kterých je 15 a jsou sdíleny všemi porty (více informací zde).
Tabulka zvolených vývodů pro externí signály
Signál | Pin | Funkce pinu |
---|---|---|
A | PC6 | TIM8_CH1 |
B | PC7 | TIM8_CH2 |
M | PC8 | GPIO_EXTI8 |
Emul A | PA9 | TIM1_CH2 |
Emul B | PA10 | TIM1_CH3 |
STEP | PE2 | GPIO_EXTI2 |
DIR | PE3 | GPIO_INPUT |
REQB | PE4 | GPIO_EXTI4 |
DAC | PA5 | DAC_OUT2 |
STOP | PE5 | GPIO_OUTPUT |
Ostatní potřebné vývody (USB OTG FS, FMC SDRAM, atd.) byly vygenerovány pomocí aplikace STM32CubeMX verze 4.11.0 (nejnovější verze dostupná zde) včetně konfigurace periférií a taktovacích kmitočtů (děličky a PLL), kdy kmitočet jádra činí 168 MHz a SDRAM poté 84 MHz. Tento program také přidává do výsledného projektu potřebné HAL (Hardware Abstraction Level) knihovny s možností volitelné Middle Wares knihovny usnadňující práci například s USB nebo implementující FREERTOS (Free Real Time Operating System) apod. V tomto projektu je využívána Middle Wares USB CDC (Communication Device Class) jako VCP (Virtual Com Port).
Program pro MCU byl vyvíjen v prostředí Em::Blocks verze 2.30 a zkompilován ARM GCC kompilátorem, jež je součástí vývojového prostředí. Firmware se skládá z hlavního programu obsahující supersmyčku s konstrukci přepínače testující, zdali nedošlo k přijetí příkazu voláním procedůry CDC_process_command(&stop, &speed, &direction, &samples, &step);
z knihovny usbd_cdc_if.h popřípadě k přerušení a tak ke změně stavu. Jednotlivé stavy jsou deklarovány pomocí globální výčtové proměnné enum { Idle, MPOS_set, MPOS_loop, STEP_set, STEP_loop, MSPD_set, CALIB_set, STEP_proc, REQB_proc } Mode = Idle;
. Jako globální proměnná je volena kvůli přístupu z podprogramů pro obsluhu přerušení. Před vstupem do smyčky se provede inicializace použitých periférií a zavolá se funkce Rotator_calib() pro zjištění konstanty úměrnosti kn mezi výstupními otáčkami rotoru a kódem DAC.
Následující obrázek ukazuje stavový diagram popisující změnu stavu na základě přijatého příkazu, splnění podmínky měření pozic nebo zmáčknutí uživatelského tlačítka (Button = 1). Zejména na začátku program čeká na stisk tlačítka pro korektní komunikaci přes virtuální sériovou linku ve smyslu zachycení začátku vysílaných dat (v okně telnetu se vypíše menu příkazů). Při zmačknutí tlačítka Reset se pochopitelně neprodleně přejde do stavu Reset, který není potřeba deklarovat ve výčtové proměnné.
Použité knihovny v projektu:
sprintf();
atoi();
Pro korektní zobrazení proměnné typu float ve formátovaném výstupu bylo nutné přidat do kompilátoru direktivu -u _printf_float
a také zvýšit velikost haldy (Heap) a zásobníku (Stack). Navýšení velikosti haldy je navíc podmíněno korektní enumerací USB zařízení (minimálně 0x800). Následující kód linker skriptu ukazuje definici velikosti haldy, zásobníku a počátečních adres jednotilivých interních pamětí MCU.
/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x1000; /* required amount of heap */ _Min_Stack_Size = 0x1000; /* required amount of stack */ /* Specify the memory areas */ MEMORY { FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K } ...
Definice přijímacího a vysílacího bufferu (#define APP_RX_DATA_SIZE 64
a #define APP_TX_DATA_SIZE 64
) byla přemístěna ze zdrojového souboru usbd_cdc_if.c do hlavičkového souboru usbd_cdc_if.h, aby byla přístupná pro hlavní program, kde se deklaruje interní buffer pro předávání textových řetězců funkci pro odeslání na osobní počítač. Terminál operačního systému Windows používá CDC příkazy pro adekvátní nastavení kódováni linky a také vyžaduje vyčíst toto kódování zpět. Tato akce je zachycena pomocí funkce static int8_t CDC_Control_HS (uint8_t cmd, uint8_t* pbuf, uint16_t length)
, do které bylo nutné doplnit jednoduchý trik uvedený v následující ukázce kódu pro zpětné odeslání přijatého kódování (více informací zde).
... uint8_t tempbuf[6]; ... static int8_t CDC_Control_HS (uint8_t cmd, uint8_t* pbuf, uint16_t length) { /* USER CODE BEGIN 10 */ switch (cmd) { ... case CDC_SET_LINE_CODING: /* Add your code here */ tempbuf[0] = pbuf[0]; tempbuf[1] = pbuf[1]; tempbuf[2] = pbuf[2]; tempbuf[3] = pbuf[3]; tempbuf[4] = pbuf[4]; tempbuf[5] = pbuf[5]; tempbuf[6] = pbuf[6]; break; case CDC_GET_LINE_CODING: /* Add your code here */ pbuf[0] = tempbuf[0]; pbuf[1] = tempbuf[1]; pbuf[2] = tempbuf[2]; pbuf[3] = tempbuf[3]; pbuf[4] = tempbuf[4]; pbuf[5] = tempbuf[5]; pbuf[6] = tempbuf[6]; break; ... } return (USBD_OK); /* USER CODE END 10 */ }
Do funkce volané při příjmu byl implementován tzv. loopback pro odeslání přijatého znaku zpět do uživatelova terminálu na osobním počítači pro zobrazování zadávaných znaků příkazu, který tak není psán „naslepo“. Tato funkce také vyhodnocuje, zdali nebylo dokončeno psaní příkazu čili jeho odeslání k zpracování zmáčknutím klávesy enter. Pokud je tedy přijat netisknutelný znak carriage return \r
tak se posílá přijatý řetězec pomocí nastavení příznakové proměnné CDC_command_ready
na nenulovou hodnotu k dalšímu zpracování funkci void CDC_process_command(uint8_t *Stop, uint8_t *Speed, uint8_t *Direction, uint32_t *Samples, uint32_t *Step)
, která zde z důvodu velkého rozsahu není uvedena, ale lze si ji prohlédnout v přiloženém archivu projektu v sekci zdrojové kódy.
static int8_t CDC_Receive_HS (uint8_t* Buf, uint32_t *Len) { /* USER CODE BEGIN 11 */ /* Loopback */ CDC_Transmit_HS(Buf, *Len); USBD_CDC_ReceivePacket(hUsbDevice_1); /* Get data from the VCP in register */ CDC_data_in[CDC_data_count] = UserRxBufferHS[0]; if(CDC_data_in[CDC_data_count] == RETURN) { CDC_command_ready = TRUE; CDC_data_count = 0; } else { CDC_data_count++; } return (USBD_OK); /* USER CODE END 11 */ }
Následuje procedura využívající funkci uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len)
, která byla vytvořená pro snadnější a spolehlivější odesílání textových řetězců. Testuje se zde zdali je možné odeslat data, kdy se v případě zaneprázdnění USB čeká 1 ms. Celkem je deset pokusů, po desátém pokusu se data zahazují.
void CDC_Send_HS(char* Buf, uint16_t Len) { uint8_t sent = 0; uint8_t try_cnt = 0; while(!sent) { /* Check if is possible send data ten times maximally */ if(((USBD_CDC_HandleTypeDef*)(hUsbDeviceHS.pClassData))->TxState == 0) { /* Data send with value check */ if(CDC_Transmit_HS((uint8_t*)(Buf), Len) == USBD_OK) { sent = 1; } } else if(try_cnt > 9) { sent = 1; } try_cnt++; if(sent == 0) { HAL_Delay(1); } } }
Inicializační funkce časovače TIM8 ve funkci čítače pulsů z inkrementálního kodéru, kde je nastavena reakce na hrany kvadraturního signálu z kodéru čímž se dosáhne čtyřnásobného rozlišení oproti původnímu. Do tohoto kódu bylo nutné přidat povolení přerušení při obnovení hodnoty čítače CNT z registru ARR (Auto-Reload Register). Před povolením přerušení je však nutné vynulovat příznak obnovení hodnoty, aby nedošlo k navýšení proměnné Rep_count
(čítač opakování) hned na začátku startu rozhraní kodéru. Pozice je pak určená jako součet hodnoty čítače CNT s hodnotou čítače opakování vynásobenou maximální periodou čítače CNT.
... rotator.h: /* Exported constants */ #define Encoder_res 400000 //PPR #define Encoder_rep_res (Encoder_res/8) ... void MX_TIM8_Init(void) { TIM_Encoder_InitTypeDef sConfig; TIM_MasterConfigTypeDef sMasterConfig; htim8.Instance = TIM8; htim8.Init.Prescaler = 0; htim8.Init.CounterMode = TIM_COUNTERMODE_UP; htim8.Init.Period = Encoder_rep_res; htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim8.Init.RepetitionCounter = 0; sConfig.EncoderMode = TIM_ENCODERMODE_TI12; sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler = TIM_ICPSC_DIV1; sConfig.IC1Filter = 0; sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV1; sConfig.IC2Filter = 0; HAL_TIM_Encoder_Init(&htim8, &sConfig); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig); /* Enable update interrupt */ __HAL_TIM_CLEAR_FLAG(&htim8, TIM_SR_UIF); __HAL_TIM_ENABLE_IT(&htim8, TIM_DIER_UIE); }
Následující kód ukazuje inicializaci flexibilního kontroléru externí paměti, kde bylo nutné doplnit odeslání konfiguračních příkazu SDRAM paměti včetně korektního časování. Použitá paměť dovoluje maximální obnovovací interval paměťové buňky 15,6 us. Pro zajištění určité spolehlivosti byla zvolena poloviční doba, které odpovídá při kmitočtu SDCLK 84 MHz hodnota obnovovacího čítače 656. Další definiční hodnoty jako je počáteční adresa paměti apod. byly přidány do hlavičkového souboru mxconstants.h.
... /* SDRAM - refresh interval time is 15.6 us maximally (half is used), fSDCLK = 168/2 MHz */ #define REFRESH_COUNT 656 #define SDRAM_REG_VALUE 0x0231 /* Macros for operations with SDRAM */ #define SDRAM_Write32(address, value) (*(__IO uint32_t *) (SDRAM_START_ADDR + (address)) = (value)) #define SDRAM_Read32(address) (*(__IO uint32_t *) (SDRAM_START_ADDR + (address))) #define Index_max (SDRAM_MEMORY_SIZE/4) ... void MX_FMC_Init(void) { FMC_SDRAM_TimingTypeDef SdramTiming; FMC_SDRAM_CommandTypeDef Command; /** Perform the SDRAM1 memory initialization sequence */ hsdram1.Instance = FMC_SDRAM_DEVICE; /* hsdram1.Init */ hsdram1.Init.SDBank = FMC_SDRAM_BANK2; hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2; hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE; hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* SDRAM Timing configuration for 84 MHz (11.9 ns) of SD clock frequency (168MHz/2) */ /* TMRD: 2 clock cycles */ SdramTiming.LoadToActiveDelay = 2; /* TXSR: min = 70 ns (7 times 11.9 ns) */ SdramTiming.ExitSelfRefreshDelay = 7; /* TRAS: min = 42 ns (4 times 11.9 ns) */ SdramTiming.SelfRefreshTime = 4; /* TRC: min = 63 ns (7 times 11.9 ns) */ SdramTiming.RowCycleDelay = 7; /* TWR: 2 Clock cycles */ SdramTiming.WriteRecoveryTime = 2; /* TRP: 15 ns => 2 times 11.9 ns */ SdramTiming.RPDelay = 2; /* TRCD: 15 ns => 2 times 11.9 ns */ SdramTiming.RCDDelay = 2; HAL_SDRAM_Init(&hsdram1, &SdramTiming); /* Send commands to the SDRAM */ /* 1/ SDRAM clock enable command */ Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT); /* 2/ Insert 100 ms delay */ HAL_Delay(100); /* 3/ Configure a PALL (precharge all) command */ Command.CommandMode = FMC_SDRAM_CMD_PALL; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT); /* 4/ Configure a Auto-Refresh command */ Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 8; Command.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT); /* 5/ Program the external memory mode register */ Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = (uint32_t)SDRAM_REG_VALUE; HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT); /* 6/ Set the refresh rate counter */ HAL_SDRAM_ProgramRefreshRate(&hsdram1, REFRESH_COUNT); HAL_Delay(100); }
Následuje obrázek ukazující výsledek testu všech buněk SDRAM paměti. S danou externí paměti dat je možné uložit 2 097 152 vzorků pozic s 4 B zarovnáním. Proto při zápisu a čtení vystupuje čtyřnásobek indexu dat.
Pro potřeby ladění programu, kdy nebyl mechanický rotační kodér takříkajíc po ruce, se použily dva kanály časovače TIM1 pro generování kvadraturního signálu potřebné frekvence. Následující kód uvádí inicializaci časovače ve funcki emulátoru kodéru. Díky předděličky PSC je možné nastavovat výstupní kmitočet v širokých mezích. Emulovaný kodér se poté spustí voláním procedury static void Emulate_Encoder_Start(TIM_HandleTypeDef* htim)
, která spustí generování signálu jednotlivých kanálů pomocí HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
.
... /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ #define PSC 1 #define EMU_F 100000 //Hz #define EMU_PERIOD (SystemCoreClock/(PSC*2*EMU_F) - 1) ... static void Encoder_Emulator_Init(TIM_HandleTypeDef* htim) { /* Initialize TIM1 peripheral */ htim->Instance = TIM1; htim->Init.Period = EMU_PERIOD; htim->Init.Prescaler = PSC; htim->Init.ClockDivision = 0; htim->Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_OC_Init(htim); /* Configure the output compare channels */ /* Output compare toggle mode configuration: Channel1 */ sConfigEmulator.OCMode = TIM_OCMODE_TOGGLE; sConfigEmulator.Pulse = (EMU_PERIOD * 1)/4; sConfigEmulator.OCPolarity = TIM_OCPOLARITY_LOW; HAL_TIM_OC_ConfigChannel(htim, &sConfigEmulator, TIM_CHANNEL_2); /* Output compare toggle mode configuration: Channel2 */ sConfigEmulator.Pulse = (EMU_PERIOD * 3)/4; HAL_TIM_OC_ConfigChannel(htim, &sConfigEmulator, TIM_CHANNEL_3); }
Knihovna rotator.h pro ovládání rotačního mechanického systému obsahuje následující procedury a funkce:
Následující kód ukazuje funkci měření rychlosti s pevně stanoveným časovým intervalem. Uvedený algoritmus obsahuje také ošetření nekorektního výpočtu rychlosti kvůli přechodu pozice nulou např. při čítání nahoru, když je počátek 399000 a koncová pozice 1000.
/* Rotor speed measurement of the rotator in rpm */ float Rotator_rpm_meas(void) { static float temp = 0.0; static uint32_t beginning = 0; static uint32_t position = 0; /* Capture and calculate delta position for 100 ms period */ beginning = __HAL_TIM_GET_COUNTER(&htim8) + Rep_count * (Encoder_rep_res + 1); HAL_Delay(100); position = __HAL_TIM_GET_COUNTER(&htim8) + Rep_count * (Encoder_rep_res + 1); /* Check direction of the rotator */ if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim8)) { /* Check cross zero position */ if(beginning > position) { temp = (float)(beginning - position); } else { /* Cross zero position correction */ temp = (float)(Encoder_res - position + beginning); } } else { if(position > beginning) { temp = (float)(position - beginning); } else { /* Cross zero position correction */ temp = (float)(Encoder_res - beginning + position); } } return 600.0 * temp/Encoder_res; }
Následující snímky obrazovek terminálů ukazují úvodní menu po stisku tlačítka. Poté během cca čtyř vteřin proběhne kalibrace rotačního mechanického systému. Po této kalibraci je rotor natočen na nulovou pozici. Program v tuto chvíli očekává příkaz, kterým se zvolí požadovaná funkce systému. Na výběr je měření pozic v závislosti na synchronizačním signálu REQB se zadaným počtem vzorků, rychlosti a směrem otáčení. Dále kontinuální měření aktuální rychlostí rotoru MSPD, kdy se rotor roztočí opět zadanou rychlostí a směrem. Funkce STEP umožňuje krokovat pozici rotoru v závislosti na zadaném kroku a externích signálech STEP a DIR. Na každou nástupnou hranu signálu STEP se rotor otočí o požadovaný krok a směr definovaný signálem DIR. A nakonec je možnost znovu provést kalibraci rotačního systému příkazem CALIB. Program umožňuje zobrazit nápovědu a kontroluje hodnoty zadávaných parametrů.
První ukázka je synchronní poziční měření s rychlostí 10 otáček za minutu ve směru hodinových ručiček pro tři vzorky. Výstupní kmitočet emulovaného kodéru činí 100 kHz. Stop signál se nastaví na nulu a výstup DAC začne postupně stoupat až dosáhne adekvátní hodnoty. Poté postupným přivedením synchronizačních pulzů dojde k zaznamenání pozice do paměti SDRAM. Po dosáhnutí zadaného počtu vzorků napětí výstupu DAC začne rychle klesat a STOP signál se nastaví na logickou jedničku. Do terminálu se vypíšou zaznamenané hodnoty pozic z externí paměti.
Při testování funkce stepování byly vyzkoušeny zejména krajní hodnoty kroku a to 200 000 a experimentálně 1. Při tomto testu se nezobrazil informační text o konfiguraci měřicího procesu kvůli zaneprázdněnému USB. Po desátém pokusu o odeslání se data zahodí. Tento stav se při dalším testování neopakoval. Po přivedení nástupné hrany na vstup STEP se nastaví na výstupu DAC napětí menší než 1,25 V při nulovém napětí signálu DIR. Poté co čítač napočítá daný počet kroků se výstupní napětí nastaví zpět na 1,25 V a zobrazí se dosažená pozice. Měření bylo po několika testech ukončeno příkazem STOP. Pokusem na rotačním zařízení bylo zjištěno, že nejmenší hodnota kroku činí 80 000 pozic při cca 10 otáčkách za minutu kvůli dynamickým vlastnostem celého systému.
Mód měření rychlosti se aktivuje příkazem MSPD s požadovanou rychlostí a směrem otáčení rotoru. Po nastavení odpovídající hodnotě napětí na výstupu DAC se periodicky co 100 ms vypisuje v okně terminálu informace o aktuální rychlosti, která při kmitočtu kvadraturního signálu 100 kHz a s uvažovaným rozlišením kodéru 400 000 PPR činí 30 otáček za minutu. Měření se korektně ukončilo stiskem tlačítka.
Příkaz CALIB byl také testován, ačkoliv s emulovaným signálem kodéru o konstantním kmitočtu vypočtená hodnota nedává smysl.
Obrázek nalevo ukazuje test krokování pozice s emulovaným kodérem při ladění programu a snímek napravo uvádí měření rychlosti při nastavených otáčkách, které je použito ke kontrole kalibrace a také ke zjištění regulačních charakteristik systému.
Následující snímky ukazují výstupní emulovaný kvadraturní signál generovaný při nastaveném požadovaném kmitočtu 1 kHz s předděličkou 1000. První snímek emuluje otáčení ve směru hodinových ručiček. Druhý směr opačný.
Videoukázka činnosti modulu s mechanickým rotačním systémem:
Použité a vytvořené zdrojové soubory knihoven a hlavního programu projektu se nacházejí v přiloženém archivu. Program pro MCU
Použití aplikace STM32CubeMX výrazně urychluje vývoj embedded aplikací, avšak tato aplikace ještě skýtá pár nedostatků ve formě poznámek k nastavení periférií. Například při nastavení časovače do funkce čítání pulsů z inkrementálního kodéru v módu TI12 by mohla informovat, že není dovolené nastavovat jednotlivé vstupy kanálů, aby reagovaly jak na nástupnou tak i sestupnou hranu pro správnou funkčnost.
Vytvořený firmware je plně funkční, kdy důkazem jsou vyfocené obrazovky terminálu pro komunikaci s modulem přes virtuální sériový port, videoukázka a naměřené průběhy signálu emulovaného kodéru. Při měření rychlosti otáčení s emulovaným kodérem je vidět vliv zaokrouhlovací chyby floatu při přechodu pozice nulou, kdy se provádí korekce se započítáním rozlišení kodéru. Nicméně tato chyba se projevuje na druhém desetinném místě a není až tak významná.
Při měření rychlosti otáčení s mechanickým systémem se nastavené otáčky od měřených odlišují z důvodů zesílení servozesilovače, které není konstantní v celém pracovním rozsahu. Konečná pozice u krokování s emulovaným kodérem běžícím na kmitočtu 100 kHz je větší o 7 pozic kvůli pozdějšímu vyčtení obsahu čítače při odesílání zprávy přes USB.