发新帖本帖赏金 150.00元(功能说明)我要提问
123下一页
返回列表
[MM32生态]

基于MM32实现音频播放系统的应用实例

[复制链接]
2221|59
手机看帖
扫描二维码
随时随地手机跟帖
xld0932|  楼主 | 2022-5-16 10:21 | 显示全部楼层 |阅读模式
本帖最后由 xld0932 于 2022-5-16 10:52 编辑

#申请原创#   @21小跑堂


介绍

I2S总线又称为Inter-IC Sound总线,它是集成在芯片内的音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专门应用于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟和数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真问题。

MM32F3270系列MCU最多支持3个I2S接口,同一时刻只能工作在发送或者接收接收状态,支持16位、24位和32位三种数据格式、数据传输始终是MSB优先、每个I2S接口都支持DMA传输方式;此外MM32F3270系列MCU的I2S还支持4种I2S协议标准,分别是飞利浦标准、MSB对齐标准、LSB对齐标准、以及PCM标准,我们可以根据MM32F3270系列MCU连接的I2S外设所支持的协议标准进行灵活配置和选择。

实现功能

基于MM32-EVBoard(MB-039)开发板,实现音频播放系统。通过DMA的方式,将存储在TF卡中的音频文件(WAV格式/MP3格式)通过I2S总线,将音频数据发送给CS4344音频数模转换芯片,通过与其连接的音响设备播放出音乐;通过板载的4个按键来实现对音频文件的开始播放、停止播放、音频文件选择等操作,并在TFT液晶显示屏上显示系统运行信息、音频文件目录,以及当前的播放状态等内容。根据如上功能要求,需要实现的技术点如下:
1、GPIO口控制LED闪烁和KEY按键检测;LED控制引脚与I2S引脚复用,所以在播放音乐的时候,LED是表象上不闪烁的;KEY在识**需要结合音频的当前状态做处理;
2、TFT LCD用于运行显示,通过FSMC接口与MCU进行连接,通过8080总线方式进行显示控制和内容显示;显示的字符包含ASCII码和汉字,其中ASCII码字库存放在MCU的FLASH程序空间,中文GBK编码字库存放在SPI FLASH中;
3、SPI FLASH用于存放中文GBK编码字库,通过板载的UART8接口,结合Xmodem串行文件传输协议,从电脑端把GBK_FONG.BIN文件传输并写入到板载的SPI FLASH芯片中;再通过回读取的方式,将SPI FLASH存储的字库数据通过Xmodem上传到电脑端,比较下载文件与读取到的文件是否相一致,确认字库的正确性;
4、通过MCU的SDIO接口来操作TF卡,移植FatFs文件系统对音频文件进行识别,对音频文件数据进行读取操作;
5、通过MCU的I2C接口来与CS4344音频数模转换芯片进行数据传输,实现播放音频文件,采用DMA传输方式,开启DMA传输完成中断和半传输完成中断,通过乒乓操作实现音频文件播放的流畅效果;
6、另外最重要的就是音频文件的识别和解码;对于WAV文件来说,它是未经编码/压缩的音频数据文件,在固定的文件头信息后面是音频数据,只需要在音频文件解析后将获取到的音频数据直接发送到I2S总线即可实现播放;而对于MP3文件来说,它是经过编码/压缩的音频数据文件,在没有外部解码芯片支持的时候,需要用软件解码的方式来实现音频数据的提取操作,本文中将会介绍Helix和libmad这两种开源的,且非常常用的软件解码方式;

软件环境
Windows操作系统下开发,使用的IDE和相应的软件如下:
1、Keil MDK开发软件
2、MobaXterm终端软件,用于监控程序运行及代码中SHELL功能的操作
3、SecureCRT终端软件,通过Xmodem传输协议结合MCU功能代码实现,用于将GBK汉字字库编码数据下载到板载的SPI FLASH芯片中
4、Beyond Compare比较软件,用于比较GBK汉字字编码下载和上传数据的一致性
5、酷狗音乐,下载音频文件

