STM32CubeMX-SPI+DMA 驱动 2812 灯带

STM32CubeMX-SPI+DMA 驱动 2812 灯带,第1张

文章目录
  • STM32CubeMX-SPI+DMA 驱动 2812 灯带

    • 一、初始准备

      • 1.硬件平台

    • 二、 *** 作步骤

      • 1.CubeMX生成初始化代码
        • 1.1 建立工程(通用步骤)
        • 1.2配置SPI和DMA外设
        • 1.3生成代码(通用步骤)
      • 2.编写代码
      • 3.程序下载,观察现象(通用步骤)

    • 三、实验现象

STM32CubeMX-SPI+DMA 驱动 2812 灯带

本章内容使用 STM32CubeMX 软件配置 STM32F407ZGT6 开发板,使用 SPI + DMA 方式来驱动 WS2812 彩灯

教程包含通用步骤以及专用步骤,其中,通用步骤为STM32CubeMX配置其他外设工程的通用 *** 作,STM32CubeMX系列教程基本通用,专用 *** 作则是针对当前工程进行的配置


一、初始准备 1.硬件平台

主控使用正点原子STM32F4探索者:

WS2812 灯环:

WS2812 是一个单总线控制的 RGB 彩灯,,通过单根总线向其写入数据,通过写入的数据控制 RGB 颜色,因为是单总线,所以通过总线上高低电平时间长短的不同来区分 0 和 1,手册中关于 0 和 1 的区分如下:

具体时间对应如下:

数据发送速度可达 800Kbps,就是 1.25us 发送一位数据,因为协议有一定的兼容性,所以实际上一个位的周期在1.25us±300ns之间都能识别到,因为是 us 级延时,所以时间要控制精准很难,因此我们借助 SPI 来控制 WS2812,我们用 SPI 的 MOSI 接口的一个 Byte(8位)模拟 WS2812 的一个位,比如下面的 SPI 我 设置的 5.25M 速率,一个字节约为 1.5us,所以可以通过发送了一个字节的数据控制电平时间,然后模拟 0 和 1 到 WS2812,比如下面发送的 0xF8

高电平时间 950ns,低电平时间我们不用管,只要整体传输速率在 2812 范围内就行就行,2812 识别 0 和 1 主要还是看高电平时间,所以下面这个 SPI 发的数据是 0xF8 代表的就是 2812 的 1

同理,当发送的数据是 0xC0 时这代表 0

到此,我们就可以通过 SPI 发送的不同数据来发送 0 和 1 到 WS2812,SPI 数据发送 0 和 1的原理明白了,WS2812 控制等的颜色需要 24 位,分别对应 G、R、B 的三个颜色的值,当我们发完一个灯的 RGB 值后,继续发送就会切换到下一个灯的 RGB 色彩控制,循环下去,所以 WS2812 可以无线套娃,可 DIY 性很高

但我们仔细想想,发送 24 位控制 1 个灯,相当于 SPI 要发送 24 个字节去控制一个灯,这对资源的占用太大了,严重占用 CPU 资源,所以我们一般用 SPI 控制灯的时候顺便加上 DMA,减小 CPU 压力


二、 *** 作步骤 1.CubeMX生成初始化代码 1.1 建立工程(通用步骤)

  • 芯片选择

打开cube软件,选择从芯片来创建工程,一般开发都是使用这个来开发,有的时候也可能使用另外两个,但不多,第二个基于ST提供的开发板创建工程,针对性高,第三个则选择ST提供的例程来创建工程

F4探索者的主控为STM32F407ZGT6,所以在搜索框找到STM32F407ZG后点击具体芯片,再开始工程

  • 配置时钟源

我们点开SystemCore(系统内核设置),再点击RCC配置HSE和LSE时钟源,这里我都选择使用外部时钟,配置后,我们可以看到右边芯片引脚分配图的两个时钟源引脚点亮,表示时钟配置为外部源

  • 配置时钟树

我们进入ClockConfiguration配置时钟树,使时钟的输入路径和大小符合我们预期,探索者的晶振和时钟倍频如下

一般配置正确时颜色蓝白为主,配置错误时则会出现紫色,提示我们要修改值

具体时钟树的了解可以看我很久之前的文章,有做一些分析

CSDN文章链接-时钟树分析

1.2配置SPI和DMA外设
  • 开启 SPI1,配置为只有主机发送,然后配置 SPI 为 5.25M 跳变沿选第二个(2Edge)

因为跳变沿为 1 的话,MOSI 的空闲电平为高电平,为 2 的话则会延续上次发送的最后电平,我们发送数据的末尾都是低电平,这样 WS2812 不会误判

  • 开启 DMA,参数保持默认

1.3生成代码(通用步骤)

点击进入Project Manager 配置生成工程的名字,存储路径**(不要有中文)**以及编译器,这里我们选MDK-ARM(Keil被收购后改名)

配置生成选项,主要为下面三大块,第一个我们选择只拷贝必要的库,第二个选择为每个外设生成.c和.h文件,保存之前的用户代码,以及删除之前的生成代码,第三个不选择

PS:用户代码段是一下注释之间的代码,只有原始的用户代码段注释才有效,用户自己添加的无效

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

最后点击生成代码

2.编写代码

在工程目录创建 Hardware 文件夹,下面在创建个 ws2812 文件夹,里面放上如下文件

在 MDK 工程里面导入文件和其路径

然后编写驱动代码,这里我参考了这篇文章,在其基础上进行修改,改动的挺多,完善内存使用,同时将刷新和设置隔离,方便我们可以在 RTOS 中使用

WS2812b幻彩ARGB灯珠的STM32F103的CPU-SPI方式驱动-作者:爱莎女王

