Obsługa linii wejścia-wyjścia, czyli GPIO to pozornie prosty temat. Niestety jak zwykle pozory mylą.
Tym razem zajmę się prędkością działania wyjść. Za ustawianie prędkości portu odpowiedzialny jestr rejestr GPIO_OSPEEDR:
Jak łatwo zauważyć, każdy pin jest konfigurowany za pomocą dwóch bitów. Mamy możliwość ustawienia trzech prędkości przełączania wyprowadzenia: niskiej, średniej i wysokiej.
Co znaczą te określenia odnajdziemy w nocie katalogowej naszego mikrokontrolera:
Postanowiłem sprawdzić jak działanie wygląda w praktyce. Napisałem więc prosty program, który jednocześnie przełącza trzy piny - z których każdy ma inne ustawienia prędkości:
W ramach testów podłączyłem oscyloskop do wspomnianych wyprowadzeń. Wolnozmienny sygnał nie pokazuje różnic między ustawieniami pinów:
Jednak po zmianie podstawy czasu widać różnice w czasie narastania sygnału. Kanał 1 to wysoka prędkość działania wyjścia, drugi - średnia i trzeci - niska:
Podobnie sytuacja wygląda dla zbocza opadającego:
Właściwie na tym mógłbym zakończyć testowanie prędkości wyprowadzeń GPIO, ale nie byłbym sobą gdybym nie spróbował napisać programu, który wygeneruje możliwie wysoką częstotliwość na wyjściu.
Usunięcie opóźnień z prezentowanego wcześniej programu niewiele pomogło. Kolejnym krokiem było więc ustawienie maksymalnej optymalizacji kodu. Niestety nadal uzyskana częstotliwość niewiele przekraczała 2 MHz.
Postanowiłem więc zmienić C na stary dobry asembler i przygotowałem taką funkcję testową:
Powtarzanie instrukcji str zamiast prostej pętli miało na celu wyeliminowanie opóźnienia instrukcji skoku.
Efekt był dużo lepszy niż w przypadku języka C:
Nie dawało mi jednak spokoju skąd się bierze asymetria sygnału. Postanowiłem więc przenieść program testowy z pamięci Flash do SRAM. Efekt był znacznie lepszy:
Wykonanie instrukcji LDR oraz STR zajmuje 2 cykle maszynowe. Przy częstotliwości taktowania 48 MHz utrzymujemy więc 12 MHz na wyjściu.
Wniosek jest taki, że STM32F030 nie jest demonem prędkości. No i warto pamiętać, że opóźnienia dostępów do pamięci flash mogą mieć wpływ na działanie programu.
Tym razem zajmę się prędkością działania wyjść. Za ustawianie prędkości portu odpowiedzialny jestr rejestr GPIO_OSPEEDR:
Jak łatwo zauważyć, każdy pin jest konfigurowany za pomocą dwóch bitów. Mamy możliwość ustawienia trzech prędkości przełączania wyprowadzenia: niskiej, średniej i wysokiej.
Co znaczą te określenia odnajdziemy w nocie katalogowej naszego mikrokontrolera:
Postanowiłem sprawdzić jak działanie wygląda w praktyce. Napisałem więc prosty program, który jednocześnie przełącza trzy piny - z których każdy ma inne ustawienia prędkości:
#include "stm32f0xx.h" void clock_init(void) { RCC->CFGR = RCC_CFGR_PLLSRC_HSI_DIV2 | RCC_CFGR_PLLMUL12; RCC->CR |= RCC_CR_PLLON; while ((RCC->CR & RCC_CR_PLLRDY) == 0) ; FLASH->ACR |= FLASH_ACR_LATENCY; RCC->CFGR |= RCC_CFGR_SW_PLL; while ((RCC->CFGR & RCC_CFGR_SWS_PLL) != RCC_CFGR_SWS_PLL) ; } int main(void) { clock_init(); RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOCEN; GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0 | GPIO_MODER_MODER5_0; GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_0; GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5_0|GPIO_OSPEEDER_OSPEEDR5_1; while (1) { GPIOC->BSRR = GPIO_BSRR_BS_8 | GPIO_BSRR_BS_9 | GPIO_BSRR_BS_5; for (volatile uint32_t dly = 0; dly < 1000; dly++); GPIOC->BSRR = GPIO_BSRR_BR_8 | GPIO_BSRR_BR_9 | GPIO_BSRR_BR_5; for (volatile uint32_t dly = 0; dly < 1000; dly++); } }
W ramach testów podłączyłem oscyloskop do wspomnianych wyprowadzeń. Wolnozmienny sygnał nie pokazuje różnic między ustawieniami pinów:
Jednak po zmianie podstawy czasu widać różnice w czasie narastania sygnału. Kanał 1 to wysoka prędkość działania wyjścia, drugi - średnia i trzeci - niska:
Podobnie sytuacja wygląda dla zbocza opadającego:
Usunięcie opóźnień z prezentowanego wcześniej programu niewiele pomogło. Kolejnym krokiem było więc ustawienie maksymalnej optymalizacji kodu. Niestety nadal uzyskana częstotliwość niewiele przekraczała 2 MHz.
Postanowiłem więc zmienić C na stary dobry asembler i przygotowałem taką funkcję testową:
.syntax unified .thumb .global test_gpio .equ GPIOC, 0x48000800 .equ BSRR, 0x18 .equ BRR, 0x28 .section .text .type test_gpio, %function test_gpio: ldr r0, =0x0100 | 0x0200 | 0x0020 ldr r1, =GPIOC ldr r2, =GPIOC main_loop: str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] str r0, [r1, BSRR] str r0, [r1, BRR] b main_loop
Powtarzanie instrukcji str zamiast prostej pętli miało na celu wyeliminowanie opóźnienia instrukcji skoku.
Efekt był dużo lepszy niż w przypadku języka C:
Nie dawało mi jednak spokoju skąd się bierze asymetria sygnału. Postanowiłem więc przenieść program testowy z pamięci Flash do SRAM. Efekt był znacznie lepszy:
Wykonanie instrukcji LDR oraz STR zajmuje 2 cykle maszynowe. Przy częstotliwości taktowania 48 MHz utrzymujemy więc 12 MHz na wyjściu.
Wniosek jest taki, że STM32F030 nie jest demonem prędkości. No i warto pamiętać, że opóźnienia dostępów do pamięci flash mogą mieć wpływ na działanie programu.
Asymetria sygnału jest bardzo wyraźna. Dlatego szkolenie sql katowice jest
OdpowiedzUsuń