当前位置:   article > 正文

【STM32】硬件SPI+DMA驱动WS2812灯珠,基于HAL库_spi驱动ws2812

spi驱动ws2812

一、简单介绍

二、模块与接线

笔者使用的WS2812是淘宝买的灯环,如下图所示

留出来四个接口,分别是DOUT,DIN,VCC,GND

根据描述

我们使用到的仅为DIN,VCC,GND

推荐使用5V供电,即VCC连接至单片机的5V引脚

DIN作为数据接收端,需要连接至单片机SPI的MOSI引脚,笔者选择开启SPI1,因此连接到PA7

GND连接至单片机的GND引脚

三、cubemx配置

时钟配置为48MHz

设置SPI

开启DMA

四、驱动编写

在数据包与数据包之间需要大于280us的延时,一个数据包有好几个数据帧,取决于想点亮几个灯珠

数据帧格式如上,G R B每种颜色用8bit24位表示一个颜色,分给一个灯珠

当想表示0的时候,需要电平持续200~300多ns,然后是580ns~1us的电平

当想表示1的时候,需要电平持续580ns~1us,然后是580ns~1us的电平

笔者设置SPI为6MHz,一个时钟周期大概是160ns(1/6MHz)

那么想表示0,就需要大约2个时钟的高电平(160*2=320ns)紧接着6个时钟的低电平(6*160=960ns),就是由SPI发出0xC0

想表示1,就需要大约4个时钟的高电平(4*160=640ns)紧接着4个时钟的低电平(4*160=640ns),就是由SPI发出0xF0

定义结构体和存放信息的数组和宏定义

  1. #define WS2812_0 0xC0
  2. #define WS2812_1 0xF0
  3. #define WS2812_RST 0x00
  4. #define LED_NUMS 16
  5. #define RGB_BIT 24
  6. typedef struct
  7. {
  8. uint8_t R;
  9. uint8_t G;
  10. uint8_t B;
  11. }LEDType;
  12. volatile uint8_t RGB_BIT_Buffer[RGB_BIT];
  13. volatile uint8_t buffer[RGB_BIT*LED_NUMS];
  14. volatile LEDType LED[LED_NUMS];

这里的RGB_BIT_Buffer是一个含有24个字节的数组,用来存放一个LED灯的色彩信息

buffer用来存放笔者所用模块的16个灯珠的所有色彩信息

创建RGB_BIT_Buffer

这个函数非常关键,用于将色彩转化为24位的数据

  1. /**
  2. create 24 byte sent by SPI using RGB values.
  3. */
  4. static static void WS2812_CreatData(uint8_t R,uint8_t G,uint8_t B)
  5. {
  6. uint8_t temp[RGB_BIT] = {0};
  7. for (uint8_t i=0;i<8;i++){
  8. temp[7-i] = (G & 0x01) ? WS2812_1 : WS2812_0;
  9. G = G >> 1;
  10. }
  11. for (uint8_t i=0;i<8;i++){
  12. temp[15-i] = (R & 0x01) ? WS2812_1 : WS2812_0;
  13. R = R >> 1;
  14. }
  15. for (uint8_t i=0;i<8;i++){
  16. temp[23-i] = (B & 0x01) ? WS2812_1 : WS2812_0;
  17. B = B >> 1;
  18. }
  19. memcpy(RGB_BIT_Buffer, temp, RGB_BIT);
  20. }

处理buffer

将16个灯珠的色彩进行处理,变成一个大的数组,存入buffer

  1. /**
  2. cook the whole buffer made by many(16 pieces) RGB_BIT_Buffers.
  3. */
  4. void WS2812_MakeBuffer()
  5. {
  6. for(uint16_t i=0;i < LED_NUMS;i++)
  7. {
  8. WS2812_CreatData(LED[i].R,LED[i].G,LED[i].B);
  9. memcpy(buffer + i * RGB_BIT, RGB_BIT_Buffer, RGB_BIT);
  10. }
  11. }

关灯函数

  1. void WS2812_TurnOff()
  2. {
  3. for(uint16_t i=0;i<LED_NUMS*24;i++)
  4. {
  5. buffer[i]=WS2812_0;
  6. }
  7. }

点灯函数