硬件环境
1.jpg
1、MM32-EVBoard(MB-039)开发板
2、8GB容量的TF卡及读卡器,通过读卡器连接电脑,将音频文件存放在TF卡中
3、音箱设备
4、Micro USB数据线、音频接口连接线
5、USB转TTL调试工具

WAV音频文件

WAV文件Waveform的简写,也称为波形文件,是一种可以存储声音波形的数字音频格式。WAV支持多种音频数字、采样频率和声道,标准格式化的WAV文件和CD的格式一样,也是44.1kHz的采样频率、16位量化数据,具有真实记录声音波形的特点,基本无数据压缩,所以数据体量也变得相对大些。

WAV文件的编码包括两方面的内容,一个是按一定格式存储数据,另外一个就是采用一定的算法压缩数据。WAV格式对音频流的编码没有硬性规定,支持非压缩算法的PCM(Plus Code Modulation)脉冲编码调制格式,也支持其它一些压缩算法。典型的WAV文件格式如下图所示:
21.png

WAV文件结构
在Windows环境下,大部分多媒体文件都是按照资源互换文件格式(Resources Interchange File Format)存放信息的,简称RIFF格式。构成RIFF文件的基本单位称之为块(Chunk),每个RIFF文件都是由若干个块组成的,而每个块都是由块标识、块长度以及数据这三部分组成的。块标识是由4ASCII码字符组成的,如果不满4个字符则在右边以空格来补齐;块长度也占4个字节存储空间,保存的是当前块数据的长度,但不包含块标识和块长度字段;所以一个块的实际长度就是块长度字段的数值再加上8字节。

RIFF格式规定,只有RIFF块和LIST块可以包含子块,其它的块都不允许包含子块;而一个RIFF格式文档本身就是一个块。分析一个RIFF文档的组成部分,第一部分的4个字节为文档的标识符“RIFF”,同时也是RIFF的块标识,指示该文档是一个有效的RIFF格式文档;第二部分的4个字节为块长度,指示文件的数据长度,其数据为文件总长度减8字节;第三部分为块数据,其中,前4个字节为文件格式类型标识,如WAVE、AVI等;面后面其它部分就是RIFF块的子块了。

WAV文件采用的是RIFF格式结构,其至少由RIFF块、fmt块和data块组成,若是基于压缩编码的WAV文件还必须包含fact块;其中fmt块、data块和fact块都是RIFF块的子块。WAV文件的文件格式类型标识符为“WAVE”,其基本结构如下所示:
WAV文件结构
RIFF块
文件格式类型“WAVE”
fmt块
fact块(压缩算法格式需要包含些块)
data块

WAV文件头格式
偏移地址
长度
数据类型
字段名称
字段说明
0x00
4
字符
文档标识
“RIFF”
0x04
4
长整型数
文件数据长度
从下一个字段首地址开始到文件末尾的总字节数
0x08
4
字符
文件格式类型
“WAVE”
0x0C
4
字符
格式块标识
“fmt “  (带个格式哦)
0x10
4
长整型数
格式块长度
其数值由编码格式决定
0x14
2
整型数
编码格式代码
见下表
0x16
2
整型数
声道个数
单声道为1、双/立体声道为2
0x18
4
长整型数
采样频率
每个声道单位时间采样次数
0x1C
4
长整型数
数据传输率
声道数*采样频率*每个样本的数据位数/8
0x20
2
整型数
数据块对齐单位
采样帧大小:声道数*位数/8
0x22
2
整型数
采样位数
存储每个采样值所用的二进制位数
0x24
对基本格式块的扩充部分

WAV常见的压缩编码格式
格式编码
格式名称
fmt块长度
fact块
0x0001
PCM/非压缩格式
16
N
0x0002
Microsoft ADPCM
18
Y
0x0003
IEEE float
18
Y
0x0006
ITU G.711 u-law
18
Y
0x0007
ITU G.711 u-law
18
Y
0x0031
GSM 6.10
20
Y
0x0040
ITU G.721 ADPCM
Y
0xFFFE
参考扩展格式子块中的编码格式
40
N

