LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

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

Autor: Jan Szemiet