|
| 1 | +// Copyright (c) 2025 Cesanta Software Limited |
| 2 | +// https://www.st.com/resource/en/reference_manual/rm0456-stm32u5-series-armbased-32bit-mcus-stmicroelectronics.pdf |
| 3 | +// SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +#pragma once |
| 6 | +#include <stm32u5a5xx.h> |
| 7 | + |
| 8 | +#include <stdbool.h> |
| 9 | +#include <stdint.h> |
| 10 | +#include <stdio.h> |
| 11 | +#include <string.h> |
| 12 | + |
| 13 | +#define BIT(x) (1UL << (x)) |
| 14 | +#define CLRSET(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) |
| 15 | +#define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) |
| 16 | +#define PINNO(pin) (pin & 255) |
| 17 | +#define PINBANK(pin) (pin >> 8) |
| 18 | + |
| 19 | +// System clock |
| 20 | +enum { AHB_DIV = 1, APB1_DIV = 1, APB2_DIV = 1 }; |
| 21 | +enum { PLL_HSI = 16, PLL_M = 1, PLL_N = 10, PLL_R = 2 }; // 80 Mhz |
| 22 | +// #define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_R) * 1000000) |
| 23 | +#define SYS_FREQUENCY 16000000 |
| 24 | +#define APB2_FREQUENCY (SYS_FREQUENCY / APB2_DIV) |
| 25 | +#define APB1_FREQUENCY (SYS_FREQUENCY / APB1_DIV) |
| 26 | + |
| 27 | +static inline void spin(volatile uint32_t count) { |
| 28 | + while (count--) (void) 0; |
| 29 | +} |
| 30 | + |
| 31 | +enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; |
| 32 | +enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; |
| 33 | +enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; |
| 34 | +enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; |
| 35 | + |
| 36 | +static inline GPIO_TypeDef *gpio_bank(uint16_t pin) { |
| 37 | + switch (PINBANK(pin)) { |
| 38 | + case 0: return GPIOA; |
| 39 | + case 1: return GPIOB; |
| 40 | + case 2: return GPIOC; |
| 41 | + case 3: return GPIOD; |
| 42 | + case 4: return GPIOE; |
| 43 | + case 5: return GPIOF; |
| 44 | + case 6: return GPIOG; |
| 45 | + case 7: return GPIOH; |
| 46 | + case 8: return GPIOI; |
| 47 | + case 9: return GPIOJ; |
| 48 | + default: return NULL; |
| 49 | + } |
| 50 | +} |
| 51 | +static inline void gpio_toggle(uint16_t pin) { |
| 52 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 53 | + uint32_t mask = BIT(PINNO(pin)); |
| 54 | + gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0); |
| 55 | +} |
| 56 | +static inline int gpio_read(uint16_t pin) { |
| 57 | + return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0; |
| 58 | +} |
| 59 | +static inline void gpio_write(uint16_t pin, bool val) { |
| 60 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 61 | + gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16); |
| 62 | +} |
| 63 | +static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, |
| 64 | + uint8_t speed, uint8_t pull, uint8_t af) { |
| 65 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 66 | + uint8_t n = (uint8_t) (PINNO(pin)); |
| 67 | + RCC->AHB2ENR1 |= BIT(PINBANK(pin)); // Enable GPIO clock |
| 68 | + CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n); |
| 69 | + CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2)); |
| 70 | + CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2)); |
| 71 | + CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4), |
| 72 | + ((uint32_t) af) << ((n & 7) * 4)); |
| 73 | + CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2)); |
| 74 | +} |
| 75 | +static inline void gpio_input(uint16_t pin) { |
| 76 | + gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, |
| 77 | + GPIO_PULL_NONE, 0); |
| 78 | +} |
| 79 | +static inline void gpio_output(uint16_t pin) { |
| 80 | + gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, |
| 81 | + GPIO_PULL_NONE, 0); |
| 82 | +} |
| 83 | + |
| 84 | +static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) { |
| 85 | + // https://www.st.com/resource/en/datasheet/stm32u5a5aj.pdf |
| 86 | + uint8_t af = 7; // Alternate function |
| 87 | + uint16_t rx = 0, tx = 0; // pins |
| 88 | + uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 |
| 89 | + |
| 90 | + if (uart == USART1) { |
| 91 | + freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN; |
| 92 | + tx = PIN('A', 9), rx = PIN('A', 10); |
| 93 | + } else if (uart == USART2) { |
| 94 | + freq = APB1_FREQUENCY, RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; |
| 95 | + tx = PIN('A', 2), rx = PIN('A', 3); |
| 96 | + } else if (uart == USART2) { |
| 97 | + freq = APB1_FREQUENCY, RCC->APB1ENR1 |= RCC_APB1ENR1_USART3EN; |
| 98 | + tx = PIN('B', 10), rx = PIN('B', 11); |
| 99 | + } else { |
| 100 | + return false; |
| 101 | + } |
| 102 | + |
| 103 | + gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); |
| 104 | + gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); |
| 105 | + uart->CR1 = 0; // Disable this UART |
| 106 | + uart->BRR = freq / baud; // Set baud rate |
| 107 | + uart->CR1 |= BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE |
| 108 | + return true; |
| 109 | +} |
| 110 | +static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { |
| 111 | + uart->TDR = byte; |
| 112 | + while ((uart->ISR & BIT(7)) == 0) spin(1); |
| 113 | +} |
| 114 | +static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { |
| 115 | + while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); |
| 116 | +} |
| 117 | +static inline int uart_read_ready(USART_TypeDef *uart) { |
| 118 | + return uart->ISR & BIT(5); // If RXNE bit is set, data is ready |
| 119 | +} |
| 120 | +static inline uint8_t uart_read_byte(USART_TypeDef *uart) { |
| 121 | + return (uint8_t) (uart->RDR & 255); |
| 122 | +} |
| 123 | + |
| 124 | +static inline void rng_init(void) { |
| 125 | + RCC->AHB2ENR1 |= RCC_AHB2ENR1_RNGEN; |
| 126 | + RNG->CR |= RNG_CR_RNGEN; |
| 127 | +} |
| 128 | +static inline uint32_t rng_read(void) { |
| 129 | + while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0; |
| 130 | + return RNG->DR; |
| 131 | +} |
| 132 | + |
| 133 | +#define UUID ((uint8_t *) UID_BASE) // Unique 96-bit chip ID. TRM 41.1 |
| 134 | + |
| 135 | +// Helper macro for MAC generation |
| 136 | +#define GENERATE_LOCALLY_ADMINISTERED_MAC() \ |
| 137 | + {2, \ |
| 138 | + UUID[0] ^ UUID[1], \ |
| 139 | + UUID[2] ^ UUID[3], \ |
| 140 | + UUID[4] ^ UUID[5], \ |
| 141 | + UUID[6] ^ UUID[7] ^ UUID[8], \ |
| 142 | + UUID[9] ^ UUID[10] ^ UUID[11]} |
| 143 | + |
| 144 | +// t: expiration time, prd: period, now: current time. Return true if expired |
| 145 | +static inline bool timer_expired(volatile uint64_t *t, uint64_t prd, |
| 146 | + uint64_t now) { |
| 147 | + if (now + prd < *t) *t = 0; // Time wrapped? Reset timer |
| 148 | + if (*t == 0) *t = now + prd; // Firt poll? Set expiration |
| 149 | + if (*t > now) return false; // Not expired yet, return |
| 150 | + *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time |
| 151 | + return true; // Expired, return true |
| 152 | +} |
| 153 | +// Fill in stack with markers, in order to calculate stack usage later |
| 154 | +extern unsigned char _estack, _end; |
| 155 | +static inline void stack_fill(void) { |
| 156 | + uint32_t dummy, *p = (uint32_t *) &_end; |
| 157 | + while (p < &dummy) *p++ = 0xa5a5a5a5; |
| 158 | +} |
| 159 | + |
| 160 | +static inline long stack_usage(void) { |
| 161 | + uint32_t *sp = (uint32_t *) &_estack, *end = (uint32_t *) &_end, *p = sp - 1; |
| 162 | + while (p > end && *p != 0xa5a5a5a5) p--; |
| 163 | + return (sp - p) * sizeof(*p); |
| 164 | +} |
| 165 | + |
| 166 | +static inline void clock_init(void) { |
| 167 | + SCB->CPACR |= 15 << 20; // Enable FPU |
| 168 | + FLASH->ACR |= FLASH_ACR_LATENCY_4WS; |
| 169 | + // FLASH->ACR |= FLASH_ACR_LATENCY_4WS | FLASH_ACR_ICEN | FLASH_ACR_DCEN; |
| 170 | +#if 0 |
| 171 | +#if 0 |
| 172 | + CLRSET(RCC->PLLCFGR, RCC_PLLCFGR_PLLM, (PLL_M - 1) << RCC_PLLCFGR_PLLM_Pos); |
| 173 | + CLRSET(RCC->PLLCFGR, RCC_PLLCFGR_PLLN, PLL_N << RCC_PLLCFGR_PLLN_Pos); |
| 174 | + CLRSET(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLCFGR_PLLSRC_HSI); |
| 175 | + RCC->PLLCFGR |= RCC_PLLCFGR_PLLREN; |
| 176 | +#else |
| 177 | + RCC->PLLCFGR = BIT(24) | BIT(20) | (PLL_N << 8) | RCC_PLLCFGR_PLLSRC_HSI | |
| 178 | + (7 << 27) | BIT(16); |
| 179 | + RCC->CR |= RCC_CR_PLLON; |
| 180 | +#endif |
| 181 | + while (!(RCC->CR & RCC_CR_PLLRDY)) spin(1); |
| 182 | +#if 0 |
| 183 | + CLRSET(RCC->CFGR, RCC_CFGR_PPRE1, (3 + APB1_DIV / 2) << RCC_CFGR_PPRE1_Pos); |
| 184 | + CLRSET(RCC->CFGR, RCC_CFGR_PPRE2, (3 + APB2_DIV / 2) << RCC_CFGR_PPRE1_Pos); |
| 185 | +#endif |
| 186 | + RCC->CFGR |= RCC_CFGR_SW_PLL; |
| 187 | + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) spin(1); |
| 188 | +#else |
| 189 | + RCC->CR |= RCC_CR_HSION; |
| 190 | + while (!(RCC->CR & RCC_CR_HSIRDY)) spin(1); |
| 191 | + RCC->CFGR1 &= ~(RCC_CFGR1_SW); |
| 192 | + RCC->CFGR1 |= (RCC_CFGR1_SW_0); |
| 193 | + while ((RCC->CFGR1 & RCC_CFGR1_SWS) != RCC_CFGR1_SWS_0) spin(1); |
| 194 | +#endif |
| 195 | + |
| 196 | + rng_init(); // Initialise random number generator |
| 197 | + RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN; // Enable SYSCFG |
| 198 | + SystemCoreClock = SYS_FREQUENCY; // Required by CMSIS |
| 199 | + SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms |
| 200 | +} |
0 commit comments