Skip to content

Instantly share code, notes, and snippets.

@NikiSchlifke
Created May 2, 2018 18:50
Show Gist options
  • Save NikiSchlifke/5deea20d921163f08d5624266ecdeb52 to your computer and use it in GitHub Desktop.
Save NikiSchlifke/5deea20d921163f08d5624266ecdeb52 to your computer and use it in GitHub Desktop.
Trying to use SPI to control ws2811 led driver chips
#include "stdint.h"
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/f1/timer.h>
#include <libopencm3/stm32/f1/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/f1/dma.h>
#include <libopencm3/stm32/f1/spi.h>
#include <ws2811.h>
static void clock_setup(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz();
/* Enable GPIOA, GPIOB, clock. */
rcc_periph_clock_enable(RCC_GPIOB);
/* Enable clocks for GPIO port A (for GPIO_USART2_TX) and USART2. */
rcc_periph_clock_enable(RCC_AFIO);
/* Enable SPI2 Periph and gpio clocks */
rcc_periph_clock_enable(RCC_SPI2);
/* Enable DMA1 clock */
rcc_periph_clock_enable(RCC_DMA1);
}
static void spi_setup(void) {
/* Configure GPIOs: SS=PA4, SCK=PA5, MISO=PA6 and MOSI=PA7 */
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO15 );
/* Reset SPI, SPI_CR1 register cleared, SPI is disabled */
spi_reset(SPI2);
/* Explicitly disable I2S in favour of SPI operation */
SPI2_I2SCFGR = 0;
/* Set up SPI in Master mode with:
* Clock baud rate: 1/32 of peripheral clock frequency
* Clock polarity: Idle High
* Clock phase: Data valid on 2nd clock pulse
* Data frame format: 8-bit
* Frame format: MSB First
*/
spi_init_master(SPI2, SPI_CR1_BAUDRATE_FPCLK_DIV_16, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
SPI_CR1_CPHA_CLK_TRANSITION_2, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST);
/*
* Set NSS management to software.
*
* Note:
* Setting nss high is very important, even if we are controlling the GPIO
* ourselves this bit needs to be at least set to 1, otherwise the spi
* peripheral will not send any data out.
*/
spi_enable_software_slave_management(SPI2);
spi_set_nss_high(SPI2);
/* Enable SPI2 periph. */
spi_enable(SPI2);
}
/**
* DMA part here see:
* https://github.com/libopencm3/libopencm3-examples/blob/master/examples/stm32/f1/lisa-m-2/spi_dma/spi_dma.c
*
Peripherals|Channel 1|Channel 2|Channel 3 |Channel 4 |Channel 5 |Channel 6 |Channel 7 |
ADC1 |ADC1 | | | | | | |
2 SPI/I S | |SPI1_RX |SPI1_TX |SPI/I2S2_RX |SPI/I2S2_TX| | |
USART | |USART3_TX|USART3_RX |USART1_TX |USART1_RX |USART2_RX |USART2_TX |
2 I C | | | |I2C2_TX |I2C2_RX |I2C1_TX |I2C1_RX |
TIM1 | |TIM1_CH1 |TIM1_CH2 |TIM1_CH4 TIM1_TRIG TIM1_COM|TIM1_UP |TIM1_CH3 | |
TIM2 |TIM2_CH3 |TIM2_UP | | |TIM2_CH1 | |TIM2_CH2 TIM2_CH4|
TIM3 | |TIM3_CH3 |TIM3_CH4 TIM3_UP| | |TIM3_CH1 TIM3_TRIG| |
TIM4 |TIM4_CH1 | | |TIM4_CH2 |TIM4_CH3 | |TIM4_UP |
*/
/* This is for the counter state flag */
typedef enum {
TX_UP_RX_HOLD = 0,
TX_HOLD_RX_UP,
TX_DOWN_RX_DOWN
} cnt_state;
/* This is a global spi state flag */
volatile typedef enum {
NONE = 0,
ONE,
DONE
} trans_status;
volatile trans_status transceive_status;
static void dma_int_enable(void) {
/* SPI2 TX on DMA1 Channel 3 */
nvic_set_priority(NVIC_DMA1_CHANNEL5_IRQ, 0);
nvic_enable_irq(NVIC_DMA1_CHANNEL5_IRQ);
}
static void dma_setup(void)
{
dma_int_enable();
}
static int spi_dma_transmit(uint8_t *tx_buf, uint16_t tx_len)
{
/* Check for 0 length in both tx and rx */
if (tx_len < 1) {
/* return -1 as error */
return -1;
}
/* Reset DMA channels*/
dma_channel_reset(DMA1, DMA_CHANNEL5);
/* Reset SPI data and status registers.
* Here we assume that the SPI peripheral is NOT
* busy any longer, i.e. the last activity was verified
* complete elsewhere in the program.
*/
volatile uint8_t temp_data __attribute__ ((unused));
while (SPI_SR(SPI2) & (SPI_SR_RXNE | SPI_SR_OVR)) {
temp_data = SPI_DR(SPI2);
}
/* Reset status flag appropriately (both 0 case caught above) */
transceive_status = NONE;
if (tx_len < 1) {
transceive_status = ONE;
}
/* Set up tx dma */
if (tx_len > 0) {
dma_set_peripheral_address(DMA1, DMA_CHANNEL5, (uint32_t)&SPI2_DR);
dma_set_memory_address(DMA1, DMA_CHANNEL5, (uint32_t)tx_buf);
dma_set_number_of_data(DMA1, DMA_CHANNEL5, tx_len);
dma_set_read_from_memory(DMA1, DMA_CHANNEL5);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL5);
dma_set_peripheral_size(DMA1, DMA_CHANNEL5, DMA_CCR_PSIZE_8BIT);
dma_set_memory_size(DMA1, DMA_CHANNEL5, DMA_CCR_MSIZE_8BIT);
dma_set_priority(DMA1, DMA_CHANNEL5, DMA_CCR_PL_HIGH);
//dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL5);
dma_enable_channel(DMA1, DMA_CHANNEL5);
/* Enable the spi transfer via dma
* This will immediately start the transmission,
*/
spi_enable_tx_dma(SPI2);
}
return 0;
}
/**
* SPI transmit completed with DMA
* */
void dma1_channel5_isr(void)
{
if ((DMA1_ISR &DMA_ISR_TCIF5) != 0) {
DMA1_IFCR |= DMA_IFCR_CTCIF5;
}
dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL5);
spi_disable_tx_dma(SPI2);
dma_disable_channel(DMA1, DMA_CHANNEL5);
/* Increment the status to indicate one of the transfers is complete */
transceive_status++;
}
/**
* Main program
* @return
*/
int main(void)
{
RgbColor white = {255, 255, 255};
RgbColor red = {255, 0, 0};
RgbColor green = {0, 255, 0};
RgbColor blue = {0, 0, 255};
SPIColor spiColor = { 0, 0, 0 };
SPIColor colors[2];
convertRGBColorToSPIColor(&white, &colors[0]);
convertRGBColorToSPIColor(&red, &colors[1]);
clock_setup();
spi_setup();
dma_setup();
uint8_t testBuffer[] = { 0, 140, 170, 255};
/* Fill Buffer and transmit */
while (1) {
fillBuffer((SPIColor **) &colors);
//spi_dma_transmit((uint8_t *) &spiBuffer.data , sizeof(spiBuffer.data));
spi_dma_transmit((uint8_t *) &testBuffer, sizeof(testBuffer));
while (transceive_status != DONE)
;
while (!(SPI_SR(SPI1) & SPI_SR_TXE))
;
while (SPI_SR(SPI1) & SPI_SR_BSY)
;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment