本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:Magicoe是攻城狮

这个demo和想法是参考了大神的文章
https://aijishu.com/a/1060000…

我简单的想法是 和 星辰 这个内核呼应下,星辰大海嘛,这首歌还挺好听的,咱们的目标是星辰大海,目标要大一点 应该能实现。

先说音频播放的接口,一般是I2S,这一点大神的文章里介绍的很详细了,我就不再赘述。我关注的目标是rt-thread上播放音乐这码事,稍微区别下。

rt-thread提供了wav播放的包,名字叫wavplayer,我懒得移植helix的MP3
库了,估计有很多人移植来着,我就不趟了,我的核心是IoT方向,今早收拢战线回归主要战场。
wavplayer的下载链接,当然如果你有空用脚本生成那倒无所谓
https://github.com/RT-Thread-…

gitee我没找到…
在keil的工程里添加wavplayer的源码
这里我没有用到record录音的功能,所以这部分c文件就编译屏蔽了

wavplayer需要用到rtt的optparse的相关定义和函数,添加optparse.c及其include的路径到工程

接下来就是rt-thread的audio框架的相关代码了,在component文件夹下
主要是audio.c和audio_pipe.c

嗯,预想实现功能,rtconfig.h的修改是必不可少的,咱们需要打开如下几个宏定义,欸~

一切就绪就是本次文章的重头了,也是我干到夜里2点没干动的代码—drv_sound.c
我先把源文件列在这里,再讲我的思路,整的不好容易 啪啪啪 的破音,破音要么是数据DMA搬运的不连续,要么就是数据有问题,反正多搜索网上的帖子就好。
我代码偷懒了,并没有实现audio频率的设置,音量的设置(板子不支持)等我觉得很烦的功能,懒的搞,能播就行

