WS281xUKit评估学习板入门指南

WS281xUKit评估学习板入门指南

第一部分、序

由于作者水平有限,文档和视频中难免有出错和讲得不好的地方,欢迎各位读者和观众善意地提出意见和建议,谢谢!

第二部分、硬件概述

WS2812B简介

  • WS2812B幻彩灯珠是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同,每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内部振荡器和12V高压可编程定电流控制部分,有效保证了像素点光的颜色高度一致。

  • 数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。

  • LED具有低电压驱动,环保节能,亮度高,散射角度大,一致性好,超低功率,超长寿命等优点。将控制电路集成于LED上面,电路变得更加简单,体积小,安装更加简便。

时序波形图

image

数据传输时间 (时序)

image

评估板原理图

提供PDF,如看不清请查阅PDF文档。
image

第三部分、基础篇

3.1 配置时序

本节将配置时序波形图,实现0码和1码波形输出

3.1.1硬件设计:

如下图,PC6可以配置SPI的MOSI,如下图所示
image

同时我们通过拨动开关可选PWM和SPI模式,如下图,并把DIN数据信号引出,方便抓取波形和驱动其他WS2812模块。
image

3.1.2 软件设计:

3.1.2.1初始化代码配置

如下代码是初始化PC6为SPI DMA模式,并且是SPI_Direction_1Line_Tx,

void ws281x_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //PORTA时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1时钟使能
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输

    /* PC6  SPI1_MOSI */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
	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_8;		//定义波特率预分频的值:波特率预分频值为8
	SPI_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 -> DATAR); //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); //关闭全部的灯需要一定的时间
}
3.1.2.2关于SPI时钟的配置

首先是系统时钟system_ch32v00x.c中,配置HSI:48Mhz,
image

那么SPI的8分频为6Mhz,

那么T=1/f=1/6=0.1666666666666667us;

SPI发送1个字节为8位,1位0.1666us,那么根据WS2812的波形图,

  • 1码:T1H≈T1L;那么0.1666us*4=0.666us;

  • 0码:T0H那么0.1666us*2=0.3333us;

​ 也就是说1码只要发0xF0,而0码发0xC0;

3.1.2.3WS2812驱动代码实现

pixelBuffer是要通过SPI DMA发送出去的缓存区,WS_HIGH和WS_LOW对应1码和0码,ws281x_closeAll是全发1码的,最后直接在main中while调用ws281x_closeAll

#define PIXEL_NUM 16

//硬件spi模拟ws2812时序(用spi的8位数据模拟ws281x的一位数据)
//要将系统时钟设置为48M,分频数设置为8,则SPI的通信频率为6M,传输一位数据的时间约为166纳秒(ns)
//4*166 = 666ns   2*166 = 333ns  符合WS281X芯片的通信时序。
//  _____   
// |     |___|   11110000  high level
//  ___         
// |   |_____|   11000000  low level

#define WS_HIGH 0XF0
#define WS_LOW  0XC0
uint8_t pixelBuffer[PIXEL_NUM][24] ;
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(); 
}

3.1.3 下载验证:

我们就可以把代码下载到开发板上,可以抓到0码如下,
image

ws281x_closeAll中WS_LOW改成WS_HIGH的得到1码时序如下,如无意外灯亮全白。
image

3.2 实现七彩跑马灯

上节实现0码和1码波形输出,成功关闭和点亮(全白)了2812,这节我们实现七彩跑马灯并介绍几个函数。

3.2.1软件设计:

3.2.1.1设置单个像素的颜色
/**
 * 设置单个像素的颜色
 *
 * @param n 像素索引
 * @param GRBcolor 颜色值,使用GRB格式
 */
void ws281x_setPixelColor(uint16_t n, uint32_t GRBcolor)
{
  uint8_t i;
  // 检查像素索引是否在有效范围内
  if(n < PIXEL_NUM)
  {
    // 遍历24位颜色值
    for(i = 0; i < 24; ++i)
    {
      // 提取颜色值的每个位并设置到像素缓冲区
      pixelBuffer[n][i] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW);
    }
  }
}
3.2.1.2以指定颜色进行填充动画效果
/**
 * 以指定颜色进行填充动画效果
 *
 * @param c 填充的颜色,使用GRB格式
 * @param wait 每个像素之间的延迟时间,以毫秒为单位
 */
void ws281x_colorWipe(uint32_t c, uint8_t wait) {
  // 遍历所有像素
  for(uint16_t i = 0; i < PIXEL_NUM; i++) {
    // 设置像素颜色
    ws281x_setPixelColor(i, c);

    // 更新LED显示
    ws281x_show();

    // 延迟指定时间
    Delay_Ms(wait);
  }
}

3.2.1.2以指定颜色进行填充动画效果
/**
 * 更新LED显示,使用DMA传输数据
 */
