一. 关于WS2812

WS2812 内部集成了处理芯片和3颗不同颜色的led灯(红,绿,蓝),通过单总线协议分别控制三个灯的亮度强弱,达到全彩的效果。

WS2812B Datasheet

二. WS2812灯珠的几种驱动方式

  1. 使用延时函数
    直接翻转IO口产生时序,这种方式最为简单易用,只需要控制延时的时间,就可以从产生0和1码,它需要占用系统资源。
    使用 SPI 数据传输产生时序
  2. 通过SPI控制
    只需要控制在合适的波特率,在传输不同数据的时候,可以产生符合要求的0和1码,这种方式需要等同于使用了一个SPI设备
  3. 使用 DMA+Timer 产生时序
    这种方式需要使用一个定时器,其中一个通道固定产生一个周期1.25us的PWM,占空比2/3,接着需要另一个通道,在周期的1/3处搬运数据到IO口,若为1,PWM不变,若为0,PWM则为0码,这种方式有更大的局限性,由于DMA只能搬运至少一个字节,所以每次会同时改变8个IO口的高低电平,或许使用位带操作可以解决这问题
  4. 使用 Timer+PWM+DMA 产生时序
    本文讨论的实现方案,这种方案有2种驱动的方式,一种是直接建立一个大的数组,存放所有灯珠的数据,然后启动DMA传输,第二种是建立2个灯组数据大小的数组,当DMA传输一个灯珠数据时,改变另一个灯组数据,通过不断改变数组的方式,节约内存,相比较而言,第一种方式较为直观,第二种方式则可以解决灯珠较多的情况,本文讨论第一种的原理和程序的实现。

三. STM32CubeMX 相关配置

基于 STM32F405RGT6

由于项目选择的TIM8定时器, 查询Datasheet得知, TIM8挂载于APB2总线

APB2的时钟基准为168MHz

计算自动重装载数值:
我们要产生一个周期为1.25us的PWM,
则 自动重装载值 = 0.00000125 * 168000000 = 210
减一不多说


四. 代码实现部分

