新闻详情

幻彩灯珠--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


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

随机文章
  3570是1860全新升级吗?  3570和1860实际上全是指灯珠的尺寸,3570表明灯珠尺寸是3.5*7.0mm,1860指的是灯珠尺寸为1.8*6.0mm。  假如一样的色度下,一颗3570灯珠可以替代2颗1860。因此,不能说是更新,只有说加工工艺不一样而已。应用哪一种,应当和灯珠的价钱,加工工艺的简单化关联大一点。3570加工工艺通俗一点,色度还更高。可是处理速度高,表明发烫更...
3535灯珠在汽车灯具中的应用是当前的主流趋势。5050灯珠是5mm*5mm*1.6mm,光强高(纯白光,暖光的稍微小一点)。他的工作电压和普通的LED一样,只需要3.0-3.4V,电流也是一样60MA。UVC灯珠工艺难点在于点胶量的控制,在胶体高度、点胶位置均有详细的工艺要求。幻彩灯珠应用于:LED幻彩灯带,LED幻彩灯条,LED柔性霓虹管,LED硬灯条,LED S形灯带,玩具,仪器,数码...
2835LED灯珠分为白光和色光,色光有多种颜色,在不同的地方和环境中安装不同颜色的2835灯珠,不同颜色的2835LED灯珠可以营造不同的氛围,感染情绪。白光灯珠相对于小功率LED灯珠来说,led大功率灯珠的功率更高,亮度更亮,价格更高。...
LED次品灯为用户敲响了一个警钟市场上的LED照明产品种类繁多的LED,而线性增加,LED灯制造商越来越激增。幻彩灯珠应用于:LED幻彩灯带,LED幻彩灯条,LED柔性霓虹管,LED硬灯条,LED S形灯带,玩具,仪器,数码产品等区域使用。...
2018年8月30日,广东汕头地区连续暴雨,造成严重水灾。天成照明总经理林坚耿先生通过深圳市潮汕商会踊跃捐资,为受灾群众的及时援助和灾后重建奉献了一份爱心和力量。深圳市潮汕商会特此颁发《感谢状》以表彰其仁心仁爱。
2018年11月26日,深圳狮子会陆丰助学“幸福奖教奖学金项目”活动在广东省陆丰市甲东镇15所小学展开,天成照明作为狮子会成员对此次活动给予了大力支持,受到甲东镇人民政府、陆丰市教育局、深圳狮子会的肯定。
2019年1月10日,深圳市照明与显示工程行业协会(SLDA)年会在深圳登喜路国际大酒店隆重举行。天成照明深受深圳市照明与显示工程行业协会及社会公众的肯定和支持,荣膺“2018年度特别贡献奖”、“光爱100贫困大学生帮扶计划工公益大使”荣誉...
2019年09月12日下午,深圳市天成照明有限公司围绕“月满金秋 · 情系天成”的主题在宝安区华美酒店召开2019金秋丰收季表彰大会暨中秋家宴。天成照明总经办林坚耿先生、李浩锐先生等全体天成人出席了本次年度大会。在本次大会中,人力资源部、财...

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

联系我们

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

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

传真:0755-29573533

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

地址:深圳市宝安区松岗街道东方大道33号第四栋

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


(关注我们)
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________

友情链接: led光源LED灯带 | 洗地机‍ | 不锈钢无缝管‍   | 方木多片锯‍ | 泰兴减速机‍ | led灯珠‍ | 湿电除尘‍ | 铝挤压型材‍‍ | 网带输送机‍‍ |   电动叉车‍    |   手术无影灯‍    | 波峰焊‍ |   滚针轴承|   高温退火炉   |   试验筛 |   螺旋叶片 |   梅花联轴器 ‍ |   电热管‍   |   青岛电缆桥架‍‍ | N80石油套管‍ |   不锈钢焊管‍ | 玻璃钢冷却塔‍ | 干式变压器‍ | 墙布‍ | 西安装修公司‍ | ‍ 工矿灯

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