扩展子块格式
偏移地址
长度
数据类型
字段名称
字段说明
0x24
2
整型数
扩展区长度
22
0x26
2
整型数
有效采样位数
最大值为每个采样字节数*8
0x28
4
长整型数
扬声器位置
声道号与扬声器映射的二进制掩码
0x32
2
整型数
编码格式
真正的编码格式代码
0x34
14
……

fact块格式
字段
长度
内容
块标识
4
“fact”
块长度
4
4
采样总数
4
采样总数(每个通道)

WAV文件格式实例解析
22.png
  • 5249 4646这个是ASCII字符”RIFF”,这部分是固定格式,表明了这是一个有效的RIFF格式文件;
  • AC96 5E03这个是WAV文件的数据大小,对应0x035E96AC,十进制值为56530604;这个值再加上8个字节就是WAV文件总长度了,如下图所示:

23.png

  • 5741 5645这个是ASCII字符”WAVE”
  • 666D 7420这个是ASCII字符”fmt ”,即fmt块的块标识
  • 1000 0000这个是fmt格式块长度,16字节
  • 0100这个是编码格式,0x0001对应PCM/非压缩格式
  • 0200这个是声道个数,0x0002表示两声道或者立体声道
  • 44AC 0000这个是采样频率,对应16进制数为0x0000AC44,对应十进制为44100
其它的字段数据可以对照上述表格一一列举出来,但需要注意的是数据的大小端不同的存储方式哈!!!

WAV文件格式在代码中的结构体定义
WAV.h /* 由于字数限制,请参考源代码 */

WAV文件格式在MM32中的结构解析
static uint8_t WAV_DecodeFile(WAV_TypeDef *pWav, char *Path, char *Name) /* 由于字数限制,请参考源代码 */

WAV文件格式在MM32中的播放实现
void WAV_PlaySong(char *Path, char *Name)
{
    WAV_TypeDef WaveFile;
    char FilePath[100];

    /* 获取WAV文件的信息 */
    if(WAV_DecodeFile(&WaveFile, Path, Name) == 0)
    {
        if((WaveFile.BitsPerSample == 16) && (WaveFile.nChannels == 2) &&
           (WaveFile.SampleRate  > 44000) && (WaveFile.SampleRate < 48100))
        {
            I2S_InitGPIO();

            I2S_PowerON(1);

            I2S_Configure(I2S_Standard_Phillips, I2S_DataFormat_16b,
                          I2S_AudioFreq_44k,     I2S_Mode_MasterTx);
        }
        else
        {
            printf("\r\nWAV File Error!\r\n");  return;
        }
    }
    else
    {
        printf("\r\nNot WAV File!\r\n");    return;
    }

    memset( FilePath, 0x00, sizeof(FilePath));
    sprintf(FilePath, "%s%s",   Path,   Name);

    WAV_RES = f_open(&WAV_File, FilePath, FA_READ);

    if(WAV_RES == FR_OK)
    {
        WAV_NextIndex = 0;
        WAV_PlayEnded = 0;

        WAV_PlaybackProgress = 0;

        WAV_PrepareData();
        WAV_PlayHandler();
    }
    else
    {
        printf("\r\nWAV File Open Error : %d", WAV_RES);
    }
}

MP3音频文件

MP3格式音乐文件普遍存在我们生活中,实际上MP3本身是一种音频编码方式,全称为 Moving Picture Experts Group Audio Layer III(MPEG Audio Layer 3)。MPEG音频文件是MPEG标准中的声音部分,根据压缩质量和编码复杂程度划分为三层,即 Layer-1、Layer2、Layer3,且分别对应MP1、MP2、MP3 这三种声音文件,其中MP3压缩率可达到10:1至12:1,可以大大减少文件占用存储空间大小。MPEG 音频编码的层次越高,编码器越复杂,压缩率也越高。MP3是利用人耳对高频声音信号不敏感的特性,将时域波形信号转换成频域信号,并划分成多个频段,对不同的频段使用不同的压缩率,对高频加大压缩比(甚至忽略信号)对低频信号使用小压缩比,保证信号不失真。这样一来就相当于抛弃人耳基本听不到的高频声音,只保留能听到的低频部分,这样可得到很高的压缩率。

MP3文件结构
MP3文件大致分为3个部分:TAG_V2(ID3V2)、音频数据、TAG_V1(ID3V1)。ID3是MP3文件中附加关于该MP3文件的歌手、标题、专辑名称、年代、风格等等信息,有两个版本ID3V1ID3V2ID3V1固定存放在MP3文件末尾,固定长度为128字节,以TAG三个字符开头,后面跟上歌曲信息。因为ID3V1可存储信息量有限,有些MP3文件添加了ID3V2ID3V2是可选的,如果存在ID3V2那它必然存在在MP3文件起始位置,它实际是ID3V1的补充。

MP3数据帧
经过压缩后的MP3文件数据是由多个帧组成的,帧是MP3文件的最小组成单位。每个帧又由帧头 、附加信息和声音数据组成,每个帧的长度会随着位率的不同而变化,有些MP3文件末尾还有些额外的字节数据存放非声音数据的说明信息等。每个帧包含一段音频的压缩数据,通过解码库解码即可得到对应PCM音频数据,就可以通过I2S发送到CS4344芯片播放音乐,按顺序解码所有帧就可以得到整个MP3文件的音轨。

MP3解码库
MP3文件是经过压缩算法压缩而存在的,为得到PCM信号,需要对MP3文件进行解码,解码 过程大致为:比特流分析、霍夫曼编码、逆量化处理、立体声处理、频谱重排列、抗锯齿处理、 IMDCT 变换、子带合成、PCM输出。整个过程涉及很多算法计算,要自己编程实现不是一件现 实的事情,还好有很多公司经过长期努力实现了解码库编程。 具体的MP3文件格式内容解析可以参考附件中的《MP3文件格式解析》一文,文本不需要对MP3数据帧的细节做深入的研究,我只需要掌握如何使用当前比较常用的开源软件来播放MP3音频文件即可。

现在合适在小型嵌入式控制器移植运行的有两个版本的开源MP3解码库,分别为libmad解码库和Helix解码库。libmad是一个开源的高精度MPEG音频解码库,是专门面向嵌入式应用的MP3解码程序可以简单地实现MP3数据解码工作,支持MPEG-1、MPEG-2,以及MPEG-2.5标准,它可以提供24PCM输出,用定点运算模拟浮点运算,因此不需要处理器有浮点运算功能,非常适合没有浮点支持的平台上使用libmadMP3解码中关键部分采用了优化的算法,这些优化算法能够大幅度地减少计算量,而且大多应用于MP3解码的VLSI实现中libmad的源代码文件目录下的mad.h文件中,可以看到绝大部分该库的数据结构和API等软件库结构清晰,易于使用和开发Helix解码库支持浮点和定点这两种的计算实现,同样支持MPEG-1、MPEG-2以及MPEG-2.5标准的Layer3解码,此外Helix解码库支持可变位速率、恒定位速率,以及立体声和单声道音频格式。这两个解码库都是一帧为解码单位的,一次解码一帧。各有优点,至于哪个更好,我还没有深入研究,但从使用下来最明显的体验来说,就是libmad库对堆栈空间大小的要求肯定是比Helix要大得多的,所以如果为了节省些RAM空间,可以优先考虑选择Helix库。

Helix MP3解码库在MM32中的播放实现
void MP3_Helix_PlaySong(char *Path, char *Name)
{
    char FilePath[100];
    memset( FilePath, 0x00, sizeof(FilePath));
    sprintf(FilePath, "%s%s",   Path,   Name);

    if(f_open(&MP3_Helix_File, FilePath, FA_READ) == FR_OK)
    {
        hMP3Decoder = MP3InitDecoder();                 /* 初始化MP3解码器 */

        if(hMP3Decoder == 0)
        {
            f_close(&MP3_Helix_File); return;
        }

        MP3_Helix_RES = f_read(&MP3_Helix_File, MP3_Helix_iBuffer, MP3_HELIX_I_BUFFER_SIZE, &MP3_Helix_BR);

        if((MP3_Helix_RES == FR_OK) && (MP3_Helix_BR != 0))
        {
            printf("\r\nMP3 Play\r\n");

            I2S_InitGPIO();
            I2S_PowerON(1);

            MP3_Helix_InPointer  = MP3_Helix_iBuffer;   /* 数据读取缓存指针 */
            MP3_Helix_BytesLeft  = MP3_Helix_BR;

            MP3_Helix_PlayEnded  = 0;
            MP3_Helix_SampleRate = 0;

            I2S_DMA_Finish = 1;

            while(!MP3_Helix_PlayEnded)
            {
                MP3_Helix_PrepareData();

                EVENT_Scanning();

                MP3_Helix_PlayHandler();

                TASK_Scheduling();
            }
        }
    }
    else
    {
        printf("\r\nMP3 File Open Fail!\r\n");
    }
}

libmad MP3解码库在MM32中的播放实现
void MP3_libmad_PlaySong(char *Path, char *Name)
{
    char     FilePath[100];
    int      TagSize   = 0;
    uint32_t FrameCount  = 0;

    /* First the structures used by libmad must be initialized. */
    mad_stream_init(&MP3_libmad_Stream);
    mad_frame_init( &MP3_libmad_Frame );
    mad_synth_init( &MP3_libmad_Synth );
    mad_timer_reset(&MP3_libmad_Timer );

    memset( FilePath, 0x00, sizeof(FilePath));
    sprintf(FilePath, "%s%s",   Path,   Name);

    if(f_open(&MP3_libmad_File, FilePath, FA_READ) == FR_OK)
    {
        I2S_InitGPIO();
        I2S_PowerON(1);

        MP3_libmad_PlayEnded = 0;
        I2S_DMA_Finish       = 1;


        MP3_libmad_RES = f_read(&MP3_libmad_File, MP3_libmad_iBuffer, MP3_LIBMAD_I_BUFFER_SIZE, &MP3_libmad_BR);

        if(strncmp("ID3", (char *)MP3_libmad_iBuffer, 3) == 0)
        {
            /*计算标签信息总大小  不包括标签头的10个字节*/
            TagSize =  ((unsigned int)MP3_libmad_iBuffer[6] << 21) |
                       ((unsigned int)MP3_libmad_iBuffer[7] << 14) |
                       ((unsigned int)MP3_libmad_iBuffer[8] << 7)  |
                       ((unsigned int)MP3_libmad_iBuffer[9] << 0);

            TagSize += 10;

            printf("\r\nMP3 TAG Size : %d\r\n", TagSize);
        }

        f_lseek(&MP3_libmad_File, TagSize);   /* 跳过TAG信息 */


        while(1)
        {
            if((MP3_libmad_Stream.buffer == NULL) || (MP3_libmad_Stream.error == MAD_ERROR_BUFLEN))
            {
                size_t         ReadSize, Remaining;               
                unsigned char *ReadStart = NULL;

                if(MP3_libmad_Stream.next_frame != NULL)
                {
                    Remaining = MP3_libmad_Stream.bufend - MP3_libmad_Stream.next_frame;
                    memmove(MP3_libmad_iBuffer, MP3_libmad_Stream.next_frame, Remaining);

                    ReadStart = MP3_libmad_iBuffer       + Remaining;
                    ReadSize  = MP3_LIBMAD_I_BUFFER_SIZE - Remaining;
                }
                else
                {
                    ReadSize  = MP3_LIBMAD_I_BUFFER_SIZE,
                    ReadStart = MP3_libmad_iBuffer,
                    Remaining = 0;
                }

                MP3_libmad_RES = f_read(&MP3_libmad_File, (char *)ReadStart, ReadSize, &MP3_libmad_BR);

                if((MP3_libmad_BR <= 0) || (MP3_libmad_BR < ReadSize))
                {
                    printf("\r\nEnd Of File\r\n");  break;
                }

                mad_stream_buffer(&MP3_libmad_Stream, MP3_libmad_iBuffer, MP3_libmad_BR + Remaining);
                MP3_libmad_Stream.error = MAD_ERROR_NONE;
            }

            if(mad_frame_decode(&MP3_libmad_Frame, &MP3_libmad_Stream))
            {
                if(MAD_RECOVERABLE(MP3_libmad_Stream.error))
                {
                    if((MP3_libmad_Stream.error != MAD_ERROR_LOSTSYNC) || (MP3_libmad_Stream.this_frame != NULL))
                    {
                        printf("\r\nRecoverable   Frame Level Error (%s)\r\n", MP3_libmad_MadErrorString(&MP3_libmad_Stream));
                    }

                    continue;
                }
                else
                {
                    if(MP3_libmad_Stream.error == MAD_ERROR_BUFLEN)
                    {
                        continue;
                    }
                    else
                    {
                        printf("\r\nUnrecoverable Frame Level Error (%s)\r\n", MP3_libmad_MadErrorString(&MP3_libmad_Stream));
                        break;
                    }
                }
             }

            if(FrameCount == 0)
            {
                MP3_libmad_PrintFrameInfo(&MP3_libmad_Frame.header);
            }

            FrameCount++;
            mad_timer_add(&MP3_libmad_Timer, MP3_libmad_Frame.header.duration);

            mad_synth_frame(&MP3_libmad_Synth, &MP3_libmad_Frame);

            for(uint32_t i = 0; i < MP3_libmad_Synth.pcm.length; i++)
            {
                short Sample = MP3_libmad_MadFixedToSshort(MP3_libmad_Synth.pcm.samples[0][i]);

                MP3_libmad_oBuffer[MP3_libmad_NextIndex][MP3_libmad_BufferSize++] = Sample;

                if(MAD_NCHANNELS(&MP3_libmad_Frame.header) == 2)        
                {
                    Sample = MP3_libmad_MadFixedToSshort(MP3_libmad_Synth.pcm.samples[1][i]);
                }

                MP3_libmad_oBuffer[MP3_libmad_NextIndex][MP3_libmad_BufferSize++] = Sample;

                MP3_libmad_PlayHandler(MP3_libmad_Synth.pcm.samplerate);
            }

            if(MP3_libmad_PlayEnded == 1)
            {
                break;
            }
        }

        DMA_Cmd(DMA2_Channel2, DISABLE);

        f_close(&MP3_libmad_File);

        I2S_PowerON(0);
    }

    mad_synth_finish( &MP3_libmad_Synth );
    mad_frame_finish( &MP3_libmad_Frame );
    mad_stream_finish(&MP3_libmad_Stream);

    char Buffer[80];
    mad_timer_string(MP3_libmad_Timer, Buffer, "%lu:%02lu.%03u", MAD_UNITS_MINUTES, MAD_UNITS_MILLISECONDS, 0);
    printf("\r\n%d Frames Decoded (%s).\r\n", FrameCount, Buffer);
}

