- 项目介绍
此次项目主要是基于MM32F0140C4P 实现 I2S 音频驱动,产生LRCLK,DATA,BCLK,MCLK用于驱动pcm5102a,示例中左右声道分别产生两种频率的正弦波单频信号。
- 项目设计思路(含设计框图)
首先驱动uart2 用于打印log,然后使用自制的jlink进行swd 下载调试。
IIS 驱动首先需要进行SPI初始化,管脚初始化,中断初始化,中断选择传输完成中断,采用pingpongbuffer的形式避免中断噪声。
MM32F0140是使用高性能的 Arm® Cortex-M0 为内核的 32 位微控制器,最高工作频率可达 72MHz,内置高速存储器,丰富的增强型 I/O 端口和多种外设。MM32F0140微控制器集成了FlexCAN外设,这是一个在汽车电子系统中常用的外设模块,配合适当的软件,可用于实现连入CAN总线网络的嵌入式系统产品。
片上资源:
- 64KB Flash,8KB SRAM
- 1 个 12 位的 ADC和 1 个比较器
- 1 个 16 位通用定时器、1 个 32 位通用定时器、3 个 16 位基本定时器、1 个 16 位高级定时器
- 3 个 UART 接口、2 个 SPI 接口和 1 个 I2C 接口
- 1 个 FlexCAN 接口
- 工作电压为 2.0V - 5.5V
- 高可靠性: 支持最高 ±6000V HBM ESD,±2000V CDM ESD,高温 Latch-up 可耐电流 ±300mA
- 工作温度范围(环境温度)-40℃ - 85℃ 工业型和-40℃ - 105℃ 扩展工业型(后缀为V)
- 多种省电工作模式支持低功耗应用的需求
- 与 MM32F031 引脚兼容
- 提供 LQFP48、LQFP32、QFN32 和 TSSOP20 封装
此处提供的芯片是LQFP32信号
- 搜集素材的思路
搜集素材主要是在灵动官网进行资料收集,
硬件原理图参考硬禾设计
其他不明白的地方在群里请教苏勇老师。
软件学习主要基于官网的mindsdk进行学习。
- 画原理图、PCB制板过程中遇到的问题,以及解决方法
画原理图中对每个GPIO的功能不是很熟悉,后来在官网文档 中找到管脚复用的说明,对后续的功能开发及管脚映射有了相应的了解
- 实现结果展示(调试过程中遇到什么问题)
按照datasheet 的指引将boot0 接地 接电源 发现都不能进烧写,这里还是不是很明白,后来需将boot0 悬空,然后就可以通过swd 正常烧写了。
图中通过SPI 驱动pcm5102a codec ,通过3.5mm耳机孔插入耳机可以听到两种频率的正弦波信号。
代码讲解部分
时钟配置,配置系统时钟72M
{
CLOCK_ResetToDefault();
CLOCK_BootToHSE48MHz();
/* UART. */
RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART2, true);
RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_UART2);
/* SPI2. */
RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);
RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);
/* DMA1. */
RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_DMA1, true);
RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_DMA1);
/* GPIOA. */
RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOA, true);
RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOA);
/* GPIOB. */
RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOB);
初始化使用到的gpio
void BOARD_InitPins(void)
{
/* PA2 - UART1_TX. */
GPIO_Init_Type gpio_init;
gpio_init.Pins = GPIO_PIN_2;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);
/* PA3 - UART1_RX. */
gpio_init.Pins = GPIO_PIN_3;
gpio_init.PinMode = GPIO_PinMode_In_Floating;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);
/* PA0 - SPI_FLASH_CS. */ /*SPI2*/ //WS
gpio_init.Pins = GPIO_PIN_0;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_3);
/* PA10 - SPI_FLASH_SCK. *//*spi2*/ //I2S CK
gpio_init.Pins = GPIO_PIN_10;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_7);
/* PA12 - SPI_FLASH_MISO. *//*spi2*/ //MCK
gpio_init.Pins = GPIO_PIN_12;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_4);
/* PA11 - SPI_FLASH_MOSI. *//*spi2*/ //SD
gpio_init.Pins = GPIO_PIN_11;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_4);
gpio_init.Pins = LED1_Pin;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(LED1_Port, &gpio_init);
GPIO_PinAFConf(LED1_Port, gpio_init.Pins, GPIO_AF_4);
i2s相关初始化
/* I2S master init. */
void app_i2s_master_init(void)
{
I2S_Master_Init_Type i2s_master_init;
i2s_master_init.ClockFreqHz = BOARD_I2S_FREQ;
i2s_master_init.SampleRate = BOARD_I2S_SAMPLE_RATE;
i2s_master_init.DataWidth = BOARD_I2S_DATA_WIDTH;
i2s_master_init.Protocol = BOARD_I2S_PROTOCOL;
i2s_master_init.EnableMCLK = true;
i2s_master_init.Polarity = BOARD_I2S_CPOL;
i2s_master_init.XferMode = I2S_XferMode_TxOnly;
I2S_InitMaster(BOARD_I2S_PORT, &i2s_master_init);
/* Enable I2S tx buffer empty interrupt. */
I2S_EnableInterrupts(BOARD_I2S_PORT, I2S_INT_TX_EMPTY, true);
NVIC_EnableIRQ(BOARD_I2S_IRQn);
I2S_Enable(BOARD_I2S_PORT, true);
}
这里使用的是8k采样率,按照官方文档,尽量使用11.025k 倍频的采样率进行audio 传输,采样率误差相对偏小。
在spi2的中断中根据pingpong idx 更新不同的通道的audio 数据,如现在在传输左通道,此处更新右通道,下次传输右通道,然后更新左通道的数据
/* Get interrupt status. */
uint32_t flag = I2S_GetInterruptStatus(BOARD_I2S_PORT);
if (0u != (flag & I2S_INT_TX_EMPTY) )
{
if (I2S_Channel_Left != I2S_GetXferChannel(BOARD_I2S_PORT) )
{
app_i2s_put_data_left(); /* Send right data, buffer put left data to prepare next send. */
}
else
{
app_i2s_put_data_right(); /* Send left data, buffer put right data to prepare next send. */
}
}
/* Clear interrupt status. */
I2S_ClearInterruptStatus(BOARD_I2S_PORT, flag);
通过逻辑分析仪抓取到的波形如下
WS 是8k方波
BCLK 是256k
MCLK 是2M
采用飞利浦格式
- 在芯片设计过程中,遇到什么难题以及解决方法,或未来针对这个芯片的扩展项目
在 F0144C4P的mindsdk 中未发现相关I2S的示例,后来通过苏的老师的帮助后,通过f5270的相关示例学习了相关的驱动方法,SDK编写的通俗易懂,跟stm32类似,方便上手;后续在此基础上实现基于spi的sd卡驱动,通过sd卡读取数据后,发送给I2S 外设,实现歌曲播放
- 芯片的优势与局限
芯片的优势是麻雀虽小,五脏俱全,基本常用的外设都具备,局限局势主频相对偏低,内存和rom空间相对偏小,建议后续增加usb 相关外设支持,后续想在此基础上实现麦克风驱动并通过usb传输给pc。
- 原理图/PCB图(放在项目附件)
已放附件请查阅
- 可编译下载的代码(放在项目的附件,用于验证)