单片机(MCU)系列:SPI+DMA驱动WS2812B

本次主要参考:
https://blog.51cto.com/xfxuezhang/5873175

MCU:STM32F411CEU6,主频96M
外设:SPI2(引脚为PB12、PB13、PB14、PB15,波特率为3M),DMA1(数据流4,通道0)
WS2812B:接收波特率为750Kbps

说明:如果SPI2上挂有多个设备,需要用CS信号控制MOSI的锁存电路。DMA是防止发送相邻两个Byte时中间间隔过大。SPI的MOSI向WS2812B发送数据,每4个SPI的bit表示一个WS2812B的bit码。因为WS2812B要求先传输高位,SPI配置为MSB模式,于是有0b’1100表示WS2812B的1码;0b’1000表示0码。(如果所用主控支持,将SPI的波特率设置在3M~3.2M之间都可以)。每个WS2812B灯珠需要SPI发送12个字节。

相关代码大致如下(已全部测试通过):

1、使能相关时钟

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);  

2、初始化SPI2和DMA1,执行SPI2_DMA_Init(),即可完成初始化

#define SENDBUFF_SIZE (1024*2)			// 缓存长度  
static uint8_t TX_Buff[SENDBUFF_SIZE];	// 发送缓存

static void SPI2_PortInit(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |  GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_PinAFConfig(GPIOB, 13, GPIO_AF_SPI2);
	GPIO_PinAFConfig(GPIOB, 14, GPIO_AF_SPI2);
	GPIO_PinAFConfig(GPIOB, 15, GPIO_AF_SPI2);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	SPI2_BusEnd();
}

static void SPI2_TX_DMA_Init(void)
{
    /* 通道0,数据流4 */  
    // DMA结构体
    DMA_InitTypeDef DMA_InitStructure;
    /* 使能DMA时钟 */
    /* 复位初始化DMA数据流 */
    DMA_DeInit(DMA1_Stream4);
    /* 确保DMA数据流复位完成 */
    while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);

    /* 配置 DMA Stream */  
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    /* 外设地址 */  
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (uint32_t)(&(SPI2->DR));    
    /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;
    /* 方向:存储器到外设 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/       
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    /* 外设非增量模式 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    /* 存储器增量模式 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* 外设数据长度:8位 */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    /* 内存数据长度:8位 */
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    /* DMA模式:正常模式 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    /* 优先级:高 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    /* 禁用FIFO */
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    /* 外设突发单次传输 */
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    /* 存储器突发单次传输 */
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

    /* 初始化DMA Stream */     
    DMA_Init(DMA1_Stream4, &DMA_InitStructure);
	
    /* 开启传输完成中断  */     
    DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);
	
	DMA_Cmd(DMA1_Stream4, ENABLE);
}

static void SPI2_TX_DMA_NVICInit(void){
    // 中断初始化 
    // 中断结构体
    NVIC_InitTypeDef NVIC_InitStructure;
    /* DMA发送中断源 */  
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn; 
    /* 抢断优先级 */  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    /* 响应优先级 */  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;              
    /* 使能外部中断通道 */ 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                      
    /* 配置NVIC */        
    NVIC_Init(&NVIC_InitStructure);
}

void SPI2_DMA_Init(void){
	SPI_InitTypeDef spi_init_structure;
	
	SPI2_PortInit();
	
	spi_init_structure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    spi_init_structure.SPI_Mode = SPI_Mode_Master;
    spi_init_structure.SPI_DataSize = SPI_DataSize_8b;
    spi_init_structure.SPI_CPOL = SPI_CPOL_High;
    spi_init_structure.SPI_CPHA = SPI_CPHA_2Edge;
    spi_init_structure.SPI_NSS = SPI_NSS_Soft;
    spi_init_structure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    spi_init_structure.SPI_FirstBit = SPI_FirstBit_MSB;
    spi_init_structure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &spi_init_structure);

    SPI_Cmd(SPI2, ENABLE);
	
	SPI2_TX_DMA_Init();
	SPI2_TX_DMA_NVICInit();
}

void SPI2_BusStart(void){
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

void SPI2_BusEnd(void){
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

void SPI2_DMA_SendBuf(uint32_t SizeLen)
{   
	//SPI向DMA发出请求,DMA会通过仲裁器自动回应请求 
	SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
    // 关闭发送 DMA     
    DMA_Cmd(DMA1_Stream4, DISABLE); 
    // 设置发送的数据量    
    DMA_SetCurrDataCounter(DMA1_Stream4, SizeLen);
    // 清空数据
    SPI_I2S_ReceiveData(SPI2);       
    // 擦除DMA标志位 
    DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
    // 片选拉低,接收数据
    SPI2_BusStart(); 
    // 开启发送 DMA
    DMA_Cmd(DMA1_Stream4, ENABLE);  
}

uint8_t *Get_SPI2DMATxBuff(void){
	return TX_Buff;
}

void DMA1_Stream4_IRQHandler(void)
{
    // DMA 发送完成
    if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4)) 
    {
        // 清除DMA发送完成标志
        DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);  
        // 片选拉高,数据发送完毕
		SPI2_BusEnd();
	}
}

