Bootloader dla zestawu FREEboard – wprowadzenie
Pole instance służy do selekcji konkretnej kopii modułu komunikacyjnego w sytuacji, gdy występują ich dwie lub więcej, np. UART0, UART1 i UART2. Pole pinmuxConfig przechowuje wskaźnik do jednej z funkcji zdefiniowanych w pliku hardware_init_x.c. Przykładowa tablica deskryptorów dla zestawu FREEboard lub FRDM-KL25Z mogłaby wyglądać tak jak poniżej:
const peripheral_descriptor_t g_peripherals[] = { // UART0 { .typeMask = kPeripheralType_UART, .instance = 0, .pinmuxConfig = uart_pinmux_config, .controlInterface = &g_uart0ControlInterface, .byteInterface = &g_uart0ByteInterface, .packetInterface = &g_framingPacketInterface }, // I2C0 { .typeMask = kPeripheralType_I2CSlave, .instance = 0, .pinmuxConfig = i2c_pinmux_config, .controlInterface = &g_i2cControlInterface, .byteInterface = &g_i2cByteInterface, .packetInterface = &g_framingPacketInterface }, // SPI0 { .typeMask = kPeripheralType_SPISlave, .instance = 0, .pinmuxConfig = spi_pinmux_config, .controlInterface = &g_spiControlInterface, .byteInterface = &g_spiByteInterface, .packetInterface = &g_framingPacketInterface }, #if !BL_MIN_PROFILE // USB HID { .typeMask = kPeripheralType_USB_HID, .instance = 0, .pinmuxConfig = NULL, .controlInterface = &g_usbHidControlInterface, .byteInterface = NULL, .packetInterface = &g_usbHidPacketInterface }, #endif // !BL_MIN_PROFILE { 0 } // Terminator };
Rys. 4. Diagram blokowy komponentów i zależności pomiędzy nimi w architekturze interfejsu peryferia
Struktury, do których wskaźniki są przechowywane w pozostałych trzech polach deskryptora, warto omówić szczegółowo. Pierwszy typ struktury reprezentuje abstrakcyjny interfejs sterujący (abstract control interface):
typedef struct _peripheral_control_interface { bool (*pollForActivity)(const peripheral_descriptor_t *self); status_t (*init)(const peripheral_descriptor_t *self, serial_byte_receive_func_t function); void (*shutdown)(const peripheral_descriptor_t *self); void (*pump)(const peripheral_descriptor_t *self); } peripheral_control_interface_t;
Pola danej struktury przechowują wskaźniki do funkcji, które jako pierwszy argument przyjmują wskaźnik do deskryptora peryferia przedstawionego wcześniej:
- pollForActivity() – sprawdza, czy komunikacja została rozpoczęta,
- init() – inicjalizuje sterownik,
- shutdown() – wyłącza sterownik,
- pump() – realizuje dodatkowy czas przetwarzania dla układu peryferyjnego.
Typ status_p występujący jako typ wartości zwracanej w funkcji init() informuje o wyniku przeprowadzonej operacji. Kolejna struktura, typu peripheral_byte_interface_t, reprezentuje abstrakcyjny interfejs bajtowy (abstract byte interface):
typedef struct _peripheral_byte_interface { status_t (*init)(const peripheral_descriptor_t *self); status_t (*read)(const peripheral_descriptor_t *self, uint8_t *buffer, uint32_t requestedBytes); status_t (*write)(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount); } peripheral_byte_interface_t;
Tak jak poprzednio, elementy powyższej struktury przechowują wskaźniki do następujących funkcji:
- init() – inicjalizuje interfejs,
- read() – przenosi zadaną ilość bajtów z wewnętrznego bufora danych wejściowych do wskazanego bufora zewnętrznego; jest funkcją blokującą, czyli działającą do momentu kiedy zostanie przeniesiona zadana liczba bajtów,
- write() – kopiuje zadaną ilość bajtów ze wskazanego bufora zewnętrznego do wewnętrznego bufora danych wyjściowych.
Trzecia i ostatnia struktura ma typ peripheral_packet_interface_t oraz reprezentuje abstrakcyjny interfejs pakietowy (abstract packet interface):
typedef struct _peripheral_packet_interface { status_t (*init)(const peripheral_descriptor_t *self); status_t (*readPacket)(const peripheral_descriptor_t *self, uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); status_t (*writePacket)(const peripheral_descriptor_t *self, const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); void (*abortDataPhase)(const peripheral_descriptor_t *self); status_t (*finalize)(const peripheral_descriptor_t *self); uint32_t (*getMaxPacketSize)(const peripheral_descriptor_t *self); void (*byteReceivedCallback)(uint8_t byte); } peripheral_packet_interface_t;
Interfejs tworzą poniższe funkcje:
- init() – inicjalizuje peryferia,
- readPacket() – odbiera kompletny pakiet przy pomocy wybranego peryferia oraz zwraca dwa wskaźniki: do bufora, w którym zostały zapisane dane wejściowego pakietu, i do zmiennej przechowującej liczbę odebranych bajtów danych; dodatkowy parametr określa czy wejściowy pakiet powinien zawierać dane lub instrukcje,
- writePacket() – wysyła do wybranego peryferia kompletny pakiet utworzony z danych wskazanego bufora zewnętrznego o zadanym wymiarze; parametr packetType decyduje o rodzaju danych w pakiecie (dane lub instrukcje),
- abortDataPhase() – przerywa odbiór pakietów z danymi,
- finalize() – wyłącza peryferia, gdy wszystkie operacje transferu zostaną zakończone,
- getMaxPacketSize() – zwraca maksymalny rozmiar bufora wewnętrznego,
- byteReceivedCallback() – funkcja zwrotna, wywoływana po odebraniu przez interfejs nowego bajtu danych.
Jan Szemiet
Literatura
[1] Demo Application User’s Guide for the FRDM-KL25Z and TWR-KL25Z48 Freescale Platforms
[2] http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=KBOOT