WS2812灯珠(二)

阅读: 评论:0

WS2812灯珠(二)

WS2812灯珠(二)

    通过硬件SPI的可以很巧妙的模拟出WS2812的通信时序,用spi的8位数据模拟ws281x的一位数据。

要将系统时钟设置为56M,SPI分频数设置为8,则SPI的通信频率为7M,1s/7M≈143ns 即传输一位数据的时间约为143纳秒(ns)
 3*143 = 429ns   5*143 = 715ns  符合WS281X芯片的通信时序。
 11111000  high level  (十六进制:0XF8)表示WS281X的1码
 11100000  low level   (十六进制:0XE0)表示WS281X的0码

程序头文件部分: 通过宏的方式定义了灯珠个数和WS281X的0码和1码。

#ifndef __WS2812_H
#define __WS2812_H#include "stm32f10x.h"#define PIXEL_NUM 11//硬件spi模拟ws2811时序(用spi的8位数据模拟ws281x的一位数据)
//要将系统时钟设置为56M,分频数设置为8,则SPI的通信频率为7M,传输一位数据的时间约为143纳秒(ns)
//3*143 = 429ns   5*143 = 715ns  符合WS281X芯片的通信时序。
//  _____   
// |     |___|   11111000  high level
//  ___         
// |   |_____|   11100000  low level#define WS_HIGH 0XF8
#define WS_LOW  0XE0void 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 */

程序的关键在硬件SPI和DMA初始化部分:在配置时用到了硬件SPI1的MOSI脚,驱动过程中并不需要接收数据,因而将SPI1方向设置为单线发送即可,设置为主机模式,因为用SPI的8位数据来模拟WS281X的一位数据,故将SPI数据大小设为8位帧结构。对于CPOL和CPHA的设置,CPOL设置为低的情况下要将CPHA设置为第二个跳边沿采样(之前将CPOL设置为低,CPHA设为第一个跳边沿采样,通过示波器查看波形与所需要的时序刚好相反)。DMA部分的设置注意将数据方向设为从内存到外设,将数据宽度设为8位即可。

void ws281x_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef  SPI_InitStructure;DMA_InitTypeDef DMA_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //PORTA时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1时钟使能 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输/* PA7  SPI1_MOSI */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PA7复用推挽输出 SPIGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOASPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPISPI_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_8;		//定义波特率预分频的值:波特率预分频值为16SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); //使能SPI外设SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);DMA_DeInit(DMA1_Channel3);   //将DMA的通道1寄存器重设为缺省值DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(SPI1 -> 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_Channel3, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器 ws281x_closeAll();  //关闭全部的灯delay_ms(100); //关闭全部的灯需要一定的时间  
}

配置完成之后便可以构思底层控制函数了,为了方便多个LED灯珠的可控制首先要定义一个缓冲区pixelBuffer[PIXEL_NUM][24]    通过设定颜色将数据填入缓冲区再通过更新函数将数据传入到LED灯珠上。

//关闭所有灯珠
void ws281x_closeAll(void)
{uint16_t i;uint8_t j;for(i = 0; i < PIXEL_NUM; ++i){for(j = 0; j < 24; ++j){pixelBuffer[i][j] = WS_LOW;}}ws281x_show(); 
}
//将三原色单独数据合并为24位数据
uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue)
{return green << 16 | red << 8 | blue;
}
//设定第n个灯珠的颜色
void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor)
{uint8_t i;if(n < PIXEL_NUM){for(i = 0; i < 24; ++i){pixelBuffer[n][i] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW);}}
}
//设定第n个灯珠的颜色
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][i] = (((ws281x_color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW);}}
}
//更新颜色显示(在设定颜色后将颜色数据存入缓存只有执行该函数后才会进行显示)
void ws281x_show(void)
{DMA_Cmd(DMA1_Channel3, DISABLE );  //关闭USART1 TX DMA1 所指示的通道 DMA_ClearFlag(DMA1_FLAG_TC3);    DMA_SetCurrDataCounter(DMA1_Channel3,24 * PIXEL_NUM );//DMA通道的DMA缓存的大小DMA_Cmd(DMA1_Channel3, ENABLE);  //使能USART1 TX DMA1 所指示的通道 
}

有了上面的底层控制函数,便可以为灯珠显示加特技了,在这里移植了Adafruit_NeoPixel库的部分函数,用以实现炫酷的显示效果。

// 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);
}// Fill the dots one after the other with a color
void ws281x_colorWipe(uint32_t c, uint8_t wait) {for(uint16_t i=0; i<PIXEL_NUM; i++) {ws281x_setPixelColor(i, c);ws281x_show();delay_ms(wait);}
}void ws281x_rainbow(uint8_t wait) {uint16_t i, j;for(j=0; j<256; j++) {for(i=0; i<PIXEL_NUM; i++) {ws281x_setPixelColor(i, ws281x_wheel((i+j) & 255));}ws281x_show();delay_ms(wait);}
}// Slightly different, this makes the rainbow equally distributed throughout
void ws281x_rainbowCycle(uint8_t wait) {uint16_t i, j;for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheelfor(i=0; i< PIXEL_NUM; i++) {ws281x_setPixelColor(i,ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255));}ws281x_show();delay_ms(wait);}
}//Theatre-style crawling lights.
void ws281x_theaterChase(uint32_t c, uint8_t wait) {for (int j=0; j<10; j++) {  //do 10 cycles of chasingfor (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();delay_ms(wait);for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {ws281x_setPixelColor(i+q, 0);        //turn every third pixel off}}}
}//Theatre-style crawling lights with rainbow effect
void ws281x_theaterChaseRainbow(uint8_t wait) {for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheelfor (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();delay_ms(wait);for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {ws281x_setPixelColor(i+q, 0);        //turn every third pixel off}}}
}

如果需要完整测试例程可以到如下链接下载:

本文发布于:2024-01-28 03:32:26,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17063839524479.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:灯珠
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23