/**
* @file 在 tim.c 文件增加以下内容
*/void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{HAL_TIM_PWM_Stop_DMA(&htim8, TIM_CHANNEL_3);
}
/**
* @file ws2812.c
* @brief WS2812 LED driver, use (PC8) TIM8-CH3 PWM mode.
* @author William
* @date 2022.5.20
*/#include "ws2812.h"
#include "tim.h"/* WS2812B f=800k, T=1.25us */
#define ONE_PULSE    (143) //1 码 (2/3*T)
#define ZERO_PULSE   (67)  //0 码 (1/3*T)
#define RESET_PULSE  (9000)  //低电平复位信号50us
#define LED_DATA_LEN (24)  //led 颜色数据长度, 一个灯珠需要24bits
#define WS2812_DATA_LEN (RESET_PULSE + LED_NUMS * LED_DATA_LEN) //ws2812灯条需要的总数组长度/* !! 若嵌套循环使用, 须注意变量(i) !! */
#define LOOP_ALL for(size_t i = 0; i < LED_NUMS; i++) /* 所有灯珠 */#define DEFAULT_BRIGHTNESS (100) //灯带默认亮度: 0~100static uint16_t RGB_buffer[WS2812_DATA_LEN] = {0};static void ws2812_refresh(void);
static void color_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b);/* ************************************ Public Functions ************************************ *//**
* @brief WS2812初始化, 全黑
*/
void ws2812_init(void)
{ws2812_set_dark(MAX);
}/**
* @brief 设置某个灯珠颜色RGB
*
* @param uint8_t R,G,B: RGB色彩格式 红,绿,蓝通道数据
*        uint16_t num, 指定设置颜色的灯珠位号
*/
void ws2812_set_RGB(uint8_t R, uint8_t G, uint8_t B, uint16_t num)
{if (num > LED_NUMS)return;uint16_t *p = (RGB_buffer + RESET_PULSE) + (num * LED_DATA_LEN);for (uint16_t i = 0; i < 8; i++){p[i]      = (G << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;p[i + 8]  = (R << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;p[i + 16] = (B << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;}
}/**
* @brief 设置某个灯珠颜色HSV
*
* @param uint8_t H,S,V: HSV色彩格式
*        uint16_t num, 指定设置颜色的灯珠位号
*/
void ws2812_set_HSV(uint16_t H, uint16_t S, uint16_t V, uint16_t num)
{uint32_t R = 0, G = 0, B = 0;if (num > LED_NUMS)return;uint16_t *p = (RGB_buffer + RESET_PULSE) + (num * LED_DATA_LEN);color_hsv2rgb(H, S, V, &R, &G, &B);for (uint16_t i = 0; i < 8; i++){p[i]      = (G << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;p[i + 8]  = (R << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;p[i + 16] = (B << i) & (0x80) ? ONE_PULSE : ZERO_PULSE;}
}/**
* @brief 灭灯
*/
void ws2812_set_dark(uint8_t type)
{switch (type){default:LOOP_ALL{ws2812_set_RGB(0x00, 0x00, 0x00, i);}break;}ws2812_refresh();
}/**
* @brief WS2812全彩渐变
* @note 需放置循环体内
*/
void ws2812_colorful_shadow(void)
{for (uint16_t color = 0; color < 360; color++){for (uint8_t i = 0; i < LED_NUMS; i++){ws2812_set_HSV(color, 100, DEFAULT_BRIGHTNESS, i);}ws2812_refresh();osDelay(50);}
}/* ************************************ Static Functions ************************************ *//**
* @brief WS2812颜色数据刷新, 修改颜色值后调用
*/
static void ws2812_refresh(void)
{HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_3, (uint32_t *)RGB_buffer, WS2812_DATA_LEN);
}/*** @brief 将HSV颜色空间转换为RGB颜色空间** @param  h HSV颜色空间的H:色调, 范围0~360* @param  s HSV颜色空间的S:饱和度, 范围0~100* @param  v HSV颜色空间的V:明度, 范围0~100* @param  r 转换后RGB-R值的指针* @param  g 转换后RGB-G值的指针* @param  b 转换后RGB-B值的指针**/
static void color_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b)
{h %= 360; // h -> [0,360]uint32_t rgb_max = v * 2.55f;uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;uint32_t i = h / 60;uint32_t diff = h % 60;uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;switch (i){case 0:*r = rgb_max;*g = rgb_min + rgb_adj;*b = rgb_min;break;case 1:*r = rgb_max - rgb_adj;*g = rgb_max;*b = rgb_min;break;case 2:*r = rgb_min;*g = rgb_max;*b = rgb_min + rgb_adj;break;case 3:*r = rgb_min;*g = rgb_max - rgb_adj;*b = rgb_max;break;case 4:*r = rgb_min + rgb_adj;*g = rgb_min;*b = rgb_max;break;default:*r = rgb_max;*g = rgb_min;*b = rgb_max - rgb_adj;break;}
}
/**
* @file ws2812.h
* @brief WS2812 LED driver, use TIM8-CH3 PWM mode.
* @author William
* @date 2022.5.20
*/#ifndef  __WS2812_H
#define __WS2812_H#include "stdint.h"#define LED_NUMS (9) //灯珠数量/* HSV格式常用色值预设 */
#define RED 0
#define YELLOW 60
#define GREEN 120
#define CYAN 180
#define BLUE 240void ws2812_init(void);
void ws2812_set_RGB(uint8_t R, uint8_t G, uint8_t B, uint16_t num);
void ws2812_set_HSV(uint16_t H, uint16_t S, uint16_t V, uint16_t num);
void ws2812_set_dark(uint8_t type);
void ws2812_colorful_shadow(void);#endif  //__WS2812_H

声明:
本文有参考其他博客, 时间久远, 已忘记原文链接
另外, 本文整合并精简了部分代码, 也增加了常用的颜色格式转换等函数, 更容易使用些
互相交流学习, 不喜勿喷

STM32使用PWM+DMA方式驱动WS2812灯珠相关推荐

  1. WS2812灯珠(三)-- STM32 PWM+DMA方式驱动

    WS2812灯珠(三)-- STM32 PWM+DMA方式驱动 文章目录 WS2812灯珠(三)-- STM32 PWM+DMA方式驱动 一.理论 二.代码实践 一.理论 PWM输出就是对外输出脉宽( ...

  2. 树莓派驱动 WS2812 灯珠 不亮的问题

    前言 树莓派在控制某些硬件外设上坑还真不少,今天就又踩了一个(其实有两天了).其实越复杂的问题往往是有越简单的解决办法. 树莓派驱动ws2812 网上一搜几乎都是 用的 rpi-ws281x 这个库 ...

  3. 嵌入式作业STM32采用串口DMA方式发送数据

    目录 前言 要求 一.DMA的基本介绍 DMA的基本定义 DMA的主要特征 STM32F411x系列芯片DMA控制器 二.通过CubeMX配置项目 1.创建项目 2.选择芯片STM32F103C8T6 ...

  4. WS2812灯珠(五)---移植Adafruit_NeoPixel库

    将Adafruit_NeoPixel库移植为C版本 Adafruit_NeoPixel库为实现WS2812类似系列的灯珠实现非常酷炫的效果提供了各种接口函数,应用层可以很方便的利用这些接口函数实现各种 ...

  5. WS2812灯珠(四)---实现全彩呼吸灯效果

    WS2812灯珠实现呼吸灯效果主要涉及到呼吸函数及颜色模型两部分的内容.清楚了这两点结合之前的灯珠驱动程序,便可以实现任意颜色的呼吸变换效果了. 呼吸函数 具体的呼吸函数细节这里就不介绍了,感兴趣的可 ...

  6. Arduino控制1302颗ws2812灯珠显示圣诞树和圣诞老人(附程序源码)

    Arduino控制1302颗ws2812灯珠显示圣诞树和圣诞老人 设计者:STCode(公众号同名) 效果直接看视频~ Arduino控制ws2812灯带显示圣诞树和圣诞老人 1)项目介绍 该设计一共 ...

  7. STM32输出PWM波形并实现呼吸灯

    文章目录 一.环境配置 二.PWM简介 三.使用STM32CubeMX配置工程 四.使用Keil配置代码 五.运行效果 六.用Keil自带的逻辑仿真器观察占空比 七.总结 八.参考资料 一.环境配置 ...

  8. STM32输出PWM波形及LED呼吸灯

    目录 一.PWM的介绍 1.概述 2.优点 3.主要产生方法 SPWM法 1.等面积法 2.硬件调制法 3.软件生成法 4.PWM电路图 5.PWM基本原理 二.cubemx项目的建立 1.选择芯片S ...

  9. 关于RS485通讯中使用STM32串口以DMA方式发送数据丢失字节的问题

    1.开发平台 计算机操作系统:WIN7 64位: 开发环境:Keil MDK 5.14: MCU:STM32F407ZET6: STM32F4xx固件库:STM32F4xx_DSP_StdPeriph ...

最新文章

  1. 给 Python 初学者推荐的 IDE 哦!
  2. Java拾遗:001 - 重写 equals 和 hashCode 方法
  3. C# try catch finally 执行
  4. 在 Oracle 和 PHP 中使用 LOB
  5. 【OpenKruise v0.9.0】新增 Pod 重启、删除防护等重磅功能
  6. BP神经网络——从二次代价函数(Quadratic cost)到交叉熵(cross-entropy cost)代价函数
  7. linux的基本命令tail,Linux基本命令(示例代码)
  8. LINUX获得当前用户名
  9. html设置导背景宽度,calc()实现满屏背景定宽内容
  10. macbook 安装mysql_mac下安装mysql
  11. 计分器 java_非常实用的java语言自动答题计时计分器[Java代码]
  12. 外贸建站自己买主机空间好吗
  13. 高效能人士的七个习惯读后感与总结概括-(第一章)
  14. cmd命令行常用指令
  15. 学计算机u盘多少g合适,U盘设定分配单元的大小多少合适
  16. 当大数据遇到保险:传统精算模型将被颠覆
  17. JMM,synchronized
  18. eps图片太大压缩小
  19. 关于MAXIMO数据限制代码的理解
  20. 思科模拟器8.1版本身份验证失败禁用网络解决

热门文章

  1. python上海交通大学出版社答案网_上海交通大学出版社python答案
  2. 音乐网站毕业设计html,静态音乐网站设计(毕业论文).doc
  3. 热插拔能力,热交换,热冗余
  4. Kali应用——(一)信息收集
  5. [CTO札记]‘信息/行为外播’--与开放SNS平台的互动
  6. U盘超级加密(U盘,移动硬盘,共享文件夹加密软件)
  7. 制作三星I9088 刷机ROM的实践(二)
  8. Jonny Mo的读书笔记——《完成任务不找借口》之二
  9. java 实现58热敏票据打印
  10. 鸿蒙系统平板电脑能安装吗,平板电脑已预装鸿蒙系统,我们来看看效果