代码实现

代码实现部分,我们只展示出功能实现的主体部分,对于像FatFs文件系统的移植、LED控制、KEY按键处理、Xmodem文件传输协议的实现等部分,这些在之前的分享帖中有详细的描述,可以参考之前的分享**,当然也可以直接下载附件中的软件工程源代码,直接查看源代码。

音频播放整体功能控制逻辑
void AUDIO_Handler(void) /* 由于字数限制,请参考源代码 */

根据识别按键处理对应功能
void KEY_Handler(eKEY_VALUE value, eKEY_TYPE type) /* 由于字数限制,请参考源代码 */

加载中文字库编码点阵数据,根据LCD显示方向进行旋转
void LCD_ShowCN(uint16_t StartX, uint16_t StartY, const char *str) /* 由于字数限制,请参考源代码 */

实现自动判断中英文字符并显示在显示屏上
uint16_t LCD_ShowLOG(uint16_t StartX, uint16_t StartY, const char *str) /* 由于字数限制,请参考源代码 */

通过FatFs文件系统列举当前目录文件,并显示在LCD屏上
FRESULT AUDIO_ScanFiles(char *path) /* 由于字数限制,请参考源代码 */

测试运行

系统初始化加载
2.jpg

歌曲目录文件显示
3.jpg

播放WAV歌曲
4.jpg

通过Helix解码库播放MP3歌曲
5.jpg

