[HAL]STM32 SPI+DMA驱动WS2812

这篇具有很好参考价值的文章主要介绍了[HAL]STM32 SPI+DMA驱动WS2812。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

该程序是纯手敲,非Cube生成!所有代码均注释。

源码在文章后面获取

WS2818 简介

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

Keyword: 单线通讯、归零码、Reset、RGB顺序

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

RGB一共有24bit位 ->相当于驱动一个灯要24bit位 ->驱动若干个灯要24* n bit位,通过Reset码决定数据终止(保持)

24bit位应该如何发送?

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档
hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

可见:

表示低电平需要 T0H和T0L的配合,其关键在于高电平的时间,图中所示T0H时间为 0.85us ± 150ns

Q:怎么控制高低电平的时间

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

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

编程思路

① 若选择STM32F103C8T6最小系统的话,SPI1的时钟为72Mhz -> 需要16分频 -> 4.5Mhz -> 一个指令周期0.22us -> 0.22us * 8 = 1.76us与一个数据传输周期差不多(在F1已是较为接近)

② SPI需要发送什么数据来表示低电平 -> 0码时,高电平时间为0.4us ->一个指令周期为0.22us,所以需要两次高电平就够时间 -> 可表示为1100 0000 -> 0xC0 ;

③ 表示高电平 ->0码时,高电平时间0.85us -> 一个指令周期为0.22us,所以需要四次高电平时间就够了 -> 可表示为 1111 0000 –> 0 x F0 ;

④ Reset码可通过延迟函数来实现

程序基于正点原子的模板

实现目标: 灯的单个点亮(或每个灯显示不同颜色)

文件目录

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

main函数

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "led.h"
#include "spi_dma.h"
#include "rgb.h"
extern DMA_HandleTypeDef hdma1;
extern SPI_HandleTypeDef hspi1;

uint8_t textbuf[3] = {0xF0,0xF0,0xC0};
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    led_init();                         /* LED初始化 */
    Spi_Dma_Init();
    
    while(1)
    { 
      RGB_RED(50);      // 50个红色灯珠亮
      //HAL_SPI_Transmit_DMA(&hspi1,textbuf,8);
      //HAL_SPI_Transmit(&hspi1,textbuf,24,1000);
      //delay_us(60);
      //RGB_RST();
      delay_us(100);
    }
}

SPI和DMA初始化 - 头文件

#ifndef __SPI_DMA_H
#define __SPI_DMA_H


#include  "stm32f1xx.h"

extern SPI_HandleTypeDef hspi1;  // SPI 1
extern DMA_HandleTypeDef hdma1;  // DMA 1

void SPI_DMA_Init(void);         // SPI 和 DMA初始化

#endif

SPI和DMA初始化 - 源文件

#include "spi_dma.h"


SPI_HandleTypeDef hspi1;  // SPI 1
DMA_HandleTypeDef hdma1;
// SPI 和 DMA初始化
void SPI_DMA_Init(void)
{
  hspi1.Instance               =  SPI1 ; // SPI1 基地址
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16 ; //波特率设置
  hspi1.Init.CLKPhase          = SPI_PHASE_2EDGE;           //时钟相位
  hspi1.Init.CLKPolarity       = SPI_POLARITY_HIGH;         //是时钟极性
  hspi1.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;//硬件CEC校验
  hspi1.Init.CRCPolynomial     = 10;                        //校验多项式
  hspi1.Init.DataSize          = SPI_DATASIZE_8BIT;         //帧格式(8bit)
  hspi1.Init.Direction         = SPI_DIRECTION_2LINES;      //全双工
  hspi1.Init.FirstBit          = SPI_FIRSTBIT_MSB;          //数据传输顺序
  hspi1.Init.Mode              = SPI_MODE_MASTER ;          //SPI模式
  hspi1.Init.NSS               = SPI_NSS_SOFT;              // 软件控制SS
  hspi1.Init.TIMode            = SPI_TIMODE_DISABLE;        // 帧格式(Motorola)
  HAL_SPI_Init(&hspi1); //SPI1 初始化
  
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn,0,0); //设置中断分组
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);// 使能中断
}

// SPI 初始化回调函数
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
  GPIO_InitTypeDef GPIO_InitTypeSture ={0};
  
  __HAL_RCC_SPI1_CLK_ENABLE(); // 使能SPI1时钟
  __HAL_RCC_GPIOA_CLK_ENABLE();// 使能GPIOA时钟
  __DMA1_CLK_ENABLE();         // 使能DMA1时钟
  
  if(hspi ->Instance ==SPI1) //判断基地址是否为SPI1
  {
    GPIO_InitTypeSture.Mode = GPIO_MODE_AF_PP;     //一定要复用推挽输出
    GPIO_InitTypeSture.Pin  = GPIO_PIN_5 | GPIO_PIN_7;
    GPIO_InitTypeSture.Pull = GPIO_PULLUP;
    GPIO_InitTypeSture.Speed = GPIO_SPEED_FREQ_HIGH; 
    HAL_GPIO_Init(GPIOA,&GPIO_InitTypeSture); //GPIO 初始化
    
    hdma1.Instance                 = DMA1_Channel3;         //基地址为DMA1通道3
    hdma1.Init.Direction           = DMA_MEMORY_TO_PERIPH;  // 内存到外设
    hdma1.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE ;   //一个字
    hdma1.Init.MemInc              = DMA_MINC_ENABLE;       //内存自增
    hdma1.Init.Mode                = DMA_NORMAL;            //不循环
    hdma1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE ;   //一个字
    hdma1.Init.PeriphInc           = DMA_PINC_DISABLE ;     //外设不自增
    hdma1.Init.Priority            = DMA_PRIORITY_MEDIUM;   // 优先级
    HAL_DMA_Init(&hdma1); //DMA 初始化
    
    __HAL_LINKDMA(hspi,hdmatx,hdma1);  // SPI与DMA进行连接
  }
}

// DMA1通道3中断服务函数
void DMA1_Channel3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma1);  //处理DMA中断
}

WS2812 - 头文件

#ifndef __RGB_H
#define __RGB_H


#include "stm32f1xx.h" 

#include "spi_dma.h"

typedef struct
{
  uint8_t R;
  uint8_t G;
  uint8_t B;
}RGB_InitStruce;

// RGB数据发送程序
void RGB_Send(uint8_t* data);
// RGB 保持
void RGB_Rst(void);

// 存入缓存数组
void RGB_Set_Buf(uint8_t num,RGB_InitStruce color);
// RGB发送处理函数
void RGB_Send_Handler(uint8_t num);
//  RGB 红色测试函数
void RGB_Red(uint8_t num);
//  RGB 蓝色测试函数
void RGB_Blue(uint8_t num);


#endif

WS2812 - 源文件

#include "rgb.h"

#define RGB_NUM         60       // 灯珠数量
#define RGB_LowLevel    0xC0     // 0码
#define RGB_HighLevel   0xF0     // 1码

extern SPI_HandleTypeDef hspi1;  // SPI 1
extern DMA_HandleTypeDef hdma1;  // DMA 1

// 颜色定义
RGB_InitStruce RED    = {30,0,0};  // 红色
RGB_InitStruce GREEN  = {0,30,0};  // 绿色
RGB_InitStruce BLUE   = {0,0,30};  // 蓝色



RGB_InitStruce RGB_TempBuf[RGB_NUM]; // 颜色缓存数组

// RGB数据发送程序
void RGB_Send(uint8_t* data)
{
  while(HAL_DMA_GetState(&hdma1) != HAL_DMA_STATE_READY);  // 判断DMA是否空闲
  HAL_SPI_Transmit_DMA(&hspi1,data,24); // DMA发送
}

