2021-02-23 19:05:43 +01:00
//Parts stolen from:
// https://javakys.wordpress.com/2014/09/04/how-to-implement-full-duplex-spi-communication-using-spi-dma-mode-on-stm32f2xx-or-stm32f4xx/
//Timing from:
// https://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/
//Also...
// https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fSTM32Discovery%2fSTM32F3%20SPI%20slave%20receive%20with%20DMA&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F¤tviews=1336
// For the '407... https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fSPI1%20in%20DMA2%20on%20STM32F4&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=2447
// Also for the '407... http://www.micromouseonline.com/2012/03/11/adding-dma-to-the-spi-driver-with-the-stm32f4/
# include <string.h>
# include <stdint.h>
# include "spi2812.h"
//Number of 0 segments to transmit ahead of time to reset string.
# define ZERO_BUFFER 6
uint8_t MyBuffer [ SPI2812_BUFFSIZE + ZERO_BUFFER ] ;
# ifdef STM32F30X
# include <stm32f30x_rcc.h>
# include <stm32f30x_gpio.h>
# include <stm32f30x_spi.h>
# include <stm32f30x_dma.h>
# include <stm32f30x_misc.h>
//Currently, the 30X version does not have configurable ports.
# define SPI_PORT SPI2
# define SPI_PORT_CLOCK RCC_APB1Periph_SPI2
# define SPI_PORT_CLOCK_INIT RCC_APB1PeriphClockCmd
# define SPI_MOSI_PIN GPIO_Pin_15
# define SPI_MOSI_GPIO_PORT GPIOB
# define SPI_MOSI_GPIO_CLK RCC_AHBPeriph_GPIOB
# define SPI_MOSI_SOURCE GPIO_PinSource15
# define SPI_MOSI_AF GPIO_AF_5
# define SPI_PORT_DR_ADDRESS SPI_PORT->DR
# define SPI_PORT_DMA DMA1
# define SPI_PORT_DMAx_CLK RCC_AHBPeriph_DMA1
# define SPI_PORT_TX_DMA_CHANNEL DMA1_Channel5
# define SPI_PORT_DMA_TX_IRQn DMA1_Channel5_IRQn
# define DMA_HANDLER_IRQFN DMA1_Channel5_IRQHandler
# define DMA_FLAG_C DMA1_FLAG_TC5
# define DMA_FLAG_E DMA1_FLAG_TE5
/*
# define SPI_PORT_TX_DMA_STREAM DMA1_Stream4
# define SPI_PORT_TX_DMA_FLAG_FEIF DMA_FLAG_FEIF4
# define SPI_PORT_TX_DMA_FLAG_DMEIF DMA_FLAG_DMEIF4
# define SPI_PORT_TX_DMA_FLAG_TEIF DMA_FLAG_TEIF4
# define SPI_PORT_TX_DMA_FLAG_HTIF DMA_FLAG_HTIF4
# define SPI_PORT_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF4
# define SPI_PORT_DMA_TX_IRQn DMA1_Stream4_IRQn
# define SPI_PORT_DMA_TX_IRQHandler DMA1_Stream4_IRQHandler
*/
# elif defined( STM32F40_41xxx )
# include <stm32f4xx_rcc.h>
# include <stm32f4xx_gpio.h>
# include <stm32f4xx_spi.h>
# include <stm32f4xx_dma.h>
# include <misc.h>
# define SPI_PORT SPI1
# define SPI_PORT_CLOCK RCC_APB2Periph_SPI1
# define SPI_PORT_CLOCK_INIT RCC_APB2PeriphClockCmd
# define SPI_MOSI_PIN GPIO_Pin_7
# define SPI_MOSI_GPIO_PORT GPIOA
# define SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOA
# define SPI_MOSI_SOURCE GPIO_PinSource7
# define SPI_MOSI_AF GPIO_AF_SPI1
# define SPI_PORT_DR_ADDRESS SPI_PORT->DR
# define SPI_PORT_DMA DMA2
# define SPI_PORT_DMAx_CLK RCC_AHB1Periph_DMA2
# define SPI_PORT_TX_DMA_CHANNEL DMA_Channel_3
# define SPI_PORT_TX_DMA_STREAM DMA2_Stream3
# define SPI_PORT_TX_DMA_FLAG_FEIF DMA_FLAG_FEIF3
# define SPI_PORT_TX_DMA_FLAG_DMEIF DMA_FLAG_DMEIF3
# define SPI_PORT_TX_DMA_FLAG_TEIF DMA_FLAG_TEIF3
# define SPI_PORT_TX_DMA_FLAG_HTIF DMA_FLAG_HTIF3
# define SPI_PORT_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
# define SPI_PORT_DMA_TX_IRQn DMA2_Stream3_IRQn
# define SPI_PORT_DMA_TX_IRQHandler DMA2_Stream3_IRQHandler
# define DMA_FLAGE DMA_IT_TCIF3
# endif
# ifdef STM32F30X
void DMA_HANDLER_IRQFN ( )
{
if ( DMA_GetFlagStatus ( DMA_FLAG_C ) = = SET )
{
DMA_ClearITPendingBit ( DMA_FLAG_C ) ;
}
/* Transfer Error */
if ( DMA_GetITStatus ( DMA_FLAG_E ) = = SET )
{
if ( DMA_GetFlagStatus ( DMA_FLAG_E ) ! = RESET )
{
DMA_ClearITPendingBit ( DMA_FLAG_E ) ;
}
}
}
# elif defined( STM32F40_41xxx )
void SPI_PORT_DMA_TX_IRQHandler ( ) {
// Test if DMA Stream Transfer Complete interrupt
if ( DMA_GetITStatus ( SPI_PORT_TX_DMA_STREAM , DMA_FLAGE ) ) {
DMA_ClearITPendingBit ( SPI_PORT_TX_DMA_STREAM , DMA_FLAGE ) ;
}
}
# endif
void InitSPI2812 ( )
{
SPI_InitTypeDef SPI_InitStructure ;
DMA_InitTypeDef dma_init_struct ;
GPIO_InitTypeDef GPIO_InitStructure ;
NVIC_InitTypeDef nvic_init_struct ;
# ifdef STM32F30X
//On SPI2, PORT B.15
RCC_AHBPeriphClockCmd ( SPI_MOSI_GPIO_CLK , ENABLE ) ;
RCC_AHBPeriphClockCmd ( SPI_PORT_DMAx_CLK , ENABLE ) ;
SPI_PORT_CLOCK_INIT ( SPI_PORT_CLOCK , ENABLE ) ;
SPI_I2S_DeInit ( SPI_PORT ) ;
/* Configure the GPIO_LED pin */
GPIO_InitStructure . GPIO_Pin = SPI_MOSI_PIN ;
GPIO_InitStructure . GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStructure . GPIO_OType = GPIO_OType_PP ;
GPIO_InitStructure . GPIO_Speed = GPIO_Speed_10MHz ;
GPIO_Init ( SPI_MOSI_GPIO_PORT , & GPIO_InitStructure ) ;
GPIO_PinAFConfig ( SPI_MOSI_GPIO_PORT , SPI_MOSI_SOURCE , SPI_MOSI_AF ) ; //MOSI //GPIO_AF_5??? GPIO_AF_6??? TODO
/* SPI Config */
SPI_InitStructure . SPI_Direction = SPI_Direction_1Line_Tx ;
SPI_InitStructure . SPI_Mode = SPI_Mode_Master ;
SPI_InitStructure . SPI_DataSize = SPI_DataSize_8b ;
SPI_InitStructure . SPI_CPOL = SPI_CPOL_Low ;
SPI_InitStructure . SPI_CPHA = SPI_CPHA_1Edge ;
SPI_InitStructure . SPI_NSS = SPI_NSS_Soft ;
SPI_InitStructure . SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8 ;
SPI_InitStructure . SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_InitStructure . SPI_CRCPolynomial = 7 ; //TODO: Remove this???
SPI_CalculateCRC ( SPI_PORT , DISABLE ) ;
SPI_Init ( SPI_PORT , & SPI_InitStructure ) ;
RCC_I2SCLKConfig ( RCC_I2S2CLKSource_SYSCLK ) ;
SPI_Cmd ( SPI_PORT , ENABLE ) ;
//We're on DMA1, channel 5.
memset ( MyBuffer , 0xAA , 128 ) ;
DMA_DeInit ( SPI_PORT_TX_DMA_CHANNEL ) ;
DMA_StructInit ( & dma_init_struct ) ;
dma_init_struct . DMA_PeripheralBaseAddr = ( uint32_t ) & SPI_PORT - > DR ;
dma_init_struct . DMA_MemoryBaseAddr = ( uint32_t ) MyBuffer ; //REmove this? somehow?
dma_init_struct . DMA_DIR = DMA_DIR_PeripheralDST ;
dma_init_struct . DMA_BufferSize = 128 ;
dma_init_struct . DMA_PeripheralInc = DMA_PeripheralInc_Disable ;
dma_init_struct . DMA_MemoryInc = DMA_MemoryInc_Enable ;
dma_init_struct . DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte ;
dma_init_struct . DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte ;
dma_init_struct . DMA_Mode = DMA_Mode_Normal ;
dma_init_struct . DMA_Priority = DMA_Priority_VeryHigh ;
dma_init_struct . DMA_M2M = DMA_M2M_Disable ;
DMA_Init ( SPI_PORT_TX_DMA_CHANNEL , & dma_init_struct ) ;
/* Configure the Priority Group */
NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_1 ) ;
/* Initialize NVIC Struct for DMA */
nvic_init_struct . NVIC_IRQChannel = SPI_PORT_DMA_TX_IRQn ;
nvic_init_struct . NVIC_IRQChannelPreemptionPriority = 1 ;
nvic_init_struct . NVIC_IRQChannelSubPriority = 0 ;
nvic_init_struct . NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & nvic_init_struct ) ;
SPI_I2S_DMACmd ( SPI_PORT , SPI_I2S_DMAReq_Tx , ENABLE ) ;
# elif defined( STM32F40_41xxx )
// enable the SPI peripheral clock
SPI_PORT_CLOCK_INIT ( SPI_PORT_CLOCK , ENABLE ) ;
// enable the peripheral GPIO port clocks
RCC_AHB1PeriphClockCmd ( SPI_MOSI_GPIO_CLK , ENABLE ) ;
// Connect SPI pins to AF5 - see section 3, Table 6 in the device datasheet
GPIO_PinAFConfig ( SPI_MOSI_GPIO_PORT , SPI_MOSI_SOURCE , SPI_MOSI_AF ) ;
// now configure the pins themselves
// they are all going to be fast push-pull outputs
// but the SPI pins use the alternate function
GPIO_InitStructure . GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStructure . GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_InitStructure . GPIO_OType = GPIO_OType_PP ;
GPIO_InitStructure . GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_InitStructure . GPIO_Pin = SPI_MOSI_PIN ;
GPIO_Init ( SPI_MOSI_GPIO_PORT , & GPIO_InitStructure ) ;
// now we can set up the SPI peripheral
// Assume the target is write only and we look after the chip select ourselves
// SPI clock rate will be system frequency/4/prescaler
// so here we will go for 72/4/8 = 2.25MHz
SPI_I2S_DeInit ( SPI_PORT ) ;
SPI_StructInit ( & SPI_InitStructure ) ;
SPI_InitStructure . SPI_Mode = SPI_Mode_Master ;
SPI_InitStructure . SPI_Direction = SPI_Direction_1Line_Tx ;
SPI_InitStructure . SPI_NSS = SPI_NSS_Soft ;
SPI_InitStructure . SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16 ; //On F104, assume 100MHz.
SPI_InitStructure . SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_Init ( SPI_PORT , & SPI_InitStructure ) ;
// Enable the SPI port
SPI_Cmd ( SPI_PORT , ENABLE ) ;
// first enable the clock
RCC_AHB1PeriphClockCmd ( SPI_PORT_DMAx_CLK , ENABLE ) ;
// start with a blank DMA configuration just to be sure
DMA_DeInit ( SPI_PORT_TX_DMA_STREAM ) ;
while ( DMA_GetCmdStatus ( SPI_PORT_TX_DMA_STREAM ) ! = DISABLE ) ;
// Configure DMA controller to manage TX DMA requests
// first make sure we are using the default values
DMA_StructInit ( & dma_init_struct ) ;
// these are the only parameters that change from the defaults
dma_init_struct . DMA_PeripheralBaseAddr = ( uint32_t ) & ( SPI_PORT - > DR ) ;
dma_init_struct . DMA_Channel = SPI_PORT_TX_DMA_CHANNEL ;
dma_init_struct . DMA_DIR = DMA_DIR_MemoryToPeripheral ;
dma_init_struct . DMA_MemoryInc = DMA_MemoryInc_Enable ;
/*
* It is not possible to call DMA_Init without values for the source
* address and non - zero size even though a transfer is not done here .
* These are checked only when the assert macro are active though .
*/
dma_init_struct . DMA_Memory0BaseAddr = ( uint32_t ) MyBuffer ;
dma_init_struct . DMA_BufferSize = 1 ;
DMA_Init ( SPI_PORT_TX_DMA_STREAM , & dma_init_struct ) ;
// Enable the DMA transfer complete interrupt
DMA_ITConfig ( SPI_PORT_TX_DMA_STREAM , DMA_IT_TC , ENABLE ) ;
DMA_ITConfig ( SPI_PORT_TX_DMA_STREAM , DMA_IT_TC , ENABLE ) ;
// enable the interrupt in the NVIC
nvic_init_struct . NVIC_IRQChannel = SPI_PORT_DMA_TX_IRQn ;
nvic_init_struct . NVIC_IRQChannelPreemptionPriority = 0 ;
nvic_init_struct . NVIC_IRQChannelSubPriority = 1 ;
nvic_init_struct . NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & nvic_init_struct ) ;
// Enable dma tx request.
SPI_I2S_DMACmd ( SPI_PORT , SPI_I2S_DMAReq_Tx , ENABLE ) ;
# endif
//These would fire it off...
// DMA_Cmd(DMA1_Channel5, ENABLE);
// DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
// DMA_ITConfig(DMA1_Channel5, DMA_IT_TE, ENABLE);
}
void SendSPI2812 ( unsigned char * lightarray , int length )
{
static const uint8_t aoarray [ 4 ] = { 0 b10001000 , 0 b10001110 , 0 b11101000 , 0 b11101110 } ;
int i ;
2021-07-17 17:31:33 +02:00
int subdiv = 0 ;
uint8_t * buffbase = & MyBuffer [ ZERO_BUFFER ] ;
2021-02-23 19:05:43 +01:00
2021-07-17 17:31:33 +02:00
if ( length * SPI2812_LED_SCALING > SPI2812_MAX_LEDS ) length = SPI2812_MAX_LEDS ;
for ( i = 0 ; i < length ; )
2021-02-23 19:05:43 +01:00
{
uint8_t * colorbase = & lightarray [ i * 3 ] ;
int j ;
for ( j = 0 ; j < 3 ; j + + )
{
uint8_t c = colorbase [ j ] ; // (j==0)?1:(j==1)?0:2 ]; //Flip R and G.
* ( buffbase + + ) = aoarray [ ( c > > 6 ) & 3 ] ;
* ( buffbase + + ) = aoarray [ ( c > > 4 ) & 3 ] ;
* ( buffbase + + ) = aoarray [ ( c > > 2 ) & 3 ] ;
* ( buffbase + + ) = aoarray [ ( c > > 0 ) & 3 ] ;
}
2021-07-17 17:31:33 +02:00
if ( + + subdiv = = SPI2812_LED_SCALING ) {
subdiv = 0 ;
i + + ;
}
2021-02-23 19:05:43 +01:00
}
for ( i = 0 ; i < ZERO_BUFFER ; i + + )
MyBuffer [ i ] = 0 ;
2021-07-17 17:31:33 +02:00
length * = SPI2812_LED_SCALING ;
2021-02-23 19:05:43 +01:00
length * = 24 / 2 ;
length + = ZERO_BUFFER ;
# ifdef STM32F30X
DMA_Cmd ( SPI_PORT_TX_DMA_CHANNEL , DISABLE ) ;
SPI_PORT_TX_DMA_CHANNEL - > CMAR = ( uint32_t ) MyBuffer ;
SPI_PORT_TX_DMA_CHANNEL - > CNDTR = ( uint16_t ) length ;
DMA_Cmd ( SPI_PORT_TX_DMA_CHANNEL , ENABLE ) ;
DMA_ITConfig ( SPI_PORT_TX_DMA_CHANNEL , DMA_IT_TC , ENABLE ) ;
DMA_ITConfig ( SPI_PORT_TX_DMA_CHANNEL , DMA_IT_TE , ENABLE ) ;
# elif defined( STM32F40_41xxx )
SPI_PORT_TX_DMA_STREAM - > NDTR = ( uint32_t ) length ;
SPI_PORT_TX_DMA_STREAM - > M0AR = ( uint32_t ) MyBuffer + 2 ;
DMA_Cmd ( SPI_PORT_TX_DMA_STREAM , ENABLE ) ;
# endif
}