From: Yaraslau Liashchynski <yliashchyn...@luxoft.com> --- bsps/arm/stm32l4/dma/stm32l4_dma.c | 183 +++++++++ bsps/arm/stm32l4/gpio/stm32l4_gpio.c | 145 +++++++ bsps/arm/stm32l4/include/bsp/stm32l4_dma.h | 105 ++++++ bsps/arm/stm32l4/include/bsp/stm32l4_gpio.h | 129 +++++++ bsps/arm/stm32l4/include/bsp/stm32l4_spi.h | 24 ++ bsps/arm/stm32l4/spi/stm32l4_spi.c | 564 ++++++++++++++++++++++++++++ c/src/lib/libbsp/arm/stm32l4/Makefile.am | 5 + 7 files changed, 1155 insertions(+) create mode 100644 bsps/arm/stm32l4/dma/stm32l4_dma.c create mode 100644 bsps/arm/stm32l4/gpio/stm32l4_gpio.c create mode 100644 bsps/arm/stm32l4/include/bsp/stm32l4_dma.h create mode 100644 bsps/arm/stm32l4/include/bsp/stm32l4_gpio.h create mode 100644 bsps/arm/stm32l4/include/bsp/stm32l4_spi.h create mode 100644 bsps/arm/stm32l4/spi/stm32l4_spi.c
diff --git a/bsps/arm/stm32l4/dma/stm32l4_dma.c b/bsps/arm/stm32l4/dma/stm32l4_dma.c new file mode 100644 index 0000000..d715e50 --- /dev/null +++ b/bsps/arm/stm32l4/dma/stm32l4_dma.c @@ -0,0 +1,183 @@ +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <stdarg.h> + +#include <bsp/stm32l4xx.h> +#include <bsp/stm32l4_dma.h> + +#define CELL_LEN 4 +#define REG_SZ 32 + +static struct dma_cb dma1_channel_cb_func[CHANNEL_MAX]; +static struct dma_cb dma2_channel_cb_func[CHANNEL_MAX]; + +static uint32_t get_cell_mask(uint32_t pos) +{ + for(uint8_t i = 0; i < REG_SZ; i += CELL_LEN) + { + uint8_t bit_pos = REG_SZ - __builtin_clz(pos) - 1; + + if((bit_pos < i + CELL_LEN) && (bit_pos >= i)) + { + uint32_t val = 0; + for(uint8_t j = 0; j < CELL_LEN; j++) + { + val |= 1 << j; + } + + return val << i; + } + } + + return 0; +} + +static void dma_channel_config(struct dma_driver *dma) +{ + struct dma_ch *channel = dma->channel_cfg; + struct dma_cb *cb_func = &channel->cb_func; + struct dma_cb *cb_ptr = (dma->dmax == DMA1)? dma1_channel_cb_func : + dma2_channel_cb_func; + const size_t num = channel->ch_num; + + cb_ptr[num].half_transfer = cb_func->half_transfer; + cb_ptr[num].transfer_error = cb_func->transfer_error; + cb_ptr[num].transfer_complete = cb_func->transfer_complete; + + DMA_Channel_TypeDef *ch = channel->ch; + + ch->CCR = channel->config; + ch->CPAR = channel->periph_addr; + + uint32_t mask = get_cell_mask(channel->ch_select); + + if(dma->dmax == DMA1) + { + DMA1_CSELR->CSELR &= ~mask; + DMA1_CSELR->CSELR |= channel->ch_select; + } else + { + DMA2_CSELR->CSELR &= ~mask; + DMA2_CSELR->CSELR |= channel->ch_select; + } +} + +static void dma_init(struct dma_driver *dma_driver) +{ + dma_channel_config(dma_driver); + + dma_driver->dmax->IFCR = 0; +} + +static inline void dma_rcc_enable(DMA_TypeDef *dmax) +{ + RCC->AHB1ENR |= (dmax == DMA1)? RCC_AHB1ENR_DMA1EN : RCC_AHB1ENR_DMA2EN; +} + +void dma_driver_config(void *dma_driver, ...) +{ + va_list list_ptr; + va_start(list_ptr, dma_driver); + struct dma_driver *driver = (struct dma_driver *)dma_driver; + + while(true) + { + if(driver == NULL) + { + break; + } + + dma_rcc_enable(driver->dmax); + dma_init(driver); + + driver = va_arg(list_ptr, struct dma_driver *); + } + + va_end(list_ptr); +} + +void DMA1_Channel5_IRQHandler(void) +{ + if((DMA1->ISR & DMA_ISR_TCIF5) && (DMA1_Channel5->CCR & DMA_CCR_TCIE)) { + if(dma1_channel_cb_func[CH5].transfer_complete == NULL) return; + + dma1_channel_cb_func[CH5].transfer_complete(); + } + + if((DMA1->ISR & DMA_ISR_HTIF5) && (DMA1_Channel5->CCR & DMA_CCR_HTIE)) { + if(dma1_channel_cb_func[CH5].half_transfer == NULL) return; + + dma1_channel_cb_func[CH5].half_transfer(); + } + + if((DMA1->ISR & DMA_ISR_TEIF5) && (DMA1_Channel5->CCR & DMA_CCR_TEIE)) { + if(dma1_channel_cb_func[CH5].transfer_error == NULL) return; + + dma1_channel_cb_func[CH5].transfer_error(); + } +} + +void DMA1_Channel4_IRQHandler(void) +{ + if((DMA1->ISR & DMA_ISR_TCIF4) && (DMA1_Channel4->CCR & DMA_CCR_TCIE)) { + if(dma1_channel_cb_func[CH4].transfer_complete == NULL) return; + + dma1_channel_cb_func[CH4].transfer_complete(); + } + + if((DMA1->ISR & DMA_ISR_HTIF4) && (DMA1_Channel4->CCR & DMA_CCR_HTIE)) { + if(dma1_channel_cb_func[CH4].half_transfer == NULL) return; + + dma1_channel_cb_func[CH4].half_transfer(); + } + + if((DMA1->ISR & DMA_ISR_TEIF4) && (DMA1_Channel4->CCR & DMA_CCR_TEIE)) { + if(dma1_channel_cb_func[CH4].transfer_error == NULL) return; + + dma1_channel_cb_func[CH4].transfer_error(); + } +} + +void DMA1_Channel3_IRQHandler(void) +{ + if((DMA1->ISR & DMA_ISR_TCIF3) && (DMA1_Channel3->CCR & DMA_CCR_TCIE)) { + if(dma1_channel_cb_func[CH3].transfer_complete == NULL) return; + + dma1_channel_cb_func[CH3].transfer_complete(); + } + + if((DMA1->ISR & DMA_ISR_HTIF3) && (DMA1_Channel3->CCR & DMA_CCR_HTIE)) { + if(dma1_channel_cb_func[CH3].half_transfer == NULL) return; + + dma1_channel_cb_func[CH3].half_transfer(); + } + + if((DMA1->ISR & DMA_ISR_TEIF3) && (DMA1_Channel3->CCR & DMA_CCR_TEIE)) { + if(dma1_channel_cb_func[CH3].transfer_error == NULL) return; + + dma1_channel_cb_func[CH3].transfer_error(); + } +} + +void DMA1_Channel2_IRQHandler(void) +{ + if((DMA1->ISR & DMA_ISR_TCIF2) && (DMA1_Channel2->CCR & DMA_CCR_TCIE)) { + if(dma1_channel_cb_func[CH2].transfer_complete == NULL) return; + + dma1_channel_cb_func[CH2].transfer_complete(); + } + + if((DMA1->ISR & DMA_ISR_HTIF2) && (DMA1_Channel2->CCR & DMA_CCR_HTIE)) { + if(dma1_channel_cb_func[CH2].half_transfer == NULL) return; + + dma1_channel_cb_func[CH2].half_transfer(); + } + + if((DMA1->ISR & DMA_ISR_TEIF2) && (DMA1_Channel2->CCR & DMA_CCR_TEIE)) { + if(dma1_channel_cb_func[CH2].transfer_error == NULL) return; + + dma1_channel_cb_func[CH2].transfer_error(); + } +} diff --git a/bsps/arm/stm32l4/gpio/stm32l4_gpio.c b/bsps/arm/stm32l4/gpio/stm32l4_gpio.c new file mode 100644 index 0000000..223d010 --- /dev/null +++ b/bsps/arm/stm32l4/gpio/stm32l4_gpio.c @@ -0,0 +1,145 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stdbool.h> +#include <assert.h> + +#include <bsp/stm32l4xx.h> +#include <bsp/stm32l4_gpio.h> + +static uint8_t gpio_get_pin_number(int pin) +{ + return ((PA0 <= pin) && (pin <= PA15))? pin - PA0 : + ((PB0 <= pin) && (pin <= PB15))? pin - PB0 : + ((PC0 <= pin) && (pin <= PC15))? pin - PC0 : + ((PD0 <= pin) && (pin <= PD15))? pin - PD0 : + ((PE0 <= pin) && (pin <= PE15))? pin - PE0 : + ((PF0 <= pin) && (pin <= PF15))? pin - PF0 : + ((PG0 <= pin) && (pin <= PG15))? pin - PG0 : + ((PH0 <= pin) && (pin <= PH15))? pin - PH0 : 0; +} + +static void gpio_set_mode(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + enum gpio_mode mode = gpio_driver->mode; + + gpiox->MODER &= ~(3 << (pin * 2)); + gpiox->MODER |= mode << (pin * 2); +} + +static void gpio_set_alter_func(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + enum gpio_alter_func func = gpio_driver->func; + + const size_t afr = pin > 7? (pin -= 8), 1 : 0; + + gpiox->AFR[afr] &= ~(0x0F << (pin * 4)); + gpiox->AFR[afr] |= func << (pin * 4); +} + +static void gpio_set_output_type(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + enum gpio_type otype = gpio_driver->otype; + + gpiox->OTYPER |= otype << pin; +} + +static void gpio_set_pull_mode(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + enum gpio_pull pull = gpio_driver->pull; + + gpiox->PUPDR &= ~(3 << (pin * 2)); + gpiox->PUPDR |= pull << (pin * 2); +} + +static void gpio_set_speed(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + enum gpio_speed speed = gpio_driver->speed; + + gpiox->OSPEEDR &= ~(3 << (pin * 2)); + gpiox->OSPEEDR |= speed << (pin * 2); +} + +static void gpio_set_output_low(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + + gpiox->BRR |= 1 << pin; + gpio_driver->state = GPIO_LOW; +} + +static enum gpio_level gpio_get_input_level( + struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + + return (gpiox->IDR & (1 << pin)); +} + +static void gpio_set_output_high(struct gpio_driver *gpio_driver) +{ + GPIO_TypeDef *gpiox = gpio_driver->gpiox; + uint8_t pin = gpio_get_pin_number(gpio_driver->pin); + + gpiox->BSRR |= 1 << pin; + gpio_driver->state = GPIO_HIGH; +} + +static void gpio_rcc_enable(GPIO_TypeDef *gpio) +{ + uint32_t mask = + (gpio == GPIOA)? RCC_AHB2ENR_GPIOAEN : + (gpio == GPIOB)? RCC_AHB2ENR_GPIOBEN : + (gpio == GPIOC)? RCC_AHB2ENR_GPIOCEN : + (gpio == GPIOD)? RCC_AHB2ENR_GPIODEN : + (gpio == GPIOE)? RCC_AHB2ENR_GPIOEEN : + (gpio == GPIOF)? RCC_AHB2ENR_GPIOFEN : + (gpio == GPIOG)? RCC_AHB2ENR_GPIOGEN : + (gpio == GPIOH)? RCC_AHB2ENR_GPIOHEN : 0; + + assert(mask != 0); + + RCC->AHB2ENR |= mask; +} + +static void gpio_driver_init(struct gpio_driver *driver) +{ + gpio_set_mode(driver); + gpio_set_speed(driver); + gpio_set_pull_mode(driver); + gpio_set_alter_func(driver); + gpio_set_output_type(driver); +} + +void gpio_driver_config(void *gpio_driver, ...) +{ + va_list list_ptr; + va_start(list_ptr, gpio_driver); + struct gpio_driver *driver = (struct gpio_driver *)gpio_driver; + + while(true) { + if(driver == NULL) break; + + gpio_rcc_enable(driver->gpiox); + driver->set_output_low = gpio_set_output_low; + driver->set_output_high = gpio_set_output_high; + driver->gpio_get_input_level = gpio_get_input_level; + gpio_driver_init(driver); + driver = va_arg(list_ptr, struct gpio_driver *); + } + + va_end(list_ptr); +} + diff --git a/bsps/arm/stm32l4/include/bsp/stm32l4_dma.h b/bsps/arm/stm32l4/include/bsp/stm32l4_dma.h new file mode 100644 index 0000000..34330c0 --- /dev/null +++ b/bsps/arm/stm32l4/include/bsp/stm32l4_dma.h @@ -0,0 +1,105 @@ +/** + * stm32l4_dma.h + * + * Created on: Jan 25, 2016 + * Author: leschinskii + */ + +#ifndef SRC_DRIVERS_INCLUDE_STM32L4_DMA_H_ +#define SRC_DRIVERS_INCLUDE_STM32L4_DMA_H_ + + +enum channel_numb { + CH1 = 0, + CH2, + CH3, + CH4, + CH5, + CH6, + CH7, + CHANNEL_MAX +}; + +struct dma_cb { + void(*transfer_complete)(void); + void(*half_transfer)(void); + void(*transfer_error)(void); +}; + +struct dma_ch { + enum channel_numb ch_num; + DMA_Channel_TypeDef *ch; + const char name[16]; + + struct dma_cb cb_func; + uint8_t irq_channel; + uint32_t ch_select; + uint32_t mem_addr; + uint32_t periph_addr; + uint32_t config; + uint32_t transf_sz; +}; + +struct dma_driver { + DMA_TypeDef *dmax; + struct dma_ch *channel_cfg; +}; + +void dma_driver_config(void *dma_driver, ...); + +void DMA1_Channel5_IRQHandler(void); +void DMA1_Channel4_IRQHandler(void); +void DMA1_Channel3_IRQHandler(void); +void DMA1_Channel2_IRQHandler(void); + +#define DMA1_CH1_ADC1 ((uint32_t)0x00) +#define DMA1_CH1_TIM2_CH3 ((uint32_t)0x04) +#define DMA1_CH1_TIM17_CH1_UP ((uint32_t)0x05) +#define DMA1_CH1_TIM4_CH1 ((uint32_t)0x06) +#define DMA1_CH2_ADC2 ((uint32_t)0x00) +#define DMA1_CH2_SPI1_RX ((uint32_t)0x10) +#define DMA1_CH2_USART3_TX ((uint32_t)0x20) +#define DMA1_CH2_I2C3_TX ((uint32_t)0x30) +#define DMA1_CH2_TIM2_UP ((uint32_t)0x40) +#define DMA1_CH2_TIM3_CH3 ((uint32_t)0x50) +#define DMA1_CH2_TIM1_CH1 ((uint32_t)0x70) +#define DMA1_CH3_ADC3 ((uint32_t)0x000) +#define DMA1_CH3_SPI1_TX ((uint32_t)0x100) +#define DMA1_CH3_USART3_RX ((uint32_t)0x200) +#define DMA1_CH3_I2C3_RX ((uint32_t)0x300) +#define DMA1_CH3_TIM16_CH1_UP ((uint32_t)0x400) +#define DMA1_CH3_TIM3_CH4_UP ((uint32_t)0x500) +#define DMA1_CH3_TIM6_UP_DAC1 ((uint32_t)0x600) +#define DMA1_CH3_TIM1_CH2 ((uint32_t)0x700) +#define DMA1_CH4_DFSDM0 ((uint32_t)0x0000) +#define DMA1_CH4_SPI2_RX ((uint32_t)0x1000) +#define DMA1_CH4_USART1_TX ((uint32_t)0x2000) +#define DMA1_CH4_I2C2_TX ((uint32_t)0x3000) +#define DMA1_CH4_TIM7_UP_DAC2 ((uint32_t)0x5000) +#define DMA1_CH4_TIM4_CH2 ((uint32_t)0x6000) +#define DMA1_CH4_TIM1_CH4_TRIG_COM ((uint32_t)0x7000) +#define DMA1_CH5_DFSDM1 ((uint32_t)0x00000) +#define DMA1_CH5_SPI2_TX ((uint32_t)0x10000) +#define DMA1_CH5_USART1_RX ((uint32_t)0x20000) +#define DMA1_CH5_I2C2_RX ((uint32_t)0x30000) +#define DMA1_CH5_TIM2_CH1 ((uint32_t)0x40000) +#define DMA1_CH5_QUADSPI ((uint32_t)0x50000) +#define DMA1_CH5_TIM4_CH3 ((uint32_t)0x60000) +#define DMA1_CH5_TIM15_CH1_UP_TRIG_COM ((uint32_t)0x70000) +#define DMA1_CH6_DFSDM2 ((uint32_t)0x000000) +#define DMA1_CH6_SAI2_A ((uint32_t)0x100000) +#define DMA1_CH6_USART2_RX ((uint32_t)0x200000) +#define DMA1_CH6_I2C1_TX ((uint32_t)0x300000) +#define DMA1_CH6_TIM16_CH1_UP ((uint32_t)0x400000) +#define DMA1_CH6_TIM3_CH1_TRIG ((uint32_t)0x500000) +#define DMA1_CH6_TIM1_UP ((uint32_t)0x700000) +#define DMA1_CH7_DFSDM3 ((uint32_t)0x0000000) +#define DMA1_CH7_SAI2_B ((uint32_t)0x1000000) +#define DMA1_CH7_USART2_TX ((uint32_t)0x2000000) +#define DMA1_CH7_I2C1_RX ((uint32_t)0x3000000) +#define DMA1_CH7_TIM2_CH2_CH4 ((uint32_t)0x4000000) +#define DMA1_CH7_TIM17_CH1_UP ((uint32_t)0x5000000) +#define DMA1_CH7_TIM4_UP ((uint32_t)0x6000000) +#define DMA1_CH7_TIM1_CH3 ((uint32_t)0x7000000) + +#endif /* SRC_DRIVERS_INCLUDE_STM32L4_DMA_H_ */ diff --git a/bsps/arm/stm32l4/include/bsp/stm32l4_gpio.h b/bsps/arm/stm32l4/include/bsp/stm32l4_gpio.h new file mode 100644 index 0000000..ee5e2ae --- /dev/null +++ b/bsps/arm/stm32l4/include/bsp/stm32l4_gpio.h @@ -0,0 +1,129 @@ +/** + * stm32l4_gpio.h + * + * Created on: Jan 15, 2016 + * Author: leschinskii + */ + +#ifndef SRC_DRIVERS_INCLUDE_STM32L4_GPIO_H_ +#define SRC_DRIVERS_INCLUDE_STM32L4_GPIO_H_ + +#include <bsp/stm32l4xx.h> + +enum gpio_level { + GPIO_LOW = 0, + GPIO_HIGH +}; + +enum gpio_mode { + GPIO_MODE_INPUT = 0, + GPIO_MODE_OUTPUT, + GPIO_MODE_ALTER, + GPIO_MODE_ANALOG +}; + +enum gpio_type { + GPIO_TYPE_PUSH_PULL = 0, + GPIO_TYPE_OPENDRAIN +}; + +enum gpio_speed { + GPIO_SPEED_2MHZ = 0, + GPIO_SPEED_25MHZ, + GPIO_SPEED_50MHZ, + GPIO_SPEED_100MHZ +}; + +enum gpio_pull { + GPIO_PULLUP_NO = 0, + GPIO_PULLUP, + GPIO_PULLDN +}; +enum gpio_alter_func { + GPIO_AF_SYS = 0, + GPIO_AF_TIM1 = 1, + GPIO_AF_TIM2 = 1, + GPIO_AF_TIM3 = 2, + GPIO_AF_TIM4 = 2, + GPIO_AF_TIM5 = 2, + GPIO_AF_TIM8 = 3, + GPIO_AF_TIM9 = 3, + GPIO_AF_TIM10 = 3, + GPIO_AF_TIM11 = 3, + GPIO_AF_I2C1 = 4, + GPIO_AF_I2C2 = 4, + GPIO_AF_I2C3 = 4, + GPIO_AF_SPI1 = 5, + GPIO_AF_SPI2 = 5, + GPIO_AF_I2S1 = 5, + GPIO_AF_I2S2ext = 5, + GPIO_AF_SPI3 = 6, + GPIO_AF_I2Sext = 6, + GPIO_AF_I2S3 = 6, + GPIO_AF_USART1 = 7, + GPIO_AF_USART2 = 7, + GPIO_AF_USART3 = 7, + GPIO_AF_I2S3ext = 7, + GPIO_AF_UART4 = 8, + GPIO_AF_UART5 = 8, + GPIO_AF_USART6 = 8, + GPIO_AF_CAN1 = 9, + GPIO_AF_CAN2 = 9, + GPIO_AF_TIM12 = 9, + GPIO_AF_TIM13 = 9, + GPIO_AF_TIM14 = 9, + GPIO_AF_OTG = 10, + GPIO_AF_ETH = 11, + GPIO_AF_FSMC = 12, + GPIO_AF_SDIO = 12, + GPIO_AF_OTG_FS = 12, + GPIO_AF_DCMI = 13, + GPIO_AF_14 = 14, + GPIO_AF_15 = 15, +}; + +enum gpio_pins { + PA0 = 0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, + PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, + + PB0 = 0x10, PB1, PB2, PB3, PB4, PB5, PB6, PB7, + PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, + + PC0 = 0x20, PC1, PC2, PC3, PC4, PC5, PC6, PC7, + PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, + + PD0 = 0x30, PD1, PD2, PD3, PD4, PD5, PD6, PD7, + PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, + + PE0 = 0x40, PE1, PE2, PE3, PE4, PE5, PE6, PE7, + PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15, + + PF0 = 0x50, PF1, PF2, PF3, PF4, PF5, PF6, PF7, + PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15, + + PG0 = 0x60, PG1, PG2, PG3, PG4, PG5, PG6, PG7, + PG8, PG9, PG10, PG11, PG12, PG13, PG14, PG15, + + PH0 = 0x70, PH1, PH2, PH3, PH4, PH5, PH6, PH7, + PH8, PH9, PH10, PH11, PH12, PH13, PH14, PH15 +}; + +struct gpio_driver { + GPIO_TypeDef *gpiox; + + enum gpio_pins pin; + enum gpio_mode mode; + enum gpio_pull pull; + enum gpio_type otype; + enum gpio_speed speed; + enum gpio_alter_func func; + enum gpio_level state; + + void (*set_output_low)(struct gpio_driver *gpio_driver); + void (*set_output_high)(struct gpio_driver *gpio_driver); + enum gpio_level (*gpio_get_input_level)(struct gpio_driver *gpio_driver); +}; + +void gpio_driver_config(void *gpio_driver, ...); + +#endif /* SRC_DRIVERS_INCLUDE_STM32L4_GPIO_H_ */ diff --git a/bsps/arm/stm32l4/include/bsp/stm32l4_spi.h b/bsps/arm/stm32l4/include/bsp/stm32l4_spi.h new file mode 100644 index 0000000..771b1cf --- /dev/null +++ b/bsps/arm/stm32l4/include/bsp/stm32l4_spi.h @@ -0,0 +1,24 @@ +#ifndef SRC_DRIVERS_INCLUDE_STM32L4_SPI_H_ +#define SRC_DRIVERS_INCLUDE_STM32L4_SPI_H_ + +#define MAX_SPI_FREQUENCY 500000 + +struct spi_driver +{ + void *spix; + + struct gpio_driver *mosi; + struct gpio_driver *miso; + struct gpio_driver *sck; + struct gpio_driver *css; + + uint8_t irq_n; + const char name[16]; + + struct dma_driver *dma_tx; + struct dma_driver *dma_rx; +}; + +int stm32l4_register_spi_1(void); + +#endif /* SRC_DRIVERS_INCLUDE_STM32L4_SPI_H_ */ diff --git a/bsps/arm/stm32l4/spi/stm32l4_spi.c b/bsps/arm/stm32l4/spi/stm32l4_spi.c new file mode 100644 index 0000000..8bad729 --- /dev/null +++ b/bsps/arm/stm32l4/spi/stm32l4_spi.c @@ -0,0 +1,564 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "bsp/stm32l4xx.h" + +#include <rtems/bspIo.h> + +#include <rtems/irq-extension.h> +#include <dev/spi/spi.h> + +#include <bsp/stm32l4_spi.h> +#include <bsp/stm32l4_gpio.h> +#include <bsp/stm32l4_dma.h> + +#define EIGHT_BIT_DATA_MASK (SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2) + +#define SIXTEEN_BIT_DATA_MASK (SPI_CR2_DS_0 | SPI_CR2_DS_1 | \ + SPI_CR2_DS_2 | SPI_CR2_DS_3) + +#define MAX_SPI_TXRX_BUFFER_LEN 32 +#define STM32L4_SPI_1_BUS_PATH "/dev/spi-1" +#define STM32L4_SPI_2_BUS_PATH "/dev/spi-2" + +static struct dma_driver spi1_dma_tx = { + .dmax = DMA1, + .channel_cfg = &(struct dma_ch) + { + .ch_num = CH3, + .irq_channel = DMA1_Channel3_IRQn, + .ch_select = DMA1_CH3_SPI1_TX, + .ch = DMA1_Channel3, + .periph_addr = (uint32_t)&SPI1->DR, + .config = DMA_CCR_TCIE | DMA_CCR_DIR | DMA_CCR_MSIZE_0 | + DMA_CCR_MINC | DMA_CCR_PSIZE_0 + } +}; + +static struct dma_driver spi1_dma_rx = { + .dmax = DMA1, + .channel_cfg = &(struct dma_ch) + { + .ch_num = CH2, + .irq_channel = DMA1_Channel2_IRQn, + .ch_select = DMA1_CH2_SPI1_RX, + .ch = DMA1_Channel2, + .periph_addr = (uint32_t)&SPI1->DR, + .config = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_PSIZE_0 | DMA_CCR_MSIZE_0 + } +}; + +static struct gpio_driver spi1_css = +{ + .gpiox = GPIOE, + .pin = PE12, + .mode = GPIO_MODE_ALTER, + .func = GPIO_AF_SPI1, + .pull = GPIO_PULLUP_NO, + .otype = GPIO_TYPE_PUSH_PULL, + .speed = GPIO_SPEED_50MHZ, +}; + +static struct gpio_driver spi1_sck = +{ + .gpiox = GPIOE, + .pin = PE13, + .mode = GPIO_MODE_ALTER, + .pull = GPIO_PULLUP_NO, + .otype = GPIO_TYPE_PUSH_PULL, + .speed = GPIO_SPEED_50MHZ, + .func = GPIO_AF_SPI1 +}; + +static struct gpio_driver spi1_miso = +{ + .gpiox = GPIOE, + .pin = PE14, + .mode = GPIO_MODE_ALTER, + .pull = GPIO_PULLUP_NO, + .otype = GPIO_TYPE_PUSH_PULL, + .speed = GPIO_SPEED_50MHZ, + .func = GPIO_AF_SPI1 +}; + +static struct gpio_driver spi1_mosi = +{ + .gpiox = GPIOE, + .pin = PE15, + .mode = GPIO_MODE_ALTER, + .pull = GPIO_PULLUP_NO, + .otype = GPIO_TYPE_PUSH_PULL, + .speed = GPIO_SPEED_50MHZ, + .func = GPIO_AF_SPI1 +}; + +static struct spi_driver spi1 = +{ + .spix = SPI1, + .mosi = &spi1_mosi, + .miso = &spi1_miso, + .sck = &spi1_sck, + .css = &spi1_css, + + .irq_n = SPI1_IRQn, + .name = "STM32L4_SPI1", + + .dma_tx = &spi1_dma_tx, + .dma_rx = &spi1_dma_rx +}; + +enum spi_major_num +{ + SPI_NUM_INVALID = -1, + SPI1_NUM = 0, + SPI2_NUM, + SPI_NUM_TOTAL +}; + +struct stm32l4_spi_bus +{ + struct spi_bus base; + struct spi_driver driver; + + rtems_binary_semaphore sem; + + const struct spi_ioc_transfer *msg; + size_t messages_left; + size_t msg_count; +}; + +static struct stm32l4_spi_bus* spi_bus_table[SPI_NUM_TOTAL]; +static const uint8_t spi_dummy_buffer[MAX_SPI_TXRX_BUFFER_LEN]; + +static void spi1_irq_handler(void); +static void spi2_irq_handler(void); +static void spi1_dma_rx_complete_handler(void); +static void spi1_dma_tx_complete_handler(void); +static void spi2_dma_rx_complete_handler(void); +static void spi2_dma_tx_complete_handler(void); + +static inline enum spi_major_num get_spi_major_number(const SPI_TypeDef *spix) +{ + return (spix == SPI1)? SPI1_NUM : + (spix == SPI2)? SPI2_NUM : SPI_NUM_INVALID; +} + +static int spi_install_corresponding_irq_handlers( + const struct stm32l4_spi_bus *bus) +{ + enum spi_major_num num = get_spi_major_number(bus->driver.spix); + + if(num == SPI_NUM_INVALID) + { + return -1; + } + + rtems_interrupt_handler_install( + (rtems_vector_number) bus->driver.irq_n, + bus->driver.name, + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) ((num == SPI1_NUM)? spi1_irq_handler : + spi2_irq_handler), + NULL); + + rtems_interrupt_handler_install( + (rtems_vector_number) bus->driver.dma_tx->channel_cfg->irq_channel, + bus->driver.dma_tx->channel_cfg->name, + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) ((num == SPI1_NUM)? DMA1_Channel3_IRQHandler : + DMA1_Channel5_IRQHandler), + NULL); + + rtems_interrupt_handler_install( + (rtems_vector_number) bus->driver.dma_rx->channel_cfg->irq_channel, + bus->driver.dma_rx->channel_cfg->name, + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) ((num == SPI1_NUM)? DMA1_Channel2_IRQHandler : + DMA1_Channel4_IRQHandler), + NULL); + + return 0; +} + +static int spi_rcc_enable(const struct spi_driver *dev) +{ + enum spi_major_num num = get_spi_major_number(dev->spix); + + if(num == SPI_NUM_INVALID) + { + return -1; + } + + if(num == SPI1_NUM) + { + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + + return 1; + } + + RCC->APB1ENR1 |= RCC_APB1ENR1_SPI2EN; + + return 1; +} + +static void spi_configure(struct stm32l4_spi_bus *bus) +{ + struct spi_driver *spi_driver = &bus->driver; + + gpio_driver_config( + spi_driver->sck, + spi_driver->mosi, + spi_driver->miso, + spi_driver->css, + NULL); + + spi_rcc_enable(spi_driver); + + + dma_driver_config(spi_driver->dma_tx, spi_driver->dma_rx, NULL); + + SPI_TypeDef *spix = (SPI_TypeDef *)bus->driver.spix; + + uint32_t bits_per_word = (bus->base.bits_per_word == 16)? + SIXTEEN_BIT_DATA_MASK : EIGHT_BIT_DATA_MASK; + + spix->CR2 = bits_per_word | SPI_CR2_NSSP; + + spix->CR1 = SPI_CR1_BR_0 | SPI_CR1_BR_2 | + SPI_CR1_MSTR | SPI_CR1_SPE; +} + +static void spi_disable(struct spi_driver *spi_driver) +{ + SPI_TypeDef *spix = (SPI_TypeDef *)spi_driver->spix; + + spix->CR1 &= ~SPI_CR1_SPE; +} + +static void spi_css_deselect(struct spi_driver *spi_driver) +{ + struct gpio_driver *css = spi_driver->css; + + css->set_output_high(css); +} + +static void spi_css_select(struct spi_driver *spi_driver) +{ + struct gpio_driver *css = spi_driver->css; + + css->set_output_low(css); +} + +static void spi_destroy(spi_bus *base) +{ + struct stm32l4_spi_bus *bus = (struct stm32l4_spi_bus *)base; + + spi_disable(&bus->driver); + + spi_bus_destroy_and_free(&bus->base); + rtems_recursive_mutex_destroy(&bus->base.mutex); +} + +static int spi_check_configuration( + struct stm32l4_spi_bus *bus, + const spi_ioc_transfer *msg) +{ + bool config_is_invalid = + msg->speed_hz > bus->base.max_speed_hz || + msg->bits_per_word < 8 || + msg->bits_per_word > 16 || + msg->mode > SPI_MODE_3; + + if(config_is_invalid == true) + { + return -EINVAL; + } + + bool config_is_unset = + msg->bits_per_word != bus->base.bits_per_word || + msg->delay_usecs != bus->base.delay_usecs || + msg->speed_hz != bus->base.speed_hz || + msg->mode != bus->base.mode || + msg->cs != bus->base.cs; + + if(config_is_unset == true) + { + bus->base.mode = msg->mode; + bus->base.speed_hz = msg->speed_hz; + bus->base.bits_per_word = msg->bits_per_word; + bus->base.cs = msg->cs; + bus->base.delay_usecs = msg->delay_usecs; + spi_configure(bus); + } + + return 0; +} + +static int spi_do_transfer( + struct stm32l4_spi_bus *bus, + const spi_ioc_transfer *msgs, + const size_t idx) +{ + bool data_is_invalid = + ((msgs[idx].tx_buf == NULL) && + (msgs[idx].rx_buf == NULL)) || + (msgs[idx].len > MAX_SPI_TXRX_BUFFER_LEN); + + int ret = 0; + + if(data_is_invalid == true) + { + return -EINVAL; + } + + ret = spi_check_configuration(bus, &msgs[idx]); + if(ret != 0) + { + return -EINVAL; + } + + spi_css_select(&bus->driver); + + SPI_TypeDef *spix = bus->driver.spix; + + struct dma_ch *rx_ch = bus->driver.dma_rx->channel_cfg; + struct dma_ch *tx_ch = bus->driver.dma_tx->channel_cfg; + + tx_ch->ch->CMAR = (uint32_t)((msgs[idx].tx_buf == NULL)? + spi_dummy_buffer : msgs[idx].tx_buf); + tx_ch->ch->CNDTR = msgs[idx].len; + + rx_ch->ch->CMAR = (uint32_t)((msgs[idx].rx_buf == NULL)? + spi_dummy_buffer : msgs[idx].rx_buf); + rx_ch->ch->CNDTR = msgs[idx].len; + + spix->CR2 |= SPI_CR2_TXEIE; + + return ret; +} + +static int spi_setup_trasfer( + struct stm32l4_spi_bus *bus, + const spi_ioc_transfer *msgs, + const uint32_t msg_count) +{ + bool bus_is_broken = (bus == NULL) || + (msg_count < bus->messages_left) || + (msg_count == 0); + int ret = 0; + + if(bus_is_broken == true) + { + return -EINVAL; + } + + if(msgs->cs_change == true) + { + spi_css_deselect(&bus->driver); + } + + if(bus->messages_left > 0) + { + const size_t msg_idx = msg_count - bus->messages_left; + + ret = spi_do_transfer(bus, msgs, msg_idx); + + if(ret != 0) + { + rtems_binary_semaphore_post(&bus->sem); + return ret; + } + + bus->messages_left--; + } + else + { + rtems_binary_semaphore_post(&bus->sem); + } + + return ret; +} + +static int spi_transfer( + spi_bus *base, + const spi_ioc_transfer *msgs, + uint32_t msg_count) +{ + struct stm32l4_spi_bus *bus = (struct stm32l4_spi_bus *)base; + + SPI_TypeDef *spix = (SPI_TypeDef *)bus->driver.spix; + enum spi_major_num num = get_spi_major_number(spix); + + bus->msg = &msgs[0]; + spi_bus_table[num] = bus; + spi_bus_table[num]->msg_count = msg_count; + spi_bus_table[num]->messages_left = msg_count; + + int msg_error = spi_setup_trasfer( + spi_bus_table[num], + spi_bus_table[num]->msg, + spi_bus_table[num]->messages_left); + + rtems_binary_semaphore_wait(&bus->sem); + + return (msg_error == 0)? 0 : -EINVAL; +} + +static int spi_setup(spi_bus *base) +{ + struct stm32l4_spi_bus *bus = (struct stm32l4_spi_bus *)base; + + bool config_is_unset = + bus->base.speed_hz > MAX_SPI_FREQUENCY || + bus->base.bits_per_word < 8 || + bus->base.bits_per_word > 16; + + if(config_is_unset == true) + { + return -EINVAL; + } + + spi_configure(bus); + + return 0; +} + +static void spi1_dma_tx_complete_handler(void) +{ + DMA1->IFCR |= DMA_IFCR_CTCIF3; + DMA1_Channel3->CCR &= ~DMA_CCR_EN; + + SPI1->CR2 &= ~SPI_CR2_TXEIE; + SPI1->CR2 &= ~SPI_CR2_TXDMAEN; + SPI1->CR2 |= SPI_CR2_RXNEIE; +} + +static void spi1_dma_rx_complete_handler(void) +{ + DMA1->IFCR |= DMA_IFCR_CTCIF2; + DMA1_Channel2->CCR &= ~DMA_CCR_EN; + + SPI1->CR2 &= ~SPI_CR2_RXDMAEN; + SPI1->CR2 &= ~SPI_CR2_RXNEIE; + + spi_setup_trasfer( + spi_bus_table[SPI1_NUM], + spi_bus_table[SPI1_NUM]->msg, + spi_bus_table[SPI1_NUM]->msg_count); +} + +static void spi1_irq_handler(void) +{ + uint32_t sr = SPI1->SR; + uint32_t cr = SPI1->CR2; + + if((sr & SPI_SR_TXE) && (cr & SPI_CR2_TXEIE)) + { + SPI1->CR2 |= SPI_CR2_TXDMAEN; + DMA1_Channel3->CCR |= DMA_CCR_EN; + } + + if((sr & SPI_SR_RXNE) && (cr & SPI_CR2_RXNEIE)) + { + SPI1->CR2 |= SPI_CR2_RXDMAEN; + DMA1_Channel2->CCR |= DMA_CCR_EN; + } + +} + +static void spi2_dma_tx_complete_handler(void) +{ + DMA1->IFCR |= DMA_IFCR_CTCIF5; + DMA1_Channel5->CCR &= ~DMA_CCR_EN; + + SPI2->CR2 &= ~SPI_CR2_TXEIE; + SPI2->CR2 &= ~SPI_CR2_TXDMAEN; + SPI2->CR2 |= SPI_CR2_RXNEIE; +} + +static void spi2_dma_rx_complete_handler(void) +{ + DMA1->IFCR |= DMA_IFCR_CTCIF4; + DMA1_Channel4->CCR &= ~DMA_CCR_EN; + + SPI2->CR2 &= ~SPI_CR2_RXDMAEN; + SPI2->CR2 &= ~SPI_CR2_RXNEIE; + + spi_setup_trasfer( + spi_bus_table[SPI2_NUM], + spi_bus_table[SPI2_NUM]->msg, + spi_bus_table[SPI2_NUM]->msg_count); +} + +static void spi2_irq_handler(void) +{ + uint32_t sr = SPI2->SR; + uint32_t cr = SPI2->CR2; + + if((sr & SPI_SR_TXE) && (cr & SPI_CR2_TXEIE)) + { + SPI2->CR2 |= SPI_CR2_TXDMAEN; + DMA1_Channel5->CCR |= DMA_CCR_EN; + } + + if((sr & SPI_SR_RXNE) && (cr & SPI_CR2_RXNEIE)) + { + SPI2->CR2 |= SPI_CR2_RXDMAEN; + DMA1_Channel4->CCR |= DMA_CCR_EN; + } +} + +static int stm32l4_spi_bus_register( + const char *spi_bus_path, + const struct spi_driver *spi_driver) +{ + struct stm32l4_spi_bus *bus; + + bus = (struct stm32l4_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus)); + if(bus == NULL) + { + printk("[%s]:%d\n", __FUNCTION__, __LINE__); + return -1; + } + + bus->base.transfer = spi_transfer; + bus->base.destroy = spi_destroy; + bus->base.setup = spi_setup; + bus->base.max_speed_hz = 500000; + bus->base.bits_per_word = 8; + bus->base.speed_hz = bus->base.max_speed_hz; + bus->base.delay_usecs = 1; + bus->base.cs = 1; + + memcpy(&bus->driver, spi_driver, sizeof(bus->driver)); + + enum spi_major_num num = get_spi_major_number(bus->driver.spix); + + if(num == SPI_NUM_INVALID) + { + return -1; + } + + bus->driver.dma_tx->channel_cfg->cb_func.transfer_complete = + (num == SPI1_NUM)? spi1_dma_tx_complete_handler : + spi2_dma_tx_complete_handler; + + bus->driver.dma_rx->channel_cfg->cb_func.transfer_complete = + (num == SPI1_NUM)? spi1_dma_rx_complete_handler : + spi2_dma_rx_complete_handler; + + spi_install_corresponding_irq_handlers(bus); + + rtems_binary_semaphore_init(&bus->sem, "STM32L4 SPI"); + spi_configure(bus); + + return spi_bus_register(&bus->base, spi_bus_path); +} + +int stm32l4_register_spi_1(void) +{ + int ret = stm32l4_spi_bus_register(STM32L4_SPI_1_BUS_PATH , &spi1); + + return ret; +} diff --git a/c/src/lib/libbsp/arm/stm32l4/Makefile.am b/c/src/lib/libbsp/arm/stm32l4/Makefile.am index 4c8b78c..a9e43ff 100644 --- a/c/src/lib/libbsp/arm/stm32l4/Makefile.am +++ b/c/src/lib/libbsp/arm/stm32l4/Makefile.am @@ -53,6 +53,11 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/irq/irq-dispatch-armv # Console librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/stm32l4/console/usart.c +# BSP +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/stm32l4/spi/stm32l4_spi.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/stm32l4/gpio/stm32l4_gpio.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/stm32l4/dma/stm32l4_dma.c + # Clock librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/clock/clock-armv7m.c -- 2.7.4 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel