diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2020-11-22 01:06:30 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2020-11-22 01:06:30 -0700 |
commit | 9f28e53c71d28d04e2775c59944d2887a99f1e86 (patch) | |
tree | c0ecb2872c6d27acf08aa73919d709f949200de5 /02-usart/src/arch | |
parent | ebb9123c00d1e9629376b6f0a2f1f4e7e550c2af (diff) | |
download | stm32l4-9f28e53c71d28d04e2775c59944d2887a99f1e86.tar.gz stm32l4-9f28e53c71d28d04e2775c59944d2887a99f1e86.tar.bz2 stm32l4-9f28e53c71d28d04e2775c59944d2887a99f1e86.zip |
Large reorganization.
What was in core/ is now moved to arch/stm34l4xxx/peripherals. This new
directory is *supposed to* to contain raw header files defining just the
pertinent register structures for the various peripherals. Peripheral
management belongs somewhere in the new `kern/..` directories. This is
not completely the case at the moment, so more refactoring needs to be
done.
What was sitting in the root has now been moved into the kern/
directory. The kern/ directory is to contain everything else
other than raw device register definitions. The root of the kern/
tree is reserved for standard library-esque headers.
The kern/<peripheral> directory contains management systems for that
peripheral. (At the moment DMA is the only peripheral with a decent
management system.) Preferably these peripheral systems should only
include their correlating header in arch/stm34l4xxx/peripherals, and
use other management systems for handling other peripherals rather
than manipulating their raw registers directly. (Though this ideal
will require much more critical mass of management systems.)
Diffstat (limited to '02-usart/src/arch')
-rw-r--r-- | 02-usart/src/arch/stm32l4xxx/peripherals/clock.c | 107 | ||||
-rw-r--r-- | 02-usart/src/arch/stm32l4xxx/peripherals/gpio.c | 52 | ||||
-rw-r--r-- | 02-usart/src/arch/stm32l4xxx/peripherals/init.c | 49 | ||||
-rw-r--r-- | 02-usart/src/arch/stm32l4xxx/peripherals/irq.c | 92 | ||||
-rw-r--r-- | 02-usart/src/arch/stm32l4xxx/peripherals/usart.c | 147 |
5 files changed, 447 insertions, 0 deletions
diff --git a/02-usart/src/arch/stm32l4xxx/peripherals/clock.c b/02-usart/src/arch/stm32l4xxx/peripherals/clock.c new file mode 100644 index 0000000..ba127a9 --- /dev/null +++ b/02-usart/src/arch/stm32l4xxx/peripherals/clock.c @@ -0,0 +1,107 @@ +/* + * This file sets the system clock to its full glory of 80Mhz + */ + +#include "arch/stm32l4xxx/peripherals/clock.h" +#include "arch/stm32l4xxx/peripherals/flash.h" + +#include <stdint.h> +#include "kern/spin.h" + +#define TIMEOUT 10000 + +int pll_off() +{ + uint32_t c; + + RCC.c_r &= ~BIT(24); /* Turn off pll. */ + for (c = 0; c < TIMEOUT && RCC.c_r & BIT(25); ++c) + ; /* Wait for OFF. */ + + if (c == TIMEOUT) { + return E_TIMEOUT; + } + + return 0; +} + +int pll_on() +{ + uint32_t c; + + RCC.c_r |= BIT(24); /* Turn on PLL. */ + for (c = 0; c < TIMEOUT && !(RCC.c_r & BIT(25)); ++c) + ; /* Wait for RDY. */ + + if (c == TIMEOUT) { + return E_TIMEOUT; + } + + return 0; +} + +int configure_pll( + uint8_t pllp_div_factor, pll_divisor_t pllr, /* System clock divisor. */ + pll_divisor_t pllq, /* Divison factor for PLL48M1CLK. */ + pllp_divisor_t pllp, /* Divison factor for PLLSAI2CLK. */ + uint8_t plln, /* PLL numerator. */ + pllm_divisor_t pllm, /* PLL denominator. */ + pll_src_t pllsrc /* PLL source */) +{ + if (RCC.c_r & BIT(25)) { + /* PLL must be off to configure it. */ + return E_NOT_OFF; + } + + /* Make sure inputs are valid. */ + if (pllp_div_factor == 1 || pllp_div_factor > 31) { + return E_BADPLLP_DIV; + } + if (plln < 8 || plln > 86) { + return E_BADPLLN; + } + + RCC.pllcfg_r = (pllp_div_factor << 27) | (pllr << 24) | (pllq << 20) | + (pllp << 16) | (plln << 8) | (pllm << 4) | (pllsrc << 0); + + return 0; +} + +int set_system_clock_MHz(uint8_t mhz) +{ + /* Set the source of the system colck to MSI temporarily. */ + set_system_clock_src(SYSTEM_CLOCK_SRC_MSI); + + if (mhz <= 8 || mhz > 80) { + return E_BAD_ARG; + } + + pll_off(); + + configure_pll( + 0 /* pllp_div_factor */, PLL_DIVISOR_4 /* pllr: VCO / 4 = mhz MHz. */, + PLL_DIVISOR_4 /* pllq: VCO / 4 = mhz MHz */, PLLP_DIVISOR_7 /* pllp */, + + /* The following set the frequency of VCO to (mhz*4)MHz: mhz * 1 * 4MHz. + */ + mhz /* plln | mhz */, PLLM_DIVISOR_1 /* pllm | 01 */, + PLL_SRC_MSI /* pll src | 04 Mhz */); + + pll_on(); + + /* Configure the flash to have 4 wait states. This is required at + * 80 MHz. */ + FLASH.ac_r &= ~0x07; + FLASH.ac_r |= 0x04; + + /* Set the source of the system colck to PLL. */ + set_system_clock_src(SYSTEM_CLOCK_SRC_PLL); + return 0; +} + +int set_system_clock_src(system_clock_src_t src) +{ + uint8_t value = RCC.cfg_r & ~0x03; + RCC.cfg_r = value | src; + return 0; +} diff --git a/02-usart/src/arch/stm32l4xxx/peripherals/gpio.c b/02-usart/src/arch/stm32l4xxx/peripherals/gpio.c new file mode 100644 index 0000000..a1e82c7 --- /dev/null +++ b/02-usart/src/arch/stm32l4xxx/peripherals/gpio.c @@ -0,0 +1,52 @@ +#include "arch/stm32l4xxx/peripherals/gpio.h" +#include "arch/stm32l4xxx/peripherals/rcc.h" + +/* + * Sets the mode of a pin on a gpio por. + */ +void set_gpio_pin_mode( + __IO gpio_port_t* gpio_port, gpio_pin_t pin, gpio_pin_mode_t mode) +{ + /* Each pin has a 2-bit mode provided at bits pin#*2 and pin#*2+1 */ + gpio_port->mode_r &= ~(0x03 << pin * 2); + gpio_port->mode_r |= mode << pin * 2; +} + +gpio_output_pin_t set_gpio_pin_output( + __IO gpio_port_t* gpio_port, gpio_pin_t pin) +{ + set_gpio_pin_mode(gpio_port, pin, MODE_OUTPUT); + + return (gpio_output_pin_t){.gpio_port = gpio_port, .pin = pin}; +} + +void set_gpio_output_pin(gpio_output_pin_t pin, bool onoff) +{ + if (onoff) { + pin.gpio_port->output_r |= 1 << pin.pin; + } else { + pin.gpio_port->output_r &= ~(1 << pin.pin); + } +} + +void set_gpio_alternate_function( + __IO gpio_port_t* port, gpio_pin_t gpio_pin, alternate_function_t afn) +{ + __IO uint32_t* reg; + if (gpio_pin < 8) { + reg = &(port->af_rl); + } else { + reg = &(port->af_rh); + gpio_pin -= 8; + } + + uint32_t tmp = *reg & (~0x0f << gpio_pin * 4); + *reg = tmp | (afn << gpio_pin * 4); +} + +#define GPIO_PORTS_BASE_ADDR ((uint8_t*)0x48000000) +__IO gpio_port_t* enable_gpio(gpio_port_number_t gpio_port_number) +{ + RCC.ahb2en_r |= 1 << gpio_port_number; /* Enable the GPIO port. */ + return (__IO gpio_port_t*)(GPIO_PORTS_BASE_ADDR + (gpio_port_number * 0x400)); +} diff --git a/02-usart/src/arch/stm32l4xxx/peripherals/init.c b/02-usart/src/arch/stm32l4xxx/peripherals/init.c new file mode 100644 index 0000000..47bfaa5 --- /dev/null +++ b/02-usart/src/arch/stm32l4xxx/peripherals/init.c @@ -0,0 +1,49 @@ +#include "arch.h" +#include "arch/stm32l4xxx/peripherals/system.h" + +/* Forward-declare the main function. This is implemented in main.c. */ +void main(); + +/* These are defined in the linker script. */ + +#ifdef ARCH_STM32L4 +extern uint32_t INIT_DATA_VALUES; +extern uint32_t DATA_SEGMENT_START; +extern uint32_t DATA_SEGMENT_STOP; +extern uint32_t BSS_START; +extern uint32_t BSS_END; + +/* + * Runs before main. Initializes the data and bss segments by loading them + * into memory. + */ +_Noreturn void on_reset() +{ + uint32_t* src; + uint32_t* dest; + + src = &INIT_DATA_VALUES; + dest = &DATA_SEGMENT_START; + + /* Copy the values from flash into the data segment. */ + while (dest != &DATA_SEGMENT_STOP) { + *(dest++) = *(src++); + } + + /* Everything in the BSS segment is set to zero. */ + dest = &BSS_START; + while (dest != &BSS_END) { + *(dest++) = 0; + } + + /* Set the vector offset table to be at the start + * of FLASH memory. */ + SCB.vto_r = 0x08000000; + + /* Jump to main. */ + main(); + + for(;;); +} + +#endif /* ARCH_STM32L4 */ diff --git a/02-usart/src/arch/stm32l4xxx/peripherals/irq.c b/02-usart/src/arch/stm32l4xxx/peripherals/irq.c new file mode 100644 index 0000000..8fb3e49 --- /dev/null +++ b/02-usart/src/arch/stm32l4xxx/peripherals/irq.c @@ -0,0 +1,92 @@ +#include "arch/stm32l4xxx/peripherals/irq.h" +#include "arch/stm32l4xxx/peripherals/gpio.h" +#include "arch/stm32l4xxx/peripherals/nvic.h" + +#include "arch.h" +#include "kern/delay.h" + +#define IRQ_RESERVED(n) +#define IRQ(name, uname_, n) \ + void WEAK name () { \ + unhandled_isr(n); \ + } +#include "arch/stm32l4xxx/peripherals/isrs.inc" +#undef IRQ_RESERVED +#undef IRQ + +void isr_simple_pin_on() +{ + __IO gpio_port_t* port_b = enable_gpio(GPIO_PORT_B); + gpio_output_pin_t pin3 = set_gpio_pin_output(port_b, PIN_3); + + pin_on(pin3); +} + +#define IRQ_RESERVED(n) 0, +#define IRQ(name, uname_, n) name, +const void* vectors[] __attribute__((section(".vectors"))) = { + (void*)0x2000c000, /* Top of stack at top of sram1. 48k */ +#include "arch/stm32l4xxx/peripherals/isrs.inc" +}; +#undef IRQ_RESERVED +#undef IRQ + +/* Encodes the provided number as a series of flashes on the on-board + * LED. The flashes follow as such: + * + * Before the bits of the code are flashed, a rapid succession of 20 flashes + * followed by a pause will occur indicating that the next 8 flashes indicate + * the bits of the provided code. + * + * Eoch of the next eight flashes indicate either a 1 or 0 depending on the + * length of flash. The first flash is the least-significant bit, the next the + * second least, the third third least, etc. + * + * - A quick flash followed by a long pause indicates a 0 bit. + * - A "long" flash followed by a equally long pause indicates a 1 bit. + */ +void unhandled_isr(uint8_t number) +{ + __IO gpio_port_t* port_b = enable_gpio(GPIO_PORT_B); + gpio_output_pin_t pin3 = set_gpio_pin_output(port_b, PIN_3); + for (;;) { + for (int i = 0; i < 20; ++ i) { + pin_on(pin3); + delay(1000000); + pin_off(pin3); + delay(1000000); + } + delay(50000000); + + int n = number; + for (int i = 0; i < 8; ++ i) { + if (n & 1) { + // LSB is a 1 + pin_on(pin3); + delay(15000000); + pin_off(pin3); + delay(15000000); + } else { + // LSB is a 0 + pin_on(pin3); + delay(1000000); + pin_off(pin3); + delay(29000000); + } + + n >>= 1; + } + } +} + +void enable_interrupts(interrupt_set_t* interrupts) +{ + for (int i = 0; i < sizeof(NVIC.ise_r) / sizeof(uint32_t); ++ i) + NVIC.ise_r[i] = interrupts->irqs[i]; +} + +void disable_interrupts(interrupt_set_t* interrupts) +{ + for (int i = 0; i < sizeof(NVIC.ise_r) / sizeof(uint32_t); ++ i) + NVIC.ice_r[i] = interrupts->irqs[i]; +} diff --git a/02-usart/src/arch/stm32l4xxx/peripherals/usart.c b/02-usart/src/arch/stm32l4xxx/peripherals/usart.c new file mode 100644 index 0000000..d37eee2 --- /dev/null +++ b/02-usart/src/arch/stm32l4xxx/peripherals/usart.c @@ -0,0 +1,147 @@ +#include "arch/stm32l4xxx/peripherals/usart.h" +#include "kern/delay.h" +#include "kern/lib.h" +#include <stdarg.h> + +void set_usart1_clock_src(__IO rcc_t* rcc, usart_clk_src_t usart_clk_src) +{ + rcc->ccip_r = (rcc->ccip_r & (~0x03)) | usart_clk_src; +} + +void set_usart2_clock_src(__IO rcc_t* rcc, usart_clk_src_t usart_clk_src) +{ + rcc->ccip_r = (rcc->ccip_r & ~(0x03 << 2)) | (usart_clk_src << 2); +} + +void set_usart2_clock_enabled(__IO rcc_t* rcc, bool enable) +{ + if (enable) { + rcc->apb1en1_r |= BIT(17); + } else { + rcc->apb1en1_r &= ~BIT(17); + } +} + +void set_usart1_clock_enabled(__IO rcc_t* rcc, bool enable) +{ + if (enable) { + rcc->apb2en_r |= BIT(14); + } else { + rcc->apb2en_r &= ~BIT(14); + } +} + +void usart_set_parity(__IO usart_t* usart, usart_parity_t parity) +{ + uint32_t c_r1 = usart->c_r1; + c_r1 &= ~(0x3 << 9); + c_r1 |= parity; + usart->c_r1 = c_r1; +} + +void usart_set_enabled(__IO usart_t* usart, usart_enable_t enabled) +{ + if (!enabled) { + regset(usart->c_r1, usart_ue, 0); + } else { + /* Set the rx enabled. */ + regset(usart->c_r1, usart_re, !!(enabled & USART_ENABLE_RX)); + regset(usart->c_r1, usart_te, !!(enabled & USART_ENABLE_TX)); + regset(usart->c_r1, usart_ue, 1); + } +} + +void usart_transmit_byte_sync(__IO usart_t* usart, uint8_t byte) +{ + usart->td_r = byte; + /* Per the manual, when bit 7 of the IS register is set, then the usart + * data has been sent to the shift register. + * + * This bit is cleared by writing to the TD register. */ + while (!(usart->is_r & BIT(7))) + ; +} + +void usart_transmit_bytes_sync(__IO usart_t* usart, const uint8_t* bytes, uint32_t n) +{ + while (n --) { + usart_transmit_byte_sync(usart, *(bytes ++)); + } +} + +void usart_transmit_str_sync(__IO usart_t* usart, const char* str) +{ + while (*str) { + if (*str == '\n') { + usart_transmit_byte_sync(usart, '\r'); + } + usart_transmit_byte_sync(usart, *(str ++)); + } +} + +void usart_enable_dma(__IO usart_t* usart, usart_enable_t enabled) +{ + switch(enabled) { + case USART_ENABLE_DISABLED: + regset(usart->c_r3, usart_dmar, 0); + regset(usart->c_r3, usart_dmat, 0); + break; + + case USART_ENABLE_TX: + regset(usart->c_r3, usart_dmat, 1); + break; + + case USART_ENABLE_RX: + regset(usart->c_r3, usart_dmar, 1); + break; + }; +} + +void usart_printf(__IO usart_t* usart, const char* fmt, ...) +{ + va_list l; + union { + void* ptr; + char* str; + int i; + } b; + char buf[128]; + + va_start(l, fmt); + + while (*fmt != 0) { + if (*fmt == '%') { + switch (*(++fmt)) { + case 0: + goto end; + case '%': + usart_transmit_byte_sync(usart, '%'); + break; + case 'p': + b.ptr = va_arg(l, void*); + hexify(ptr2reg(b.ptr), buf); + usart_transmit_str_sync(usart, "0x"); + usart_transmit_str_sync(usart, buf); + break; + case 'd': + case 'i': + b.i = va_arg(l, int); + decimalify(b.i, buf); + usart_transmit_str_sync(usart, buf); + break; + case 's': + b.str = va_arg(l, char*); + usart_transmit_str_sync(usart, b.str); + } + ++ fmt; + } else { + if (*fmt == '\n') { + usart_transmit_byte_sync(usart, '\r'); + } + usart_transmit_byte_sync(usart, *(fmt ++)); + } + } + +end: + va_end(l); +} |