diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2020-12-04 23:16:31 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2020-12-04 23:16:31 -0700 |
commit | 7002cb8380406173407c9e8c8d16ebd670fff55c (patch) | |
tree | b6ba377167ce193879347d692f087084f130c561 | |
parent | 83deae717de8b940b0cb04d1d1989b0a4c250e35 (diff) | |
download | stm32l4-7002cb8380406173407c9e8c8d16ebd670fff55c.tar.gz stm32l4-7002cb8380406173407c9e8c8d16ebd670fff55c.tar.bz2 stm32l4-7002cb8380406173407c9e8c8d16ebd670fff55c.zip |
Added kernel-level abstraction over the spi interface.
-rw-r--r-- | include/arch/stm32l4xxx/peripherals/rcc.h | 31 | ||||
-rw-r--r-- | include/arch/stm32l4xxx/peripherals/spi.h | 58 | ||||
-rw-r--r-- | include/arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc | 2 | ||||
-rw-r--r-- | include/kern/common.h | 25 | ||||
-rw-r--r-- | include/kern/spi/spi_manager.h | 179 | ||||
-rw-r--r-- | src/kern/main.c | 207 | ||||
-rw-r--r-- | src/kern/spi/spi_manager.c | 151 | ||||
-rw-r--r-- | tests/test_spi.c | 63 |
8 files changed, 552 insertions, 164 deletions
diff --git a/include/arch/stm32l4xxx/peripherals/rcc.h b/include/arch/stm32l4xxx/peripherals/rcc.h index 65b2e86..cb04b94 100644 --- a/include/arch/stm32l4xxx/peripherals/rcc.h +++ b/include/arch/stm32l4xxx/peripherals/rcc.h @@ -80,8 +80,39 @@ typedef struct { __IO uint32_t reserved_5; /* Not used. offset 0x54. */ +#define rcc_lptim1en (1 << 31) /* Low power timer 1 clock enable */ +#define rcc_opampen (1 << 30) /* OPAMP interface clock enable */ +#define rcc_dac1en (1 << 29) /* DAC1 interface clock enable */ +#define rcc_pwren (1 << 28) /* Power interface clock enable */ +#define rcc_can2en (1 << 26) /* CAN2 clock enable (this bit is reserved for STM32L47x/L48x devices) */ +#define rcc_can1en (1 << 25) /* CAN1 clock enable */ +#define rcc_crsen (1 << 24) /* Clock Recovery System clock enable (this bit is reserved for STM32L47x/L48x */ +#define rcc_i2c3en (1 << 23) /* I2C3 clock enable */ +#define rcc_i2c2en (1 << 22) /* I2C2 clock enable */ +#define rcc_i2c1en (1 << 21) /* I2C1 clock enable */ +#define rcc_uart5en (1 << 20) /* UART5 clock enable */ +#define rcc_uart4en (1 << 19) /* UART4 clock enable */ +#define rcc_usart3en (1 << 18) /* USART3 clock enable */ +#define rcc_usart2en (1 << 17) /* USART2 clock enable */ +#define rcc_spi3en (1 << 15) /* SPI3 clock enable */ +#define rcc_spi2en (1 << 14) /* SPI2 clock enable */ +#define rcc_wwdgen (1 << 11) /* Window watchdog clock enable */ +#define rcc_rtcapben (1 << 10) /* RTC APB clock enable (this bit is reserved for STM32L47x/L48x devices) */ +#define rcc_lcden (1 << 9) /* LCD clock enable (this bit is reserved for STM32L471/L4x5 devices) */ +#define rcc_tim7en (1 << 5) /* TIM7 timer clock enable */ +#define rcc_tim6en (1 << 4) /* TIM6 timer clock enable */ +#define rcc_tim5en (1 << 3) /* TIM5 timer clock enable */ +#define rcc_tim4en (1 << 2) /* TIM4 timer clock enable */ +#define rcc_tim3en (1 << 1) /* TIM3 timer clock enable */ +#define rcc_tim2en (1 << 0) /* TIM2 timer clock enable */ __IO uint32_t apb1en1_r; /* APB1 Peripheral enable register 1. 0x58 */ + +#define lptim2en (1 << 5) /*Low power timer 2 clock enable */ +#define swpmi1en (1 << 2) /* Single wire protocol clock enable */ +#define i2c4en (1 << 1) /* I2C4 clock enable (this bit is reserved for STM32L47x/L48x devices) */ +#define lpuart1en (1 << 0) /* Low power UART 1 clock enable */ __IO uint32_t apb1en2_r; /* APB1 Peripheral enable register 2. 0x5C */ + #define rcc_syscfgen (1 << 0) #define rcc_fwen (1 << 7) #define rcc_sdmmc1en (1 << 10) diff --git a/include/arch/stm32l4xxx/peripherals/spi.h b/include/arch/stm32l4xxx/peripherals/spi.h index 478664e..eb9741e 100644 --- a/include/arch/stm32l4xxx/peripherals/spi.h +++ b/include/arch/stm32l4xxx/peripherals/spi.h @@ -4,38 +4,38 @@ #include "kern/common.h" #include "arch.h" -#define SPI1 (*((spi_t*)(SPI1_BASE))) -#define SPI3 (*((spi_t*)(SPI3_BASE))) +#define SPI1 (*((spi_regs_t*)(SPI1_BASE))) +#define SPI3 (*((spi_regs_t*)(SPI3_BASE))) typedef enum { - SPI_BAUD_FPCLK_DIV_2 = 0, - SPI_BAUD_FPCLK_DIV_4 = 1, - SPI_BAUD_FPCLK_DIV_8 = 2, - SPI_BAUD_FPCLK_DIV_16 = 3, - SPI_BAUD_FPCLK_DIV_32 = 4, - SPI_BAUD_FPCLK_DIV_64 = 5, - SPI_BAUD_FPCLK_DIV_128 = 6, - SPI_BAUD_FPCLK_DIV_256 = 7, + SPI_BAUD_RATE_FPCLK_DIV_2 = 0, + SPI_BAUD_RATE_FPCLK_DIV_4 = 1, + SPI_BAUD_RATE_FPCLK_DIV_8 = 2, + SPI_BAUD_RATE_FPCLK_DIV_16 = 3, + SPI_BAUD_RATE_FPCLK_DIV_32 = 4, + SPI_BAUD_RATE_FPCLK_DIV_64 = 5, + SPI_BAUD_RATE_FPCLK_DIV_128 = 6, + SPI_BAUD_RATE_FPCLK_DIV_256 = 7, } spi_baud_rate_t; typedef enum { - SPI_DATA_SIZE_NOT_USED_0 = 0, - SPI_DATA_SIZE_NOT_USED_1 = 1, - SPI_DATA_SIZE_NOT_USED_2 = 2, - SPI_DATA_SIZE_4_BITS = 3, - SPI_DATA_SIZE_5_BITS = 4, - SPI_DATA_SIZE_6_BITS = 5, - SPI_DATA_SIZE_7_BITS = 6, - SPI_DATA_SIZE_8_BITS = 7, - SPI_DATA_SIZE_9_BITS = 8, - SPI_DATA_SIZE_10_BITS = 9, - SPI_DATA_SIZE_11_BITS = 10, - SPI_DATA_SIZE_12_BITS = 11, - SPI_DATA_SIZE_13_BITS = 12, - SPI_DATA_SIZE_14_BITS = 13, - SPI_DATA_SIZE_15_BITS = 14, - SPI_DATA_SIZE_16_BITS = 15, -} spi_data_size_t; + SPI_REG_DATA_SIZE_NOT_USED_0 = 0, + SPI_REG_DATA_SIZE_NOT_USED_1 = 1, + SPI_REG_DATA_SIZE_NOT_USED_2 = 2, + SPI_REG_DATA_SIZE_4_BITS = 3, + SPI_REG_DATA_SIZE_5_BITS = 4, + SPI_REG_DATA_SIZE_6_BITS = 5, + SPI_REG_DATA_SIZE_7_BITS = 6, + SPI_REG_DATA_SIZE_8_BITS = 7, + SPI_REG_DATA_SIZE_9_BITS = 8, + SPI_REG_DATA_SIZE_10_BITS = 9, + SPI_REG_DATA_SIZE_11_BITS = 10, + SPI_REG_DATA_SIZE_12_BITS = 11, + SPI_REG_DATA_SIZE_13_BITS = 12, + SPI_REG_DATA_SIZE_14_BITS = 13, + SPI_REG_DATA_SIZE_15_BITS = 14, + SPI_REG_DATA_SIZE_16_BITS = 15, +} spi_reg_data_size_t; typedef enum { SPI_FIFO_STATUS_EMPTY = 0, @@ -110,8 +110,8 @@ typedef __IO struct { /* spi tx CRC register. */ uint32_t txcrc_r; -} spi_t; +} spi_regs_t; -static_assert(offsetof(spi_t, txcrc_r) == 0x18, "Offset check failed."); +static_assert(offsetof(spi_regs_t, txcrc_r) == 0x18, "Offset check failed."); #endif /* CORE_SPI_H_ */ diff --git a/include/arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc b/include/arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc new file mode 100644 index 0000000..5a91e8b --- /dev/null +++ b/include/arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc @@ -0,0 +1,2 @@ +SPI(SPI1, SPI1) +SPI(SPI2, SPI3) /* Stm32l432 doesn't have a SPI2. */ diff --git a/include/kern/common.h b/include/kern/common.h index c5afe3f..021229b 100644 --- a/include/kern/common.h +++ b/include/kern/common.h @@ -1,9 +1,11 @@ #ifndef COMMON__H #define COMMON__H -#include <stdint.h> -#include <stddef.h> #include <assert.h> +#include <stddef.h> +#include <stdint.h> + +typedef enum { ENDIANNESS_LITTLE, ENDIANNESS_BIG } endianness_t; #define WEAK __attribute__((weak)) #define NORETURN __attribute__((noreturn)) @@ -28,23 +30,18 @@ #define PACKED __attribute__((packed)) #define BIT(n) (1 << (n)) -#define RESERVED_CONCAT_IMPL(x, y) x ## y +#define RESERVED_CONCAT_IMPL(x, y) x##y #define RESERVED_MACRO_CONCAT(x, y) RESERVED_CONCAT_IMPL(x, y) -#define RESERVED(n) \ - bits_t RESERVED_MACRO_CONCAT(_r, __COUNTER__) :n - -#define RESERVE(type) \ - __IO type RESERVED_MACRO_CONCAT(_r, __COUNTER__) +#define RESERVED(n) bits_t RESERVED_MACRO_CONCAT(_r, __COUNTER__) : n + +#define RESERVE(type) __IO type RESERVED_MACRO_CONCAT(_r, __COUNTER__) -#define ptr2reg(ptr) \ - ((uint32_t) (ptrdiff_t) (ptr)) +#define ptr2reg(ptr) ((uint32_t)(ptrdiff_t)(ptr)) typedef __IO uint32_t bits_t; -#define regset(reg, mask, val) \ - ((reg) = ((reg) & ~mask) | (val << CTZ(mask))) +#define regset(reg, mask, val) ((reg) = ((reg) & ~mask) | (val << CTZ(mask))) -#define regget(reg, mask) \ - (((reg) & mask) >> (CTZ(mask))) +#define regget(reg, mask) (((reg)&mask) >> (CTZ(mask))) #endif /* COMMON_H */ diff --git a/include/kern/spi/spi_manager.h b/include/kern/spi/spi_manager.h new file mode 100644 index 0000000..53e4c2a --- /dev/null +++ b/include/kern/spi/spi_manager.h @@ -0,0 +1,179 @@ +#ifndef KERN_SPI_SPI_MANAGER_H_ +#define KERN_SPI_SPI_MANAGER_H_ + +#include "kern/common.h" + +#define SPI_ERROR_ALREADY_IN_USE 1 +#define SPI_ERROR_SELECTION_NOT_VALID 2 + +typedef enum { SPI_CRCL_8_BITS, SPI_CRCL_16_BITS } spi_crcl_t; + +typedef enum { + SPI_DATA_SIZE_NOT_USED_0 = 0, + SPI_DATA_SIZE_NOT_USED_1 = 1, + SPI_DATA_SIZE_NOT_USED_2 = 2, + SPI_DATA_SIZE_4_BITS = 3, + SPI_DATA_SIZE_5_BITS = 4, + SPI_DATA_SIZE_6_BITS = 5, + SPI_DATA_SIZE_7_BITS = 6, + SPI_DATA_SIZE_8_BITS = 7, + SPI_DATA_SIZE_9_BITS = 8, + SPI_DATA_SIZE_10_BITS = 9, + SPI_DATA_SIZE_11_BITS = 10, + SPI_DATA_SIZE_12_BITS = 11, + SPI_DATA_SIZE_13_BITS = 12, + SPI_DATA_SIZE_14_BITS = 13, + SPI_DATA_SIZE_15_BITS = 14, + SPI_DATA_SIZE_16_BITS = 15, +} spi_data_size_t; + +typedef enum { + SPI_BAUD_FPCLK_DIV_2 = 0, + SPI_BAUD_FPCLK_DIV_4 = 1, + SPI_BAUD_FPCLK_DIV_8 = 2, + SPI_BAUD_FPCLK_DIV_16 = 3, + SPI_BAUD_FPCLK_DIV_32 = 4, + SPI_BAUD_FPCLK_DIV_64 = 5, + SPI_BAUD_FPCLK_DIV_128 = 6, + SPI_BAUD_FPCLK_DIV_256 = 7, +} spi_baud_t; + +typedef enum { SPI_MODE_MASTER, SPI_MODE_SLAVE } spi_mode_t; + +typedef enum { SPI_POLARITY_0, SPI_POLARITY_1 } spi_polarity_t; + +typedef enum { + /* The first clock transition is the first data capture edge. */ + SPI_CLOCK_PHASE_FIRST, + /* The second clock transition is the first data capture edge. */ + SPI_CLOCK_PHASE_SECOND, +} spi_clock_phase_t; + +typedef enum { + SPI_DMA_PARITY_EVEN, + SPI_DMA_PARITY_ODD, +} spi_dma_parity_t; + +typedef enum { + SPI_FIFO_THRESHOLD_HALF, + SPI_FIFO_THRESHOLD_QUARTER, +} spi_fifo_threshold_t; + +typedef enum { + SPI_FRAME_FORMAT_MOTOROLA, + SPI_FRAME_FORMAT_TI, +} spi_frame_format_t; + +typedef struct { + bool enable_bidirectional_data_mode; + + /* Enable hardware crc checking. */ + bool enable_crc; + + /* CRC length. */ + spi_crcl_t crc_length; + + bool receieve_only; + + bool enable_software_slave_management; + + bool internal_slave_select; + + endianness_t endianness; + + spi_baud_t baud; + + spi_mode_t spi_mode; + + spi_polarity_t polarity; + + spi_clock_phase_t clock_phase; + + spi_dma_parity_t number_of_data_parity_tx; + + spi_dma_parity_t number_of_data_parity_rx; + + spi_fifo_threshold_t rx_interrupt_fifo_threshold; + + spi_data_size_t data_size; + + bool enable_tx_buffer_empty_interrupt; + + bool enable_rx_buffer_not_empty_interrupt; + + bool enable_error_interrupt; + + spi_frame_format_t frame_format; + + bool enable_nss_pulse; + + bool enable_ss_output; + + bool enable_tx_dma; + + bool enable_rx_dma; + + union { + struct { + bool output_enable; + } bidir_opts_t; + }; +} spi_opts_t; + +#define DEFAULT_SPI_OPTS { \ + .enable_bidirectional_data_mode = 0,\ + .enable_crc = 0,\ + .crc_length = SPI_CRCL_8_BITS,\ + .receieve_only = 0,\ + .enable_software_slave_management = 1,\ + .internal_slave_select = 1,\ + .endianness = ENDIANNESS_LITTLE,\ + .baud = SPI_BAUD_FPCLK_DIV_32,\ + .spi_mode = SPI_MODE_MASTER,\ + .polarity = SPI_POLARITY_0,\ + .clock_phase = SPI_CLOCK_PHASE_FIRST,\ + .number_of_data_parity_tx = SPI_DMA_PARITY_EVEN,\ + .number_of_data_parity_rx = SPI_DMA_PARITY_EVEN,\ + .rx_interrupt_fifo_threshold = SPI_FIFO_THRESHOLD_HALF,\ + .data_size = SPI_DATA_SIZE_8_BITS,\ + .enable_tx_buffer_empty_interrupt = 0,\ + .enable_rx_buffer_not_empty_interrupt = 0,\ + .enable_error_interrupt = 0,\ + .frame_format = SPI_FRAME_FORMAT_MOTOROLA,\ + .enable_nss_pulse = 0,\ + .enable_ss_output = 0,\ + .enable_tx_dma = 0 ,\ + .enable_rx_dma = 0,\ +} + +typedef struct spi spi_t; + +typedef enum { +#define SPI(sp, u) SPI_SELECT_##sp, +#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc" +#undef SPI + + N_SPI_SELECT +} spi_select_t; + +/* Reserve a spi bus. If an error occurs, NULL is returned + * and *ec is set to the appropriate value. */ +spi_t* reserve_spi(spi_select_t spi_select, spi_opts_t* opts, int* ec); + +/* Sets the SPI to enable. */ +void spi_start(spi_t* spi); + +/* Release the spi bus. */; +void release_spi(spi_t* spi); + +void spi_set_crc_next(spi_t* spi); + +void spi_write_8_sync(spi_t* spi, uint8_t data); + +void spi_write_16_sync(spi_t* spi, uint16_t data); + +uint8_t spi_read_8_sync(spi_t* spi); + +uint16_t spi_read_16_sync(spi_t* spi); + +#endif /* KERN_SPI_SPI_MANAGER_H_ */ diff --git a/src/kern/main.c b/src/kern/main.c index c76d025..aeb884d 100644 --- a/src/kern/main.c +++ b/src/kern/main.c @@ -1,14 +1,13 @@ #include "arch.h" #include "arch/arm/cortex-m4/mpu.h" #include "arch/stm32l4xxx/peripherals/clock.h" +#include "arch/stm32l4xxx/peripherals/spi.h" #include "arch/stm32l4xxx/peripherals/dma.h" #include "arch/stm32l4xxx/peripherals/irq.h" #include "arch/stm32l4xxx/peripherals/rcc.h" -#include "arch/stm32l4xxx/peripherals/spi.h" #include "arch/stm32l4xxx/peripherals/system.h" #include "drv/ws2812B/ws2812b.h" #include "kern/delay.h" -#include "kern/systick/systick_manager.h" #include "kern/dma/dma_manager.h" #include "kern/gpio/gpio_manager.h" #include "kern/gpio/sysled.h" @@ -18,6 +17,8 @@ #include "kern/mpu/mpu_manager.h" #include "kern/panic.h" #include "kern/priv.h" +#include "kern/spi/spi_manager.h" +#include "kern/systick/systick_manager.h" #include "user/syscall.h" void on_hard_fault() @@ -27,7 +28,7 @@ void on_hard_fault() #ifdef ARCH_STM32L4 -void configure_spi1() +spi_t* configure_spi() { int ec = 0; @@ -47,38 +48,16 @@ void configure_spi1() panic("Unable to set pin PA5 (ec=%d)\n", ec); } - regset(RCC.apb2en_r, rcc_spi1en, 1); - - uint32_t reg = 0; - regset(reg, spi_ldma_tx, 0); - regset(reg, spi_ldma_rx, 0); - regset(reg, spi_frxth, 0); - regset(reg, spi_ds, SPI_DATA_SIZE_8_BITS); - regset(reg, spi_txeie, 0); - regset(reg, spi_rxneie, 0); - regset(reg, spi_errie, 0); - regset(reg, spi_frf, 0); - regset(reg, spi_nssp, 0); - regset(reg, spi_ssoe, 0); - regset(reg, spi_txdmaen, 0); - regset(reg, spi_rxdmaen, 0); - SPI1.c_r2 = reg; - - reg = 0; - regset(reg, spi_bidimode, 0); - regset(reg, spi_crcen, 0); - regset(reg, spi_crcnext, 0); - regset(reg, spi_crcl, 0); - regset(reg, spi_rxonly, 0); - regset(reg, spi_ssm, 1); - regset(reg, spi_ssi, 1); - regset(reg, spi_lsbfirst, 0); - regset(reg, spi_spe, 1); - regset(reg, spi_br, SPI_BAUD_FPCLK_DIV_32); - regset(reg, spi_mstr, 1); - regset(reg, spi_cpol, 0); - regset(reg, spi_cpha, 0); - SPI1.c_r1 = reg; + + spi_opts_t opts = DEFAULT_SPI_OPTS; + opts.endianness = ENDIANNESS_BIG; + spi_t* spi = reserve_spi(SPI_SELECT_SPI1, &opts, &ec); + + if (ec) { + panic("Unable to reserve spi bus. (ec=%d)\n", ec); + } + + return spi; } static uint8_t* compiled; @@ -90,100 +69,100 @@ static uint32_t time; static void on_systick(void* nil) { - // klogf("Systick.\n"); ++time; } -static void spi_write(uint8_t byte) -{ - while (!regget(SPI1.s_r, spi_txe)) - ; - SPI1.dl_r = byte; - asm volatile("nop"); -} - -static void write_rgb(uint8_t red, uint8_t green, uint8_t blue) +static void write_rgb(spi_t* spi, uint8_t red, uint8_t green, uint8_t blue) { #undef BIT #define BIT(b, n) (!!((b) & (1 << (n)))) - spi_write( + spi_write_8_sync( + spi, (1 << 7) | (BIT(green, 7) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(green, 6) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(green, 6) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(green, 5) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(green, 4) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(green, 4) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(green, 3) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(green, 2) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(green, 2) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(green, 1) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(green, 0) << 2) | (0 << 1) | (0 << 0)); + (BIT(green, 0) << 2) | (0 << 1) | (0 << 0)); - spi_write( + spi_write_8_sync( + spi, (1 << 7) | (BIT(red, 7) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(red, 6) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(red, 6) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(red, 5) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(red, 4) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(red, 4) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(red, 3) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(red, 2) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(red, 2) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(red, 1) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(red, 0) << 2) | (0 << 1) | (0 << 0)); + (BIT(red, 0) << 2) | (0 << 1) | (0 << 0)); - spi_write( + spi_write_8_sync( + spi, (1 << 7) | (BIT(blue, 7) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(blue, 6) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(blue, 6) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(blue, 5) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(blue, 4) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(blue, 4) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(blue, 3) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(blue, 2) << 2) | (0 << 1) | (0 << 0)); - spi_write( + (BIT(blue, 2) << 2) | (0 << 1) | (0 << 0)); + spi_write_8_sync( + spi, (1 << 7) | (BIT(blue, 1) << 6) | (0 << 5) | (0 << 4) | (1 << 3) | - (BIT(blue, 0) << 2) | (0 << 1) | (0 << 0)); + (BIT(blue, 0) << 2) | (0 << 1) | (0 << 0)); } -void latch() +void latch(spi_t* spi) { for (int i = 0; i < 20; ++i) { - spi_write(0); + spi_write_8_sync(spi, 0); } } +#define min(a, b) (a) < (b) ? (a) : (b) + static uint8_t amp(uint8_t in) { - uint8_t out = in; + uint32_t out = in; - for (int i = 0; i < 10; ++ i) { + for (int i = 0; i < 20; ++i) { out = (out * in) / 256; } - return out; + return min(out, 255); +} + +static uint32_t bytescale(uint32_t n, uint32_t sc) +{ + return n * sc / 255; } /* Main function. This gets executed from the interrupt vector defined above. */ int main() { + klogf("Entering main\n"); gpio_reserved_pin_t sysled = get_sysled(); - klogf("Start\n"); - klogf("sintable: %p\n", sintable); - klogf("sintable[5]: %d\n", sintable[5]); - klogf("Flashed with OpenOCD!\n"); - systick_add_callback(on_systick, NULL); enable_systick(1000); - /* Enable interrupts. */ - regset(SCB.stcs_r, scb_tickint, 1); - - /* Start the systick. */ - regset(SCB.stcs_r, scb_enable, 1); - -#define SIZE 128 +#define SIZE 256 rgb_t rgb[SIZE]; for (int i = 0; i < SIZE; ++i) { rgb[i].g = 0xff; @@ -191,57 +170,47 @@ int main() rgb[i].b = 0xff; } - uint8_t red = 0x40; - uint8_t green = 0x40; - uint8_t blue = 0x40; - - // compiled = ws2812b_compile_rgb(rgb, SIZE); - // compiled_len = SIZE * 9; + uint32_t red = 0x40; + uint32_t green = 0x40; + uint32_t brightness = 255; - configure_spi1(); + klogf("Configure Spi\n"); + spi_t* spi = configure_spi(); + klogf("Done Configuring Spi\n"); for (int i = 0; i < 100; ++i) { - write_rgb(0, 0, 0); + write_rgb(spi, 0, 0, 0); } - latch(); + klogf("Latch\n"); + latch(spi); for (;;) { set_gpio_pin_high(sysled); - latch(); + klogf("Frame\n"); + + latch(spi); int i; for (i = 0; i < SIZE; ++i) { - red = byte_sin((i * 4 * 2 + time / 1000) & 0xff) / 2; - green = byte_sin((i * 4 * 3 + time / 1000) & 0xff) / 2; - blue = 0; - // blue = amp(byte_sin((i * 2 * 3 + time / 1000) & 0xff)) / 2; + red = byte_sin(time / 1000 + i * 4); + green = 255 - red; - // uint8_t red = byte_sin((i * 8 + 64 + time / 100) & 0xff); - // uint8_t green = byte_sin((i * 8 + 0 + time / 100) & 0xff); - // uint32_t blue = (((red - 128) * (green - 128)) / 256) + 128; + brightness = 3 * byte_sin(time / 5000) / 4 + 63; - write_rgb(red, green, blue); - } + uint32_t white = amp(byte_sin(time / 6310 + i / 4)); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(0,0,0); - // write_rgb(red, green, blue); + write_rgb( + spi, + bytescale(min(red + white, 255), brightness), + bytescale(min(green + white, 255), brightness), + bytescale(white, brightness)); + } set_gpio_pin_low(sysled); - latch(); - } - - for (;;) { - spi_write(0); + latch(spi); } } diff --git a/src/kern/spi/spi_manager.c b/src/kern/spi/spi_manager.c new file mode 100644 index 0000000..83b6e1f --- /dev/null +++ b/src/kern/spi/spi_manager.c @@ -0,0 +1,151 @@ +#include "kern/spi/spi_manager.h" + +#include "arch/stm32l4xxx/peripherals/rcc.h" +#include "arch/stm32l4xxx/peripherals/spi.h" +#include "kern/log.h" + +struct spi { + __IO spi_regs_t* regs; + spi_select_t selection; + bool reserved; +}; + +static spi_t spi_buses[N_SPI_SELECT]; + +static void enable_spi_clock(spi_t* spi, int enable) +{ + switch (spi->selection) { + case SPI_SELECT_SPI1: + regset(RCC.apb2en_r, rcc_spi1en, !!enable); + break; + + case SPI_SELECT_SPI2: + regset(RCC.apb1en1_r, rcc_spi3en, !!enable); + break; + + default: + break; + } +} + +static spi_t* configure_spi(spi_t* spi, spi_opts_t* opts, int* ec) +{ + enable_spi_clock(spi, 1); + + uint32_t reg = 0; + regset(reg, spi_ldma_tx, opts->number_of_data_parity_tx); + regset(reg, spi_ldma_rx, opts->number_of_data_parity_rx); + regset(reg, spi_frxth, opts->rx_interrupt_fifo_threshold); + regset(reg, spi_ds, opts->data_size); + regset(reg, spi_txeie, opts->enable_tx_buffer_empty_interrupt); + regset(reg, spi_rxneie, opts->enable_rx_buffer_not_empty_interrupt); + regset(reg, spi_errie, opts->enable_error_interrupt); + regset(reg, spi_frf, opts->frame_format); + regset(reg, spi_nssp, opts->enable_nss_pulse); + regset(reg, spi_ssoe, opts->enable_ss_output); + regset(reg, spi_txdmaen, opts->enable_tx_dma); + regset(reg, spi_rxdmaen, opts->enable_rx_dma); + + spi->regs->c_r2 = reg; + + reg = 0; + regset(reg, spi_bidimode, opts->enable_bidirectional_data_mode); + regset(reg, spi_crcen, opts->enable_crc); + regset(reg, spi_crcnext, 0); + regset(reg, spi_crcl, opts->crc_length); + regset(reg, spi_rxonly, opts->receieve_only); + regset(reg, spi_ssm, opts->enable_software_slave_management); + regset(reg, spi_ssi, opts->internal_slave_select); + regset(reg, spi_lsbfirst, !opts->endianness); + regset(reg, spi_spe, 1); + regset(reg, spi_br, opts->baud); + regset(reg, spi_mstr, !opts->spi_mode); + regset(reg, spi_cpol, opts->polarity); + regset(reg, spi_cpha, opts->clock_phase); + + spi->regs->c_r1 = reg; + + return spi; +} + +static int try_reserve_spi(spi_t* spi) +{ + return !__sync_fetch_and_or(&spi->reserved, 1); +} + +void spi_start(spi_t* spi) +{ + regset(spi->regs->c_r1, spi_spe, 1); +} + +void release_spi(spi_t* spi) +{ + enable_spi_clock(spi, 0); + regset(spi->regs->c_r1, spi_spe, 0); + spi->reserved = 0; +} + +void spi_set_crc_next(spi_t* spi) +{ + regset(spi->regs->c_r1, spi_crcnext, 1); +} + +spi_t* reserve_spi(spi_select_t spi_select, spi_opts_t* opts, int* ec) +{ + *ec = 0; + + if (spi_select < 0 || spi_select >= N_SPI_SELECT) { + *ec = SPI_ERROR_SELECTION_NOT_VALID; + return NULL; + } + + spi_t* ret = &spi_buses[spi_select]; + + if (!try_reserve_spi(ret)) { + *ec = SPI_ERROR_ALREADY_IN_USE; + return NULL; + } + + ret->selection = spi_select; + switch (spi_select) { +#define SPI(sp, ptr) \ + case SPI_SELECT_##sp: \ + ret->regs = &ptr; \ + break; +#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc" +#undef SPI + default: + *ec = SPI_ERROR_SELECTION_NOT_VALID; + return NULL; + } + + return configure_spi(ret, opts, ec); +} + +void spi_write_8_sync(spi_t* spi, uint8_t data) +{ + while (!regget(spi->regs->s_r, spi_txe)) + ; + spi->regs->dl_r = data; +} + +void spi_write_16_sync(spi_t* spi, uint16_t data) +{ + while (!regget(spi->regs->s_r, spi_txe)) + ; + spi->regs->d_r = data; +} + +uint8_t spi_read_8_sync(spi_t* spi) +{ + while (!regget(spi->regs->s_r, spi_rxne)) + ; + return spi->regs->dl_r; +} + +uint16_t spi_read_16_sync(spi_t* spi) +{ + while (!regget(spi->regs->s_r, spi_rxne)) + ; + return spi->regs->d_r; +} diff --git a/tests/test_spi.c b/tests/test_spi.c index 866f6e8..7e60c60 100644 --- a/tests/test_spi.c +++ b/tests/test_spi.c @@ -1,10 +1,69 @@ +#include <stdio.h> + +#include "arch/stm32l4xxx/peripherals/rcc.h" #include "arch/stm32l4xxx/peripherals/spi.h" +#include "kern/common.h" +#include "kern/spi/spi_manager.h" #include "test_harness.h" TEST(spi, smoke) { - __IO spi_t* spi = &SPI1; - spi->s_r = 1; + int ec = 1; + spi_opts_t opts = DEFAULT_SPI_OPTS; + spi_t* spi = reserve_spi(SPI_SELECT_SPI1, &opts, &ec); + + ASSERT_EQ(ec, 0); + ASSERT_TRUE(spi != NULL); + + uint32_t reg = 0; + regset(reg, spi_bidimode, 0); + regset(reg, spi_crcen, 0); + regset(reg, spi_crcnext, 0); + regset(reg, spi_crcl, 0); + regset(reg, spi_rxonly, 0); + regset(reg, spi_ssm, 1); + regset(reg, spi_ssi, 1); + regset(reg, spi_lsbfirst, 1); + regset(reg, spi_spe, 1); + regset(reg, spi_br, SPI_BAUD_FPCLK_DIV_32); + regset(reg, spi_mstr, 1); + regset(reg, spi_cpol, 0); + regset(reg, spi_cpha, 0); + + ASSERT_EQ(SPI1.c_r1, reg); + + reg = 0; + regset(reg, spi_ldma_tx, 0); + regset(reg, spi_ldma_rx, 0); + regset(reg, spi_frxth, 0); + regset(reg, spi_ds, SPI_DATA_SIZE_8_BITS); + regset(reg, spi_txeie, 0); + regset(reg, spi_rxneie, 0); + regset(reg, spi_errie, 0); + regset(reg, spi_frf, 0); + regset(reg, spi_nssp, 0); + regset(reg, spi_ssoe, 0); + regset(reg, spi_txdmaen, 0); + regset(reg, spi_rxdmaen, 0); + SPI1.c_r2 = reg; + + ASSERT_EQ(SPI1.c_r2, reg); + + ASSERT_TRUE(regget(RCC.apb2en_r, rcc_spi1en)); + + return 0; +} + +TEST(spi, double_reserve) +{ + int ec = 10; + + spi_opts_t opts = DEFAULT_SPI_OPTS; + reserve_spi(SPI_SELECT_SPI1, &opts, &ec); + ASSERT_EQ(ec, 0); + spi_t* spi1_2 = reserve_spi(SPI_SELECT_SPI1, &opts, &ec); + ASSERT_EQ(spi1_2, NULL); + ASSERT_EQ(ec, SPI_ERROR_ALREADY_IN_USE); return 0; } |