首页 > 其他分享 >CH32V00+WS2812制作音乐谱显示

CH32V00+WS2812制作音乐谱显示

时间:2022-11-03 11:22:35浏览次数:67  
标签:blue buffer 音乐 ws2812 rgb CH32V00 bit WS2812 data

image
CH32V003,自带运放、SPI、PWM等外设模块,关键还便宜,便宜,便宜!
可以尝试来实现一个低成本的音乐谱显示。

1. 硬件设计

显示方面,使用64颗ws2812组成8*8的显示阵列,通过 CH32V003 的SPI模拟ws2812的时序进行驱动。

音频采集,使用CH32V003内部运放+麦克风即可。

整体硬件原理图如下:

image

image

2. SPI 驱动 ws2812

2.1 ws2812 简介

ws2812 将控制电路和RGB灯集成在一个封装中,通过级联,MCU使用 800Kbps 单线通讯即可完成 30fps 下1024个ws2812灯的控制。

通讯协议如下:

image

image

image

image

不同厂家生产的 ws2812 时序可能有区别,不过一般在误差范围内都可以识别。

2.2 SPI+DMA模拟ws2812时序

通过上一节对 ws2812 时序的介绍,完成一个 ws2812 控制需要发送 24bit GRB 的颜色数据,比特率为 800Kbps。

为了可以使用 SPI 模拟 ws2812 的时序,需要将 GRB 颜色数据中每 1 个 bit 膨胀为 4 个 bit,即:

  • 1 表示为:1110
  • 0 表示为:1000

这样 0 bit 中高电平约占 1/4,低电平约占 3/4。1 bit 中高电平约占 3/4,低电平约占 1/4。符合通讯协议。

此时,驱动一个 ws2812,SPI MOSI 引脚需要发送 4 x 24 bits = 12 Bytes。SPI 的时钟频率设置为 800 x 4 = 3.2MHz 左右。

CH32V003 主频设置为 48MHz, SPI 设置 16 分频,为 3MHz,在误差范围内,实测可以正常驱动WS2812。

代码如下:

/**
 * @brief 
 * 
 * @param ws2812_bit_buffer 
 * @param ws2812_byte_buffer 
 * 
 *  1bit 膨胀位 4bit
 *  1:1110
 *  0:1000
 */
void ws2812_set_grb(ws2812_bit_buffer_t *ws2812_bit_buffer, ws2812_byte_buffer_t *ws2812_byte_buffer)
{
    ws2812_byte_buffer_t ws2812_color_data = 
    {
        .green = ws2812_byte_buffer->green,
        .red   = ws2812_byte_buffer->red,
        .blue  = ws2812_byte_buffer->blue
    };

    for(uint8_t i = 0; i<4; i++)
    {
        ws2812_bit_buffer->green >>= 8;
        ws2812_bit_buffer->red   >>= 8;
        ws2812_bit_buffer->blue  >>= 8;
        /**
         * @brief 
         * 每 2 bit 的 RGB 数据膨胀为 1byte 的 spi 数据 
         * 每个 byte 中,第 8bit 和第 3bit 位固定为 1,第 4bit 和第 0bit 位固定为 0,剩余 bit 根据颜色值设定
         */
        ws2812_bit_buffer->green |= ( 0x88 | ((ws2812_color_data.green & 0x80)>>1) | ((ws2812_color_data.green & 0x80)>>2)| \
                                             ((ws2812_color_data.green & 0x40)>>4) | ((ws2812_color_data.green & 0x40)>>5) )<<24;
        ws2812_bit_buffer->red   |= ( 0x88 | ((ws2812_color_data.red & 0x80)>>1)   | ((ws2812_color_data.red & 0x80)>>2)| \
                                             ((ws2812_color_data.red & 0x40)>>4)   | ((ws2812_color_data.red & 0x40)>>5) )<<24;
        ws2812_bit_buffer->blue  |= ( 0x88 | ((ws2812_color_data.blue & 0x80)>>1)  | ((ws2812_color_data.blue & 0x80)>>2)| \
                                             ((ws2812_color_data.blue & 0x40)>>4)  | ((ws2812_color_data.blue & 0x40)>>5) )<<24;

        ws2812_color_data.green <<= 2;
        ws2812_color_data.red   <<= 2;
        ws2812_color_data.blue  <<= 2;                              
    } 
}

MOSI 输出时序如下:

