- #ifndef __SPI_DMA_WS2812_H
- #define __SPI_DMA_WS2812_H
- #include "ht32.h"
- #include "ht32_board.h"
- #include "stdint.h"
- #include "bsp_sys.h"
- /*
- 要将系统时钟设置为60M,SPI分频数设置为8,则SPI的通信频率为7.5M,1s/7.5≈133ns 即传输一位数据的时间约为55纳秒(ns)8*133 =1.06us 符合WS2812
- 2*133 = 266ns 6*133 = 799ns 符合WS281X芯片的通信时序。
- 1111 1100high level (十六进制:0XFC)表示WS281X的1码
- 1100 0000 low level (十六进制:0XC0)表示WS281X的0码
- 程序头文件部分: 通过宏的方式定义了灯珠个数和WS281X的0码和1码。
- */
- // _____
- // | |___| 1111 1100 high level
- // ___
- // | |_____| 1111 0000 low level
- #define Delay_ms(wait) delay_ms(wait)
- #define WS2812B //选择WS2812B
- #define PIXEL_NUM1 2 //RGB灯的数量 //灯带1灯数(组数)
- #define PIXEL_NUM2 2 //灯带2灯数(组数)
- #define PIXEL_NUM3 2 //灯带3灯数(组数)
- #define PIXEL_NUM4 2 //灯带4灯数(组数)
- #define PIXEL_NUM (PIXEL_NUM1+PIXEL_NUM2+PIXEL_NUM3+PIXEL_NUM4)//灯数统计
- #define GRB 24 //3*8 //Red8位 Green8位 Blue8位 加起来24位
- #ifdef WS2812B
- #define WS_HIGH 0xFC
- #endif
- #define WS_LOW 0xC0
- /*
- R G B
- 红: 255 0 0
- 黄: 255 255 0
- 绿: 0 255 0
- 青: 0 255 255
- 蓝: 0 0 255
- 紫: 255 0 255
- 白: 255 255 255
- */
- typedef union _rgbPixelBuffer
- {
- struct
- {
- uint8_t PixelBuffer1[PIXEL_NUM1][GRB];
- uint8_t PixelBuffer2[PIXEL_NUM2][GRB];
- uint8_t PixelBuffer3[PIXEL_NUM3][GRB];
- uint8_t PixelBuffer4[PIXEL_NUM4][GRB];
- }buff;
- uint8_t All_Buffer[PIXEL_NUM][GRB];
- uint8_t Buffer[PIXEL_NUM*GRB];
- }RGB_PixelBuffer,*PRGB_PixelBuffer;
- void WS281x_Init(void);
- void WS281x_CloseAll(void);
- uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue);
- void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor);
- void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);
- void WS281x_Show(void);
- void WS281x_RainbowCycle(uint8_t wait);
- 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);
- void RGB_DEBUG_TEST(void);
- #endif
源文件,由于没有调通PDMA,故代码中屏蔽了PDMA部分代码
- #include "spi_dma_ws2812.h"
- #include "string.h"
- RGB_PixelBuffer pixelBuffer;
- uint32_t SPI_clock;
-
- void WS281x_Init(void)
- {
- SPI_InitTypeDef SPI_InitStructure;
- // PDMACH_InitTypeDef PDMACH_InitStructure;
- CKCU_PeripClockConfig_TypeDef CKCUClock = {{0}};
- CKCUClock.Bit.SPI0 = 1;//打开SPI0的时钟
- CKCUClock.Bit.PA = 1;//打开GPIOA的时钟
- CKCUClock.Bit.AFIO = 1;//打开复用时钟
- // CKCUClock.Bit.PDMA = 1;//外设DMA时钟
- CKCU_PeripClockConfig(CKCUClock, ENABLE);//使能
- AFIO_GPxConfig(GPIO_PA,AFIO_PIN_5,AFIO_MODE_5);//GPIOA 的PA5复用为SPI0的MOSI
- /* Configure the GPIO pin*/
- GPIO_PullResistorConfig(HT_GPIOA, GPIO_PIN_5, GPIO_PR_DOWN);//配置下拉
- GPIO_DriveConfig(HT_GPIOA, GPIO_PIN_5,GPIO_DV_4MA);
- GPIO_DirectionConfig(HT_GPIOA, GPIO_PIN_5, GPIO_DIR_OUT);
-
- // PDMACH_InitStructure.PDMACH_DstAddr = (uint32_t)&HT_SPI0->DR;
- // PDMACH_InitStructure.PDMACH_SrcAddr = (uint32_t)&pixelBuffer;
- // PDMACH_InitStructure.PDMACH_AdrMod = SRC_ADR_LIN_INC | DST_ADR_FIX;
- // PDMACH_InitStructure.PDMACH_DataSize = WIDTH_8BIT;
- // PDMACH_InitStructure.PDMACH_Priority = VH_PRIO;
- // PDMACH_InitStructure.PDMACH_BlkCnt = 1;
- // PDMACH_InitStructure.PDMACH_BlkLen = 24;
- // PDMA_Config(PDMA_CH1, &PDMACH_InitStructure);
- // PDMA_EnaCmd(PDMA_CH1, ENABLE);
- // PDMA_ClearFlag(PDMA_CH1, PDMA_FLAG_TC | PDMA_INT_GE);
- // PDMA_IntConfig(PDMA_CH1, (PDMA_INT_GE | PDMA_INT_TC), ENABLE);
- /* SPI Configuration*/
- SPI_DeInit(HT_SPI0);
- SPI_InitStructure.SPI_Mode = SPI_MASTER;
- SPI_InitStructure.SPI_FIFO = SPI_FIFO_DISABLE;
- SPI_InitStructure.SPI_DataLength = SPI_DATALENGTH_8;
- SPI_InitStructure.SPI_SELMode = SPI_SEL_SOFTWARE;
- SPI_InitStructure.SPI_SELPolarity = SPI_SELPOLARITY_HIGH;
- SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_HIGH;
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_SECOND;
- SPI_InitStructure.SPI_TxFIFOTriggerLevel = 0;
- SPI_InitStructure.SPI_ClockPrescaler = 8;
- SPI_Init(HT_SPI0, &SPI_InitStructure);
- SPI_SoftwareSELCmd(HT_SPI0,SPI_SEL_ACTIVE);
- // SPI_PDMACmd(HT_SPI0,SPI_PDMAREQ_TX,ENABLE);
- SPI_Cmd(HT_SPI0,ENABLE);
- WS281x_CloseAll(); //关闭全部的灯
- delay_ms(100); //关闭全部的灯需要一定的时间
-
- }
- //uint16_t dATA;
- //更新颜色显示(在设定颜色后将颜色数据存入缓存只有执行该函数后才会进行显示)
- void WS281x_Show(void)
- {
- // //关闭通道 才能重新写入传输值
- // PDMA_EnaCmd(PDMA_CH1, DISABLE);
- // SPI_Cmd(HT_SPI0,DISABLE);
- // PDMA_TranSizeConfig(PDMA_CH1,1,24);
- // //开启通道
- // SPI_Cmd(HT_SPI0,ENABLE);
- // PDMA_EnaCmd(PDMA_CH1, ENABLE);
- // PDMA_SwTrigCmd(PDMA_CH1, ENABLE);
- // SPI_SoftwareSELCmd(HT_SPI0,SPI_SEL_ACTIVE);
- // while (PDMA_GetFlagStatus(PDMA_CH1, PDMA_FLAG_TC) != SET);
- // PDMA_ClearFlag(PDMA_CH1, PDMA_FLAG_GE|PDMA_FLAG_TC);
- // dATA=SPI_ReceiveData(HT_SPI0);
- int i = 0,k=0;
- // for(i=0;i<PIXEL_NUM;i++)
- // {
- // for(j=0;j<GRB;j++)
- // {
- // SPI_SendData(HT_SPI0,(u32)pixelBuffer.All_Buffer[i][j]);
- // for(k=0;k<10;k++)
- // {
- // __NOP();
- // }
- // }
- // }
- for(i=0;i<PIXEL_NUM*GRB;i++)
- {
- SPI_SendData(HT_SPI0,(u32)pixelBuffer.Buffer[i]);
- for(k=0;k<10;k++)
- {
- __NOP();
- }
- }
-
- }
- //配置完成之后便可以构思底层控制函数了,为了方便多个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 < GRB; ++j)
- {
- pixelBuffer.All_Buffer[i][j] = WS_LOW;
- }
- }
- WS281x_Show();
- }
- uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue)
- {
- return green << 16 | red << 8 | blue;
- }
- void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor)
- {
- uint8_t i;
- if(n < PIXEL_NUM)
- {
- for(i = 0; i < GRB; i++)
- {
- pixelBuffer.All_Buffer[n][i] = ((GRBColor << i) & 0x800000) ? WS_HIGH : WS_LOW;
- }
- }
- }
- 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 < GRB; ++i)
- {
- pixelBuffer.All_Buffer[n][i] = (((WS281x_Color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW);
- }
- }
- }
- // 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 wheel
- for(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 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();
- 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 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();
- Delay_ms(wait);
- for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {
- WS281x_SetPixelColor(i+q, 0); //turn every third pixel off
- }
- }
- }
- }
- // Slightly different, this makes the rainbow equally distributed throughout
- void WS2812_RainbowRotate(uint16_t wait) {
- uint16_t i, j;
- 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);
- }
- }
- //hsv和rgb互相转
- float retmax(float a, float b, float c) //求最大值
- {
- float max = 0;
- max = a;
- if (max < b)
- max = b;
- if (max < c)
- max = c;
- return max;
- }
- float retmin(float a, float b, float c) //求最小值
- {
- float min = 0;
- min = a;
- if (min > b)
- min = b;
- if (min > c)
- min = c;
- return min;
- }
- //R,G,B参数传入范围(0~100)
- //转换结果h(0~360),s(0~100),v(0~100)
- void rgb_to_hsv(uint16_t *H, uint16_t *S, uint16_t *V, uint8_t r, uint8_t g, uint8_t b) {
- float max = 0, min = 0;
- float R = (float)r;
- float G = (float)g;
- float B = (float)b;
- float h, s, v;
- R = R / 255.0;
- G = G / 255.0;
- B = B / 255.0;
- max = retmax(R, G, B);
- min = retmin(R, G, B);
- v = max;
- if (max == 0)
- s = 0;
- else
- s = 1 - (min / max);
- if (max == min)
- h = 0;
- else if (max == R && G >= B)
- h = 60 * ((G - B) / (max - min));
- else if (max == R && G < B)
- h = 60 * ((G - B) / (max - min)) + 360;
- else if (max == G)
- h = 60 * ((B - R) / (max - min)) + 120;
- else if (max == B)
- h = 60 * ((R - G) / (max - min)) + 240;
- v = v * 100;
- s = s * 100;
- *H = (int) h;
- *S = (int) s;
- *V = (int) v;
- }
- //参数入参范围h(0~360),s(0~100),v(0~100),这里要注意,要把s,v缩放到0~1之间
- //转换结果R(0~100),G(0~100),B(0~100),如需转换到0~255,只需把后面的乘100改成乘255
- void hsv_to_rgb(int h, int s, int v, uint8_t *r, uint8_t *g, uint8_t *b) {
- float C = 0, X = 0, Y = 0, Z = 0;
- int i = 0;
- float H = (float) (h);
- float S = (float) (s) / 100.0;
- float V = (float) (v) / 100.0;
- float R, G, B;
- if (S == 0)
- R = G = B = V;
- else {
- H = H / 60;
- i = (int) H;
- C = H - i;
- X = V * (1 - S);
- Y = V * (1 - S * C);
- Z = V * (1 - S * (1 - C));
- switch (i) {
- case 0:
- R = V;
- G = Z;
- B = X;
- break;
- case 1:
- R = Y;
- G = V;
- B = X;
- break;
- case 2:
- R = X;
- G = V;
- B = Z;
- break;
- case 3:
- R = X;
- G = Y;
- B = V;
- break;
- case 4:
- R = Z;
- G = X;
- B = V;
- break;
- case 5:
- R = V;
- G = X;
- B = Y;
- break;
- }
- }
- R = R * 255;
- G = G * 255;
- B = B * 255;
- *r = (int) R;
- *g = (int) G;
- *b = (int) B;
- }
- void RGB_DEBUG_TEST(void)
- {
- int i,j;
- for(i=0;i<3;i++)
- {
-
- for(j=0;j<8;j++)
- {
- if(i==0)
- {
- WS281x_ColorWipe(WS281x_Color(255, 0, 255), 10); // Red
- }
- else if(i==1)
- {
- WS281x_ColorWipe(WS281x_Color(0, 255, 255), 10); // Green
- }
- else
- {
- WS281x_ColorWipe(WS281x_Color(255, 255, 0), 10); // Blue
- }
- }
- }
- for(i=0;i<3;i++)
- {
-
- for(j=0;j<8;j++)
- {
- if(i==0)
- {
- WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
- }
- else if(i==1)
- {
- WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
- }
- else
- {
- WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
- }
- }
- }
- for(i=0;i<3;i++)
- {
-
- for(j=0;j<8;j++)
- {
- if(i==0)
- {
- WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
- Delay_ms(100);
- WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
- }
- else if(i==1)
- {
- WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
- Delay_ms(100);
- WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
- }
- else
- {
- WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
- Delay_ms(100);
- WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
- Delay_ms(100);
- WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
- Delay_ms(100);
- WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
- }
- }
- }
- WS2812_RainbowRotate(2);
- // Some example procedures showing how to display to the pixels:
- WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
- WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
- WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
- WS281x_TheaterChase(WS281x_Color(127, 127, 127), 10); // White
- WS281x_TheaterChase(WS281x_Color(127, 0, 0), 10); // Red
- WS281x_TheaterChase(WS281x_Color(0, 0, 127), 10); // Blue
- WS281x_Rainbow(10);
- WS281x_RainbowCycle(10);
- WS281x_TheaterChaseRainbow(10);
- WS281x_CloseAll();
- }
五、实物效果展示