/** Copyright (c) 2020-2021, Bluetrum Development Team** SPDX-License-Identifier: Apache-2.0** Date           Author       Notes* 2020-12-12     greedyhao    first implementation*/#include <rtthread.h>
#include "rtdevice.h"#define DBG_TAG              "drv.snd_dev"
#define DBG_LVL              DBG_ERROR
#include <rtdbg.h>#include <stdio.h>
#include <stdint.h>
#include "hal_common.h"
#include "hal_rcc.h"
#include "hal_i2s.h"
#include "hal_dma.h"
#include "hal_dma_request.h"
#include "hal_gpio.h"#include "clock_init.h"#define SAI_AUDIO_FREQUENCY_48K         ((uint32_t)48000u)
#define SAI_AUDIO_FREQUENCY_44K         ((uint32_t)44100u)
#define SAI_AUDIO_FREQUENCY_38K         ((uint32_t)38000u)
#define TX_FIFO_SIZE                    (4096*2)struct sound_device
{struct rt_audio_device audio;struct rt_audio_configure replay_config;rt_uint8_t *tx_fifo;rt_uint8_t  volume;
};static struct sound_device snd_dev = {0};#pragma pack (4)
volatile uint8_t g_PlayIndex = 0;
uint8_t g_AudioBuf[TX_FIFO_SIZE] __attribute__((section(".ARM.__at_0x20000000"))) ;
#pragma pack()/* I2S IRQ. */
void DMA1_CH5_IRQHandler(void)
{rt_interrupt_enter();if (0u != (DMA_GetChannelInterruptStatus(DMA1, DMA_REQ_DMA1_SPI2_TX) & DMA_CHN_INT_XFER_DONE) ){DMA_ClearChannelInterruptStatus(DMA1, DMA_REQ_DMA1_SPI2_TX, DMA_CHN_INT_XFER_DONE);DMA_EnableChannel(DMA1, DMA_REQ_DMA1_SPI2_TX, true);g_PlayIndex = 1;rt_audio_tx_complete(&snd_dev.audio); }if(0u != (DMA_CHN_INT_XFER_HALF_DONE & DMA_GetChannelInterruptStatus(DMA1, DMA_REQ_DMA1_SPI2_TX)) ){DMA_ClearChannelInterruptStatus(DMA1, DMA_REQ_DMA1_SPI2_TX, DMA_CHN_INT_XFER_HALF_DONE);g_PlayIndex = 0;rt_audio_tx_complete(&snd_dev.audio);}rt_interrupt_leave();
}//apll = 采样率*ADPLL_DIV*512
//audio pll init
void adpll_init(uint8_t out_spr)
{GPIO_Init_Type gpio_init;/* SPI2. */RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);/* PD3 - I2S_CK. */gpio_init.Pins  = GPIO_PIN_3;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOD, &gpio_init);GPIO_PinAFConf(GPIOD, gpio_init.Pins, GPIO_AF_5);/* PE6 - I2S_SD. */gpio_init.Pins  = GPIO_PIN_6;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOE, &gpio_init);GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);/* PE4 - I2S_WS. */gpio_init.Pins  = GPIO_PIN_4;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOE, &gpio_init);GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);/* PE5 - I2S_MCK. */gpio_init.Pins  = GPIO_PIN_5;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOE, &gpio_init);GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);/* Setup the DMA for I2S RX. */DMA_Channel_Init_Type dma_channel_init;dma_channel_init.MemAddr           = (uint32_t)(g_AudioBuf);dma_channel_init.MemAddrIncMode    = DMA_AddrIncMode_IncAfterXfer;dma_channel_init.PeriphAddr        = I2S_GetTxDataRegAddr(SPI2);  /* use tx data register here. */dma_channel_init.PeriphAddrIncMode = DMA_AddrIncMode_StayAfterXfer;dma_channel_init.Priority          = DMA_Priority_Highest;dma_channel_init.XferCount         = TX_FIFO_SIZE/2;dma_channel_init.XferMode          = DMA_XferMode_MemoryToPeriph;dma_channel_init.ReloadMode        = DMA_ReloadMode_AutoReload;dma_channel_init.XferWidth         = DMA_XferWidth_16b;DMA_InitChannel(DMA1, DMA_REQ_DMA1_SPI2_TX, &dma_channel_init);/* Enable DMA transfer done interrupt. */DMA_EnableChannelInterrupts(DMA1, DMA_REQ_DMA1_SPI2_TX, DMA_CHN_INT_XFER_DONE, true);DMA_EnableChannelInterrupts(DMA1, DMA_REQ_DMA1_SPI2_TX, DMA_CHN_INT_XFER_HALF_DONE, true);NVIC_EnableIRQ(DMA1_CH5_IRQn);/* Setup the I2S. */I2S_Master_Init_Type i2s_master_init;i2s_master_init.ClockFreqHz  = CLOCK_APB1_FREQ;i2s_master_init.SampleRate   = SAI_AUDIO_FREQUENCY_44K;i2s_master_init.DataWidth    = I2S_DataWidth_16b;i2s_master_init.Protocol     = I2S_Protocol_PHILIPS;i2s_master_init.EnableMCLK   = true;i2s_master_init.Polarity     = I2S_Polarity_1;i2s_master_init.XferMode     = I2S_XferMode_TxOnly;I2S_InitMaster(SPI2, &i2s_master_init);I2S_EnableDMA(SPI2, true);I2S_Enable(SPI2, true);DMA_EnableChannel(DMA1, DMA_REQ_DMA1_SPI2_TX, true);
}static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{rt_err_t result = RT_EOK;struct sound_device *snd_dev = RT_NULL;RT_ASSERT(audio != RT_NULL);snd_dev = (struct sound_device *)audio->parent.user_data;LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);switch (caps->main_type){case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */{switch (caps->sub_type){case AUDIO_TYPE_QUERY:caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;break;default:result = -RT_ERROR;break;}break;}case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */{switch (caps->sub_type){case AUDIO_DSP_PARAM:caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;caps->udata.config.channels     = snd_dev->replay_config.channels;caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;break;case AUDIO_DSP_SAMPLERATE:caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;break;case AUDIO_DSP_CHANNELS:caps->udata.config.channels     = snd_dev->replay_config.channels;break;case AUDIO_DSP_SAMPLEBITS:caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;break;default:result = -RT_ERROR;break;}break;}case AUDIO_TYPE_MIXER: /* report the Mixer Units */{switch (caps->sub_type){case AUDIO_MIXER_QUERY:caps->udata.mask = AUDIO_MIXER_VOLUME;break;case AUDIO_MIXER_VOLUME://  caps->udata.value =  saia_volume_get();break;default:result = -RT_ERROR;break;}break;}default:result = -RT_ERROR;break;}return RT_EOK;
}static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{rt_err_t result = RT_EOK;struct sound_device *snd_dev = RT_NULL;RT_ASSERT(audio != RT_NULL);snd_dev = (struct sound_device *)audio->parent.user_data;switch (caps->main_type){case AUDIO_TYPE_MIXER:{switch (caps->sub_type){case AUDIO_MIXER_VOLUME:{rt_uint8_t volume = caps->udata.value;//   saia_volume_set(volume);snd_dev->volume = volume;LOG_D("set volume %d", volume);break;}case AUDIO_MIXER_EXTEND:break;default:result = -RT_ERROR;break;}break;}case AUDIO_TYPE_OUTPUT:{switch (caps->sub_type){case AUDIO_DSP_PARAM:{/* set samplerate *///  saia_frequency_set(caps->udata.config.samplerate);/* set channels *///  saia_channels_set(caps->udata.config.channels);/* save configs */snd_dev->replay_config.samplerate = caps->udata.config.samplerate;snd_dev->replay_config.channels   = caps->udata.config.channels;snd_dev->replay_config.samplebits = caps->udata.config.samplebits;LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);break;}case AUDIO_DSP_SAMPLERATE:{//   saia_frequency_set(caps->udata.config.samplerate);snd_dev->replay_config.samplerate = caps->udata.config.samplerate;LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);break;}case AUDIO_DSP_CHANNELS:{//  saia_channels_set(caps->udata.config.channels);snd_dev->replay_config.channels   = caps->udata.config.channels;LOG_D("set channels %d", snd_dev->replay_config.channels);break;}case AUDIO_DSP_SAMPLEBITS:{/* not support */snd_dev->replay_config.samplebits = caps->udata.config.samplebits;break;}default:result = -RT_ERROR;break;}break;}default:break;}return RT_EOK;
}static rt_err_t sound_init(struct rt_audio_device *audio)
{struct sound_device *snd_dev = RT_NULL;RT_ASSERT(audio != RT_NULL);snd_dev = (struct sound_device *)audio->parent.user_data;adpll_init(0);/* set default params */// saia_frequency_set(snd_dev->replay_config.samplerate);//saia_channels_set(snd_dev->replay_config.channels);//saia_volume_set(snd_dev->volume);return RT_EOK;
}static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
{struct sound_device *snd_dev = RT_NULL;RT_ASSERT(audio != RT_NULL);snd_dev = (struct sound_device *)audio->parent.user_data;if (stream == AUDIO_STREAM_REPLAY){LOG_D("open sound device");DMA_EnableChannel(DMA1, DMA_REQ_DMA1_SPI2_TX, true);}return RT_EOK;
}static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
{RT_ASSERT(audio != RT_NULL);if (stream == AUDIO_STREAM_REPLAY){LOG_D("close sound device");DMA_EnableChannel(DMA1, DMA_REQ_DMA1_SPI2_TX, false);}return RT_EOK;
}rt_size_t sound_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
{struct sound_device *snd_dev = RT_NULL;rt_size_t count = 0;//    RT_ASSERT(audio != RT_NULL);
//    snd_dev = (struct sound_device *)audio->parent.user_data;
//    rt_kprintf("Size %d   %d\r\n", size, g_PlayIndex);if(g_PlayIndex == 0){memcpy(&g_AudioBuf[0], writeBuf, size);}else{memcpy(&g_AudioBuf[TX_FIFO_SIZE/2], writeBuf, size);}return size;
}static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{struct sound_device *snd_dev = RT_NULL;RT_ASSERT(audio != RT_NULL);snd_dev = (struct sound_device *)audio->parent.user_data;/***               TX_FIFO* +----------------+----------------+* |     block1     |     block2     |* +----------------+----------------+*  \  block_size  /*/info->buffer      = snd_dev->tx_fifo;info->total_size  = TX_FIFO_SIZE;info->block_size  = TX_FIFO_SIZE/2;info->block_count = 2;
}static struct rt_audio_ops ops =
{.getcaps     = sound_getcaps,.configure   = sound_configure,.init        = sound_init,.start       = sound_start,.stop        = sound_stop,.transmit    = sound_transmit,//NULL,//sound_transmit,.buffer_info = sound_buffer_info,
};static int rt_hw_sound_init(void)
{rt_uint8_t *tx_fifo = RT_NULL;rt_uint8_t *rx_fifo = RT_NULL;tx_fifo = rt_malloc(TX_FIFO_SIZE);if(tx_fifo == RT_NULL){rt_kprintf("Sound can alloc tx_fifo\r\n");return -RT_ENOMEM;}rt_memset(&g_AudioBuf[0], 0x00, TX_FIFO_SIZE);rt_memset(tx_fifo, 0, TX_FIFO_SIZE/2);snd_dev.tx_fifo = tx_fifo;/* init default configuration */{snd_dev.replay_config.samplerate = SAI_AUDIO_FREQUENCY_44K;snd_dev.replay_config.channels   = 2;snd_dev.replay_config.samplebits = 16;snd_dev.volume                   = 55;}/* register snd_dev device */snd_dev.audio.ops = &ops;rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);

DMA搬运wav的音频数据我遇到了很多问题,最后用一个大的buffer给DMA搬运,把这个buffer拆成两半,DMA使用half transfer interrupt以及transfered interrupt即传输一半给中断,传输完成给中断。
传输一半的时候让wav解码任务读取一部分内容放到buffer前半部分,传输完成通知wav解码任务再读取一部分内容放到buffer的后半部分。类似pingpong buffer 这种机制,才能保证wav播放的连续性。

好吧编译后下载运行看视频的结果,wav播放的命令是wavplayer -s 文件名

【MM32F5270开发板试用】六、如何用 星辰内核 + 国产RTOS 通过I2S播放 “星辰大海”相关推荐

  1. 【MM32F5270开发板试用】 MindSDK使用测评

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:胖墩墩 一.前言: 非常感谢极术社区和灵动微电子开展的MM32F5270开发板试用申请活动 ...

  2. 【MM32F5270开发板试用】手势传感PAJ7620U2的智能家居应用

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:风云再起 一.序 很高兴能参加极术社区联合灵动微电子组织的[灵动MM32F5270开发板试 ...

  3. 【MM32F5270开发板试用】播放TF卡WAV格式音乐,I2S驱动CS4344

    [MM32F5270开发板试用]播放TF卡WAV格式音乐,I2S驱动CS4344 上四篇文章: [MM32F5270开发板试用]一.依靠SPI_SD,移植FatFs文件系统 [MM32F5270开发板 ...

  4. 【MM32F5270开发板试用】快速移植STM32应用到MM32F5270(以OLED为例)

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:@#@ 本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活 ...

  5. 【MM32F5270开发板试用】RT-Thread SPI 驱动适配指南

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:xusiwei1236 本文记录了我在社区"Rice我叫加饭?"大佬移 ...

  6. 【MM32F5270开发板试用】GPIO输入+EXTI外部中断例程demo试用

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:Zeee 前言: 首先,感谢灵动微电子与极术社区给予宝贵的试用机会.借助本次对Plus-F ...

  7. 【MM32F5270开发板试用】定制MicroPython及读取MPU6050数据到OLED1306

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:HonestQiao 前言 这次有幸获得MM32F5270开发板的试用,非常幸运. 收到板 ...

  8. 【MM32F5270开发板试用】移植Google Chrome小恐龙游戏到MM32F5270

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:曾是一颗薏米 一.项目背景 在几年前,Google 给 Chrome 浏览器加了一个有趣的 ...

  9. 【MM32F5270开发板试用】一、让MM32F5270支持RT-Thread~打通串口UART

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:Magicoe是攻城狮 喜欢RT-Thread的代码10余年-,但凡有新上手的MCU必定看 ...

最新文章

  1. Python 的种类以及特点
  2. 南京邮电大学网络攻防训练平台(NCTF)-异性相吸-Writeup
  3. sqlserver linkserver
  4. 函数指针与指针函数的使用与小结
  5. mac 10.10.5 mysql_Macbook os x 10.10.5 下装mysql 一些记录
  6. 计算机网络(十九)-IEEE802.11无线局域网
  7. 镶套iframe 鼠标滚动无效解决办法
  8. html文件钓起始标志,关于html页面head标签顺序
  9. itop docker3.0.0安装
  10. 信号降噪方法——基于自适应神经模糊推理系统(ANFIS)的降噪处理
  11. 2的次方表(1~64次方)
  12. VI设计手册制作全流程
  13. ASTC 自适应可伸缩纹理压缩
  14. redis集群节点宕机
  15. 任务管理器被管理员停用怎么办
  16. 网络显示连接正常,就是网页打不开(也适用于ie可以打开,google打不开情况)
  17. 电脑的大脑——CPU
  18. MySQL 字段的基本操作:添加、修改和删除字段(详解)
  19. Python开发一个炸金花小游戏,注意别玩上瘾了
  20. 分布式文件系统FastDFS

热门文章

  1. JavaScript 学习笔记(八)前后端交互;Ajax进行前后端交互
  2. 01背包、完全背包(异同)
  3. HIVE优化的四种方法
  4. 学习笔记:Spring中default-autowire与autowire区别
  5. j3455文件服务器,UNRAID下解决华擎 J3455-ITX  IOMMU 分组(4口网卡顺利分开直通 )...
  6. Android 8.0 VTS 测试 FAIL 项解决记录
  7. 用png格式图片和非png格式图片做水印图片
  8. Android 第三次作业 contentprovider与resolver
  9. CFD网格你应该了解的常识
  10. Apollo(一)-基本介绍