image

时序模拟正确后,就可以将需要显示的颜色数据准备好,通过DMA+SPI推出去即可。

2.3 将 HSV 转化为 RGB

HSV 表达彩色图像的方式由三个部分组成:

  • Hue(色调、色相)。Hue 用角度度量,取值范围为0~360°,表示色彩信息,即所处的光谱颜色的位置。如Hue=0 表示红色,Hue=120 表示绿色,Hue=240 表示蓝色等等
  • Saturation(饱和度、色彩纯净度)。饱和度表示颜色接近光谱色的程度。饱和度越高,说明颜色越深,越接近光谱色饱和度越低,说明颜色越浅,越接近白色。饱和度为0表示纯白色。取值范围为0~100%,值越大,颜色越饱和。
  • Value(明度)。 明度决定颜色空间中颜色的明暗程度,明度越高,表示颜色越明亮,范围是 0-100%。明度为0表示纯黑色(此时颜色最暗)。

image

HSV 转化成 RGB 的方法如下:

:::tip参考wiki

image

:::

因为HSV使用起来更加直观、方便,所以代码逻辑部分通常使用 HSV。但WS2812B 灯珠的驱动使用的是RGB,所以需要进行转换。

参考代码如下:

/**
 * @brief 将HSV颜色空间转换为RGB颜色空间
 * 
 * @param  h HSV颜色空间的H:色调。单位°,范围0~360。(Hue 调整颜色,0°-红色,120°-绿色,240°-蓝色,以此类推)
 * @param  s HSV颜色空间的S:饱和度。单位%,范围0~100。(Saturation 饱和度高,颜色深而艳;饱和度低,颜色浅而发白)
 * @param  v HSV颜色空间的V:明度。单位%,范围0~100。(Value 控制明暗,明度越高亮度越亮,越低亮度越低)
 * @param  rgb_buffer RGB值的指针
 *
 * Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV
 *
 */
void ws2812_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, ws2812_byte_buffer_t *rgb_buffer)
{
    h %= 360; // h -> [0,360]
    uint32_t rgb_max = v * 2.55f;
    uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;

    uint32_t i = h / 60;
    uint32_t diff = h % 60;

    // RGB adjustment amount by hue
    uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;

    switch (i) {
    case 0:
        rgb_buffer->red = rgb_max;
        rgb_buffer->green = rgb_min + rgb_adj;
        rgb_buffer->blue = rgb_min;
        break;
    case 1:
        rgb_buffer->red = rgb_max - rgb_adj;
        rgb_buffer->green = rgb_max;
        rgb_buffer->blue = rgb_min;
        break;
    case 2:
        rgb_buffer->red = rgb_min;
        rgb_buffer->green = rgb_max;
        rgb_buffer->blue = rgb_min + rgb_adj;
        break;
    case 3:
        rgb_buffer->red = rgb_min;
        rgb_buffer->green = rgb_max - rgb_adj;
        rgb_buffer->blue = rgb_max;
        break;
    case 4:
        rgb_buffer->red = rgb_min + rgb_adj;
        rgb_buffer->green = rgb_min;
        rgb_buffer->blue = rgb_max;
        break;
    default:
        rgb_buffer->red = rgb_max;
        rgb_buffer->green = rgb_min;
        rgb_buffer->blue = rgb_max - rgb_adj;
        break;
    }
}

使用HSV,可以轻松实现呼吸灯的效果:

image

3. 麦克风采集音频

3.1 OPA+ADC+DMA 采集音频

因为 CH32V003 内部自带 OPA 运放,所以外围只需接上麦克风,搭好放大电路,即可开启 ADC 采集数据。

考虑到平常我们听到的音乐频率一般都低于5KHz,所以将 ADC 的采样频率设置为10KHz。

ADC 通过定时器 TRGO 事件触发采集。

启用 DMA 搬运 ADC 采集结果。

3.2 FFT 分析频谱

音乐谱,最重要的还得分析出频谱,这时候就得靠 FFT 了,参考了网上大牛的超简洁 FFT 算法,代码如下:

点击查看代码
/* fix_fft.c - Fixed-point in-place Fast Fourier Transform  */
/*
  All data are fixed-point short integers, in which -32768
  to +32768 represent -1.0 to +1.0 respectively. Integer
  arithmetic is used for speed, instead of the more natural
  floating-point.
  For the forward FFT (time -> freq), fixed scaling is
  performed to prevent arithmetic overflow, and to map a 0dB
  sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq
  coefficients. The return value is always 0.
  For the inverse FFT (freq -> time), fixed scaling cannot be
  done, as two 0dB coefficients would sum to a peak amplitude
  of 64K, overflowing the 32k range of the fixed-point integers.
  Thus, the fix_fft() routine performs variable scaling, and
  returns a value which is the number of bits LEFT by which
  the output must be shifted to get the actual amplitude
  (i.e. if fix_fft() returns 3, each value of fr[] and fi[]
  must be multiplied by 8 (2**3) for proper scaling.
  Clearly, this cannot be done within fixed-point short
  integers. In practice, if the result is to be used as a
  filter, the scale_shift can usually be ignored, as the
  result will be approximately correctly normalized as is.
  Written by:  Tom Roberts  11/8/89
  Made portable:  Malcolm Slaney 12/15/94 [email protected]
  Enhanced:  Dimitrios P. Bouras  14 Jun 2006 [email protected]
*/
#include "fix_fft.h"

#define N_WAVE      1024    /* full length of Sinewave[] */
#define LOG2_N_WAVE 10      /* log2(N_WAVE) */

/*
  Henceforth "short" implies 16-bit word. If this is not
  the case in your architecture, please replace "short"
  with a type definition which *is* a 16-bit word.
*/