void ws281x_show(void)
{
    // 关闭USART1 TX DMA1 所指示的通道
    DMA_Cmd(DMA1_Channel3, DISABLE);

    // 清除DMA1通道3传输完成标志
    DMA_ClearFlag(DMA1_FLAG_TC3);

    // 设置DMA通道3的当前传输数据计数器为总像素数乘以每像素的位数(24位)
    DMA_SetCurrDataCounter(DMA1_Channel3, 24 * PIXEL_NUM);

    // 使能USART1 TX DMA1 所指示的通道
    DMA_Cmd(DMA1_Channel3, ENABLE);
}

3.2.2 下载验证:

我们就可以把代码下载到开发板上,可以看到赤橙黄绿青蓝紫循环跑马灯

3.3 实现theaterChase

上节我们实现七彩跑马灯并介绍几个函数,本节介绍theaterChase风格的闪烁灯光效果

3.3.1软件设计:

3.3.1.1影院风格的闪烁灯光效果
/**
 * 影院风格的闪烁灯光效果
 *
 * @param c 闪烁灯光的颜色,使用GRB格式
 * @param wait 每次闪烁之间的延迟时间,以毫秒为单位
 */
void ws281x_theaterChase(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) {  // 执行10个循环的闪烁效果
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) {
        ws281x_setPixelColor(i + q, c);    // 打开每隔三个像素的LED
      }
      ws281x_show();

      Delay_Ms(wait);

      for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) {
        ws281x_setPixelColor(i + q, 0);        // 关闭每隔三个像素的LED
      }
    }
  }
}

3.3.2 下载验证:

我们就可以把代码下载到开发板上,可以看到影院风格的闪烁灯光效果;

3.4 实现彩虹色环的变化

上节我们实现theaterChase灯,本节实现彩虹色环的变化灯光效果

3.4.1软件设计:

3.4.1.1ws281x_rainbow
/**
 * 实现彩虹色环的变化
 * @param 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);
  }
}
3.4.1.2 ws281x_wheel
// 根据给定的彩虹色环位置,返回对应的彩虹颜色值
uint32_t ws281x_wheel(uint8_t wheelPos) {
  // 将输入的位置映射为反向的颜色位置,以实现彩虹色环的旋转
  wheelPos = 255 - wheelPos;

  // 根据位置计算不同的彩虹颜色
  if(wheelPos < 85) {
    // 当位置小于 85 时,生成从红色到绿色的过渡色
    return ws281x_color(255 - wheelPos * 3, 0, wheelPos * 3);
  }
  if(wheelPos < 170) {
    // 当位置在 85 到 169 之间时,生成从绿色到蓝色的过渡色
    wheelPos -= 85;
    return ws281x_color(0, wheelPos * 3, 255 - wheelPos * 3);
  }

  // 当位置在 170 到 255 之间时,生成从蓝色到红色的过渡色
  wheelPos -= 170;
  return ws281x_color(wheelPos * 3, 255 - wheelPos * 3, 0);
}

3.4.2 下载验证:

我们就可以把代码下载到开发板上,可以看到彩虹色环的变化灯光效果;

3.5 实现循环的彩虹效果

上节实现彩虹色环的变化灯光效果,本节循环的彩虹效果;

3.5.1软件设计:

3.5.1.1ws281x_rainbowCycle
// Slightly different, this makes the rainbow equally distributed throughout
void ws281x_rainbowCycle(uint8_t wait) {
    static uint16_t j;  // 静态变量用于记住彩虹的当前位置
    uint16_t i;

    // 在彩虹色环上循环,一共进行 256 * 5 次循环
    while (j++ < 256 * 5) {
        for (i = 0; i < PIXEL_NUM; i++) {
            // 计算当前像素点的彩虹颜色,并设置颜色
            ws281x_setPixelColor(i, ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255));
        }

        // 显示当前设置的像素点颜色
        ws281x_show();

        // 延时一段时间,控制彩虹循环的速度
        Delay_Ms(wait);
    }
}

3.5.2 下载验证:

我们就可以把代码下载到开发板上,可以看到循环的彩虹灯光效果;

3.6 实现循环的彩虹效果

上节循环的彩虹效果,本节实现带有彩虹效果的剧院式爬行灯

3.6.1软件设计:

3.6.1.1 ws281x_theaterChaseRainbow
// 此函数使用WS281x RGB LED创建了一个彩虹色的剧院追逐效果。
// 函数接受一个名为 'wait' 的参数,用于确定每个动画步骤之间的延迟。
// 动画循环遍历了彩色轮中的所有256种颜色。

void ws281x_theaterChaseRainbow(uint8_t wait) {
  for (int j = 0; j < 256; j++) {     // 遍历彩色轮中的所有256种颜色
    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));    // 将每隔三个像素的颜色设置为彩色轮中的颜色
      }
      ws281x_show();                    // 显示像素的当前状态

      Delay_Ms(wait);                   // 等待指定的延迟时间

      for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) {
        ws281x_setPixelColor(i + q, 0);    // 关闭每隔三个像素
      }
    }
  }
}

3.6.2 下载验证:

我们就可以把代码下载到开发板上,彩虹效果的剧院式爬行灯效果;

posted @ 2023-08-14 22:19  LiJin_hh  阅读(124)  评论(0编辑  收藏  举报