[MM32生态] 【EV Board (MM32L0136C7P)测评】+SPI轻松驱动WS2812

[复制链接]
2129|35
 楼主| xiaoqi976633690 发表于 2022-12-4 10:38 | 显示全部楼层 |阅读模式
本帖最后由 xiaoqi976633690 于 2022-12-4 10:38 编辑

MM32F0136  SPI轻松驱动WS2812
#申请原创#

本例程通过使用SPI2外设,波特率设置为8mhz,驱动WS2812


  • 初始化GPIO_PIN
  1. void BOARD_InitPins(void)
  2. {
  3.     GPIO_Init_Type gpio_init;

  4.     /* PA3 - UART2_RX. */
  5.     gpio_init.Pins  = GPIO_PIN_3;
  6.     gpio_init.PinMode  = GPIO_PinMode_In_Floating;
  7.     gpio_init.Speed = GPIO_Speed_50MHz;
  8.     GPIO_Init(GPIOA, &gpio_init);
  9.     GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);

  10.     /* PA2 - UART2_TX. */
  11.     gpio_init.Pins  = GPIO_PIN_2;
  12.     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
  13.     gpio_init.Speed = GPIO_Speed_50MHz;
  14.     GPIO_Init(GPIOA, &gpio_init);
  15.     GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);

  16.     /* PB14 - SPI2_MISO. */
  17.     gpio_init.Pins  = GPIO_PIN_14;
  18.     gpio_init.PinMode  = GPIO_PinMode_In_Floating;
  19.     gpio_init.Speed = GPIO_Speed_50MHz;
  20.     GPIO_Init(GPIOB, &gpio_init);
  21.     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);

  22.     /* PB15 - SPI2_MOSI. */
  23.     gpio_init.Pins  = GPIO_PIN_15;
  24.     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
  25.     gpio_init.Speed = GPIO_Speed_50MHz;
  26.     GPIO_Init(GPIOB, &gpio_init);
  27.     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);
  28. }

   2.SPI外设初始化

  1. void SPI_1Lines_HalfDuplex_Init(void)
  2. {

  3.     /* Setup SPI module. */
  4.     SPI_Master_Init_Type spi_init;
  5.     spi_init.ClockFreqHz = BOARD_LOOP_SPI_FREQ;//24MHZ
  6.     spi_init.BaudRate = BOARD_LOOP_SPI_BAUDRATE;//8MHZ
  7.     spi_init.XferMode = SPI_XferMode_TxRx;
  8.     spi_init.PolPha = SPI_PolPha_Alt3;
  9.     spi_init.DataWidth = SPI_DataWidth_8b;
  10.     spi_init.LSB = false;
  11.     spi_init.AutoCS = true;
  12.     SPI_InitMaster(BOARD_LOOP_SPI_PORT, &spi_init);

  13.     /* Enable SPI. */
  14.     SPI_Enable(BOARD_LOOP_SPI_PORT, true);

  15. }


