aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2020-12-04 23:16:31 -0700
committerJosh Rahm <joshuarahm@gmail.com>2020-12-04 23:16:31 -0700
commit7002cb8380406173407c9e8c8d16ebd670fff55c (patch)
treeb6ba377167ce193879347d692f087084f130c561
parent83deae717de8b940b0cb04d1d1989b0a4c250e35 (diff)
downloadstm32l4-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.h31
-rw-r--r--include/arch/stm32l4xxx/peripherals/spi.h58
-rw-r--r--include/arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc2
-rw-r--r--include/kern/common.h25
-rw-r--r--include/kern/spi/spi_manager.h179
-rw-r--r--src/kern/main.c207
-rw-r--r--src/kern/spi/spi_manager.c151
-rw-r--r--tests/test_spi.c63
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;
}