|
| 1 | +// Copyright (c) 2022-2023 Cesanta Software Limited |
| 2 | +// All rights reserved |
| 3 | +// |
| 4 | +// MCU manual: RM0444, board manual: UM2591 |
| 5 | +// https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf |
| 6 | +// https://www.st.com/resource/en/user_manual/um2591-stm32g0-nucleo32-board-mb1455-stmicroelectronics.pdf |
| 7 | +// Alternate functions: https://www.st.com/resource/en/datasheet/stm32g031c6.pdf |
| 8 | + |
| 9 | +#pragma once |
| 10 | + |
| 11 | +// #define LED PIN('B', 3) |
| 12 | +#define LED PIN('C', 6) |
| 13 | +#ifndef UART_DEBUG |
| 14 | +#define UART_DEBUG USART2 |
| 15 | +#endif |
| 16 | + |
| 17 | +#include <stm32g031xx.h> |
| 18 | + |
| 19 | +#include <stdbool.h> |
| 20 | +#include <stdint.h> |
| 21 | +#include <stdio.h> |
| 22 | +#include <stdlib.h> |
| 23 | +#include <string.h> |
| 24 | + |
| 25 | +#define BIT(x) (1UL << (x)) |
| 26 | +#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) |
| 27 | +#define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) |
| 28 | +#define PINNO(pin) (pin & 255) |
| 29 | +#define PINBANK(pin) (pin >> 8) |
| 30 | + |
| 31 | +#define CPU_FREQUENCY 16000000 |
| 32 | +#define AHB_FREQUENCY CPU_FREQUENCY |
| 33 | +#define APB_FREQUENCY CPU_FREQUENCY |
| 34 | +// #define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(PPRE1 - 3))) |
| 35 | + |
| 36 | +static inline void spin(volatile uint32_t n) { |
| 37 | + while (n--) (void) 0; |
| 38 | +} |
| 39 | + |
| 40 | +enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; |
| 41 | +enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; |
| 42 | +enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; |
| 43 | +enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; |
| 44 | + |
| 45 | +#define GPIO(N) ((GPIO_TypeDef *) ((GPIOA_BASE) + 0x400 * (N))) |
| 46 | + |
| 47 | +static GPIO_TypeDef *gpio_bank(uint16_t pin) { |
| 48 | + return GPIO(PINBANK(pin)); |
| 49 | +} |
| 50 | +static inline void gpio_toggle(uint16_t pin) { |
| 51 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 52 | + uint32_t mask = BIT(PINNO(pin)); |
| 53 | + gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0); |
| 54 | +} |
| 55 | +static inline int gpio_read(uint16_t pin) { |
| 56 | + return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0; |
| 57 | +} |
| 58 | +static inline void gpio_write(uint16_t pin, bool val) { |
| 59 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 60 | + gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16); |
| 61 | +} |
| 62 | +static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, |
| 63 | + uint8_t speed, uint8_t pull, uint8_t af) { |
| 64 | + GPIO_TypeDef *gpio = gpio_bank(pin); |
| 65 | + uint8_t n = (uint8_t) (PINNO(pin)); |
| 66 | + RCC->IOPENR |= BIT(PINBANK(pin)); // Enable GPIO clock |
| 67 | + SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n); |
| 68 | + SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2)); |
| 69 | + SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2)); |
| 70 | + SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4), |
| 71 | + ((uint32_t) af) << ((n & 7) * 4)); |
| 72 | + SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2)); |
| 73 | +} |
| 74 | +static inline void gpio_input(uint16_t pin) { |
| 75 | + gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, |
| 76 | + GPIO_PULL_NONE, 0); |
| 77 | +} |
| 78 | +static inline void gpio_output(uint16_t pin) { |
| 79 | + gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, |
| 80 | + GPIO_PULL_NONE, 0); |
| 81 | +} |
| 82 | + |
| 83 | +static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) { |
| 84 | + uint8_t af = 1; // Alternate function |
| 85 | + uint16_t rx = 0, tx = 0; // pins |
| 86 | + uint32_t freq = 0; // Bus frequency |
| 87 | + |
| 88 | + if (uart == USART1) { |
| 89 | + freq = CPU_FREQUENCY, RCC->APBENR2 |= RCC_APBENR2_USART1EN; |
| 90 | + tx = PIN('A', 9), rx = PIN('A', 10); |
| 91 | + } else if (uart == USART2) { |
| 92 | + freq = CPU_FREQUENCY, RCC->APBENR1 |= RCC_APBENR1_USART2EN; |
| 93 | + tx = PIN('A', 2), rx = PIN('A', 3); |
| 94 | + } else { |
| 95 | + return false; |
| 96 | + } |
| 97 | + gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); |
| 98 | + gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); |
| 99 | + uart->CR1 = 0; // Disable UART |
| 100 | + uart->BRR = freq / baud; // Set baud rate |
| 101 | + uart->CR1 = USART_CR1_RE | USART_CR1_TE; // Set mode to TX & RX |
| 102 | + uart->CR1 |= USART_CR1_UE; // Enable UART |
| 103 | + return true; |
| 104 | +} |
| 105 | +static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { |
| 106 | + uart->TDR = byte; |
| 107 | + while ((uart->ISR & BIT(7)) == 0) spin(1); |
| 108 | +} |
| 109 | +static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { |
| 110 | + while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); |
| 111 | +} |
| 112 | +static inline int uart_read_ready(USART_TypeDef *uart) { |
| 113 | + return uart->ISR & BIT(5); // If RXNE bit is set, data is ready |
| 114 | +} |
| 115 | +static inline uint8_t uart_read_byte(USART_TypeDef *uart) { |
| 116 | + return (uint8_t) (uart->RDR & 255); |
| 117 | +} |
| 118 | + |
| 119 | +#ifndef RNG |
| 120 | +struct rng { |
| 121 | + volatile uint32_t CR, SR, DR; |
| 122 | +}; |
| 123 | +#define RNG ((struct rng *) 0x40025000) // RM0444 2.2.2 Table 6 |
| 124 | +#endif |
| 125 | + |
| 126 | +static inline void rng_init(void) { |
| 127 | + RCC->CCIPR |= 2U << 26U; // RNG clock source. Documented in 5.4.21 |
| 128 | + RCC->AHBENR |= BIT(18); // RM0444 5.4.25 Table 36 |
| 129 | + RNG->CR |= BIT(2); // 19.7.1 |
| 130 | +} |
| 131 | +static inline uint32_t rng_read(void) { |
| 132 | + while ((RNG->SR & BIT(0)) == 0) (void) 0; |
| 133 | + return RNG->DR; |
| 134 | +} |
| 135 | + |
| 136 | +// Bit-bang SPI implementation |
| 137 | +struct spi { |
| 138 | + uint16_t miso, mosi, clk, cs; // Pins |
| 139 | + int spin; // Number of NOP spins for bitbanging |
| 140 | +}; |
| 141 | + |
| 142 | +static inline void spi_begin(struct spi *spi) { |
| 143 | + gpio_write(spi->cs, 0); |
| 144 | + // printf("%s\n", __func__); |
| 145 | +} |
| 146 | + |
| 147 | +static inline void spi_end(struct spi *spi) { |
| 148 | + gpio_write(spi->cs, 1); |
| 149 | + // printf("%s\n", __func__); |
| 150 | +} |
| 151 | + |
| 152 | +static inline void spi_init(struct spi *spi) { |
| 153 | + gpio_input(spi->miso); |
| 154 | + gpio_output(spi->mosi); |
| 155 | + gpio_output(spi->clk); |
| 156 | + gpio_output(spi->cs); |
| 157 | + gpio_write(spi->cs, 1); |
| 158 | + // printf("%s\n", __func__); |
| 159 | +} |
| 160 | + |
| 161 | +// Send a byte, and return a received byte |
| 162 | +static inline uint8_t spi_txn(struct spi *spi, uint8_t write_byte) { |
| 163 | + unsigned count = spi->spin <= 0 ? 9 : (unsigned) spi->spin; |
| 164 | + uint8_t rx = 0, tx = write_byte; |
| 165 | + for (int i = 0; i < 8; i++) { |
| 166 | + gpio_write(spi->mosi, tx & 0x80U); // Set mosi |
| 167 | + gpio_write(spi->clk, 1); // Clock high |
| 168 | + spin(count); // Wait half cycle |
| 169 | + rx <<= 1U; // Shift alreay read bits |
| 170 | + if (gpio_read(spi->miso)) rx |= 1U; // Read next bit |
| 171 | + gpio_write(spi->clk, 0); // Clock low |
| 172 | + spin(count); // Wait half cycle |
| 173 | + tx <<= 1U; // Discard written bit |
| 174 | + } |
| 175 | + // printf("%s %02x %02x\n", __func__, (int) write_byte, (int) rx); |
| 176 | + return rx; // Return the received byte |
| 177 | +} |
| 178 | + |
| 179 | +#define UUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID. TRM 59.1 |
| 180 | + |
| 181 | +// Helper macro for MAC generation, byte reads not allowed |
| 182 | +#define GENERATE_LOCALLY_ADMINISTERED_MAC() \ |
| 183 | + { \ |
| 184 | + 2, UUID[0] & 255, (UUID[0] >> 10) & 255, (UUID[0] >> 19) & 255, \ |
| 185 | + UUID[1] & 255, UUID[2] & 255 \ |
| 186 | + } |
0 commit comments