NXP LPC54114双核MCU开发板:基于板载麦克风的声音氛围灯
万利LPC54114开发板资源非常丰富,板载音频采集及播放芯片、MEMS麦克风、SD卡槽等,特别适合视频音频引用的开发。本项目将利用LPC54114内置dmic外设采集麦克风信号,并通过WS2812显示声音频谱。
标签
WS2812
频谱
不是茄子
更新2022-06-07
中国科学技术大学
865

概述:

万利LPC54110开发板板载LPC54114高性能双核MCU,板载数字麦克风芯片,TF卡以及丰富的外设接口。LPC54114内部带有数字麦克风子系统DMIC,能够直接实现PDM、I2S等数字音频方面的应用。本项目基于万利LPC54110开发板,通过DMIC子系统实现音频信号16ksps的拾取,并利用该信号进行FFT变换,将8kHz内频谱信息显示在8×8的WS2812面板上,实现声音氛围灯。限于时间和作者水平,该项目仅仅呈现出了该芯片的一小部分功能。

系统原理:

代码介绍: WS2812驱动

WS2812不同于其它显示驱动芯片,其仅采用一个管脚,通过高低电平持续时间长短来区分一个code(0或1)。Code0时,高电平≈400ns,低电平≈800ns;Code1时,高电平≈800ns,低电平≈400ns;RESET时,低电平大于50μs。

结合其数据特征,可以直接使用SPI中MOSI管脚,无数据时,MOSI一直输出低电平,等效于WS2812的RESET信号,需要指出的是,该RESET信号并不会清除显示,只是让各个WS2812重新进入数据采集状态。而在发送数据时,则一次性发送全部显示数据。由于SPI在时钟输出前MOSI会提前两个时钟周期上线,因此,SPI发送数据的第一个字节为0x00,显示数据从第二个字节开始填充。

考虑使用3个bit数据来表示一个code,WS2812为GRB24bit颜色模式,因此,一个WS2812的颜色需要24codes = 72bits=9bytes的数据。利用共用体数据结构,按照大端在前规则,设置如下数据结构。

union WS2812_pixelData //Stand for one pixel data for WS2812

{

unsigned int box; //4 byte

struct

{

unsigned int pixelBit0:3;

unsigned int pixelBit1:3;

unsigned int pixelBit2:3;

unsigned int pixelBit3:3;

unsigned int pixelBit4:3;

unsigned int pixelBit5:3;

unsigned int pixelBit6:3;

unsigned int pixelBit7:3;

}pixelByteX; //Share space of box, 4 byte, but 24bit used

unsigned char byteX[3]; //used for GRB

}WSpData[3];

根据数据结构开始进行底层显存masterTxData填充,发送的第一个字节为全0数据,后续字节按照应用层显存WS_masterTxDataX结合位段操作填充WS2812_CODE1(0x06,0b‘110)或者WS2812_CODE0(0x04,0b‘100)。

masterTxData[0] = 0x00;

for(i = 0;i < (TRANSFER_SIZE / 9);i++)