这个函数用来使某个灯珠呈现出设定的色彩

  1. /**
  2. an led of position will bright with color value
  3. pos is [0 , max-1]
  4. */
  5. static void WS2812_Color_Pos(uint32_t color,uint16_t Pos)
  6. {
  7. uint8_t R,G,B;
  8. uint16_t i;
  9. R=(color >> 16 ) & 0x00FF;
  10. G=(color >> 8 ) & 0x0000FF;
  11. B=(color ) & 0x0000FF;
  12. WS2812_CreatData(R,G,B);
  13. if(Pos < LED_NUMS && Pos >=0)
  14. {
  15. memcpy(buffer + RGB_BIT * Pos, RGB_BIT_Buffer,RGB_BIT);
  16. }
  17. else
  18. {
  19. WS2812_TurnOff();
  20. }
  21. }

RGB流水灯函数

根据介绍里的描述,ws2812可以显示256*256*256种颜色,但常用的RGB值其实并不多,我们只需要显示其中一些就能呈现出RGB变幻的效果了

  1. void WS2812_Show_Wheel()
  2. {
  3. static uint16_t i=0;
  4. i++;
  5. WS2812_Color_Pos(0xFF0000,(i)%16);
  6. WS2812_Color_Pos(0XFF7F00,(i+1)%16);
  7. WS2812_Color_Pos(0XFFFF00,(i+2)%16);
  8. WS2812_Color_Pos(0X7FFF00,(i+3)%16);
  9. WS2812_Color_Pos(0X00FF00,(i+4)%16);
  10. WS2812_Color_Pos(0X00FF7F,(i+5)%16);
  11. WS2812_Color_Pos(0X00FFFF,(i+6)%16);
  12. WS2812_Color_Pos(0X007FFF,(i+7)%16);
  13. WS2812_Color_Pos(0X0000FF,(i+8)%16);
  14. WS2812_Color_Pos(0X7F00FF,(i+9)%16);
  15. WS2812_Color_Pos(0XFF00FF,(i+10)%16);
  16. WS2812_Color_Pos(0XFF007F,(i+11)%16);
  17. WS2812_Color_Pos(0XFF0000,(i+12)%16);
  18. WS2812_Color_Pos(0XFF7F00,(i+13)%16);
  19. WS2812_Color_Pos(0XFFFF00,(i+14)%16);
  20. WS2812_Color_Pos(0X7FFF00,(i+15)%16);
  21. WS2812_Update();
  22. }

刷新函数

  1. void WS2812_Update()
  2. {
  3. HAL_SPI_Transmit_DMA(&hspi1,buffer,RGB_BIT*LED_NUMS);
  4. }

主函数

  1. /**
  2. * @brief The application entry point.
  3. * @retval int
  4. */
  5. int main(void)
  6. {
  7. /* USER CODE BEGIN 1 */
  8. /* USER CODE END 1 */
  9. /* MCU Configuration--------------------------------------------------------*/
  10. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  11. HAL_Init();
  12. /* USER CODE BEGIN Init */
  13. /* USER CODE END Init */
  14. /* Configure the system clock */
  15. SystemClock_Config();
  16. /* USER CODE BEGIN SysInit */
  17. /* USER CODE END SysInit */
  18. /* Initialize all configured peripherals */
  19. MX_GPIO_Init();
  20. MX_DMA_Init();
  21. MX_SPI1_Init();
  22. /* USER CODE BEGIN 2 */
  23. /* USER CODE END 2 */
  24. /* Infinite loop */
  25. /* USER CODE BEGIN WHILE */
  26. while (1)
  27. {
  28. WS2812_Show_Wheel();
  29. HAL_Delay(30);
  30. /* USER CODE END WHILE */
  31. /* USER CODE BEGIN 3 */
  32. }
  33. /* USER CODE END 3 */
  34. }

五、效果展示

六、驱动附录

