Bare-metal STM32F767 firmware drivers (GPIO, RCC, USART, I2C, SPI).
XDR (eXperimental Driver) is a lightweight, low-level driver framework for the STM32F767 microcontroller family.
It is designed without HAL or LL, focusing on:
- Clear peripheral abstraction
- Simplified user configurations
- Continuous Integration (CI) pipeline with MISRA compliance checks
The project currently implements:
- GPIO Driver — digital I/O configuration and runtime API
- RCC Driver — system clock configuration using HSI/PLL, AHB/APB prescalers
- USART Driver — initialization of USART peripherals, data transmission/reception, and support for multiple baud rates with hardcoded GPIO configurations
- I2C Driver — initialization of I2C peripherals, data transmission/reception, 7-bit addressing, standard mode, only master mode and blocking-mode
- SPI Driver — initialization of only one SPI peripheral (SPI1), data transmission/reception APIs, only master mode and one slave, blocking-mode
- TIM Driver — initialization of TIM peripherals (TIM2/3/4/5), basic timer functionality with prescaler and period configuration, timer update event handling
The drivers rely on CMSIS headers stm32f767xx.h which defines all relevant register maps.
STM32 Nucleo-144 development board is being used with the STM32F767ZI MCU for testing the drivers.
- Pin configuration (Mode, Pull-up/down)
- Pin-level read/write
- Port-level read/write
- Pin toggle
- Clock enable/disable per GPIO port
GPIO output speed is hardcoded to high speed, and alternate function (AF) register configurations are integrated into the USART, SPI, and I2C drivers.
typedef struct{
GPIO_TypeDef *xdr_gpiox;
xdr_gpio_portId xdr_gpio_portId;
uint8_t xdr_gpio_pin;
uint8_t xdr_gpio_pinMode;
uint8_t xdr_gpio_pinPuPd;
}xdr_gpio;
The xdr_gpio structure is used to configure GPIO pins.
void XDR_GPIO_Init(xdr_gpio *xdr_gpio);
void XDR_GPIO_DeInit(const xdr_gpio *xdr_gpio);
uint8_t XDR_GPIO_Read_Pin(xdr_gpio *xdr_gpio, uint8_t xdr_gpio_pin);
uint16_t XDR_GPIO_Read_Port(xdr_gpio *xdr_gpio);
void XDR_GPIO_Write_Pin(xdr_gpio *xdr_gpio, uint8_t xdr_gpio_pin, uint8_t xdr_value);
void XDR_GPIO_Write_Port(xdr_gpio *xdr_gpio, uint16_t xdr_value);
void XDR_GPIO_Toggle(xdr_gpio *xdr_gpio, uint8_t xdr_gpio_pin);
The GPIO driver supports a button EXTI event feature using the following APIs:
void XDR_GPIO_Button_EXTI_Init(void);
void XDR_EXTI13_Callback(void);
XDR_GPIO_Button_EXTI_Init: Configures GPIO pin PC13 as an input pin and enables the EXTI interrupt for the pin.XDR_EXTI13_Callback: Callback function triggered when an interrupt occurs on EXTI line 13 (connected to GPIO pin PC13). Users can implement this function in their application to handle button press events.
- Switching hardcoded system clock frequencies
- Set AHB/APB1/APB2 prescalers
- Auto-configuration of:
- PLL multipliers/dividers (hardcoded for stability)
- Hardcoded flash wait states
- 16 MHz
- 48 MHz
- 96 MHz
- 144 MHz
- 216 MHz
typedef struct{
RCC_TypeDef *rcc;
xdr_sysclk_freq sysclk_freq;
xdr_ahb_prescaler ahb_prescaler;
xdr_apb_prescaler apb1_prescaler;
xdr_apb_prescaler apb2_prescaler;
}xdr_rcc;
The xdr_rcc structure is used to configure the RCC module, allowing users to select the system clock frequency and set the AHB, APB1, and APB2 prescalers.
void XDR_RCC_Init(xdr_rcc *xdr_RCC);
uint32_t XDR_Get_SysClock(void);
uint32_t XDR_Get_HCLK(void);
uint32_t XDR_Get_PCLK1(void);
uint32_t XDR_Get_PCLK2(void);
- Initialization of USART peripherals
- Transmission and reception of data
- Support for multiple baud rates
- Used HSI as the clock source for USARTx
- Hardcoded GPIO pin configurations for USART instances
- USART3 peripheral supports RX interrupt functionality
- USART1/2/3/6
typedef struct{
USART_TypeDef *usart;
xdr_usart_instance xdr_usart_instance;
uint32_t xdr_usart_baudrate;
}xdr_usart;
The xdr_usart structure is used to configure the USART module, allowing users to select the USART instance and baud rate.
void XDR_USART_Init(xdr_usart *xdr_usart);
void XDR_USART_Send(xdr_usart *xdr_usart, uint8_t data);
uint8_t XDR_USART_Receive(xdr_usart *xdr_usart);
void XDR_USART3_EnableRxInterrupt(xdr_usart *xdr_usart);
- Initialization of I2C peripherals
- Transmission and reception of data
- 7-bit addressing and blocking
- Master-mode and standard mode only
- Hardcoded GPIO pin configurations for I2C instances
- I2C1 -> PB8 (SCL) and PB9(SDA)
- I2C2 -> PB10 (SCL) and PB11(SDA)
- I2C4 -> PF14 (SCL) and PF15(SDA)
typedef struct{
I2C_TypeDef *i2c;
xdr_i2c_instance xdr_i2c_instance;
}xdr_i2c;
The xdr_i2c structure is used to configure the I2C module, users need to select only the I2C instance(I2C 1/2/4).
void XDR_I2C_Init(xdr_i2c *xdr_I2C);
void XDR_I2C_Write(xdr_i2c *xdr_I2C, uint8_t addr7, uint8_t data);
uint8_t XDR_I2C_Read(xdr_i2c *xdr_I2C, uint8_t addr7);
- Initialization of SPI peripheral (SPI1)
- Transmission and receiving APIs
- Master-mode and only one slave
- Hardcoded GPIO pin configurations for SPI1
- SCK -> PB3
- MISO -> PB4
- MOSI -> PB5
- CS -> PA4
In order to make it easy to configure GPIO pins for SPI, direct hardware register manipulation is used in this driver, unlike the UART and I2C drivers. Currently, this is a very simple SPI driver, but in the future, more peripherals will be implemented, and GPIO configurations will be optimized.
void XDR_SPI_Init(void);
void XDR_SPI_Transmit(uint8_t data);
uint8_t XDR_SPI_Receive(void);
- Initialization of TIM peripherals (TIM2, TIM3, TIM4, TIM5)
- Basic timer functionality with prescaler and period configuration
- Timer update event handling and waiting
The timer driver supports basic timing operations and can be used for delays, periodic tasks, and time-based applications. The timers use the APB1 clock domain.
typedef struct{
TIM_TypeDef *tim;
xdr_tim_id tim_id;
uint32_t presc;
uint32_t period;
}xdr_tim;
The xdr_tim structure is used to configure the TIM module, allowing users to select the timer instance (TIM2/3/4/5), set the prescaler value, and define the auto-reload period.
void XDR_TIM_Init(xdr_tim *xdr_tim);
void XDR_TIM_WaitUpdate(xdr_tim *xdr_tim);
The project uses standard STM32 CMSIS headers integrated in the Drivers folder:
- Main device header: stm32f767xx.h
- Core ARM headers: Drivers/CMSIS/Include/
The CMSIS integration provides:
- Complete STM32F767 register definitions (RCC, GPIO, FLASH, EXTI, SYSCFG, etc.)
- Full peripheral memory map
- NVIC and core ARM Cortex-M7 definitions
- Peripheral base addresses
- Standard peripheral register structures
- System initialization functions
For learning, PLL configurations are currently implemented as predefined configurations, such as:
XDR_RCC_PLLCFGR_48MHZXDR_RCC_PLLCFGR_96MHZXDR_RCC_PLLCFGR_144MHZXDR_RCC_PLLCFGR_216MHZ
These values are applied inside the RCC driver implementation
(in stm32f767xx_xdr_rcc.c).
Flash latency is automatically configured based on the selected SYSCLK frequency.
- GPIO Driver
- RCC Driver
- USART Driver
- I2C Driver
- SPI Driver
- TIM Driver
- MISRA Compliance Checker Integration
