LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

Bootloader dla zestawu FREEboard – wprowadzenie

Prawidłowe funkcjonowanie interfejsów komunikacyjnych, takich jak np. I2C, SPI oraz UART, uzależnione jest od konfiguracji zarówno odpowiednich modułów jak również powiązanych z nimi wyprowadzeń. W większości mikrokontrolerów z rodziny KINETIS konfiguracja modułów peryferyjnych jest podobna, a różnica występuje w zakresie wyprowadzeń, gdzie do poszczególnych linii przypisana jest lista możliwych funkcji alternatywnych. Stąd też w pliku hardware_init_x.c zdefiniowane są wymagane funkcje konfigurujące, m.in. x_pinmux_config(), init_hardware() oraz deinit_hardware() (przykład dla układów z serii KL25Z):


/* This function is called for configurating pinmux for uart module
 * This function only support switching default or gpio or fixed-ALTx mode on fixed pins
 * (Although there are many ALTx-pinmux configuration choices on various pins for the same
 * peripheral module) */
void uart_pinmux_config(unsigned int instance, pinmux_type_t pinmux){
  switch(instance)
  {
    case 0:
      switch(pinmux)
      {
        case kPinmuxType_Default:
          BW_PORT_PCRn_MUX(HW_PORTA, UART0_RX_GPIO_PIN_NUM, 0);
          BW_PORT_PCRn_MUX(HW_PORTA, UART0_TX_GPIO_PIN_NUM, 0);
          break;
        case kPinmuxType_GPIO:
          // Set UART0_RX pin in GPIO mode
          BW_PORT_PCRn_MUX(HW_PORTA, UART0_RX_GPIO_PIN_NUM, UART0_RX_GPIO_ALT_MODE);
          // Set UART0_RX pin as an input
          HW_GPIO_PDDR_CLR(HW_GPIOA, 1 << UART0_RX_GPIO_PIN_NUM);
          break;
        case kPinmuxType_Peripheral:
          // Set UART0_RX pin to UART0_RX functionality
          BW_PORT_PCRn_MUX(HW_PORTA, UART0_RX_GPIO_PIN_NUM, UART0_RX_ALT_MODE);
          // Set UART0_TX pin to UART0_TX functionality
          BW_PORT_PCRn_MUX(HW_PORTA, UART0_TX_GPIO_PIN_NUM, UART0_TX_ALT_MODE);
          break;
        default:
          break;
      }
      break;
    case 1:
      break;
    case 2:
      break;
    default:
      break;
  }
}

/* This function is called for configurating pinmux for i2c module
 * This function only support switching default or gpio or fixed-ALTx mode on fixed pins
 * (Although there are many ALTx-pinmux configuration choices on various pins for the same
 * peripheral module) */
void i2c_pinmux_config(unsigned int instance, pinmux_type_t pinmux){
  switch(instance)
  {
    case 0:
      switch(pinmux)
      {
        case kPinmuxType_Default:
          BW_PORT_PCRn_MUX(HW_PORTC, 8, 0);
          BW_PORT_PCRn_MUX(HW_PORTC, 9, 0);
          break;
        case kPinmuxType_Peripheral:
          // Enable pins for I2C0.
          BW_PORT_PCRn_MUX(HW_PORTC, 8, 2);  // I2C0_SCL is ALT2 for pin PTC8
          BW_PORT_PCRn_MUX(HW_PORTC, 9, 2);  // I2C0_SDA is ALT2 for pin PTC9
          break;
        default:
          break;
      }
      break;
    case 1:
      break;
    case 2:
      break;
    default:
      break;
  }
}

/* This function is called for configurating pinmux for spi module
 * This function only support switching default or gpio or fixed-ALTx mode on fixed pins
 * (Although there are many ALTx-pinmux configuration choices on various pins for the same
 * peripheral module) */
void spi_pinmux_config(unsigned int instance, pinmux_type_t pinmux){
  switch(instance)
  {
    case 0:
      switch(pinmux)
      {
        case kPinmuxType_Default:
          BW_PORT_PCRn_MUX(HW_PORTD, 0, 0);
          BW_PORT_PCRn_MUX(HW_PORTD, 1, 0);
          BW_PORT_PCRn_MUX(HW_PORTD, 2, 0);
          BW_PORT_PCRn_MUX(HW_PORTD, 3, 0);
          break;
        case kPinmuxType_Peripheral:
          // Enable pins for SPI0 on PTD0~3 (not available on 32-pin QFN package)
          BW_PORT_PCRn_MUX(HW_PORTD, 0, 2);  // SPI0_PCS0 is ALT2 for pin PTD0
          BW_PORT_PCRn_MUX(HW_PORTD, 1, 2);  // SPI0_SCK is ALT2 for pin PTD1
          BW_PORT_PCRn_MUX(HW_PORTD, 2, 2);  // SPI0_MOSI is ALT2 for pin PTD2
          BW_PORT_PCRn_MUX(HW_PORTD, 3, 2);  // SPI0_MISO is ALT2 for pin PTD3
          break;
        default:
          break;
      }
      break;
    case 1:
      break;
    case 2:
      break;
    default:
      break;
  }
}

