W tym wpisie planowałem omówić pamięć SDRAM, ale zanim do niej przejdę jeszcze jeden, krótki wpis.
Okazuje się, że pisanie wszystkiego w jednym pliku main.c całkiem dobrze się sprawdza dla krótkiego programu. Ale im kod jest dłuższy, tym trudniej sobie poradzić i pojawia się konieczność podzielenia programu na moduły.
Co więcej okazuje się, że te moduły zaczynają tworzyć pewną bibliotekę - co niestety sprowadza się do ponownego wynajdowania koła. Użycie rejestrów miało wyeliminować bibliotekę Cube HAL, czy StdPeriph, jednak po pewnym czasie kod zmienia się we własną bibliotekę...
Nie mam jednak wyjścia, muszę coś z kodem w main.c zrobić. Na początek wydzielę ostatnio napisany kod inicjalizujący pętlę PLL.
W tym celu kopiuję początek funkcji main() do nowego pliku pll.c:
W pliku nagłówkowym pll.h umieszczam odpowiednie deklaracje:
Teraz program głowny po prostu wywołuje pll_init. Kod jest czytelniejszy, ale co ważniejsze w kolejnych programach mogę wykorzystać ten sam moduł pll.
Plik main.c:
Jednak głównym powodem powstania tego wpisu jest nowy plik Makefile. Może nie nowy, ale zmieniony - tym razem będzie kompilowanych więcej plików źródłowych. Dodałem zmienną SOURCES, która przechowuje listę z plikami do skompilowania. Dzięki temu można łatwo dodać moduł pll, a w przyszłości również kolejne.
Plik Makefile wygląda teraz następująco:
Okazuje się, że pisanie wszystkiego w jednym pliku main.c całkiem dobrze się sprawdza dla krótkiego programu. Ale im kod jest dłuższy, tym trudniej sobie poradzić i pojawia się konieczność podzielenia programu na moduły.
Co więcej okazuje się, że te moduły zaczynają tworzyć pewną bibliotekę - co niestety sprowadza się do ponownego wynajdowania koła. Użycie rejestrów miało wyeliminować bibliotekę Cube HAL, czy StdPeriph, jednak po pewnym czasie kod zmienia się we własną bibliotekę...
Nie mam jednak wyjścia, muszę coś z kodem w main.c zrobić. Na początek wydzielę ostatnio napisany kod inicjalizujący pętlę PLL.
W tym celu kopiuję początek funkcji main() do nowego pliku pll.c:
#include "pll.h" #include "stm32f429xx.h" #define PLL_M 8uL // 8 MHz / 8 = 1 MHz #define PLL_N 336uL // 336 MHz => hclk = 168 MHz #define PLL_Q 7 // USB PLL => 336/7 = 48 MHz void pll_init(void) { RCC->CR |= RCC_CR_HSEON; while ((RCC->CR & RCC_CR_HSERDY) == 0) ; RCC->PLLCFGR = PLL_M | (PLL_N << 6) | RCC_PLLCFGR_PLLSRC_HSE | (PLL_Q << 24); RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV4; RCC->CR |= RCC_CR_PLLON; while ((RCC->CR & RCC_CR_PLLRDY) == 0) ; RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR |= PWR_CR_ODEN; while ((PWR->CSR & PWR_CSR_ODRDY) == 0) ; PWR->CR |= PWR_CR_ODSWEN; while ((PWR->CSR & PWR_CSR_ODSWRDY) == 0) ; FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS; uint32_t cfgr = RCC->CFGR; cfgr &= ~RCC_CFGR_SW; cfgr |= RCC_CFGR_SW_PLL; RCC->CFGR = cfgr; while ((RCC->CFGR & RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL) ; }
W pliku nagłówkowym pll.h umieszczam odpowiednie deklaracje:
#ifndef __PLL__ #define __PLL__ #define HCLK_FREQ 168000000uL #define APB1_FREQ (HCLK_FREQ/4) #define APB2_FREQ (HCLK_FREQ/2) void pll_init(void); #endif // __PLL__
Teraz program głowny po prostu wywołuje pll_init. Kod jest czytelniejszy, ale co ważniejsze w kolejnych programach mogę wykorzystać ten sam moduł pll.
Plik main.c:
#include <stdint.h> #include <stdbool.h> #include "stm32f429xx.h" #include "pll.h" volatile uint32_t ms_counter = 0; void SysTick_Handler(void) { if (ms_counter) ms_counter--; } void delay_ms(uint32_t ms) { ms_counter = ms; while (ms_counter) ; } void usart_send(const char *s) { while (*s) { while ((USART1->SR & USART_SR_TXE) == 0); USART1->DR = *s++; } } int main(int argc, char *argv[]) { pll_init(); RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOGEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; GPIOG->MODER |= GPIO_MODER_MODE13_0|GPIO_MODER_MODE14_0; GPIOA->MODER |= GPIO_MODER_MODE9_1|GPIO_MODER_MODE10_1; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1 | GPIO_AFRH_AFSEL9_2; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10_0 | GPIO_AFRH_AFSEL10_1 | GPIO_AFRH_AFSEL10_2; SysTick->LOAD = HCLK_FREQ / 1000 - 1; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; USART1->BRR = APB2_FREQ / 115200; USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; usart_send("Hello world!\r\n"); while (1) { GPIOG->BSRR = GPIO_BSRR_BS13; GPIOG->BSRR = GPIO_BSRR_BR14; delay_ms(500); GPIOG->BSRR = GPIO_BSRR_BS14; GPIOG->BSRR = GPIO_BSRR_BR13; delay_ms(500); usart_send("stm32f429\r\n"); } return 0; }
Jednak głównym powodem powstania tego wpisu jest nowy plik Makefile. Może nie nowy, ale zmieniony - tym razem będzie kompilowanych więcej plików źródłowych. Dodałem zmienną SOURCES, która przechowuje listę z plikami do skompilowania. Dzięki temu można łatwo dodać moduł pll, a w przyszłości również kolejne.
Plik Makefile wygląda teraz następująco:
CROSS_COMPILE = arm-none-eabi- AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc SIZE = $(CROSS_COMPILE)size TARGET = bin/test07 SOURCES = src/main.c \ src/pll.c OBJECTS = $(SOURCES:src/%.c=obj/%.o) CFLAGS=-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -std=gnu11 -O0 \ -ffunction-sections -fdata-sections -g -fstack-usage -Wall -specs=nano.specs LDFLAGS=$(CFLAGS) -Wl,--gc-sections -specs=nosys.specs -Wl,-cref,-u,Reset_Handler \ -Wl,-Map=$(TARGET).map -Tsrc/stm32f4_flash.ld all: $(TARGET).elf $(SIZE) $< $(TARGET).elf: $(OBJECTS) obj/startup_stm32f429xx.o $(CC) $(LDFLAGS) -o $@ $^ obj/%.o: src/%.c $(CC) $(CFLAGS) -o $@ -c $< obj/startup_stm32f429xx.o: src/startup_stm32f429xx.s $(CC) $(CFLAGS) -o $@ -c $< debug: $(TARGET).elf openocd -f board/stm32f429discovery.cfg -f interface/stlink-v2-1.cfg \ -c "init; sleep 200; reset halt; wait_halt; \ flash write_image erase $(TARGET).elf; \ reset run; sleep 10; shutdown" clean: rm -f bin/* obj/*
Brak komentarzy:
Prześlij komentarz