ws2812.c

  1. #include "ws2812.h"
  2. volatile uint8_t RGB_BIT_Buffer[RGB_BIT];
  3. volatile uint8_t buffer[RGB_BIT*LED_NUMS];
  4. volatile LEDType LED[LED_NUMS];
  5. void WS2812_Update()
  6. {
  7. HAL_SPI_Transmit_DMA(&hspi1,buffer,RGB_BIT*LED_NUMS);
  8. }
  9. /**
  10. create 24 byte sent by SPI using RGB values.
  11. */
  12. static void WS2812_CreatData(uint8_t R,uint8_t G,uint8_t B)
  13. {
  14. uint8_t temp[RGB_BIT] = {0};
  15. for (uint8_t i=0;i<8;i++){
  16. temp[7-i] = (G & 0x01) ? WS2812_1 : WS2812_0;
  17. G = G >> 1;
  18. }
  19. for (uint8_t i=0;i<8;i++){
  20. temp[15-i] = (R & 0x01) ? WS2812_1 : WS2812_0;
  21. R = R >> 1;
  22. }
  23. for (uint8_t i=0;i<8;i++){
  24. temp[23-i] = (B & 0x01) ? WS2812_1 : WS2812_0;
  25. B = B >> 1;
  26. }
  27. memcpy(RGB_BIT_Buffer, temp, RGB_BIT);
  28. }
  29. /**
  30. cook the whole buffer made by many(16 pieces) RGB_BIT_Buffers.
  31. */
  32. static void WS2812_MakeBuffer()
  33. {
  34. for(uint16_t i=0;i < LED_NUMS;i++)
  35. {
  36. WS2812_CreatData(LED[i].R,LED[i].G,LED[i].B);
  37. memcpy(buffer + i * RGB_BIT, RGB_BIT_Buffer, RGB_BIT);
  38. }
  39. }
  40. void WS2812_TurnOff()
  41. {
  42. for(uint16_t i=0;i<LED_NUMS*24;i++)
  43. {
  44. buffer[i]=WS2812_0;
  45. }
  46. }
  47. /**
  48. an led of position will bright with color value
  49. pos is [0 , max-1]
  50. */
  51. static void WS2812_Color_Pos(uint32_t color,uint16_t Pos)
  52. {
  53. uint8_t R,G,B;
  54. uint16_t i;
  55. R=(color >> 16 ) & 0x00FF;
  56. G=(color >> 8 ) & 0x0000FF;
  57. B=(color ) & 0x0000FF;
  58. WS2812_CreatData(R,G,B);
  59. if(Pos < LED_NUMS && Pos >=0)
  60. {
  61. memcpy(buffer + RGB_BIT * Pos, RGB_BIT_Buffer,RGB_BIT);
  62. }
  63. else
  64. {
  65. WS2812_TurnOff();
  66. }
  67. }
  68. void WS2812_Show_Wheel()
  69. {
  70. static uint16_t i=0;
  71. i++;
  72. WS2812_Color_Pos(0xFF0000,(i)%16);
  73. WS2812_Color_Pos(0XFF7F00,(i+1)%16);
  74. WS2812_Color_Pos(0XFFFF00,(i+2)%16);
  75. WS2812_Color_Pos(0X7FFF00,(i+3)%16);
  76. WS2812_Color_Pos(0X00FF00,(i+4)%16);
  77. WS2812_Color_Pos(0X00FF7F,(i+5)%16);
  78. WS2812_Color_Pos(0X00FFFF,(i+6)%16);
  79. WS2812_Color_Pos(0X007FFF,(i+7)%16);
  80. WS2812_Color_Pos(0X0000FF,(i+8)%16);
  81. WS2812_Color_Pos(0X7F00FF,(i+9)%16);
  82. WS2812_Color_Pos(0XFF00FF,(i+10)%16);
  83. WS2812_Color_Pos(0XFF007F,(i+11)%16);
  84. WS2812_Color_Pos(0XFF0000,(i+12)%16);
  85. WS2812_Color_Pos(0XFF7F00,(i+13)%16);
  86. WS2812_Color_Pos(0XFFFF00,(i+14)%16);
  87. WS2812_Color_Pos(0X7FFF00,(i+15)%16);
  88. WS2812_Update();
  89. }

ws2812.h                                                                                               

  1. #ifndef WS2812_H
  2. #define WS2812_H
  3. #include "string.h"
  4. #include "main.h"
  5. #include "spi.h"
  6. #define WS2812_0 0xC0
  7. #define WS2812_1 0xF0
  8. #define WS2812_RST 0x00
  9. #define LED_NUMS 16
  10. #define RGB_BIT 24
  11. typedef struct
  12. {
  13. uint8_t R;
  14. uint8_t G;
  15. uint8_t B;
  16. }LEDType;
  17. /**
  18. turn off all leds
  19. */
  20. void WS2812_TurnOff();
  21. void WS2812_Show_Wheel();
  22. void WS2812_Update();
  23. #endif

七、工程文件源码

STM32硬件SPI配合DMA驱动WS2812流水灯,基于HAL库资源-CSDN文库

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/643447
推荐阅读
相关标签
  

闽ICP备14008679号