Compare commits

...

16 Commits

Author SHA1 Message Date
Alexander Heldt
32436a53cc Disable FLASH wait states
As it blocks the mc from reaching a ready state for unknown reason(s)
2025-01-01 12:40:01 +01:00
Alexander Heldt
af746a8af4 Add README 2025-01-01 12:39:17 +01:00
Alexander Heldt
6f6caa29ca Add ability to debug clock with MCO1 2025-01-01 12:38:25 +01:00
Alexander Heldt
bd3af02626 Use USART2 2025-01-01 12:38:25 +01:00
Alexander Heldt
aecd073963 Add usart.{h, c} 2025-01-01 12:38:25 +01:00
Alexander Heldt
3a69e75d9a Add gpio_set_af to set alternative function of a pin 2025-01-01 12:23:18 +01:00
Alexander Heldt
0f279ff9ec Add PORT macro
And use it in the `PIN` macro
2025-01-01 12:22:09 +01:00
Alexander Heldt
725bd7d3e5 Fix gpio_set_mode masking 2025-01-01 12:19:50 +01:00
Alexander Heldt
f7454eb073 Remove BIT macro 2025-01-01 11:58:40 +01:00
Alexander Heldt
9e4aaeabd5 GPIO alternative function registers are 32bit each, not 64bit 2024-12-30 11:49:48 +01:00
Alexander Heldt
c3f6a4e503 Use RCC_CFGR_SW helper function to set software clock 2024-12-30 11:48:38 +01:00
Alexander Heldt
f44ddf645b Turn off HSI earlier 2024-12-30 11:47:54 +01:00
Alexander Heldt
88dcd47552 Set correct PLL N for 96MHz 2024-12-30 11:47:18 +01:00
Alexander Heldt
c5ff505605 Correctly check PLL readiness 2024-12-30 11:46:10 +01:00
Alexander Heldt
943415dd2d TIM4 runs at 96MHz, not 48MHz 2024-12-30 11:45:04 +01:00
Alexander Heldt
6d2a01d574 Generalise naming of TIMx_ENABLE 2024-12-30 11:44:18 +01:00
10 changed files with 222 additions and 22 deletions

35
README.md Normal file
View File

@@ -0,0 +1,35 @@
# Falling sand on the STM32F411CE
## Building
Run
```sh
make build
```
## Probe for the board
Run
```sh
st-info --probe
```
## Flashing
Run
```sh
make flash
```
## Debugging
### `st-info --probe` shows 0KB flash
```
> sudo st-info --probe
Found 1 stlink programmers
version: V2J43S28
serial: 0671FF343056363043090732
flash: 0 (pagesize: 16384) <--- 0KB flash
sram: 131072
chipid: 0x431
dev-type: STM32F411xC_xE
```
This can happen when the flash is locked. One way to unlock it is to erase the entire chip via the
Windows application `ST-Link Util`.

View File