通过labmad解码库播放MP3歌曲
6.jpg

通过Xmodem串行传输协议下载中文字库到SPI FLASH

  • 通过Xmodem串行传输协议下载字库
11.png 12.png 13.png 14.png

  • 通过Xmodem串行传输协议上传字库
15.png 16.png 17.png

  • 比较下载的字库文件和上传的字库文件是否一致
18.png


附件
软件工程源代码: WAV&amp;MP3(Helix&amp;libmad).zip (3.49 MB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 150.00 元 2022-05-16
理由:恭喜通过原创文章审核!请多多加油哦!

xld0932|  楼主 | 2022-5-16 10:58 | 显示全部楼层
基于MM32-EVBoard(MB-039)开发板,结合LCD显示和FatFs文件系统实现音频播放系统,能够自动识别WAV文件和MP3文件进行播放;MP3使用软件解码方式,使用了Helix和libmad这两种解码库,并录制实际的演示效果放在B站上,感兴趣的小伙伴可以关注哦

使用特权

评论回复
www5911839| | 2022-5-16 22:55 | 显示全部楼层
上 b 站了啊,赶紧关注一波

使用特权

评论回复
www5911839| | 2022-5-16 22:57 | 显示全部楼层
一键三连
一键三连.jpg

使用特权

评论回复
xld0932|  楼主 | 2022-5-17 09:30 | 显示全部楼层
www5911839 发表于 2022-5-16 22:55
上 b 站了啊,赶紧关注一波

感谢感谢

使用特权

评论回复
xld0932|  楼主 | 2022-5-17 09:30 | 显示全部楼层

这种必须要有实物演示才能体现出效果呀

使用特权

评论回复
ROSHEN_007| | 2022-5-17 09:49 | 显示全部楼层
MM32F3277系列官网上怎么找不到,有没有keil PACK包发一下

使用特权

评论回复
xld0932|  楼主 | 2022-5-17 09:57 | 显示全部楼层
ROSHEN_007 发表于 2022-5-17 09:49
MM32F3277系列官网上怎么找不到,有没有keil PACK包发一下

刚看了下官网,产品选型里只有MM32F3273;我这个板子是官方的MM32-EVBoard(MB-039),Keil Pack包在官网有下载的:https://www.mindmotion.com.cn/support/software/keil_pack/

使用特权

评论回复
ROSHEN_007| | 2022-5-17 15:05 | 显示全部楼层
xld0932 发表于 2022-5-17 09:57
刚看了下官网,产品选型里只有MM32F3273;我这个板子是官方的MM32-EVBoard(MB-039),Keil Pack包在官网 ...

这个包我下载了,工程我打开怎么是MM32F3277

使用特权

评论回复
qiangtech| | 2022-5-17 18:10 | 显示全部楼层
nice,mark一下

使用特权

评论回复
xld0932|  楼主 | 2022-5-17 22:21 | 显示全部楼层

使用特权

评论回复
我是我是阿君| | 2022-5-19 11:18 | 显示全部楼层
真是不错,真是不错

使用特权

评论回复
xld0932|  楼主 | 2022-5-19 13:37 | 显示全部楼层

使用特权

评论回复
mutable| | 2022-5-19 20:29 | 显示全部楼层
非常的nice啊~~~

使用特权

评论回复
xld0932|  楼主 | 2022-5-19 22:36 | 显示全部楼层

使用特权

评论回复
tpgf| | 2022-6-6 12:47 | 显示全部楼层
播放效果如何

使用特权

评论回复
juliestephen| | 2022-6-6 12:49 | 显示全部楼层
MM32会占用多大的资源呢?

使用特权

评论回复
wakayi| | 2022-6-6 13:10 | 显示全部楼层
现在可以买到这个开发板吗

使用特权

评论回复
wowu| | 2022-6-6 13:19 | 显示全部楼层
这种大屏开发板很不错啊

使用特权

评论回复
pl202| | 2022-6-6 13:23 | 显示全部楼层
板子设计的非常好。  

使用特权

评论回复
发新帖 本帖赏金 150.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2992

帖子

29

粉丝