// RGB 保持
void RGB_Rst(void)
{
  uint8_t RGB_RstBuf[100] = {0};
  while(HAL_DMA_GetState(&hdma1) != HAL_DMA_STATE_READY);  // 判断DMA是否空闲
  HAL_SPI_Transmit_DMA(&hspi1,RGB_RstBuf,100); // DMA发送
}

// 存入缓存数组
// 颜色设置函数,传入ID和颜色,进而设置缓存区
void RGB_Set_Buf(uint8_t num,RGB_InitStruce Color)
{
  if( num <= RGB_NUM )
  {
    RGB_TempBuf[num].R = Color.R;
    RGB_TempBuf[num].G = Color.G;
    RGB_TempBuf[num].B = Color.B;
  }
}


// RGB发送处理函数
// 刷新函数,将颜色缓存区刷新到WS2812,输入参数是指定的刷新长度
void RGB_Send_Handler(uint8_t num)
{
  static uint8_t RGB_Buf[24] = {0};
  uint8_t i,j = 0;
  uint8_t Temp_R,Temp_G,Temp_B;
  //将数组颜色转化为24个要发送的字节数据
  if((num > 0 ) && ( num <= RGB_NUM ))
  {
    for (i = 0 ; i < num ;i++)
    {
      Temp_R = RGB_TempBuf[i].R;
      Temp_G = RGB_TempBuf[i].G;
      Temp_B = RGB_TempBuf[i].B;
      for ( j = 0 ; j < 8 ; j++ )
      {
        RGB_Buf[7  - j] =  (Temp_G& 0x01)? RGB_HighLevel : RGB_LowLevel ;
        RGB_Buf[15 - j] =  (Temp_R& 0x01)? RGB_HighLevel : RGB_LowLevel ;
        RGB_Buf[23 - j] =  (Temp_B& 0x01)? RGB_HighLevel : RGB_LowLevel ;
        Temp_R >>= 1;
        Temp_G >>= 1;
        Temp_B >>= 1;
      }
      RGB_Send(RGB_Buf);
    }
  }
}

//  RGB 红色测试函数
void RGB_Red(uint8_t num)
{
  uint8_t i = 0;
  for ( i = 0 ; i < num ; i++ )
  {
    RGB_Set_Buf(i,RED);
  }
  RGB_Send_Handler(num);
}

void RGB_Blue(uint8_t num)
{
  uint8_t i = 0;
  for ( i = 0 ; i < num ; i++ )
  {
    RGB_Set_Buf(i,BLUE);
  }
  RGB_Send_Handler(num);
}

效果图

hal_spi_transmit_dma,stm32,单片机,嵌入式硬件,音频,开源,Powered by 金山文档

下期预告

基于本文章的扩展,WS2812灯带随着音乐而变化(STM32F103C8T6)

📺 [HAL]STM32 SPI+DMA驱动WS2812灯带随音乐变化

获取源码

点击这里,文末获取下载!文章来源地址https://www.toymoban.com/news/detail-702501.html

