WeDesign 第一期基于MM32F0140 实现I2S 音乐播放
基于MM32F0140实现音乐播放器实现,可以通过codec进行双声道音乐播放
标签
WeDesign
MM32F0140
IIS
PCM5102A
xinshuwei
更新2023-05-26
345
  • 项目介绍

      此次项目主要是基于MM32F0140C4P 实现 I2S 音频驱动,产生LRCLK,DATA,BCLK,MCLK用于驱动pcm5102a,示例中左右声道分别产生两种频率的正弦波单频信号。

  • 项目设计思路(含设计框图)

Fk9elhkEZvJwi2XenXTfU1ITW1A5

首先驱动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的功能不是很熟悉,后来在官网文档 中找到管脚复用的说明,对后续的功能开发及管脚映射有了相应的了解

Fj2in7ibsPUsKzuzWH7ATMWWVJJj

  • 实现结果展示(调试过程中遇到什么问题)

 

Fv4uQ723Jm16nS1gfPnKeWwByObj

 按照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 传输,采样率误差相对偏小。

FlwVssn-0pzhIFG125ZIwE-FPGXz

在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);

通过逻辑分析仪抓取到的波形如下

FtHok-wT5ggy6GwXyJGk8ztX03aF

WS 是8k方波

BCLK 是256k

MCLK 是2M

采用飞利浦格式

 

  • 在芯片设计过程中,遇到什么难题以及解决方法,或未来针对这个芯片的扩展项目

    在 F0144C4P的mindsdk 中未发现相关I2S的示例,后来通过苏的老师的帮助后,通过f5270的相关示例学习了相关的驱动方法,SDK编写的通俗易懂,跟stm32类似,方便上手;后续在此基础上实现基于spi的sd卡驱动,通过sd卡读取数据后,发送给I2S 外设,实现歌曲播放

  • 芯片的优势与局限

     芯片的优势是麻雀虽小,五脏俱全,基本常用的外设都具备,局限局势主频相对偏低,内存和rom空间相对偏小,建议后续增加usb 相关外设支持,后续想在此基础上实现麦克风驱动并通过usb传输给pc。

  • 原理图/PCB图(放在项目附件)

    已放附件请查阅

  • 可编译下载的代码(放在项目的附件,用于验证)

  开源地址

附件下载
kicad_fthr-f0140.zip
核心板 的原理图 pcb
团队介绍
苏州工程师一枚
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号