void init_hardware(void){
  SIM->SCGC5 |= ( SIM_SCGC5_PORTA_MASK
                | SIM_SCGC5_PORTB_MASK
                | SIM_SCGC5_PORTC_MASK
                | SIM_SCGC5_PORTD_MASK
                | SIM_SCGC5_PORTE_MASK );

  SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK // set PLLFLLSEL to select the PLL for this clock source
              | SIM_SOPT2_UART0SRC(1);   // select the PLLFLLCLK as UART0 clock source

#if DEBUG
  // Enable the pins for the debug UART1
  BW_PORT_PCRn_MUX(HW_PORTC, 3, 3);   // UART1_RX is PTC3 in ALT3
  BW_PORT_PCRn_MUX(HW_PORTC, 4, 3);   // UART1_TX is PTC4 in ALT3
#endif
}

void deinit_hardware(void){
  SIM->SCGC5 &= (uint32_t)~( SIM_SCGC5_PORTA_MASK
                | SIM_SCGC5_PORTB_MASK
                | SIM_SCGC5_PORTC_MASK
                | SIM_SCGC5_PORTD_MASK
                | SIM_SCGC5_PORTE_MASK );
}

Przykładowa mapa pamięci dla mikrokontrolerów serii KL25Z128 zawarta jest w pliku memory_map_x.c i wygląda jak poniżej:

//! @brief Memory map for KL25Z128.
//!
//! This map is not const because it is updated at runtime with the actual sizes of
//! flash and RAM for the chip we're running on.
memory_map_entry_t g_memoryMap[] = {
    { 0x00000000, 0x0001ffff, &g_flashMemoryInterface },   // Flash array
    { 0x1ffff000, 0x20002fff, &g_normalMemoryInterface },  // SRAM (SRAM_L + SRAM_U)
    { 0x40000000, 0x4007ffff, &g_deviceMemoryInterface },  // AIPS peripherals
    { 0x400ff000, 0x400fffff, &g_deviceMemoryInterface },  // GPIO
    { 0x44000000, 0x5fffffff, &g_deviceMemoryInterface },  // BME
    { 0xe0000000, 0xe00fffff, &g_deviceMemoryInterface },  // M0+ private peripherals
    { 0xf0003000, 0xf0003fff, &g_deviceMemoryInterface },  // MCM
    { 0xf8000000, 0xffffffff, &g_deviceMemoryInterface },  // IOPORT (single-cycle GPIO)
    { 0 }                                                  // Terminator
};

Tablica deskryptorów, zdefiniowana w pliku peripherals_x.c, zawiera dane o rodzaju i numerze komunikacyjnych układów peryferyjnych dostępnych w mikrokontrolerze, a także wskaźniki na struktury opisujące kolejne warstwy architektury interfejsu peryferia (pod pojęciem interfejsu peryferia należy rozumieć zbiór środków w postaci funkcji, które umożliwiają pracę z układem peryferyjnym na poziomie od pojedynczych bajtów do rozbudowanych pakietów, bez wnikania w szczegóły implementacyjne tego układu) (rysunek 3). Ogólna struktura deskryptora peryferia jest następująca:

struct PeripheralDescriptor {
  uint32_t typeMask;
  uint32_t instance;
  void (*pinmuxConfig)(uint32_t instance, pinmux_type_t pinmux);
  const peripheral_control_interface_t *controlInterface;
  const peripheral_byte_interface_t *byteInterface;
  const peripheral_packet_interface_t *packetInterface;
};

Pole typeMask struktury może przyjmować jedną z poniższych wartości wskazujących rodzaj interfejsu komunikacyjnego:

  • kPeripheralType_UART – wartość 1,
  • kPeripheralType_I2CSlave – wartość 2,
  • kPeripheralType_SPISlave – wartość 4,
  • kPeripheralType_CAN – wartość 8,
  • kPeripheralType_USB_HID – wartość 16,
  • kPeripheralType_USB_CDC – wartość 32,
  • kPeripheralType_USB_DFU – wartość 64,
  • kPeripheralType_USB_MSC – wartość 128.
Autor: Jan Szemiet