/*
  Since we only use 3/4 of N_WAVE, we define only
  this many samples, in order to conserve data space.
*/
const int16_t Sinewave[N_WAVE-N_WAVE/4] = {
      0,    201,    402,    603,    804,   1005,   1206,   1406,
   1607,   1808,   2009,   2209,   2410,   2610,   2811,   3011,
   3211,   3411,   3611,   3811,   4011,   4210,   4409,   4608,
   4807,   5006,   5205,   5403,   5601,   5799,   5997,   6195,
   6392,   6589,   6786,   6982,   7179,   7375,   7571,   7766,
   7961,   8156,   8351,   8545,   8739,   8932,   9126,   9319,
   9511,   9703,   9895,  10087,  10278,  10469,  10659,  10849,
  11038,  11227,  11416,  11604,  11792,  11980,  12166,  12353,
  12539,  12724,  12909,  13094,  13278,  13462,  13645,  13827,
  14009,  14191,  14372,  14552,  14732,  14911,  15090,  15268,
  15446,  15623,  15799,  15975,  16150,  16325,  16499,  16672,
  16845,  17017,  17189,  17360,  17530,  17699,  17868,  18036,
  18204,  18371,  18537,  18702,  18867,  19031,  19194,  19357,
  19519,  19680,  19840,  20000,  20159,  20317,  20474,  20631,
  20787,  20942,  21096,  21249,  21402,  21554,  21705,  21855,
  22004,  22153,  22301,  22448,  22594,  22739,  22883,  23027,
  23169,  23311,  23452,  23592,  23731,  23869,  24006,  24143,
  24278,  24413,  24546,  24679,  24811,  24942,  25072,  25201,
  25329,  25456,  25582,  25707,  25831,  25954,  26077,  26198,
  26318,  26437,  26556,  26673,  26789,  26905,  27019,  27132,
  27244,  27355,  27466,  27575,  27683,  27790,  27896,  28001,
  28105,  28208,  28309,  28410,  28510,  28608,  28706,  28802,
  28897,  28992,  29085,  29177,  29268,  29358,  29446,  29534,
  29621,  29706,  29790,  29873,  29955,  30036,  30116,  30195,
  30272,  30349,  30424,  30498,  30571,  30643,  30713,  30783,
  30851,  30918,  30984,  31049,  31113,  31175,  31236,  31297,
  31356,  31413,  31470,  31525,  31580,  31633,  31684,  31735,
  31785,  31833,  31880,  31926,  31970,  32014,  32056,  32097,
  32137,  32176,  32213,  32249,  32284,  32318,  32350,  32382,
  32412,  32441,  32468,  32495,  32520,  32544,  32567,  32588,
  32609,  32628,  32646,  32662,  32678,  32692,  32705,  32717,
  32727,  32736,  32744,  32751,  32757,  32761,  32764,  32766,
  32767,  32766,  32764,  32761,  32757,  32751,  32744,  32736,
  32727,  32717,  32705,  32692,  32678,  32662,  32646,  32628,
  32609,  32588,  32567,  32544,  32520,  32495,  32468,  32441,
  32412,  32382,  32350,  32318,  32284,  32249,  32213,  32176,
  32137,  32097,  32056,  32014,  31970,  31926,  31880,  31833,
  31785,  31735,  31684,  31633,  31580,  31525,  31470,  31413,
  31356,  31297,  31236,  31175,  31113,  31049,  30984,  30918,
  30851,  30783,  30713,  30643,  30571,  30498,  30424,  30349,
  30272,  30195,  30116,  30036,  29955,  29873,  29790,  29706,
  29621,  29534,  29446,  29358,  29268,  29177,  29085,  28992,
  28897,  28802,  28706,  28608,  28510,  28410,  28309,  28208,
  28105,  28001,  27896,  27790,  27683,  27575,  27466,  27355,
  27244,  27132,  27019,  26905,  26789,  26673,  26556,  26437,
  26318,  26198,  26077,  25954,  25831,  25707,  25582,  25456,
  25329,  25201,  25072,  24942,  24811,  24679,  24546,  24413,
  24278,  24143,  24006,  23869,  23731,  23592,  23452,  23311,
  23169,  23027,  22883,  22739,  22594,  22448,  22301,  22153,
  22004,  21855,  21705,  21554,  21402,  21249,  21096,  20942,
  20787,  20631,  20474,  20317,  20159,  20000,  19840,  19680,
  19519,  19357,  19194,  19031,  18867,  18702,  18537,  18371,
  18204,  18036,  17868,  17699,  17530,  17360,  17189,  17017,
  16845,  16672,  16499,  16325,  16150,  15975,  15799,  15623,
  15446,  15268,  15090,  14911,  14732,  14552,  14372,  14191,
  14009,  13827,  13645,  13462,  13278,  13094,  12909,  12724,
  12539,  12353,  12166,  11980,  11792,  11604,  11416,  11227,
  11038,  10849,  10659,  10469,  10278,  10087,   9895,   9703,
   9511,   9319,   9126,   8932,   8739,   8545,   8351,   8156,
   7961,   7766,   7571,   7375,   7179,   6982,   6786,   6589,
   6392,   6195,   5997,   5799,   5601,   5403,   5205,   5006,
   4807,   4608,   4409,   4210,   4011,   3811,   3611,   3411,
   3211,   3011,   2811,   2610,   2410,   2209,   2009,   1808,
   1607,   1406,   1206,   1005,    804,    603,    402,    201,
      0,   -201,   -402,   -603,   -804,  -1005,  -1206,  -1406,
  -1607,  -1808,  -2009,  -2209,  -2410,  -2610,  -2811,  -3011,
  -3211,  -3411,  -3611,  -3811,  -4011,  -4210,  -4409,  -4608,
  -4807,  -5006,  -5205,  -5403,  -5601,  -5799,  -5997,  -6195,
  -6392,  -6589,  -6786,  -6982,  -7179,  -7375,  -7571,  -7766,
  -7961,  -8156,  -8351,  -8545,  -8739,  -8932,  -9126,  -9319,
  -9511,  -9703,  -9895, -10087, -10278, -10469, -10659, -10849,
 -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353,
 -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827,
 -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268,
 -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672,
 -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036,
 -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357,
 -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631,
 -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855,
 -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027,
 -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143,
 -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201,
 -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198,
 -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132,
 -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001,
 -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802,
 -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534,
 -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195,
 -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783,
 -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297,
 -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735,
 -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097,
 -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382,
 -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588,
 -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717,
 -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766,
};

/*
  FIX_MPY() - fixed-point multiplication & scaling.
  Substitute inline assembly for hardware-specific
  optimization suited to a particluar DSP processor.
  Scaling ensures that result remains 16-bit.
*/
int16_t FIX_MPY(int16_t a, int16_t b)
{
	/* shift right one less bit (i.e. 15-1) */
	int c = ((int)a * (int)b) >> 14;
	/* last bit shifted out = rounding-bit */
	b = c & 0x01;
	/* last shift + rounding bit */
	a = (c >> 1) + b;
	return a;
}