这里我们将SPI 频率设置为8Mhz从下图可以看出每bit占用125ns 一个byte是8x125=1000ns  
根据时序我们将
1110 0000 即0xE0 表示为WS2812的0
1111 1000 即0xF8 表示为WS2812的1
这里5个bit的1 实际是5*125=625 实际是不符合850ns的但是可以用。试过设置SPI频率的6mhz的话也就是每bit 166.666ns 但是所有数据都被识别为1,ws2812全部亮了。
15265638c07efb9349.png 81947638c08083e887.png

   3.时钟初始化

  1. void BOARD_InitBootClocks(void)
  2. {
  3.     CLOCK_ResetToDefault();
  4.     CLOCK_BootToHSE48MHz();

  5.     /* UART2. */
  6.     RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART2, true);
  7.     RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_UART2);

  8.     /* SPI2. */
  9.     RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);
  10.     RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);

  11.     /* GPIOA. */
  12.     RCC_EnableAHBPeriphs(RCC_AHB_PERIPH_GPIOA, true);
  13.     RCC_ResetAPB1Periphs(RCC_AHB_PERIPH_GPIOA);

  14.     /* GPIOB. */
  15.     RCC_EnableAHBPeriphs(RCC_AHB_PERIPH_GPIOB, true);
  16.     RCC_ResetAPB1Periphs(RCC_AHB_PERIPH_GPIOB);
  17. }
  4.WS2812驱动代码WS2812.C
  1. /*
  2. * WS2812.C
  3. *
  4. *  Created on: 2021年10月9日
  5. *      Author: Administrator
  6. */

  7. #include "board_init.h"
  8. #include "WS2812.H"

  9. /*******************************************************************************
  10. * Function Name  : SPI_1Lines_HalfDuplex_Init
  11. * Description    : Configuring the SPI for half-duplex communication.
  12. * Input          : None
  13. * Return         : None
  14. *******************************************************************************/
  15. u32 ws2812_led[led_cout]; //灯珠数量 一个存储24位rgb颜色
  16. void SPI_1Lines_HalfDuplex_Init(void)
  17. {

  18.     /* Setup SPI module. */
  19.     SPI_Master_Init_Type spi_init;
  20.     spi_init.ClockFreqHz = BOARD_LOOP_SPI_FREQ;//24MHZ
  21.     spi_init.BaudRate = BOARD_LOOP_SPI_BAUDRATE;//8MHZ
  22.     spi_init.XferMode = SPI_XferMode_TxRx;
  23.     spi_init.PolPha = SPI_PolPha_Alt3;
  24.     spi_init.DataWidth = SPI_DataWidth_8b;
  25.     spi_init.LSB = false;
  26.     spi_init.AutoCS = true;
  27.     SPI_InitMaster(BOARD_LOOP_SPI_PORT, &spi_init);

  28.     /* Enable SPI. */
  29.     SPI_Enable(BOARD_LOOP_SPI_PORT, true);

  30. }
  31. void WS2812_SET0(void)
  32. {
  33.     while ( SPI_STATUS_TX_FULL & SPI_GetStatus(BOARD_LOOP_SPI_PORT) )
  34.     {}
  35.     SPI_PutData(BOARD_LOOP_SPI_PORT, 0xE0);//1110 0000
  36. }

  37. void WS2812_SET1(void)
  38. {
  39.     while ( SPI_STATUS_TX_FULL & SPI_GetStatus(BOARD_LOOP_SPI_PORT) )
  40.     {}
  41.     SPI_PutData(BOARD_LOOP_SPI_PORT, 0xF8);//1111 1000
  42. }

  43. void Ws2812_Rest(void)
  44. {
  45.         u8 i = 60;
  46.         while (i) //0.444*200=88.8us
  47.         {
  48.                     while ( SPI_STATUS_TX_FULL & SPI_GetStatus(BOARD_LOOP_SPI_PORT) )
  49.     {}
  50.     SPI_PutData(BOARD_LOOP_SPI_PORT, 0x00);
  51.                 i--;
  52.         }
  53. }

  54. void ws2812_rgb(u8 ws_num, u8 r, u8 g, u8 b)
  55. {

  56.         u32 grb = 0;
  57.         grb = g;
  58.         grb <<= 8;
  59.         grb |= r;
  60.         grb <<= 8;
  61.         grb |= b;
  62.         ws2812_led[ws_num] = grb;
  63.         //ws2812_rx(64);
  64. }
  65. //填充LED
  66. void ws2812_rgb_all(u8 ws_count, u8 r, u8 g, u8 b)
  67. {
  68.         u8 i;
  69.         u32 grb = 0;
  70.         grb = g;
  71.         grb <<= 8;
  72.         grb |= r;
  73.         grb <<= 8;
  74.         grb |= b;
  75.         for (i = 0; i < ws_count; i++)
  76.         {
  77.                 ws2812_led[i] = grb;
  78.         }
  79.         
  80. }

  81. void ws2812_save(u8 ws_number, u8 r, u8 g, u8 b)
  82. {
  83.         u32 grb = 0;
  84.         grb = g;
  85.         grb <<= 8;
  86.         grb |= r;
  87.         grb <<= 8;
  88.         grb |= b;
  89.         ws2812_led[(ws_number - 1)] = grb;
  90. }

  91. /****************将保存的数组RGB数据发送到所有LED*******/
  92. /******************************************************/
  93. void ws2812_rx(u8 led_count)
  94. {
  95.         u8 a, i;
  96.         for (a = 0; a < led_count; a++)
  97.         {
  98.                 for (i = 0; i < 24; i++)
  99.                 {
  100.                         if ((ws2812_led[a] & 0x800000) == 0)
  101.                                 WS2812_SET0();
  102.                         else
  103.                                 WS2812_SET1();
  104.                         ws2812_led[a] <<= 1;
  105.                 }
  106.         }
  107.         Ws2812_Rest();
  108. }

  109. void ws2812_clear_all(u8 ws_count)
  110. {
  111.         u8 i;
  112.         u32 grb = 0;
  113.         for (i = 0; i < ws_count; i++)
  114.         {
  115.                 ws2812_led[i] = grb;
  116.         }

  117. }


  1. /*
  2. * WS2812.H
  3. *
  4. *  Created on: 2021年10月9日
  5. *      Author: Administrator
  6. */

  7. #ifndef USER_WS2812_H_
  8. #define USER_WS2812_H_
  9. #include "board_init.h"

  10. #define led_cout 64
  11. #define WS_ARRAY_SIZE (led_cout*3)


  12. typedef  unsigned char u8;
  13. typedef  unsigned int u32;

  14. void SPI_1Lines_HalfDuplex_Init(void);
  15. void WS2812_SET0(void);
  16. void WS2812_SET1(void);
  17. void Ws2812_Rest(void);
  18. //设置第ws_num个灯珠的颜色rgb
  19. void ws2812_rgb(u8 ws_num, u8 ws_r, u8 ws_g, u8 ws_b);

  20. //设置前ws_count个灯珠颜色为rgb
  21. void ws2812_rgb_all(u8 ws_count, u8 ws_r, u8 ws_g, u8 ws_b);

  22. //将最新的ws_data[]数组中的值发送至WS2812B模块
  23. //void ws2812_refresh(u8 ws_count);

  24. //将rgb颜色值保存到第ws_number位数组内
  25. void ws2812_save(u8 ws_number, u8 r, u8 g, u8 b);

  26. //数组中的值发送至WS2812B模块
  27. void ws2812_rx(u8 led_count);
  28. //关闭所有LED
  29. void ws2812_clear_all(u8 ws_count);

  30. #endif /* USER_WS2812_H_ */
   main函数
  1. /*
  2. * Copyright 2021 MindMotion Microelectronics Co., Ltd.
  3. * All rights reserved.
  4. *
  5. * SPDX-License-Identifier: BSD-3-Clause
  6. */
  7. #include "board_init.h"
  8. #include "WS2812.H"


  9. #define APP_SPI_BUF_LEN 16u /* SPI buffer for SPI tx. */
  10. #define BOARD_DELAY_COUNT     10000u



  11. void app_delay(uint32_t delay)
  12. {
  13.     for (uint32_t i = 0u; i < delay; i++)
  14.     {
  15.         for (uint32_t j = 0u; j < BOARD_DELAY_COUNT; j++)
  16.         {
  17.             __NOP();
  18.         }
  19.     }
  20. }

  21. int main(void)
  22. {
  23.     BOARD_Init();
  24.     printf("spi_master_basic example.\r\n");
  25.     /* Setup spi. */
  26.     SPI_1Lines_HalfDuplex_Init();
  27.                 Ws2812_Rest();
  28.                 ws2812_rgb_all(8,1,0,0);
  29.                 ws2812_rx(8);
  30.     while (1)
  31.     {
  32.                         ws2812_rgb_all(8,3,0,0);//将8颗LED的R,G,B的值写入缓存
  33.                         ws2812_rx(8);//发送LED数据并显示
  34.       app_delay(80);
  35.                         
  36.                   ws2812_rgb_all(8,2,1,0);//橙
  37.                         ws2812_rx(8);
  38.                         app_delay(80);
  39.                         
  40.                         ws2812_rgb_all(8,2,2,0);//黄
  41.                         ws2812_rx(8);
  42.                         app_delay(80);               
  43.                         
  44.                         ws2812_rgb_all(8,0,3,0);//绿
  45.                         ws2812_rx(8);
  46.                         app_delay(80);                                
  47.                         
  48.                         ws2812_rgb_all(8,0,3,3);//青
  49.                         ws2812_rx(8);
  50.                         app_delay(80);                                
  51.                         
  52.                         ws2812_rgb_all(8,0,0,3);//绿
  53.                         ws2812_rx(8);
  54.                         app_delay(80);                        
  55.                         
  56.                         ws2812_rgb_all(8,3,0,3);//绿
  57.                         ws2812_rx(8);
  58.                         app_delay(80);
  59.                         
  60.                         
  61.                         ws2812_rgb(0,3,0,0);
  62.                         ws2812_rgb(1,3,2,0);
  63.                         ws2812_rgb(2,2,3,0);
  64.                         ws2812_rgb(3,0,3,0);
  65.                         ws2812_rgb(4,0,3,2);
  66.                         ws2812_rgb(5,0,0,3);
  67.                         ws2812_rgb(6,3,0,3);
  68.                         ws2812_rgb(7,3,0,0);
  69.                         ws2812_rx(8);
  70.                         app_delay(1000);        
  71.                         
  72.                         //printf("spi_master_basic example.\r\n");

  73.     }
  74. }




