完成任务一:
读取SD卡(SD卡自备)中的音频文件,使用板卡上的3.5mm音频接口播放音乐。
LPCXpresso55S69 开发板为评估和开发基于 Arm ® Cortex ® -M33 架构的 LPC55S6x MCU 提供了理想的平台。该板包括高性能板载调试探针、音频子系统和加速度计,并提供多种选项,用于添加用于网络、传感器、显示器和其他接口的现成附加板。
MCUXpresso 工具套件完全支持 LPCXpresso55S69,该套件提供设备驱动程序、中间件和示例以允许快速开发,以及配置工具和可选的免费 IDE。MCUXpresso 软件与开源 MCU 操作系统 FreeRTOS、来自 Arm 和 IAR 等流行工具供应商的工具兼容,并且 LPCXpresso55S69 还可与 SEGGER 和 P&E Micro 提供的流行调试探针一起使用。
芯片功能图
- LPC55S6x MCU家族是全球首款基于通用Cortex-M33的微控制器。
- 该高效率MCU家族采用Armv8-M架构,性能和高级安全功能达到新水平,包括TrustZone-M和协处理器扩展。LPC55S6x家族利用协处理器扩展型号,大幅提高信号处理效率,采用专有DSP加速器,使计算的时钟周期减少了10倍。还可选择使用第二个Cortex-M33内核,支持灵活地平衡高性能与功率效率。
- 此外,LPC55S6x MCU家族依托基于40nm NVM的处理技术,具备成本效益优势,提供广泛的可扩展封装和存储器选项,并提供强大的支持,包括MCUXpresso软件和工具生态系统及低成本开发板。
以上是凑字数的,下面开始正式开始:
首先是一个入门教程的推荐:https://blog.csdn.net/k331922164/category_9640818.html
因为本次任务,我参考的是其中的LPC55S69之FatFs_SDCard这篇文章,当然其实不太严谨,因为这篇博客其实是参考的是官方SDK,既然提到这里了,(软件的安装与LPC55s69的SDK下载参考直播教程即可)
需要提一下,上图中可能因为IDE版本的变迁发生了一些变化,安照下图即可:
下面是先点Add Folder
下面这步貌似可以跳过
之后就是新建fatfs_sdcard.h和fatfs_sdcard.c加入工程:
1、fatfs_sdcard.h
#ifndef FATFS_SDCARD_H_
#define FATFS_SDCARD_H_
#include <stdio.h>
#include <string.h>
#include "fsl_sd.h"
#include "fsl_debug_console.h"
#include "ff.h"
#include "diskio.h"
#include "fsl_sd_disk.h"
#include "board.h"
#include "pin_mux.h"
#include <stdbool.h>
#include "fsl_iocon.h"
/* buffer size (in byte) for read/write operations */
#define BUFFER_SIZE (100U)
extern FATFS g_fileSystem; /* File system object */
extern FIL g_fileObject; /* File object */
extern SDK_ALIGN(uint8_t g_bufferWrite[SDK_SIZEALIGN(BUFFER_SIZE, SDMMC_DATA_BUFFER_ALIGN_CACHE)],
MAX(SDMMC_DATA_BUFFER_ALIGN_CACHE, SDMMCHOST_DMA_BUFFER_ADDR_ALIGN));
extern SDK_ALIGN(uint8_t g_bufferRead[SDK_SIZEALIGN(BUFFER_SIZE, SDMMC_DATA_BUFFER_ALIGN_CACHE)],
MAX(SDMMC_DATA_BUFFER_ALIGN_CACHE, SDMMCHOST_DMA_BUFFER_ADDR_ALIGN));
extern const sdmmchost_detect_card_t s_sdCardDetect;
extern FRESULT error;
extern UINT bytesWritten;
extern UINT bytesRead;
extern const TCHAR driverNumberBuffer[];
extern TCHAR * fileName;
void fatfs_sdcard_init(void);
void fatfs_sdcard_read(void);
void fatfs_sdcard_write(void);
status_t sdcardWaitCardInsert(void);
#endif /* FATFS_SDCARD_H_ */
2、fatfs_sdcard.c
#include "fatfs_sdcard.h"
FATFS g_fileSystem; /* File system object */
FIL g_fileObject; /* File object */
SDK_ALIGN(uint8_t g_bufferWrite[SDK_SIZEALIGN(BUFFER_SIZE, SDMMC_DATA_BUFFER_ALIGN_CACHE)],
MAX(SDMMC_DATA_BUFFER_ALIGN_CACHE, SDMMCHOST_DMA_BUFFER_ADDR_ALIGN));
SDK_ALIGN(uint8_t g_bufferRead[SDK_SIZEALIGN(BUFFER_SIZE, SDMMC_DATA_BUFFER_ALIGN_CACHE)],
MAX(SDMMC_DATA_BUFFER_ALIGN_CACHE, SDMMCHOST_DMA_BUFFER_ADDR_ALIGN));
const sdmmchost_detect_card_t s_sdCardDetect = {
.cdType = BOARD_SD_DETECT_TYPE,
.cdTimeOut_ms = (~0U),
};
FRESULT error;
UINT bytesWritten;
UINT bytesRead;
const TCHAR driverNumberBuffer[3U] = {SDDISK + '0', ':', '/'};
TCHAR * fileName = "/config.txt";
void fatfs_sdcard_init(void) {
static uint8_t initFlag = 0;
if(initFlag == 0) {
CLOCK_EnableClock(kCLOCK_InputMux);
CLOCK_AttachClk(BOARD_SDIF_CLK_ATTACH);
CLOCK_SetClkDiv(kCLOCK_DivSdioClk, (uint32_t)(SystemCoreClock / FSL_FEATURE_SDIF_MAX_SOURCE_CLOCK + 1U), true);
initFlag = 1;
}
if (sdcardWaitCardInsert() != kStatus_Success) {
PRINTF("\r\nPlease insert a card into board.\r\n");
}
if (f_mount(&g_fileSystem, driverNumberBuffer, 0U)) {
PRINTF("Mount volume failed.\r\n");
}
#if (FF_FS_RPATH >= 2U)
error = f_chdrive((char const *)&driverNumberBuffer[0U]);
if (error) {
PRINTF("Change drive failed.\r\n");
}
#endif
}
void fatfs_sdcard_write(void) {
uint8_t i;
PRINTF("Write.\r\n");
error = f_open(&g_fileObject, (const TCHAR*)fileName, (FA_WRITE | FA_CREATE_ALWAYS));
if (error) {
if (error == FR_EXIST) {
PRINTF("File exists.\r\n");
} else {
PRINTF("Open file failed.\r\n");
}
fatfs_sdcard_init();
error = f_open(&g_fileObject, (const TCHAR*)fileName, (FA_WRITE | FA_CREATE_ALWAYS));
}
for(i=0;i<8;i++) { // Writting Bytes
g_bufferWrite[i] = '0' +i;
}
error = f_write(&g_fileObject, g_bufferWrite, sizeof(g_bufferWrite), &bytesWritten);
if ((error) || (bytesWritten != sizeof(g_bufferWrite))) {
PRINTF("Write file failed. \r\n");
}
if (f_close(&g_fileObject)) {
PRINTF("\r\nClose file failed.\r\n");
}
}
void fatfs_sdcard_read(void) {
uint8_t i;
PRINTF("Read.\r\n");
error = f_open(&g_fileObject, (const TCHAR*)fileName, FA_READ);
if (error) {
if (error == FR_EXIST) {
PRINTF("File exists.\r\n");
} else {
PRINTF("Open file failed.\r\n");
}
fatfs_sdcard_init();
error = f_open(&g_fileObject, (const TCHAR*)fileName, FA_READ);
}
if (f_lseek(&g_fileObject, 0U)) {
PRINTF("Set file pointer position failed. \r\n");
}
memset(g_bufferRead, 0U, sizeof(g_bufferRead));
error = f_read(&g_fileObject, g_bufferRead, sizeof(g_bufferRead), &bytesRead);
if ((error) || (bytesRead != sizeof(g_bufferRead))) {
PRINTF("Read file failed. \r\n");
}
for(i=0;i<8;i++) { // Read Bytes
PRINTF("%c",g_bufferRead[i]);
}
if (f_close(&g_fileObject)) {
PRINTF("\r\nClose file failed.\r\n");
}
}
status_t sdcardWaitCardInsert(void) {
g_sd.host.base = SD_HOST_BASEADDR;
g_sd.host.sourceClock_Hz = SD_HOST_CLK_FREQ;
g_sd.usrParam.cd = &s_sdCardDetect;
if (SD_HostInit(&g_sd) != kStatus_Success) {
PRINTF("\r\nSD host init fail\r\n");
return kStatus_Fail;
}
SD_PowerOffCard(g_sd.host.base, g_sd.usrParam.pwr);
if (SD_WaitCardDetectStatus(SD_HOST_BASEADDR, &s_sdCardDetect, true) == kStatus_Success) {
PRINTF("\r\nCard inserted.\r\n");
SD_PowerOnCard(g_sd.host.base, g_sd.usrParam.pwr);
} else {
PRINTF("\r\nCard detect fail.\r\n");
return kStatus_Fail;
}
return kStatus_Success;
}
SD卡部分就结束了,记得配置管脚文件。
之后就是I2S部分播放音乐,首先是将.flac文件(下载自网易云)转化为.wav文件(https://convertio.co/zh/download/b78091f7b2f336c7bc270b0f94747145e7fb1b/),下载时选择文件时最好选择一般音质(文件小),因为文件过大的话读取时总会出现问题。
I2S的对应芯片WM8904参考下面这篇文章:https://www.nxp.com.cn/docs/zh/application-note/AN12644.pdf
wm8904配置文件
wm8904_config_t wm8904Config = {
.i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock = BOARD_CODEC_I2C_CLOCK_FREQ},
.recordSource = kWM8904_RecordSourceLineInput,
.recordChannelLeft = kWM8904_RecordChannelLeft2,
.recordChannelRight = kWM8904_RecordChannelRight2,
.playSource = kWM8904_PlaySourceDAC,
.slaveAddress = WM8904_I2C_ADDRESS,
.protocol = kWM8904_ProtocolI2S,
.format = {.sampleRate = kWM8904_SampleRate11025Hz, .bitWidth = kWM8904_BitWidth16},
.mclk_HZ = DEMO_I2S_MASTER_CLOCK_FREQUENCY,
.master = false,
};
之后就是阅读官方SDK的代码。I2S播放按照官方代码即可。
打开文件并播放,换文件的话记得改名:
error = f_open(&g_fileObject, _T("/m1.wav"), (FA_READ | FA_OPEN_ALWAYS));
if (error)
{
if (error == FR_EXIST)
{
PRINTF("File exists.\r\n");
}
else
{
PRINTF("Open file failed.\r\n");
return -1;
}
}
StartSoundPlayback();
uint8_t sd_flag = 1;
int flag = 1;
GPIO_PortToggle(GPIO, BOARD_LED_PORT, 1u << BOARD_LED_PIN);
while (flag == 1)
{
if (Music_flag != sd_flag)
{
sd_flag = Music_flag;
if (Music_flag == 0)
{
error = f_read(&g_fileObject, tf_Music1, sizeof(tf_Music1),
&bytesRead);
if ((error) || (bytesRead != sizeof(tf_Music1)))
{
PRINTF(" That's all. \r\n");
break;
}
}
else if (Music_flag == sd_flag)
{
error = f_read(&g_fileObject, tf_Music0, sizeof(tf_Music0),
&bytesRead);
if ((error) || (bytesRead != sizeof(tf_Music0)))
{
PRINTF(" That's all. \r\n");
break;
}
}
}
}
f_open(&g_fileObject, _T("/m2.wav"), (FA_READ | FA_OPEN_ALWAYS));
while (flag == 1)
{
if (Music_flag != sd_flag)
{
sd_flag = Music_flag;
if (Music_flag == 0)
{
error = f_read(&g_fileObject, tf_Music1, sizeof(tf_Music1),
&bytesRead);
if ((error) || (bytesRead != sizeof(tf_Music1)))
{
PRINTF(" That's all. \r\n");
break;
}
}
else if (Music_flag == sd_flag)
{
error = f_read(&g_fileObject, tf_Music0, sizeof(tf_Music0),
&bytesRead);
if ((error) || (bytesRead != sizeof(tf_Music0)))
{
PRINTF(" That's all. \r\n");
break;
}
}
}
}
不足是播放时声音有点小。
心得体会:
这是我第一次接触LPC系列的芯片,首先不得不说的NXP的IDE做得很不错,简洁明了易上手,官方SDK也很完整,缺点就是中文资料相对较少有待完善,不过官方文档也不是那么难以阅读。这次的任务也不复杂(如果只是为了完成目标的话)。
LPC55s69官方SDK中有Arzue IOT的Demo,有Arduino和microbus规格的接口,我想它会是一款不错的物联网开发板,我会好好利用的,它的高性能和安全模式也都引起了我的兴趣,值得我日后慢慢研究。
不足是期间是大三期中考试,有点忙,所以完成的其实不是很理想,有待慢慢改进。
总而言之这起的开发板非常值。