老兵精讲:基于MM32实现音频播放系统的应用实例

嵌入式ARM 2022-08-16 12:01
一、基本介绍

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、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文件格式,如下图所示:

1、WAV文件结构

在Windows环境下,大部分多媒体文件都是按照资源互换文件格式(Resources Interchange File Format)存放信息的,简称RIFF格式。构成RIFF文件的基本单位称之为块(Chunk),每个RIFF文件都是由若干个块组成的,而每个块都是由块标识、块长度以及数据这三部分组成的。块标识是由4个ASCII码字符组成的,如果不满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”,其基本结构如下所示:


2、WAV文件头格式


3、WAV文件格式实例解析


4、扩展子块格式


5、fact块格式


6、WAV文件格式实例解析


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


  • 5741 5645这个是ASCII字符”WAVE”
  • 666D 7420这个是ASCII字符”fmt ”,即fmt块的块标识
  • 1000 0000这个是fmt格式块长度,16字节
  • 0100这个是编码格式,0x0001对应PCM/非压缩格式
  • 0200这个是声道个数,0x0002表示两声道或者立体声道
  • 44AC 0000这个是采样频率,对应16进制数为0x0000AC44,对应十进制为44100

其它的字段数据可以对照上述表格一一列举出来,但需要注意的是数据的大小端不同的存储方式哈!!!

7、WAV文件格式在代码中的结构体定义

WAV.h /* 由于字数限制,请参考源代码 */
8、WAV文件格式在MM32中的结构解析
static uint8_t WAV_DecodeFile(WAV_TypeDef *pWav, char *Path, char *Name) /* 由于字数限制,请参考源代码 */
9、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是利用人耳对高频声音信号不敏感的特性,将时域波形信号转换成频域信号,并划分成多个频段,对不同的频段使用不同的压缩率,对高频加大压缩比(甚至忽略信号)对低频信号使用小压缩比,保证信号不失真。这样一来,就相当于抛弃人耳基本听不到的高频声音,只保留能听到的低频部分,这样可得到很高的压缩率。

1、MP3文件结构

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

2、MP3数据帧

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

3、MP3解码库

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

现在合适在小型嵌入式控制器移植运行的有两个版本的开源MP3解码库,分别为libmad解码库和Helix解码库。

其中,libmad是一个开源的高精度MPEG音频解码库,是专门面向嵌入式应用的MP3解码程序,可以简单地实现MP3数据解码工作,支持MPEG-1、MPEG-2,以及MPEG-2.5标准,它可以提供24位PCM输出,用定点运算模拟浮点运算,因此不需要处理器有浮点运算功能,非常适合没有浮点支持的平台上使用;libmad对MP3解码中关键部分采用了优化的算法,这些优化算法能够大幅度地减少计算量,而且大多应用于MP3解码的VLSI实现中。libmad的源代码文件目录下的mad.h文件中,可以看到绝大部分该库的数据结构和API等,软件库结构清晰,易于使用和开发。

而Helix解码库则支持浮点和定点这两种的计算实现,它同样支持MPEG-1、MPEG-2以及MPEG-2.5标准的Layer3解码,此外Helix解码库还支持可变位速率、恒定位速率,以及立体声和单声道音频格式。这两个解码库都是以一帧为解码单位的,一次解码一帧。

各有优点,至于哪个更好,我还没有深入研究,但从使用下来最明显的体验来说,就是libmad库对堆栈空间大小的要求肯定是比Helix要大得多的,所以如果为了节省些RAM空间,可以优先考虑选择Helix库。

4、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");
    }
}
5、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) /* 由于字数限制,请参考源代码 */
八、测试运行

系统初始化加载:

歌曲目录文件显示:

播放WAV歌曲:

通过Helix解码库播放MP3歌曲:


通过labmad解码库播放MP3歌曲:


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

(1)通过Xmodem串行传输协议下载字库

(2)通过Xmodem串行传输协议上传字库

(3)比较下载的字库文件和上传的字库文件是否一致

以上就是基于MM32实现音频播放系统的应用实例了,如果有需要查看原图、代码、视频演示的小伙伴,请点击底部“阅读原文”进行下载。

END

作者:xld0932
来源:21ic论坛

版权归原作者所有,如有侵权,请联系删除。

推荐阅读
让人眼前一亮的Linux终端工具!
CPU明明8个核,网卡为什么拼命折腾一号核?
为什么中国的数字是四位一进,西方的是三位一进?

→点关注,不迷路←
嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论
  • 国产光耦合器正以其创新性和多样性引领行业发展。凭借强大的研发能力,国内制造商推出了适应汽车、电信等领域独特需求的专业化光耦合器,为各行业的技术进步提供了重要支持。本文将重点探讨国产光耦合器的技术创新与产品多样性,以及它们在推动产业升级中的重要作用。国产光耦合器创新的作用满足现代需求的创新模式新设计正在满足不断变化的市场需求。例如,高速光耦合器满足了电信和数据处理系统中快速信号传输的需求。同时,栅极驱动光耦合器支持电动汽车(EV)和工业电机驱动器等大功率应用中的精确高效控制。先进材料和设计将碳化硅
    克里雅半导体科技 2024-11-29 16:18 170浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 75浏览
  • 艾迈斯欧司朗全新“样片申请”小程序,逾160种LED、传感器、多芯片组合等产品样片一触即达。轻松3步完成申请,境内免费包邮到家!本期热荐性能显著提升的OSLON® Optimal,GF CSSRML.24ams OSRAM 基于最新芯片技术推出全新LED产品OSLON® Optimal系列,实现了显著的性能升级。该系列提供五种不同颜色的光源选项,包括Hyper Red(660 nm,PDN)、Red(640 nm)、Deep Blue(450 nm,PDN)、Far Red(730 nm)及Ho
    艾迈斯欧司朗 2024-11-29 16:55 171浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 105浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 86浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 101浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 52浏览
  • 学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&
    youyeye 2024-11-30 14:30 73浏览
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 41浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 45浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 86浏览
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 71浏览
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 70浏览
  • 《高速PCB设计经验规则应用实践》+PCB绘制学习与验证读书首先看目录,我感兴趣的是这一节;作者在书中列举了一条经典规则,然后进行详细分析,通过公式推导图表列举说明了传统的这一规则是受到电容加工特点影响的,在使用了MLCC陶瓷电容后这一条规则已经不再实用了。图书还列举了高速PCB设计需要的专业工具和仿真软件,当然由于篇幅所限,只是介绍了一点点设计步骤;我最感兴趣的部分还是元件布局的经验规则,在这里列举如下:在这里,演示一下,我根据书本知识进行电机驱动的布局:这也算知行合一吧。对于布局书中有一句:
    wuyu2009 2024-11-30 20:30 106浏览
  • 在电子技术快速发展的今天,KLV15002光耦固态继电器以高性能和强可靠性完美解决行业需求。该光继电器旨在提供无与伦比的电气隔离和无缝切换,是现代系统的终极选择。无论是在电信、工业自动化还是测试环境中,KLV15002光耦合器固态继电器都完美融合了效率和耐用性,可满足当今苛刻的应用需求。为什么选择KLV15002光耦合器固态继电器?不妥协的电压隔离从本质上讲,KLV15002优先考虑安全性。输入到输出隔离达到3750Vrms(后缀为V的型号为5000Vrms),确保即使在高压情况下,敏感的低功耗
    克里雅半导体科技 2024-11-29 16:15 128浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