/*
  fix_fft() - perform forward/inverse fast Fourier transform.
  fr[n],fi[n] are real and imaginary arrays, both INPUT AND
  RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to
  0 for forward transform (FFT), or 1 for iFFT.
*/
int fix_fft(int16_t fr[], int16_t fi[], int16_t m, int16_t inverse)
{
	int16_t mr, nn, i, j, l, k, istep, n, scale, shift;
	int16_t qr, qi, tr, ti, wr, wi;

	n = 1 << m;

	/* max FFT size = N_WAVE */
	if (n > N_WAVE)
		return -1;

	mr = 0;
	nn = n - 1;
	scale = 0;

	/* decimation in time - re-order data */
	for (m=1; m<=nn; ++m) {
		l = n;
		do {
			l >>= 1;
		} while (mr+l > nn);
		mr = (mr & (l-1)) + l;

		if (mr <= m)
			continue;
		tr = fr[m];
		fr[m] = fr[mr];
		fr[mr] = tr;
		ti = fi[m];
		fi[m] = fi[mr];
		fi[mr] = ti;
	}

	l = 1;
	k = LOG2_N_WAVE-1;
	while (l < n) {
		if (inverse) {
			/* variable scaling, depending upon data */
			shift = 0;
			for (i=0; i<n; ++i) {
				j = fr[i];
				if (j < 0)
					j = -j;
				m = fi[i];
				if (m < 0)
					m = -m;
				if (j > 16383 || m > 16383) {
					shift = 1;
					break;
				}
			}
			if (shift)
				++scale;
		} else {
			/*
			  fixed scaling, for proper normalization --
			  there will be log2(n) passes, so this results
			  in an overall factor of 1/n, distributed to
			  maximize arithmetic accuracy.
			*/
			shift = 1;
		}
		/*
		  it may not be obvious, but the shift will be
		  performed on each data point exactly once,
		  during this pass.
		*/
		istep = l << 1;
		for (m=0; m<l; ++m) {
			j = m << k;
			/* 0 <= j < N_WAVE/2 */
			wr =  Sinewave[j+N_WAVE/4];
			wi = -Sinewave[j];
			if (inverse)
				wi = -wi;
			if (shift) {
				wr >>= 1;
				wi >>= 1;
			}
			for (i=m; i<n; i+=istep) {
				j = i + l;
				tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]);
				ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]);
				qr = fr[i];
				qi = fi[i];
				if (shift) {
					qr >>= 1;
					qi >>= 1;
				}
				fr[j] = qr - tr;
				fi[j] = qi - ti;
				fr[i] = qr + tr;
				fi[i] = qi + ti;
			}
		}
		--k;
		l = istep;
	}
	return scale;
}

/*
  fix_fftr() - forward/inverse FFT on array of real numbers.
  Real FFT/iFFT using half-size complex FFT by distributing
  even/odd samples into real/imaginary arrays respectively.
  In order to save data space (i.e. to avoid two arrays, one
  for real, one for imaginary samples), we proceed in the
  following two steps: a) samples are rearranged in the real
  array so that all even samples are in places 0-(N/2-1) and
  all imaginary samples in places (N/2)-(N-1), and b) fix_fft
  is called with fr and fi pointing to index 0 and index N/2
  respectively in the original array. The above guarantees
  that fix_fft "sees" consecutive real samples as alternating
  real and imaginary samples in the complex array.
*/
int fix_fftr(int16_t f[], int m, int inverse)
{
	int i, N = 1<<(m-1), scale = 0;
	int16_t tt, *fr=f, *fi=&f[N];

	if (inverse)
		scale = fix_fft(fi, fr, m-1, inverse);
	for (i=1; i<N; i+=2) {
		tt = f[N+i-1];
		f[N+i-1] = f[i];
		f[i] = tt;
	}
	if (! inverse)
		scale = fix_fft(fi, fr, m-1, inverse);
	return scale;
}
`int fix_fft(short fr[], short fi[], short m, short inverse)` 是 FFT 算法的计算函数,fr[] 是 ADC 采集到信号值的实部,fi[] 是 ADC 采集到信号值的虚部。经过 `fix_fft` 函数处理之后,fr[] 是 FFT 计算所得的实部,fi[] 是计算所得的虚部。

4. 效果演示

:::tip开源

项目开源地址: https://github.com/Taoyukai/ch32v003_ws2812_music

:::

标签:blue,buffer,音乐,ws2812,rgb,CH32V00,bit,WS2812,data
From: https://www.cnblogs.com/wahahahehehe/p/16853802.html

相关文章