先写头文件代码

#ifndef __WS2812_H
#define __WS2812_H

#include "main.h"

typedef struct				//颜色结构体
{
  uint8_t R;
  uint8_t G;
  uint8_t B;
}RGBColor_TypeDef;

#define RGB_NUM    24	// RGB数量

// 复位函数
void RGB_RST(void);
// 颜色设置函数
void RGB_Set_Color(uint8_t LedId, RGBColor_TypeDef Color);
// RGB 刷新函数
void RGB_Reflash(uint8_t reflash_num);
	
// 各种颜色测试
void RGB_RED(uint16_t RGB_LEN);		//红
void RGB_GREEN(uint16_t RGB_LEN);		//绿
void RGB_BLUE(uint16_t RGB_LEN);		//蓝
void RGB_YELLOW(uint16_t RGB_LEN);		//黄
void RGB_MAGENTA(uint16_t RGB_LEN);	//紫
void RGB_BLACK(uint16_t RGB_LEN);		//黑
void RGB_WHITE(uint16_t RGB_LEN);		//白

#endif /* __WS2812_H */

然后写源文件代码

#include "ws2812.h"
#include "spi.h"
#include "dma.h"

// 常用的颜色,亮度调的比较低
const RGBColor_TypeDef RED      = {30 ,0  ,  0};
const RGBColor_TypeDef GREEN    = {0  , 30,  0};
const RGBColor_TypeDef BLUE     = {0  ,  0, 30};
const RGBColor_TypeDef YELLOW   = { 30, 30,  0};
const RGBColor_TypeDef MAGENTA  = { 30,  0, 30};
const RGBColor_TypeDef BLACK    = {  0,  0,  0};
const RGBColor_TypeDef WHITE    = { 30, 30, 30};

//模拟bit码:0xC0 为 0,0xF8 为 1
const uint8_t code[]={0xC0,0xF8};
//灯颜色缓存区
RGBColor_TypeDef RGB_DAT[RGB_NUM];

//SPI底层发送接口,一次发24个字节,相当于1个灯
extern DMA_HandleTypeDef hdma_spi1_tx;
static void SPI_Send(uint8_t *SPI_RGB_BUFFER)
{
  /* 判断上次DMA有没有传输完成 */
	while(HAL_DMA_GetState(&hdma_spi1_tx) != HAL_DMA_STATE_READY);
  /* 发送一个(24bit)的 RGB 数据到 2812 */
	HAL_SPI_Transmit_DMA(&hspi1,SPI_RGB_BUFFER,24);  
}
//颜色设置函数,传入 ID 和 颜色,进而设置缓存区
void RGB_Set_Color(uint8_t LedId, RGBColor_TypeDef Color)  
{
  if(LedId < RGB_NUM)
	{
		RGB_DAT[LedId].G = Color.G;
		RGB_DAT[LedId].R = Color.R;
		RGB_DAT[LedId].B = Color.B;
	}
}
//刷新函数,将颜色缓存区刷新到WS2812,输入参数是指定的刷新长度
void RGB_Reflash(uint8_t reflash_num)
{
	static uint8_t RGB_BUFFER[24]={0};
	uint8_t dat_b,dat_r,dat_g;
	//将数组颜色转化为 24 个要发送的字节数据
	if(reflash_num>0 && reflash_num<=RGB_NUM)
	{
		for(int i=0;i<reflash_num;i++)
		{
			dat_g = RGB_DAT[i].G;
			dat_r = RGB_DAT[i].R;
			dat_b = RGB_DAT[i].B;
			for(int j=0;j<8;j++)
			{
				RGB_BUFFER[7-j] =code[dat_g & 0x01];
				RGB_BUFFER[15-j]=code[dat_r & 0x01];
				RGB_BUFFER[23-j]=code[dat_b & 0x01];
				dat_g >>=1;
				dat_r >>=1;
				dat_b >>=1;
			}
			SPI_Send(RGB_BUFFER);
		}
	}
}
//复位函数
void RGB_RST(void)
{
	uint8_t dat[100] = {0};
  /* 判断上次DMA有没有传输完成 */
	while(HAL_DMA_GetState(&hdma_spi1_tx) != HAL_DMA_STATE_READY);
  /* RGB RESET */
	HAL_SPI_Transmit_DMA(&hspi1,dat,100); 
	HAL_Delay(10);
}

//常用颜色的点亮测试函数
void RGB_RED(uint16_t RGB_LEN)
{
  uint8_t i;
  for(i=0;i<RGB_LEN;i++)  
    RGB_Set_Color(i,RED);
	RGB_Reflash(RGB_LEN);
}
//.........其他测试函数省略...........//

代码把刷新和设置隔离,方便在 RTOS 中调用,其实可以进一步修改,让不同硬件的颜色buffer和刷新进一步隔离,通过指针传递共用底层,我后面有时间再修改,可以关注我的仓库链接,后面更新了会推到上面

JeckXu Gitee

3.程序下载,观察现象(通用步骤)

程序下载我一般用两种方式:

第一种是使用MDK自带的下载环境下载程序,我们给单片机连接ST-Link后配置下载,点击魔术棒,选择debug

选择ST-link后,点击setting

添加对应F4的Flash

keil界面点击下载

第二种是使用Stm32Programmer下载软件,该下载软件下载方式多,下载快,下面我使用st-link下载

打开软件,点击connect左边选择stlink后再点击connect连接下载器

点击open file,找到工程路径下MDK文件夹下工程生成的hex文件

之后点击downlod下载,下载结果如下


三、实验现象

灯环刷新

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/563520.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存