到了这里,关于[HAL]STM32 SPI+DMA驱动WS2812的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • WS2812B彩灯 STM32HAL库开发:PWM+DMA(stm32f103c8t6)

    目录 一、摘要 二、WS2812B介绍 三、CUBEMX配置 四、程序介绍(KEIL编译器) 五、数据手册         1、本文使用示例单片机型号为 stm32f103c8t6 ,RGB型号为 WS2812B ;          2、主要实现功能是实现用 PWM+DMA 使RGB_LED亮起不同颜色的灯光;         3、目的:简单调通该型号

    2024年02月03日
    浏览(16)
  • 心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

    在好几年前,我好像就看到了焊武帝 jiripraus在纪念结婚五周年时,制作的一个心跳跟随心形灯,感觉太浪漫了,于是在假期的时候,我也仿照做了一个,虽然还有很多需要完善的地方,但是大致功能已经实现了,下面开源讲讲开源的项目。 心脏的外壳采用紫铜丝或黄铜丝焊

    2024年02月14日
    浏览(18)
  • STM32+PWM+DMA驱动WS2812彩灯模块(附源码)

    WS2812是一颗数字LED灯珠,采用单总线通讯,每颗灯珠支持24bit的颜色控制,也即RGB888,信号线通过DIN输入,经过一颗灯珠之后,信号线上前24bit数据会被该灯珠锁存,之后将剩下的数据信号整形之后通过DOUT输出 C1为VDD的滤波电容,一般大小为100NF。 WS2812.c pwm.c DMA.c main.c 需要源

    2024年02月15日
    浏览(14)
  • STM32C0开发(1)----SPI 驱动WS2812灯珠

    本文介绍了如何使用STM32微控制器,结合STM32CubeMX配置工具和SPI通讯接口,来驱动和控制WS2812 LED灯带。这是一个集硬件连接、软件配置和编程开发于一体的综合性项目,目标是实现对LED灯带颜色和亮度的精确控制。 最近在弄ST的课程,需要样片的可以加群申请:615061293 。 ht

    2024年02月04日
    浏览(14)
  • STM32+PWM+DMA驱动WS2812彩灯模块(评论点赞给源代码)

    WS2812是一颗数字LED灯珠,采用单总线通讯,每颗灯珠支持24bit的颜色控制,也即RGB888,信号线通过DIN输入,经过一颗灯珠之后,信号线上前24bit数据会被该灯珠锁存,之后将剩下的数据信号整形之后通过DOUT输出 C1为VDD的滤波电容,一般大小为100NF。 WS2812.c pwm.c DMA.c main.c 需要源

    2024年04月23日
    浏览(15)
  • STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库

    优点:不消耗CPU资源 缺点:占用内存较大 定时器配置 定时器通道:TIM3 CH2 分频:0 重装值:79,芯片主频64Mhz,因此PWM输出频率:64Mhz/79 ≈ 800Khz,满足芯片要求。 auto-reload preload 要关闭 output compare preload 要打开 DMA配置 外设一定要选择TIM3_UP,不要选TIM_CHx 方向是内存到外设,

    2024年02月10日
    浏览(18)
  • STM32CubeMX配置HAL库实现SPI-DMA的递归调用

    核心: STM32F407ZET6   外设ADC: ADS1258   数量:3个   ※ 核心与3个ADC使用SPI总线 “一主多从” 方式连接,PCB布线的方式与下图一致。 ※ 在电路板上STM32与三个ADS1258在同一直线上分布,STM32在一端,三个ADC依次排布。 ※ 离STM32最远ADC的DRDY硬件管脚与STM32的EXTI line4 interrupt连接

    2024年02月15日
    浏览(22)
  • 【STM32】HAL库中的SPI传输(可利用中断或DMA进行连续传输)

    【STM32】HAL库中的SPI传输(可利用中断或DMA进行连续传输) SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。 SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,

    2024年02月08日
    浏览(18)
  • stm32HAL库 G4 SPI 从机DMA可变长度接受数据乱码问题

    使用一个引脚当作SPI CS, 边沿触发; 在电平位0时候接受SPI数据20个字节 如果实际主机只发了小于20字节时候, 下一个帧就会错乱; 去老外找了之后,需要复位SPi RCC时钟才能复位掉SPI 下面的不行 正确的做法

    2024年02月10日
    浏览(13)
  • U8g2库的STM32硬件SPI(DMA)移植教程(HAL、OLED显示、四线SPI、DMA)

    本文教你把U8g2图形库移植到STM32上,基于STM32的硬件SPI、CubeMX U8g2库Github网址:https://github.com/olikraus/u8g2 U8g2库CSDN镜像网址:https://gitcode.net/mirrors/olikraus/u8g2?utm_source=csdn_github_accelerator 硬件准备:STM32C8T6(STM32系列芯片)、0.96寸OLED(128×64)、J-Link(或其他) 引脚连接: 出自此

    2024年02月09日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包