• STM32C0开发(1)----SPI 驱动WS2812灯珠


    概述

    本文介绍了如何使用STM32微控制器,结合STM32CubeMX配置工具和SPI通讯接口,来驱动和控制WS2812 LED灯带。这是一个集硬件连接、软件配置和编程开发于一体的综合性项目,目标是实现对LED灯带颜色和亮度的精确控制。

    最近在弄ST的课程,需要样片的可以加群申请:615061293 。

    在这里插入图片描述

    视频教学

    https://www.bilibili.com/video/BV1X94y157W8/

    STM32C0开发(1)----SPI 驱动WS2812灯珠

    样品申请

    https://www.wjx.top/vm/OhcKxJk.aspx#

    在这里插入图片描述

    源码下载

    https://download.csdn.net/download/qq_24312945/88489586

    芯片级联方法

    在这里插入图片描述

    芯片在上电复位以后,接收DIN端打来的数据,接收够24bit后,DO端口开始转发数据,供下一个芯片提供输入数据。在转发之前,DO口一直拉低。此时灯珠将不接收新的数据,内置RGB芯片根据接收到的24bit数据后产生的不同占空比信号,展现不同亮度。如果DIN端输入信号为RESET信号,芯片将接收到的数据送显示,芯片将在该信号结束后重新接收新的数据,在接收完开始的24bit数据后,通过DO口转发数据,灯珠在没有接收到RESET码前,RGB亮度保持不变,当接收到80us以上低电平RESET码后,灯珠内部RGB芯片将根据刚才接收到的24bit数据后产生的不同占空比信号,展现不同亮度。

    在这里插入图片描述

    数据传输

    在这里插入图片描述
    24bit数据结构:

    在这里插入图片描述

    时序

    时序波形图如下所示。

    在这里插入图片描述
    在这里插入图片描述

    SPI配置

    将SPI的配置速度设为6M,数据宽度设为8位。
    CPHA配置为第二边沿采样;在第一个跳变沿时,MOSI在空闲状态保持高电平,而在第二个跳变沿,它会保持上一次传输的最终电平。由于发送数据的最后一位总是低电平,这样配置可以避免WS2812误判。CPOL设置为高,确保SCK在空闲时保持高电平状态。

    在这里插入图片描述

    系统采用单总线协议,通过总线上高低电平的时长来区分逻辑0和1。WS2811工作在800kHz频率下,将SPI设置为6.4MHz—即其工作频率的8倍—可以确保每个字节(8位)正好对应一个逻辑位。在这种设置下,‘11111000’(0xF8)代表逻辑1,‘11000000’(0xC0)代表逻辑0。

    在这里插入图片描述

    逻辑0下发送的数据为11000000’(0xC0)。
    逻辑0下高电平位320ns。

    在这里插入图片描述

    逻辑0下低电平位1.01us。

    在这里插入图片描述

    逻辑1下发送的数据为‘11111000’(0xF8)。
    逻辑1下高电平位830ns。

    在这里插入图片描述
    逻辑1下低电平位500ns。
    在这里插入图片描述

    这种精确的时序配置和电平控制对于确保WS2812灯带的正确驱动至关重要,可以通过上述配置来优化SPI接口的性能,确保与WS2812的高效通信。

    CPHA配置

    CPHA配置为第二边沿采样下数据传输结束如下所示。

    在这里插入图片描述
    CPHA配置为第二边沿采样下数据传输结束如下所示。

    在这里插入图片描述

    由于RESET Code为低电平,且要大于80us,所以数据传输完毕必须为低电平。

    在这里插入图片描述

    代码

    在main.c中添加头文件。

    /* USER CODE BEGIN Includes */
    #include "ws2812.h"
    #include 
    /* USER CODE END Includes */
    
    • 1
    • 2
    • 3
    • 4

    在main.c中添加函数申明和移位操作。

    /* USER CODE BEGIN 0 */
    extern tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT];
    
    void move_Front()
    {
    	uint8_t i;
    	uint8_t temp[3];
    	temp[0] = gWs2812bDat[0].R;
    	temp[1] = gWs2812bDat[0].G;
    	temp[2] = gWs2812bDat[0].B;	
    	for (i = 0; i < WS2812B_AMOUNT-1; i++)
    	{
    		gWs2812bDat[i].R = gWs2812bDat[i+1].R;
    		gWs2812bDat[i].G = gWs2812bDat[i+1].G;
    		gWs2812bDat[i].B = gWs2812bDat[i+1].B;
    	}
    		gWs2812bDat[7].R = temp[0];
    		gWs2812bDat[7].G = temp[1];
    		gWs2812bDat[7].B = temp[2];
    }
    
    
    /* USER CODE END 0 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    添加初始化显示。

      /* USER CODE BEGIN 2 */
    	WS2812B_Task();
    	HAL_Delay(1000);
      /* USER CODE END 2 */
    
    • 1
    • 2
    • 3
    • 4

    添加流水灯。

      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
    	WS2812B_Task();	
    	move_Front();
    	HAL_Delay(100);
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ws2812.c

    #include "ws2812.h"
    #include "spi.h"
    
    //灯条显存SPI数据缓存
    uint8_t gWs2812bDat_SPI[WS2812B_AMOUNT * 24] = {0};	
    //灯条显存
    tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT] = {
    
    //R    G      B
    0XFF, 0X00, 0X00,	//0
    0X00, 0XFF, 0X00,	//1
    0X00, 0X00, 0XFF,	//2
    0X00, 0XFF, 0XFF,	//3
    0XFF, 0X00, 0XFF,	//4
    0XFF, 0XFF, 0X00,	//5
    0XFF, 0XFF, 0XFF,	//6
    0X00, 0X00, 0X00,	//7
    };
    		
    void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b)
    {
    	uint8_t *pR = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 8];
    	uint8_t *pG = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24];
    	uint8_t *pB = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 16];
    	
    	for(uint8_t i = 0; i <  8; i++) {
    		if(g & 0x80) {
    			*pG = CODE_1;
    		}           
    		else {           
    			*pG = CODE_0;
    		}           
    		if(r & 0x80) {           
    			*pR = CODE_1;
    		}           
    		else {           
    			*pR = CODE_0;
    		}           
    		if(b & 0x80) {           
    			*pB = CODE_1;
    		}           
    		else {           
    			*pB = CODE_0;
    		}
    		r <<= 1;
    		g <<= 1;
    		b <<= 1;
    		pR++;
    		pG++;
    		pB++;
    	}
    }
    void WS2812B_Task(void)
    {
    	uint8_t dat = 0;
    	
    	//将gWs2812bDat数据解析成SPI数据
    	for(uint8_t iLED = 0; iLED < WS2812B_AMOUNT; iLED++)
    	{
    		WS2812b_Set(iLED, gWs2812bDat[iLED].R, gWs2812bDat[iLED].G, gWs2812bDat[iLED].B);
    	}
    	//总线输出数据
    	HAL_SPI_Transmit(&hspi1, gWs2812bDat_SPI, sizeof(gWs2812bDat_SPI),0XFFFF);
    	//使总线输出低电平
    	HAL_SPI_Transmit(&hspi1, &dat, 1,0XFFFF);
    	//帧信号:一个大于50us的低电平
    	HAL_Delay(1);	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    ws2812.h

    #ifndef __WS2812_H__
    #define __WS2812_H__
    
    #include 
    
    //            编码 0 : 11000000
    #define CODE_0		0xC0
    //            编码 1 : 11111000
    #define CODE_1		0xF8
    /*ws2812b灯珠数量*/
    #define WS2812B_AMOUNT		8
    
    typedef struct
    {
    	uint8_t R;
    	uint8_t G;
    	uint8_t B;
    } tWs2812bCache_TypeDef;
    
    extern tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT];
    
    void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b);
    void WS2812B_Task(void);
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    算法竞赛入门【码蹄集进阶塔335题】(MT2321-2325)
    FFmpeg入门详解之13:everything简介
    基于Android+JavaWeb的医疗信息(电子病历)检索管理系统
    【前端学习】—多种方式实现数组拍平(十一)
    虹科示波器 | 汽车免拆检修 | 2010款奥迪A5车怠速时发动机偶尔自动熄火
    分布式事务保姆级教程
    MybatisPlus中queryWrapper的or的使用
    8-13外部排序-败者树
    3D打印:FDM打印湿度对打印件及打印机的影响和调整
    纯分享:将MySql的建表DDL转为PostgreSql的DDL
  • 原文地址:https://blog.csdn.net/qq_24312945/article/details/134151483