新闻详情

幻彩灯珠--STM32F103 SPI驱动WS2812

 二维码
发表时间:2022-03-30 09:59
文章附图

  之前发了一个基于涂鸦平台的宠物自动喂食器点此跳转,已经有小夜灯功能了,但是躁动的心岂能因此平静,功能以及花里胡哨,那就让他华丽起来,除了画手们的涂装风格,作为电子工程师,得用我们自己的方式美化。正好手里有个ws2812的灯环,全彩模式开始。

  WS2812特点:

  1.WS2812灯珠内置控制电路与RGB芯片,集成在一个5050封装的元器件中,构成一个完整的外控像素点。

  2.每个像素点的三基色可实现256级亮度显示,完成16777216种颜色的全真色彩显示,扫描频率不低于400Hz/s。

  3.串行级联接口,能通过一根信号线完成数据的接收与解码。

  4.当刷新速率30帧/秒时,低速模式级联数不小于512点,高速模式不小于1024点。

  5.数据发送速度可达800Kbps。

  数据传输:

  ws2812的每颗灯珠的控制需要24位数据,分为8位绿色+8位红色+8位蓝色,每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。灯珠之间采用串行级联,在上电复位以后,控制器可向灯带发送一串24bit的数据,比如需要点亮10颗灯,我们发送10个24bit的数据到第一个灯,第一个灯的DIN端接受控制器发过来的24bit数据,第一个灯珠会提取第一个24bit数据后会将该数据送进数据锁存器,剩余的数据经过内部整形电路向下传送,直到所有的灯都获取一个24bit数据。这10个24bit数据的发送间隔不能超过50us,否则会导致下一次的颜色数据被第一个重新锁存,就无法完成完整的点亮。( f# T% }0 [3 q

  这里需要着重点一下时序相关的知识,先看时序波形图和数据传输时间表:





  根据上面两个图可以看到ws2812的电平反转是纳秒级别的,所以在使用单片机外设时我们需要对单片机的外设的传输速度进行控制,速度慢了根本无法点亮。; S( G) r; U: ]/ l" e: d( y) W

  操作开始:在驱动ws2812的时候一般采用PWM或者SPI的方式,这两个速度较快,比直接使用IO口进行电平反转要方便,且控制效果更好。这里我们采用SPI的方式,因为在点灯是的数据发送间隔时间的约束,如果我们使用SPI发送的数据较多,中途遇到中断可能会打断我们的点灯,所以我这里使用SPI的DMA进行控制,不怕被其他影响。

  $ K& Q* H) y8 A3 N

  通过硬件SPI我们模拟WS2812的通信时序。单片机选用STM32F103,主频72M,SPI1分频设置为8,这样SPI1的通信频率为9M。时间很充足,如果使用SPI2,则需要减小分频系数,否则无法点亮。我这里采用的便是SPI2,4分频,因为SPI1被我的屏幕占用了。1 h7 F1 f/ g& Q2 l3 H

  .h文件只要定义了灯珠的个数和0码1码。不同的灯珠数量只需修改PIXEL_NUM 的值

  #ifndef __WS2812_H#define __WS2812_H

  #include "stm32f10x.h"

  #define PIXEL_NUM 24

  //硬件spi模拟ws2811时序(用spi的8位数据模拟ws281x的一位数据)//要将系统时钟设置为56M,分频数设置为8,则SPI的通信频率为7M,传输一位数据的时间约为143纳秒(ns)//3*143 = 429ns 5*143 = 715ns 符合WS281X芯片的通信时序。// _____ // | |___| 11111000 high level// ___ // | |_____| 11100000 low level" b0 v* d3 B$ G5 m

  #define WS_HIGH 0XF8#define WS_LOW 0XE0;

  void ws281x_init(void);void ws281x_closeAll(void);void ws281x_rainbowCycle(uint8_t wait);uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue);void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor);void ws281x_show(void);

  void ws281x_theaterChase(uint32_t c, uint8_t wait);void ws281x_colorWipe(uint32_t c, uint8_t wait);void ws281x_rainbow(uint8_t wait);void ws281x_theaterChaseRainbow(uint8_t wait);

  #endif /* __WS2812_H */

  #include "../BOARD/ws2812/ws2812.h"#include "usart.h"#include "delay.h"

  uint8_t pixelBuffer[PIXEL_NUM][24] ;" k* H) D$ J, e; F W; H

  7 Z0 x! U" e; j% G" m

  void ws281x_init(void){ GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; DMA_InitTypeDef DMA_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //PORTA时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //SPI1时钟使能 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输5 m/ ^; {1 S- T5 l) c

  /* PA7 SPI1_MOSI */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA7复用推挽输出 SPI GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA

  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为低电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第2个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为16 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

  SPI_Cmd(SPI2, ENABLE); //使能SPI外设 SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); DMA_DeInit(DMA1_Channel5); //将DMA的通道1寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(SPI2 -> DR); //cpar; //DMA外设ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pixelBuffer; //cmar; //DMA内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设 DMA_InitStructure.DMA_BufferSize = PIXEL_NUM * 24; //cndtr; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器 ws281x_closeAll(); //关闭全部的灯 delay_ms(100); //关闭全部的灯需要一定的时间 }

  void ws281x_closeAll(void){ uint16_t i; uint8_t j; for(i = 0; i < PIXEL_NUM; ++i) { for(j = 0; j < 24; ++j) { pixelBuffer[j] = WS_LOW; } } ws281x_show();}

  uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue){ return green << 16 | red << 8 | blue;}4 @, B7 p) q) {, `, I" X) s

  void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor){ uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < 24; ++i) { pixelBuffer[n] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW); } }}$ s) V [/ M! [& k: l2 S; F

  void ws281x_setPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue){ uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < 24; ++i) { pixelBuffer[n] = (((ws281x_color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW); } }} S8 t. {2 h0 ~+ d$ f1 f

  void ws281x_show(void){ DMA_Cmd(DMA1_Channel5, DISABLE ); //关闭USART1 TX DMA1 所指示的通道 DMA_CleaRFlag(DMA1_FLAG_TC5); DMA_SetCurrDataCounter(DMA1_Channel5,24 * PIXEL_NUM );//DMA通道的DMA缓存的大小 DMA_Cmd(DMA1_Channel5, ENABLE); //使能USART1 TX DMA1 所指示的通道}( u f4 G3 L2 H# {6 x- J) Q

  / ], A6 r3 L9 b+ c# f9 ~) R

  // Input a value 0 to 255 to get a color value.// The colours are a transition r - g - b - back to r.uint32_t ws281x_wheel(uint8_t wheelPos) { wheelPos = 255 - wheelPos; if(wheelPos < 85) { return ws281x_color(255 - wheelPos * 3, 0, wheelPos * 3); } if(wheelPos < 170) { wheelPos -= 85; return ws281x_color(0, wheelPos * 3, 255 - wheelPos * 3); } wheelPos -= 170; return ws281x_color(wheelPos * 3, 255 - wheelPos * 3, 0);}0 {# {; H7 z! J0 T$ |# Z

  // Fill the dots one after the other with a colorvoid ws281x_colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i

  void ws281x_rainbow(uint8_t wait) { uint16_t i, j;

  for(j=0; j<256; j++) { for(i=0; i

  // Slightly different, this makes the rainbow equally distributed throughoutvoid ws281x_rainbowCycle(uint8_t wait) { uint16_t i, j;( m/ S/ q* U' D, I4 ]5 B

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel for(i=0; i< PIXEL_NUM; i++) { ws281x_setPixelColor(i,ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255)); } ws281x_show(); delay_ms(wait); }}# J( `" j3 |5 l7 j2 G g

  //Theatre-style crawling lights.void ws281x_theaterChase(uint32_t c, uint8_t wait) { for (int j=0; j<10; j++) { //do 10 cycles of chasing for (int q=0; q < 3; q++) { for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, c); //turn every third pixel on } ws281x_show();% `& ^* R: S7 R

  delay_ms(wait);

  for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, 0); //turn every third pixel off } } }}2 u% i! Q$ [9 I& R6 l8 R

  //Theatre-style crawling lights with rainbow effectvoid ws281x_theaterChaseRainbow(uint8_t wait) { for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q++) { for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, ws281x_wheel( (i+j) % 255)); //turn every third pixel on } ws281x_show();. T4 W/ b( e% o, E3 n

  delay_ms(wait);

  for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, 0); //turn every third pixel off } } }}

  ( S" o5 I1 l0 m* `1 r( l- P

  ) M0 f9 L# m" x8 {

  1 Q1 n! D3 w( g9 E, S

  const char s[5];int8_t i;; R* I Y$ m( @* q' S" K

  int main(void){// usart1_init(115200); delay_init();% R( y0 V" N, i$ y) [% I

  ws281x_init();

  while(1) { // Some example procedures showing how to display to the pixels: ws281x_colorWipe(ws281x_color(255, 0, 0), 50); // Red ws281x_colorWipe(ws281x_color(0, 255, 0), 50); // Green ws281x_colorWipe(ws281x_color(0, 0, 255), 50); // Blue//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW // Send a theater pixel chase in... ws281x_theaterChase(ws281x_color(127, 127, 127), 50); // White ws281x_theaterChase(ws281x_color(127, 0, 0), 50); // Red ws281x_theaterChase(ws281x_color(0, 0, 127), 50); // Blue. K/ h) C4 i7 a. t- D) b

  //ws281x_rainbow(20); ws281x_rainbowCycle(20); ws281x_theaterChaseRainbow(200); for(i = 0; i < PIXEL_NUM; ++i) { ws281x_setPixelColor(i, ws281x_color(0,250,0)); ws281x_show(); delay_ms(500); } }}4 K1 G9 z9 ^3 Z- g/ {+ n" f

  在ws2812.c移植了Adafruit_NeoPixel库的部分函数,用以实现炫酷的显示效果。; k h. }7 o9 N

  都是基础的SPI,不说废话,移植就能用。展示一下效果吧!# U8 ]7 i! d2 f# n1 v2 g. z




  『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

最新文章
2024-08-10
2024-08-10
2024-08-07
2024-08-07
2024-08-07
2024-08-07
2024-08-07
2024-07-02
2024-07-02
2024-07-02
随机文章
LED光源的种类很多,不同的LED灯,内部结构所用的灯珠也会有细微差别。今天,小编为大家全面、系统地科普一下LED灯珠的常见类型,供大家参考使用。LED光源可以分为两大类:芯片类光源和替换类光源。芯片类光源1引脚插入型(DIP)这种LED灯珠是结构最简单的发光二极管,因为灯珠下面有两根形似“脚”的细丝,可以直接穿接在电路板上,所以称之为引脚插入式的灯珠。使用特点:它的安全性好、性能稳定,在低...
日前,富采投控旗下晶电与錼创科技全资子公司錼创显示宣布合作,将由錼创显示协助其建置6英寸Micro LED生产线。目前Micro LED大型显示器是前驱产品,尽管仍有技术与成本问题待克服,不过TrendForce集邦咨询同步看好Micro LED运用于头戴式的AR穿透式智慧眼镜、穿戴式的智慧手表以及车用的智慧驾驶座舱及透明显示产品的发展,有望刺激Micro LED高阶应用产品的诞生,预估至2...
  LED电子显示屏很重要的组成部分就有LED单元板,如果单元板有问题,会直影响LED显示屏的整体质量!所以,如何辨别LED单元板的好坏是LED显示屏商家关心的问题,下面整理了一些检测LED单元板的常见方法。  一、材质  1. LED单元板板材  有些LED单元板厂家为了低价竞争,采用廉价的阻燃纸质板或者是单面纤维板作为LED灯的PCB线路板。因为全玻纤PCB板很贵。刚开始看不出差别,一般...
  8月17日,福建泉州市2022年重大项目视频连线开工活动在晋江举行。据悉,晋江市此次共22个项目集中开工,总投资965.5亿元,涵盖光电显示等多个领域。  此次集中开工活动中,位于高端制造产业园的MiniLED封装产业项目作为安溪县的代表性重大项目,向全市展示开工场面。    图片来源:安溪县融媒体中心  据悉,安溪县MiniLED封装产业项目总投资11.67亿元,年度计划投资1.5亿元...
  1:幻彩灯珠简介  1.1 幻彩内置IC灯珠的特点  幻彩内置IC灯泡主要有两个部分:一个是LED,另一个是IC晶体管。LED部分负责产生光,而IC晶体管则负责控制LED的亮度和颜色。通常情况下,一个幻彩内置IC灯泡包含三个不同颜色的LED:红、 绿、 和 蓝 (RGB) 。 控制晶体 管开关的电流大小,来调整 LED的亮度。而通过不同比例地控制三个 LED的亮度,可以得到不同颜色的光。...
  led灯在我们的生活中使用极广,同时在使用led灯时还会出现一些故障,如只发弱光,甚至是不亮了。面对这些情况应该怎么办?  一、led灯只发弱光怎么回事  电源参数主要注意两个,一个是瓦数,一个是电源输出电压(也就是灯板上的输入DC电压)。  也可能是灯的驱动电源损坏了,若是独立电源,可买个相同规格的更换,若是一体的,建议整灯更换。  还有可能是led灯泡的问题,也可以再买个灯泡看看。 ...
  led灯是现代生活中再常见不过的灯具,人们选择led灯时会考虑其使用寿命,更需要注意保养。究竟led灯怎么保养?  一、led灯的使用寿命多少  正常来说,led灯的使用寿命差不多都是能够达到4万小时到5万小时的,这个跟led灯的品牌和性能是有很大的关系的,像大品牌的led灯各方面还是比较有保障的,使用寿命久,而且质量也是非常不错的,不容易出现问题。  现在led灯的应用还是非常广泛的,...
  led灯不亮怎么回事  led灯是我们生活中再常见不过的灯具,led灯在使用过程中容易出现不亮的情况,究竟是怎么回事?不亮的话怎么修理呢  一、led灯不亮怎么回事  1、led灯条的焊接点有虚焊现象,运输过程中的震动造成焊点脱落而导致灯带不亮。  2、焊锡质量不好,led灯条在弯折的过程中焊点容易产生脆裂、脱落现象。  3、安装时弯折角度过大,造成led灯条焊点与铜箔分离而导致不亮。 ...
  对于led灯人们都并不陌生,甚至我们生活中大部分灯具都是led灯,led灯在使用过程中可能会出现一些问题,如不亮或者变弱的问题   一、led灯不亮怎么办  1、关掉开关,在折叠梯子的帮助下,拆掉led吸顶灯的外壳,并检查灯管是否出现发黑发暗的地方。如果有,很有可能是灯管已经损坏了。必须更换了,这种LED灯管在五金商店里面可以买到,建议选择知名品牌。  2、如果灯管还是完好的,那么应该试...

关于天成    产品中心    新闻资讯    技术支持    人力资源    联系方式

联系我们

----------------------------------------------

销售热线:181 2996 9297

电话:4008-320-328 /   0755-29573979   

传真:0755-29573533

邮件:tczm@tczmled.com   邮编:518000

地址::深圳市光明区凤凰街道长凤路263号天成高新园

邮箱:jiangeng@tczmled.com   (投诉与合作)


(关注我们)
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________

友情链接:天成高科集团   TCWIN led超声波光源LED灯带 |   泰兴减速机‍ | led幻彩灯珠‍ ‍‍ | 波峰焊‍ |   滚针轴承|   高温退火炉   |   试验筛 |   螺旋叶片 |   青岛电缆桥架‍‍ | N80石油套管‍   | 玻璃钢冷却塔‍ | 西安装修公司

会员登录
登录
其他账号登录:
我的资料
留言
回到顶部