stm32f411re
Created 2022-10-30 Updated 2023-04-16
RAM: 128k
FLASH: 512k
CLOCK: 100MHz, but default seems to be 16MHz
DMA
I2C
SCL and SDA lines should be set to open-drain.
SPI
Bit-banging SPI master TX
static void spi_out(uint8_t n)
{
for (uint8_t i = 8; i--; n <<= 1) {
digitalWrite(MOSI, n>>7);
digitalWrite(SCK, HIGH);
digitalWrite(SCK, LOW);
}
}
CMSIS SPI master TX
Perform peripheral setup. Then:
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI
int i = 0;
while(1)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
// rougly equiv of HAL_SPI_Transmit(&hspi1, (uint8_t*) &i, 1, 100); :
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = i;
while(!(SPI1->SR & SPI_SR_TXE)); // takes about 1.1us
while((SPI1->SR & SPI_SR_BSY)); // takes about 3.8us
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
i++;
The timings above assume a transfer rate of 1.56MB/s. The whole transmission takes about 5.4us.
SSD1306
On blackpill, my SSD1306 seemed to require a delay of 4ms before initialisation of SSD1306, otherwise it would require toggling of the NRST button. This problem occurred on both the Cube and libopencm3, so I don't think it's a coding error per se.
/home/pi/repos/rpi/stm32f411re/libopencm3/15-ssd1306
WEAK OVERRIDE
/home/pi/repos/rpi/stm32f411re/libopencm3/11-ledmat-i2c
TIMERS and Delay
Capabilities:
TIM BITS 2 32 3 16 4 16 5 32
PSC for all timers (1,2,3,4,5,9.10) are 16-bits
Microsocend delay using timer
Uses TIM3 as an example, and toggles a pin every 1ms. No interrupts are used because we don't need them in this example.
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable clock. NB TIM 1,9,10, 11 is on a different clock
TIM3->PSC = 100-1; // assumes clock is 100MHz. Clock will increase CNT every 1us
TIM3->CR1 |= TIM_CR1_ARPE; // enable reload (i.e. wrapping)
TIM3->CR1 |= TIM_CR1_CEN; // enable the timer
uint32_t cnt = TIM3->CNT;
while (1)
{
while ((uint16_t)(TIM3->CNT - cnt) < 1000); // cast to uint16_t because TIM3 overflows at 16 bits
cnt += 1000;
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);
}
HAL
main(): HAL_TIM_Base_Start_IT(&htim2);
LL
In CubeMX you will want to enable ARR preload, unless it is a one-shot.
main():
LL_TIM_EnableCounter(TIM2); LL_TIM_EnableIT_UPDATE(TIM2);
IRQ:
void TIM2_IRQHandler(void)
{
LL_TIM_ClearFlag_UPDATE(TIM2); // necessary
... // etc.
LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_0);
}
Change frequency:
LL_TIM_SetAutoReload(TIM2, 400); LL_TIM_GenerateEvent_UPDATE(TIM2); // possibly not necessary