3、WS2812B处理,执行WS2812_Init()完成初始化,WS2812_CloseAll()设置所有WS2812B熄灭,Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data)设置对应灯珠的颜色(也就是第Num灯珠的RGB,Num是从0开始)。

#define SINGLEWS2812_BYTELEN 12//单个灯珠需要12个字节,勿动

#define WS2812_NUM 64//总共有多少灯珠

#define BIT_BLOCK	0x88//灯珠熄灭bit值
#define BIT_RESET	0x00//复位bit值
#define BIT_TEST	0x55

typedef union {
	uint32_t RGB;
	struct{
		uint8_t B;
		uint8_t G;
		uint8_t R;
		uint8_t Empty;
	};
}Color_T;

const uint8_t reset_bytelen = 120;
static uint16_t Send_AllByteLen = 0;
static uint8_t *tx_data;
static uint8_t *reset_data;
static uint8_t *rgb_data;

static const uint32_t DataTable[] = {
		0x88888888, 0x8c888888, 0xc8888888, 0xcc888888, 0x888c8888, 0x8c8c8888, 0xc88c8888, 0xcc8c8888,
		0x88c88888, 0x8cc88888, 0xc8c88888, 0xccc88888, 0x88cc8888, 0x8ccc8888, 0xc8cc8888, 0xcccc8888,
		0x88888c88, 0x8c888c88, 0xc8888c88, 0xcc888c88, 0x888c8c88, 0x8c8c8c88, 0xc88c8c88, 0xcc8c8c88,
		0x88c88c88, 0x8cc88c88, 0xc8c88c88, 0xccc88c88, 0x88cc8c88, 0x8ccc8c88, 0xc8cc8c88, 0xcccc8c88,
		0x8888c888, 0x8c88c888, 0xc888c888, 0xcc88c888, 0x888cc888, 0x8c8cc888, 0xc88cc888, 0xcc8cc888,
		0x88c8c888, 0x8cc8c888, 0xc8c8c888, 0xccc8c888, 0x88ccc888, 0x8cccc888, 0xc8ccc888, 0xccccc888,
		0x8888cc88, 0x8c88cc88, 0xc888cc88, 0xcc88cc88, 0x888ccc88, 0x8c8ccc88, 0xc88ccc88, 0xcc8ccc88,
		0x88c8cc88, 0x8cc8cc88, 0xc8c8cc88, 0xccc8cc88, 0x88cccc88, 0x8ccccc88, 0xc8cccc88, 0xcccccc88,
		0x8888888c, 0x8c88888c, 0xc888888c, 0xcc88888c, 0x888c888c, 0x8c8c888c, 0xc88c888c, 0xcc8c888c,
		0x88c8888c, 0x8cc8888c, 0xc8c8888c, 0xccc8888c, 0x88cc888c, 0x8ccc888c, 0xc8cc888c, 0xcccc888c,
		0x88888c8c, 0x8c888c8c, 0xc8888c8c, 0xcc888c8c, 0x888c8c8c, 0x8c8c8c8c, 0xc88c8c8c, 0xcc8c8c8c,
		0x88c88c8c, 0x8cc88c8c, 0xc8c88c8c, 0xccc88c8c, 0x88cc8c8c, 0x8ccc8c8c, 0xc8cc8c8c, 0xcccc8c8c,
		0x8888c88c, 0x8c88c88c, 0xc888c88c, 0xcc88c88c, 0x888cc88c, 0x8c8cc88c, 0xc88cc88c, 0xcc8cc88c,
		0x88c8c88c, 0x8cc8c88c, 0xc8c8c88c, 0xccc8c88c, 0x88ccc88c, 0x8cccc88c, 0xc8ccc88c, 0xccccc88c,
		0x8888cc8c, 0x8c88cc8c, 0xc888cc8c, 0xcc88cc8c, 0x888ccc8c, 0x8c8ccc8c, 0xc88ccc8c, 0xcc8ccc8c,
		0x88c8cc8c, 0x8cc8cc8c, 0xc8c8cc8c, 0xccc8cc8c, 0x88cccc8c, 0x8ccccc8c, 0xc8cccc8c, 0xcccccc8c,
		0x888888c8, 0x8c8888c8, 0xc88888c8, 0xcc8888c8, 0x888c88c8, 0x8c8c88c8, 0xc88c88c8, 0xcc8c88c8,
		0x88c888c8, 0x8cc888c8, 0xc8c888c8, 0xccc888c8, 0x88cc88c8, 0x8ccc88c8, 0xc8cc88c8, 0xcccc88c8,
		0x88888cc8, 0x8c888cc8, 0xc8888cc8, 0xcc888cc8, 0x888c8cc8, 0x8c8c8cc8, 0xc88c8cc8, 0xcc8c8cc8,
		0x88c88cc8, 0x8cc88cc8, 0xc8c88cc8, 0xccc88cc8, 0x88cc8cc8, 0x8ccc8cc8, 0xc8cc8cc8, 0xcccc8cc8,
		0x8888c8c8, 0x8c88c8c8, 0xc888c8c8, 0xcc88c8c8, 0x888cc8c8, 0x8c8cc8c8, 0xc88cc8c8, 0xcc8cc8c8,
		0x88c8c8c8, 0x8cc8c8c8, 0xc8c8c8c8, 0xccc8c8c8, 0x88ccc8c8, 0x8cccc8c8, 0xc8ccc8c8, 0xccccc8c8,
		0x8888ccc8, 0x8c88ccc8, 0xc888ccc8, 0xcc88ccc8, 0x888cccc8, 0x8c8cccc8, 0xc88cccc8, 0xcc8cccc8,
		0x88c8ccc8, 0x8cc8ccc8, 0xc8c8ccc8, 0xccc8ccc8, 0x88ccccc8, 0x8cccccc8, 0xc8ccccc8, 0xccccccc8,
		0x888888cc, 0x8c8888cc, 0xc88888cc, 0xcc8888cc, 0x888c88cc, 0x8c8c88cc, 0xc88c88cc, 0xcc8c88cc,
		0x88c888cc, 0x8cc888cc, 0xc8c888cc, 0xccc888cc, 0x88cc88cc, 0x8ccc88cc, 0xc8cc88cc, 0xcccc88cc,
		0x88888ccc, 0x8c888ccc, 0xc8888ccc, 0xcc888ccc, 0x888c8ccc, 0x8c8c8ccc, 0xc88c8ccc, 0xcc8c8ccc,
		0x88c88ccc, 0x8cc88ccc, 0xc8c88ccc, 0xccc88ccc, 0x88cc8ccc, 0x8ccc8ccc, 0xc8cc8ccc, 0xcccc8ccc,
		0x8888c8cc, 0x8c88c8cc, 0xc888c8cc, 0xcc88c8cc, 0x888cc8cc, 0x8c8cc8cc, 0xc88cc8cc, 0xcc8cc8cc,
		0x88c8c8cc, 0x8cc8c8cc, 0xc8c8c8cc, 0xccc8c8cc, 0x88ccc8cc, 0x8cccc8cc, 0xc8ccc8cc, 0xccccc8cc,
		0x8888cccc, 0x8c88cccc, 0xc888cccc, 0xcc88cccc, 0x888ccccc, 0x8c8ccccc, 0xc88ccccc, 0xcc8ccccc,
		0x88c8cccc, 0x8cc8cccc, 0xc8c8cccc, 0xccc8cccc, 0x88cccccc, 0x8ccccccc, 0xc8cccccc, 0xcccccccc,
	};