显示效果,忽略中间有一个坏掉了
56218638c065ea1f5c.png
spi_master_basic.zip (305 KB, 下载次数: 8)





kkzz 发表于 2022-12-6 12:48 | 显示全部楼层
完美的做法,会不会收到中断的干扰呢?
febgxu 发表于 2022-12-6 14:18 | 显示全部楼层
这个效果好,时间控制的非常精确了。
iyoum 发表于 2022-12-6 18:28 | 显示全部楼层
这个不能使用spi+dma的形式吗?
 楼主| xiaoqi976633690 发表于 2022-12-6 20:17 | 显示全部楼层
iyoum 发表于 2022-12-6 18:28
这个不能使用spi+dma的形式吗?

可以用dma的,我这个没有做上来
weifeng90 发表于 2022-12-8 18:00 来自手机 | 显示全部楼层
用PWM驱动比较实用
10299823 发表于 2022-12-9 22:31 | 显示全部楼层
延时可以驱动WS2812吗?              
xu@xupt 发表于 2022-12-10 13:22 | 显示全部楼层
感谢分享
tpgf 发表于 2023-1-3 10:36 | 显示全部楼层
WS2812也是非常经典的spi驱动的灯带芯片了
nawu 发表于 2023-1-3 11:07 | 显示全部楼层
如果用延时驱动ws2812的话 就太浪费资源了
aoyi 发表于 2023-1-3 11:15 | 显示全部楼层
WS2812RGBWS2812/2811只需一根信号线就能控制灯带上所有led
zljiu 发表于 2023-1-3 11:24 | 显示全部楼层
WS2812通过简单的外部接口、特有的级联方案便于利用MCU完成多个LED控制,极大简化了LED控制接口
gwsan 发表于 2023-1-3 11:55 | 显示全部楼层
WS2812B-2020是一个集控制电路与发光电路于一体的智能外控LED光源;其外型采用最新的molding封装工艺
tfqi 发表于 2023-1-3 12:04 | 显示全部楼层
WS2812芯片是采用单线归零码的通讯方式
Jacquetry 发表于 2023-1-5 17:14 | 显示全部楼层
iyoum 发表于 2022-12-6 18:28
这个不能使用spi+dma的形式吗?

可以用dma
timfordlare 发表于 2023-1-9 17:32 | 显示全部楼层
dma+spi组合最佳了。              
cemaj 发表于 2023-1-9 17:40 | 显示全部楼层
这个spi的速度怎么样              
jonas222 发表于 2023-1-10 19:49 | 显示全部楼层
可以使用延时进行判断的。              
i1mcu 发表于 2023-1-10 20:59 | 显示全部楼层
这个可以移植arduino的库吗?
kkzz 发表于 2023-1-10 21:38 | 显示全部楼层
需要连续使用spi写入数据吗?最大支持多少个led?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

205

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部