在线时间4067 小时
UID3441752
注册时间2017-11-21
NXP金币759431
TA的每日心情 | 开心 2024-3-26 15:16 |
---|
签到天数: 266 天 [LV.8]以坛为家I
管理员
- 积分
- 32004
- 最后登录
- 2024-4-9
|
基于LPC54114的声音氛围灯
作者:不是茄子
项目简介:
本项目基于万利LPC54110开发板,通过DMIC子系统实现音频信号16ksps的拾取,并利用该信号进行FFT变换,将8kHz内频谱信息显示在8×8的WS2812面板上,通过SPI接口完成对WS2812数据通信,实现声音氛围灯。
直接看视频介绍:
万利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)。
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的共性技术时什么呢?
|
|