Devo implementare un pacchetto di supporto della scheda per una scheda personalizzata e vorrei conoscere la tua opinione sui diversi approcci di progettazione dei driver di supporto della scheda. La mia scheda personalizzata utilizza un microcontrollore STM32 oltre ad altri componenti come LED, pulsanti e componenti di comunicazione. STM fornisce driver di supporto della scheda di esempio per le loro schede di sviluppo, discovery ed eval. Ho studiato i pacchetti di supporto della scheda STM, oltre ad altri esempi forniti da Nxp e Ti, e sono stato in grado di estrarre i seguenti approcci in termini di design / architettura:
Secondo il primo approccio, ciascun driver di scheda, cioè driver di pulsante, driver LED, driver SPI componente di basso livello, driver I2C componente basso livello e così via, alloca e configura le risorse MCU necessarie per il corretto funzionamento. È meglio spiegarlo con un esempio, quindi l'API di supporto della scheda sarebbe la seguente:
BSP_Init() {
SYSTEM_CLOCK_Init();
LED_Init();
BUTTON_Init();
COMPONENTx_Init();
COMPONENTy_Init();
}
SYSTEM_CLOCK_Init() {
// Configure MCU system clock, e.g. 16 MHz.
}
LED_Init() {
// Enable GPIO port clocks for LED pins.
// For each LED, configure GPIO pin, e.g. output, no-pull, speed.
}
BUTTON_Init() {
// Enable GPIO port clocks for button pins.
// For each Button, configure GPIO pin, e.g. input, pull-up, speed.
// Configure EXTI.
// Configure NVIC.
}
COMPONENTx_Init() {
// Enable GPIO port clock for slave select pin.
// Configure GPIO pin for slave select.
SPI1_Init();
// Init component by using SPI bus operations SPI1_Transmit() ...
}
COMPONENTy_Init() {
// Enable GPIO port clock for write / command pin.
// Configure GPIO pin for command / write pin.
I2C1_Init();
// Init component by using I2C bus operations I2C1_Transmit() ...
}
SPI1_Init() {
// Enable SPI peripheral clock.
// Enable GPIO port clocks for SPI pins.
// Configure GPIO pins for SCK, MISO, MOSI.
// Configure DMA.
// Configure SPI registers.
// Configure NVIC.
}
I2C1_Init() {
// Enable I2C peripheral clock.
// Enable GPIO port clocks for I2C pins.
// Configure GPIO pins for SCK, SDA.
// Configure DMA
// Configure I2C registers.
// Configure NVIC.
}
Secondo il secondo approccio, l'allocazione e la configurazione delle risorse MCU non sono suddivise nei diversi gruppi di API dei driver, ma sono suddivise in un'unica API per ciascuna risorsa MCU, ad es.
BSP_Init() {
CLOCK_Init();
GPIO_Init();
EXTI_Init();
NVIC_Init();
DMA_Init();
SPI1_Init();
I2C1_Init();
}
CLOCK_Init() {
// Configure system clock, e.g. 16 MHz.
// Enable GPIO port clocks.
// Enable SPI clock.
// Enable I2C clock.
}
GPIO_Init() {
// Configure LED GPIO pins.
// Configure Button GPIO pins.
// Configure SPI GPIO pins.
// Configure I2C GPIO pins.
}
EXTI_Init() {
// Configure interrupt generation for Button GPIO pins.
}
NVIC_Init() {
// Enable interrupts for DMA transfer complete, DMA error, button, SPI, I2C, ...
}
SPI1_Init() {
// Configure SPI registers.
}
I2C1_Init() {
// Configure I2C registers.
}
Infine, vorrei sottolineare che non sto utilizzando nessun HAL (Hardware Abstraction Layer) fornito dal fornitore eccetto l'accesso diretto al registro tramite ARM CMSIS.
Qual è la tua opinione sugli approcci di progettazione forniti. Quali sono i vantaggi, gli svantaggi di ciascun approccio? Come progettate i driver di supporto della scheda?