@@ -25,7 +25,7 @@ struct flash {
#define FLASH_ACR_ICEN_ENABLE (1 <<FLASH_ACR_ICEN_BIT)
// Latency
#define FLASH_ACR_LATENCY_3_WAIT_STATES (0x0111)
#define FLASH_ACR_LATENCY_3_WAIT_STATES (0b0011)
#define FLASH_ACR_LATENCY_BIT 0 // Bits [3:0]
#define FLASH_ACR_LATENCY_MASK (0b1111)

View File

@@ -6,8 +6,20 @@
void gpio_set_mode(uint16_t pin, GPIO_MODE mode) {
struct gpio *gpio = GPIO(PINPORT(pin)); // GPIO port address
int pn = PINNUM(pin); // Pin number
gpio->MODER &= ~(0x0011 << (pn * 2)); // Clear existing setting. Each pin uses 2 bits
gpio->MODER |= (mode & 0b011) << (pn * 2); // Set new mode. Each pin uses 2 bits
gpio->MODER &= ~(0b11 << (pn * 2)); // Clear existing setting. Each pin uses 2 bits
gpio->MODER |= (mode & 0b11) << (pn * 2); // Set new mode. Each pin uses 2 bits
}
void gpio_set_af(uint16_t pin, uint8_t af) {
struct gpio *gpio = GPIO(PINPORT(pin));
int pn = PINNUM(pin);
if (pn < 8) {
gpio->AFRL &= ~(0b1111 << (pn * 4)); // Each pin uses 4 bits
gpio->AFRL |= (af & 0b1111) << (pn * 4);
} else {
gpio->AFRH &= ~(0b1111 << (pn * 4)); // Each pin uses 4 bits
gpio->AFRH |= (af & 0b1111) << (pn * 4);
}
}
void gpio_write(uint16_t pin, bool val) {

View File

@@ -13,17 +13,23 @@ struct gpio {
volatile uint32_t ODR; // Port output data register
volatile uint32_t BSRR; // Port bit set/reset register
volatile uint32_t LCKR; // Port configuration lock register
volatile uint32_t AFRL[2]; // Alternative function low register
volatile uint32_t AFRH[2]; // Alternative function high register
volatile uint32_t AFRL; // Alternative function low register
volatile uint32_t AFRH; // Alternative function high register
};
// AFRH, AFRL registers
#define GPIO_AF_MCO_1 (0b0000) // Alternative function 0 (AF0)
#define GPIO_AF_USART2_RX (0b0111) // Alternative function 7 (AF7)
#define GPIO_AF_USART2_TX (0b0111) // Alternative function 7 (AF7)
#define GPIO_BASE_ADDR (0x40020000U)
#define GPIO_PORT_OFFSET (0x400U)
#define GPIO(port) ((struct gpio*)(uintptr_t)(GPIO_BASE_ADDR + (GPIO_PORT_OFFSET * port)))
#define BIT(x) (1 << x)
// Create a 8bit number from a port
#define PORT(port) (((port) - 'A') << 8)
// Create a 16bit number from a port and pin
#define PIN(port, num) ((((port) - 'A') << 8) | num)
#define PIN(port, num) (PORT(port) | num)
// get the lower byte from a PIN
#define PINNUM(pin) (pin & 0b1111)
// get the upper byte from a PIN
@@ -37,6 +43,7 @@ typedef enum {
} GPIO_MODE;
void gpio_set_mode(uint16_t pin, GPIO_MODE mode);
void gpio_set_af(uint16_t pin, uint8_t af);
void gpio_write(uint16_t pin, bool val);
#endif

View File

@@ -6,6 +6,7 @@
#include "flash.h"
#include "pwr.h"
#include "timer.h"
#include "usart.h"
#define exit 42
@@ -17,6 +18,9 @@ static void system_clock_init(void) {
PWR->CR &= ~(PWR_CR_VOS_MASK << PWR_CR_VOS_BIT);
PWR->CR |= (PWR_SCALE3 << PWR_CR_VOS_BIT);
// Turn off HSI (which is on by default)
RCC->CR &= ~RCC_CR_HSION_ON;
// Turn on HSE
RCC->CR |= RCC_CR_HSEON_ON;
@@ -32,7 +36,7 @@ static void system_clock_init(void) {
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;
// Settings to achieve system clock of 96Mhz
RCC->PLLCFGR |= RCC_PLLCFGR_PLLM(25) | RCC_PLLCFGR_PLLN(196) | RCC_PLLCFGR_PLLP(2) | RCC_PLLCFGR_PLLQ(4);
RCC->PLLCFGR |= RCC_PLLCFGR_PLLM(25) | RCC_PLLCFGR_PLLN(192) | RCC_PLLCFGR_PLLP(2) | RCC_PLLCFGR_PLLQ(4);
// Set AHB prescalar to /1
RCC->CFGR &= ~(RCC_CFGR_HPRE_MASK << RCC_CFGR_HPRE_BIT);
@@ -51,36 +55,35 @@ static void system_clock_init(void) {
// Wait indefinitely for PLL to be ready
// TODO indicate error/timeout somehow?
while (!(RCC->CR & RCC_CR_HSERDY_READY));
while (!(RCC->CR & RCC_CR_PLLRDY_LOCKED));
// Enable caching of instructions and data
FLASH->ACR |= FLASH_ACR_DCEN_ENABLE;
FLASH->ACR |= FLASH_ACR_ICEN_ENABLE;
// TODO breaks with these flash settings on; turning off for now
// Set latency to be 3 wait states (TODO: understand why exactly 3)
FLASH->ACR &= ~(FLASH_ACR_LATENCY_MASK << FLASH_ACR_LATENCY_BIT);
RCC->CFGR |= (FLASH_ACR_LATENCY_3_WAIT_STATES << FLASH_ACR_LATENCY_BIT);
/* FLASH->ACR &= ~(FLASH_ACR_LATENCY_MASK << FLASH_ACR_LATENCY_BIT); */
/* RCC->CFGR |= (FLASH_ACR_LATENCY_3_WAIT_STATES << FLASH_ACR_LATENCY_BIT); */
// Use PLL as system clock
RCC->CFGR &= ~(RCC_CFGR_SW_MASK << RCC_CFGR_SW_BIT);
RCC->CFGR |= (RCC_CFGR_SW_PLL << RCC_CFGR_SW_BIT);
RCC->CFGR |= RCC_CFGR_SW(RCC_CFGR_SW_PLL);
// Wait indefinitely for PLL clock to be selected
// TODO indicate error/timeout somehow?
while (((RCC->CFGR >> RCC_CFGR_SWS_BIT) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL);
// Turn off HSI (which is on by default)
RCC->CR &= ~RCC_CR_HSION_ON;
}
int main(void) {
(void) system_clock_init();
(void) tim4_init();
(void) usart2_init();
(void) tim4_start();
(void) usart2_start();
uint16_t led = PIN('C', 13); // Blue LED
RCC->AHB1ENR |= BIT(PINPORT(led)); // Enable GPIO clock for LED
RCC->AHB1ENR |= (1 << PINPORT(led)); // Enable GPIO clock for LED
gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode
uint16_t counter = TIM4->CNT;
@@ -90,6 +93,8 @@ int main(void) {
led_on = !led_on;
gpio_write(led, led_on);
usart2_write("hello, world!\n");
counter = TIM4->CNT;
}
};

View File

@@ -87,6 +87,19 @@ struct rcc {
#define RCC_CFGR_PPRE_DIV_NONE 0
#define RCC_CFGR_PPRE_DIV_2 (0b100)
// Microcontroller clock output 1
#define RCC_CFGR_MCO1_HSE (0b10)
#define RCC_CFGR_MCO1_PLL (0b11)
#define RCC_CFGR_MCO1_BIT 21 // Bits [22:21]
#define RCC_CFGR_MCO1_MASK (0b11)
#define RCC_CFGR_MCO1PRE_DIV4 (0b110)
#define RCC_CFGR_MCO1PRE_DIV2 (0b100)
#define RCC_CFGR_MCO1PRE_BIT 24 // Bits [26:24]
#define RCC_CFGR_MCO1PRE_MASK (0b111)
// APB2
#define RCC_CFGR_PPRE2_BIT 13 // Bits [15:13]
#define RCC_CFGR_PPRE2_MASK (0b111)
@@ -118,6 +131,9 @@ struct rcc {
#define RCC_APB1ENR_PWREN_BIT 28
#define RCC_APB1ENR_PWREN_CLOCK_ENABLE (1 << RCC_APB1ENR_PWREN_BIT)
#define RCC_APB1ENR_USART2EN_BIT 17
#define RCC_APB1ENR_USART2EN_ENABLE (1 << RCC_APB1ENR_USART2EN_BIT)
#define RCC_APB1ENR_TIM4_BIT 2
#define RCC_APB1ENR_TIM4_ENABLE (1 << RCC_APB1ENR_TIM4_BIT)

View File

@@ -10,13 +10,13 @@ void tim4_init(void) {
TIM4->CR2 = 0x0000;
// Set prescaler
// f_clk = 48MHz -> /48000 = 1KHz counting frequency = 1ms
TIM4->PSC = (uint16_t) 48000 - 1;
// f_clk = 96MHz -> 96E6/96E3 = 1E3 = 1KHz counting frequency = 1ms
TIM4->PSC = (uint16_t) 96000 - 1;
// Set ARR to maximum value to get 1ms between updates
TIM4->ARR = (uint16_t) 0xFFFF;
}
void tim4_start(void) {
TIM4->CR1 |= TIM4_ENABLE;
TIM4->CR1 |= TIM_ENABLE;
}

View File

@@ -29,8 +29,8 @@ struct timer {
#define TIM4_BASE_ADDR (0x40000800U)
#define TIM4 ((struct timer *) TIM4_BASE_ADDR)
#define TIM4_CR_CEN_BIT 0
#define TIM4_ENABLE (1 << TIM4_CR_CEN_BIT)
#define TIM_CR1_CEN_BIT 0
#define TIM_ENABLE (1 << TIM_CR1_CEN_BIT)
void tim4_init(void);
void tim4_start(void);

75
src/usart.c Normal file
View File

@@ -0,0 +1,75 @@
#include "rcc.h"
#include "gpio.h"
#include "usart.h"
void usart2_init(void) {
// Enable clock for GPIOA as USART2 is on PORT A pins
RCC->AHB1ENR |= (1 << PORT('A'));
// Configure PA2 and PA3 (USART2 pins) to use alternative functions
uint16_t txPin = PIN('A', 2);
gpio_set_mode(txPin, GPIO_MODE_AF);
gpio_set_af(txPin, GPIO_AF_USART2_TX);
uint16_t rxPin = PIN('A', 3);
gpio_set_mode(rxPin, GPIO_MODE_AF);
gpio_set_af(rxPin, GPIO_AF_USART2_RX);
// Enable MC01; for debugging
/* uint16_t clockOutPin = PIN('A', 8); */
/* gpio_set_mode(clockOutPin, GPIO_MODE_AF); */
/* gpio_set_af(clockOutPin, GPIO_AF_MCO_1); */
/* RCC->CFGR &= ~(RCC_CFGR_MCO1_MASK << RCC_CFGR_MCO1_BIT); */
/* RCC->CFGR |= (RCC_CFGR_MCO1_HSE << RCC_CFGR_MCO1_BIT); */
/* RCC->CFGR |= (RCC_CFGR_MCO1_PLL << RCC_CFGR_MCO1_BIT); */
/* RCC->CFGR &= ~(RCC_CFGR_MCO1PRE_MASK << RCC_CFGR_MCO1PRE_BIT); */
/* RCC->CFGR |= (RCC_CFGR_MCO1PRE_DIV4 << RCC_CFGR_MCO1PRE_BIT); */
// Enable USART
RCC->APB1ENR |= RCC_APB1ENR_USART2EN_ENABLE;
// Clear control registers
USART2->CR1 = 0;
USART2->CR2 = 0;
USART2->CR3 = 0;
// Calculate Baud rate:
// baud = f_clck / (8 * (2 - OVER8) * USARTDIV) =>
// (8 * (2 - OVER8) * USARTDIV) = f_clock / baud =>
// baud * (8 * (2 - OVER8) * USARTDIV) = f_clock =>
// USARTDIV = (f_clock / (baud * (8 * (2 - OVER8)))
// Target Baud rate = 115200, f_clock = 48MHz, OVER8 = 0
// USARTDIV = 48E6 / (115200 * 8 * 2) = 26.0416666
// mantissa = 26 = 0x1A
// fraction = 0.041666 * 16 = 0.666656 ~= 1
// baud = 48E6 / (8 * 2 * 26) = 115384.61538461539
// error of 0.16% (115384.61538461539 / 115200 ) = 1.001602564102564
//
// skipping fractional part as error rate is good.
USART2->BRR &= ~(USART_BRR_MANTISSA_MASK << USART_BRR_MANTISSA_BIT);
USART2->BRR |= (0x1A << USART_BRR_MANTISSA_BIT);
USART2->BRR &= ~(USART_BRR_FRACTION_MASK << USART_BRR_FRACTION_BIT);
USART2->BRR |= (0x0 << USART_BRR_MANTISSA_BIT);
// Enable transmitter and receiver
USART2->CR1 |= USART_CR1_TE_ENABLE;
USART2->CR1 |= USART_CR1_RE_ENABLE;
}
void usart2_start(void) {
USART2->CR1 |= USART_CR1_UE_ENABLE;
}
void usart2_write_byte(uint8_t c) {
USART2->DR = c;
// Wait indefinitely for transmission to be ready for data
while (!(USART2->SR & USART_SR_TC_COMPLETED));
}
void usart2_write(char *buf) {
while (*buf) usart2_write_byte(*buf++);
}

50
src/usart.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef USART_H_
#define USART_H_
#include <inttypes.h>
struct usart {
volatile uint32_t SR; // Status register
volatile uint32_t DR; // Data register
volatile uint32_t BRR; // Baud rate register
volatile uint32_t CR1; // Control register 1
volatile uint32_t CR2; // Control register 2
volatile uint32_t CR3; // Control register 3
volatile uint32_t GTPR; // Guard time and prescaler register
};
#define USART2_BASE_ADDR (0x40004400U)
#define USART2 ((struct usart *) USART2_BASE_ADDR)
// SR Register
// Transmission complete
#define USART_SR_TC_BIT 6
#define USART_SR_TC_COMPLETED (1 << USART_SR_TC_BIT)
// CR Register
// USART enable
#define USART_CR1_UE_BIT 13
#define USART_CR1_UE_ENABLE (1 << USART_CR1_UE_BIT)
// Trasmitter enable
#define USART_CR1_TE_BIT 3
#define USART_CR1_TE_ENABLE (1 << USART_CR1_TE_BIT)
// Receiver enable
#define USART_CR1_RE_BIT 2
#define USART_CR1_RE_ENABLE (1 << USART_CR1_RE_BIT)
// BRR Register
#define USART_BRR_MANTISSA_BIT 4 // Bits [15:4]
#define USART_BRR_MANTISSA_MASK (0b111111111111)
#define USART_BRR_FRACTION_BIT 0 // Bits [3:0]
#define USART_BRR_FRACTION_MASK (0b111)
void usart2_init(void);
void usart2_start(void);
void usart2_write_byte(uint8_t byte);
void usart2_write(char *buf);
#endif