//为了提高执行效率用查表实现
static uint8_t CheckTableAnalyticDataBits(uint8_t Data, uint8_t *DataList_4S){
	*(uint32_t *)DataList_4S = DataTable[Data];
	return 0;
}

static uint8_t AnalyticRGBData(uint32_t RGB_Data, uint8_t *DataList_12S){//
	Color_T a;
	a.RGB = RGB_Data;
	CheckTableAnalyticDataBits(a.G, DataList_12S);
	CheckTableAnalyticDataBits(a.R, DataList_12S+4);
	CheckTableAnalyticDataBits(a.B, DataList_12S+8);
	return 0;
}

uint8_t Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data){
	AnalyticRGBData(RGB_Data, &rgb_data[Num * SINGLEWS2812_BYTELEN]);
	return 0;
}

void WS2812_Init(void){
	tx_data = Get_SPI2DMATxBuff();
	reset_data = tx_data;
	rgb_data = &tx_data[reset_bytelen];
	if(reset_bytelen > 0){//设置复位,也就是全部发送0。每次发数据之前都发送。
		memset(reset_data, BIT_RESET, reset_bytelen);
	}
	Send_AllByteLen = reset_bytelen + WS2812_NUM * SINGLEWS2812_BYTELEN;
}

//设置所有WS2812B熄灭
void WS2812_CloseAll(void){
	memset(rgb_data, BIT_BLOCK, WS2812_NUM * SINGLEWS2812_BYTELEN);
}

4、全部设置完相应位灯珠的颜色之后,执行以下语句,即可点亮所有设置的灯珠

SPI2_DMA_SendBuf(Send_AllByteLen);

QQ:763314235,会在第一时间处理。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值