{

//Green pixel byte

G_pixel = ((*(WS_masterTxDataX + i * 3 + 2)) > WS_brightness) ? (WS_brightness) : (*(WS_masterTxDataX + i * 3 + 2));

WSpData[2].pixelByteX.pixelBit7 = ((0x01 << 7) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit6 = ((0x01 << 6) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit5 = ((0x01 << 5) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit4 = ((0x01 << 4) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit3 = ((0x01 << 3) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit2 = ((0x01 << 2) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit1 = ((0x01 << 1) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[2].pixelByteX.pixelBit0 = ((0x01 << 0) & (G_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

//Red pixel byte

R_pixel = ((*(WS_masterTxDataX + i * 3 + 1)) > WS_brightness) ? (WS_brightness) : (*(WS_masterTxDataX + i * 3 + 1));

WSpData[1].pixelByteX.pixelBit7 = ((0x01 << 7) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit6 = ((0x01 << 6) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit5 = ((0x01 << 5) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit4 = ((0x01 << 4) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit3 = ((0x01 << 3) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit2 = ((0x01 << 2) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit1 = ((0x01 << 1) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[1].pixelByteX.pixelBit0 = ((0x01 << 0) & (R_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

//Blue pixel byte

B_pixel = ((*(WS_masterTxDataX + i * 3 + 0)) > WS_brightness) ? (WS_brightness) : (*(WS_masterTxDataX + i * 3 + 0));

WSpData[0].pixelByteX.pixelBit7 = ((0x01 << 7) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit6 = ((0x01 << 6) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit5 = ((0x01 << 5) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit4 = ((0x01 << 4) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit3 = ((0x01 << 3) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit2 = ((0x01 << 2) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit1 = ((0x01 << 1) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

WSpData[0].pixelByteX.pixelBit0 = ((0x01 << 0) & (B_pixel)) ? (WS2812_CODE1) : (WS2812_CODE0);

masterTxData[9 * i + 1] = WSpData[2].byteX[2];

masterTxData[9 * i + 2] = WSpData[2].byteX[1];

masterTxData[9 * i + 3] = WSpData[2].byteX[0];

masterTxData[9 * i + 4] = WSpData[1].byteX[2];

masterTxData[9 * i + 5] = WSpData[1].byteX[1];

masterTxData[9 * i + 6] = WSpData[1].byteX[0];

masterTxData[9 * i + 7] = WSpData[0].byteX[2];

masterTxData[9 * i + 8] = WSpData[0].byteX[1];

masterTxData[9 * i + 9] = WSpData[0].byteX[0];

}

/* Set up handle for spi master */

SPI_MasterTransferCreateHandleDMA(WS2812_SPI_MASTER, &masterHandle, SPI_MasterUserCallback, NULL, &masterTxHandle,

&masterRxHandle);

/* Start master transfer */

masterXfer.txData = (uint8_t *)&masterTxData;

masterXfer.rxData = (uint8_t *)&masterRxData;

masterXfer.dataSize = TRANSFER_SIZE * sizeof(masterTxData[0]);

masterXfer.configFlags = kSPI_FrameAssert;

if (kStatus_Success != SPI_MasterTransferDMA(WS2812_SPI_MASTER, &masterHandle, &masterXfer))

{

PRINTF("There is an error when start SPI_MasterTransferDMA \r\n ");

}

/* Wait until transfer completed */

while (!isTransferCompleted)

{

}

SysTick_DelayTicks(4); //ms

DMIC子系统

DMIC子系统时LPC54114的一个特色功能,能够实现PDM、PCM、I2S数据接口之间的数据直通,大幅降低了数据驱动开发的工作量。其初始化代码以及传输启动代码如下所示。

void DMICx_Config(void)

{

dmic_channel_config_t dmic_channel_cfg;

//////////DMIC Init//////////

dmic_channel_cfg.divhfclk = kDMIC_PdmDiv1;

dmic_channel_cfg.osr = 25U;

dmic_channel_cfg.gainshft = 3U;

dmic_channel_cfg.preac2coef = kDMIC_CompValueZero;

dmic_channel_cfg.preac4coef = kDMIC_CompValueZero;

dmic_channel_cfg.dc_cut_level = kDMIC_DcCut155;

dmic_channel_cfg.post_dc_gain_reduce = 1;

dmic_channel_cfg.saturate16bit = 1U;

dmic_channel_cfg.sample_rate = kDMIC_PhyFullSpeed;

#if defined(FSL_FEATURE_DMIC_CHANNEL_HAS_SIGNEXTEND) && (FSL_FEATURE_DMIC_CHANNEL_HAS_SIGNEXTEND)

dmic_channel_cfg.enableSignExtend = true;

#endif

DMIC_Init(DMIC0);

#if !(defined(FSL_FEATURE_DMIC_HAS_NO_IOCFG) && FSL_FEATURE_DMIC_HAS_NO_IOCFG)

DMIC_SetIOCFG(DMIC0, kDMIC_PdmDual);

#endif

DMIC_Use2fs(DMIC0, true);

DMIC_SetOperationMode(DMIC0, kDMIC_OperationModeDma);

DMIC_ConfigChannel(DMIC0, kDMIC_Channel0, kDMIC_Left, &dmic_channel_cfg);

DMIC_FifoChannel(DMIC0, kDMIC_Channel0, FIFO_DEPTH, true, true);

DMIC_EnableChannelDma(DMIC0, APP_DMIC_CHANNEL, true);

DMIC_ConfigChannel(DMIC0, APP_DMIC_CHANNEL, kDMIC_Left, &dmic_channel_cfg);

DMIC_FifoChannel(DMIC0, APP_DMIC_CHANNEL, FIFO_DEPTH, true, true);

DMIC_EnableChannnel(DMIC0, APP_DMIC_CHANNEL_ENABLE);

PRINTF("Configure DMA\r\n");

// DMA_Init(DMA0);

DMA_EnableChannel(DMA0, APP_DMAREQ_CHANNEL);

DMA_SetChannelPriority(DMA0, DMAREQ_DMIC0, kDMA_ChannelPriority1);

/* Request dma channels from DMA manager. */

DMA_CreateHandle(&g_dmicRxDmaHandle, DMA0, APP_DMAREQ_CHANNEL);

/* Create DMIC DMA handle. */

DMIC_TransferCreateHandleDMA(DMIC0, &g_dmicDmaHandle, DMICx_UserCallback, NULL, &g_dmicRxDmaHandle);

}

void DMICx_start(void)

{

receiveXfer.dataSize = 2 * BUFFER_LENGTH;

receiveXfer.data = (uint16_t *)g_rxBuffer;//DmicState.Buffer;//(uint16_t *)testbuffer;//

DMIC_TransferReceiveDMA(DMIC0, &g_dmicDmaHandle, &receiveXfer, kDMIC_Channel0);

DMIC_EnableChannnel(DMIC0, DMIC_CHANEN_EN_CH0(1));

}

需要注意的是,由于DMIC和SPI均使用了DMA数据传输功能,因此,在初始化和操作时需要注意不要对DMA0重复初始化,另外,SPI传输完成后不能调用DMA_Deinit(WS2812_DMA),这样会关闭所有的DMA传输导致程序卡死。

管脚配置与时钟配置

该项目利用MCUXpresso IDE进行开发,该工具类似与STMCubeIDE,能够进行以GUI形式配置GPIO、时钟、外设等。配置时钟如下图:新建BOARD_BootClockPLL150M功能组,并配置相应的时钟。

快速傅里叶变换(FFT)

该部分代码直接搬运Arduino中的FFT例程,为了实现比较短的采样间隔,使用800kHz时钟频率,并降低采样点数为128点,虽然,频率精度变低,但是一个完整显示和处理周期时间相比于256点由45ms降低至26ms,能够较快响应外部声音变化。

在FFT部分,对计算得到的频域功率谱进行分段求和、归一,得到WS2812显示面板每一列像素数量,之后在主程序中调用WS2812_ShowLineDMA(Red,Blue4);函数进行显示。

小结:

氛围灯一直时我想做的项目之一,然而,因为各种各样的借口一拖再拖。这次,借着硬禾学堂给的机会,一鼓作气,实现了声音氛围灯的雏形,为后续继续开展相关创作奠定基础,树立信心。本次项目的感受:

  1. MCU领域日新月异,有各种各样强大功能的MCU陆续问世。身处此洪流之中,虽有官方例程加持,但核心还是ARM、RISC-V等内核原理和典型外设(DMA等)的工作原理、调试方法。
  2. 学会如何使用别人的轮子。别人的“轮子”,特别是开源平台上的,代码规范,水平高,学习他们的代码要强于自己一点一点写,做出来的功能也要更丰富。
  3. 为了扩展产品的应用,各个厂商制作了丰富的教程、例程、IDE等,加快了产品投放市场的速度。那么,嵌入式MCU的共性技术时什么呢?
附件下载
Audio_Spectrum_GRBled_20220531.zip
基于LPC54114开发板的声音氛围灯项目 - 20220531.pdf
团队介绍
菜鸟创客一枚,处于一个转折期,坚持就是胜利
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号