如果你也有文章愿意分享并希望获得官方的写作指导,可以发送文章/联系方式邮件至邮箱:xuqianqian@rt-thread.com

今天主要是讲解音频虚拟驱动来分析驱动的编写。但是这篇文章并不会讲解关于 RT-Thread IO Device 框架相关内容,如果有对这部分不太熟悉的人请先看这个链接了解基本概念,RT-Thread I/O 设备模型https://www.rt-thread.org/document/site/programming-manual/device/device

(请复制以上链接至外部浏览器打开)

1. RT-Thread 音频框架图

RT-Thread的音频分成了4个部分,但是我们只要关心上层提供的api和底层驱动需要实现的ops接口就可以了。

2. 如何使用 Audio 驱动

在写驱动之前,我们首先得知道如何测试自己的驱动,所以我们首先需要了解下 RT-Thread 系统中是如何播放音乐的!

  1#include <rtthread.h>  2#include <rtdevice.h>  3#include <dfs_posix.h>  4  5#define BUFSZ   1024  6#define SOUND_DEVICE_NAME    "sound0"    /* Audio 设备名称 */  7static rt_device_t snd_dev;              /* Audio 设备句柄 */  8  9struct RIFF_HEADER_DEF 10{ 11    char riff_id[4];     // 'R','I','F','F' 12    uint32_t riff_size; 13    char riff_format[4]; // 'W','A','V','E' 14}; 15 16struct WAVE_FORMAT_DEF 17{ 18    uint16_t FormatTag; 19    uint16_t Channels; 20    uint32_t SamplesPerSec; 21    uint32_t AvgBytesPerSec; 22    uint16_t BlockAlign; 23    uint16_t BitsPerSample; 24}; 25 26struct FMT_BLOCK_DEF 27{ 28    char fmt_id[4];    // 'f','m','t',' ' 29    uint32_t fmt_size; 30    struct WAVE_FORMAT_DEF wav_format; 31}; 32 33struct DATA_BLOCK_DEF 34{ 35    char data_id[4];     // 'R','I','F','F' 36    uint32_t data_size; 37}; 38 39struct wav_info 40{ 41    struct RIFF_HEADER_DEF header; 42    struct FMT_BLOCK_DEF   fmt_block; 43    struct DATA_BLOCK_DEF  data_block; 44}; 45 46int wavplay_sample(int argc, char **argv) 47{ 48    int fd = -1; 49    uint8_t *buffer = NULL; 50    struct wav_info *info = NULL; 51    struct rt_audio_caps caps = {0}; 52 53    if (argc != 2) 54    { 55        rt_kprintf("Usage:\n"); 56        rt_kprintf("wavplay_sample song.wav\n"); 57        return 0; 58    } 59 60    fd = open(argv[1], O_WRONLY); 61    if (fd < 0) 62    { 63        rt_kprintf("open file failed!\n"); 64        goto __exit; 65    } 66 67    buffer = rt_malloc(BUFSZ); 68    if (buffer == RT_NULL) 69        goto __exit; 70 71    info = (struct wav_info *) rt_malloc(sizeof * info); 72    if (info == RT_NULL) 73        goto __exit; 74 75    if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0) 76        goto __exit; 77    if (read(fd, &(info->fmt_block),  sizeof(struct FMT_BLOCK_DEF)) <= 0) 78        goto __exit; 79    if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0) 80        goto __exit; 81 82    rt_kprintf("wav information:\n"); 83    rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec); 84    rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels); 85 86    /* 根据设备名称查找 Audio 设备,获取设备句柄 */ 87    snd_dev = rt_device_find(SOUND_DEVICE_NAME); 88 89    /* 以只写方式打开 Audio 播放设备 */ 90    rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY); 91 92    /* 设置采样率、通道、采样位数等音频参数信息 */ 93    caps.main_type               = AUDIO_TYPE_OUTPUT;                           /* 输出类型(播放设备 )*/ 94    caps.sub_type                = AUDIO_DSP_PARAM;                             /* 设置所有音频参数信息 */ 95    caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec;    /* 采样率 */ 96    caps.udata.config.channels   = info->fmt_block.wav_format.Channels;         /* 采样通道 */ 97    caps.udata.config.samplebits = 16;                                          /* 采样位数 */ 98    rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps); 99100    while (1)101    {102        int length;103104        /* 从文件系统读取 wav 文件的音频数据 */105        length = read(fd, buffer, BUFSZ);106107        if (length <= 0)108            break;109110        /* 向 Audio 设备写入音频数据 */111        rt_device_write(snd_dev, 0, buffer, length);112    }113114    /* 关闭 Audio 设备 */115    rt_device_close(snd_dev);116117__exit:118119    if (fd >= 0)120        close(fd);121122    if (buffer)123        rt_free(buffer);124125    if (info)126        rt_free(info);127128    return 0;129}130MSH_CMD_EXPORT(wavplay_sample,  play wav file);

这段代码主要是播放 wav(pcm) 的音频。那么我们来分析下上面一段代码,这段播放一段音频数据的主要步骤如下:

1、#define SOUND_DEVICE_NAME "sound0": 首先定义播放的驱动

2、fd = open(argv[1], O_WRONLY);: 用于打开音频文件,这个没什么分析的3、snd_dev = rt_device_find(SOUND_DEVICE_NAME);: 首先查找 Audio 设备获取设备句柄

4、rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY);: 以只写方式打开 Audio 设备,也就是打开放音设备

5、rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);: 置音频参数信息(采样率、通道等)

6、length = read(fd, buffer, BUFSZ);: 读取音频文件的数据

7、rt_device_write(snd_dev, 0, buffer, length);: 向驱动写入音频文件数据,写入后就会出声音,写入的数据为pcm数据,音频相关格式是步骤5中配置的参数 8、rt_device_close(snd_dev);: 播放完成,关闭设备

这样看起来是不是非常简单,将这段代码添加到你的代码中进行编译下载,就可以了放音乐了,当然只能播放wav格式的音频。

这个时候肯定有大佬已经反应过来了,我bsp连个audio驱动都没有,脑补音乐吗!大佬不要心急,小弟这就给你把驱动慢慢道来~

3. 编写音频虚拟驱动

上来废话不多说,直接上干货:

  1#include "drv_sound.h"  2#include "drv_tina.h"  3#include "drivers/audio.h"  4  5#define DBG_TAG "drv_sound"  6#define DBG_LVL DBG_LOG  7#define DBG_COLOR  8#include <rtdbg.h>  9 10#define TX_DMA_FIFO_SIZE (2048) 11 12struct temp_sound 13{ 14    struct rt_audio_device device; 15    struct rt_audio_configure replay_config; 16    int volume; 17    rt_uint8_t *tx_fifo; 18}; 19 20static rt_err_t getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) 21{ 22    struct temp_sound *sound = RT_NULL; 23 24    RT_ASSERT(audio != RT_NULL); 25    sound = (struct temp_sound *)audio->parent.user_data; (void)sound; 26 27    return RT_EOK; 28} 29 30static rt_err_t configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) 31{ 32    struct temp_sound *sound = RT_NULL; 33 34    RT_ASSERT(audio != RT_NULL); 35    sound = (struct temp_sound *)audio->parent.user_data; (void)sound; 36 37    return RT_EOK; 38} 39 40static rt_err_t init(struct rt_audio_device *audio) 41{ 42    struct temp_sound *sound = RT_NULL; 43 44    RT_ASSERT(audio != RT_NULL); 45    sound = (struct temp_sound *)audio->parent.user_data; (void)sound; 46 47    return RT_EOK; 48} 49 50static rt_err_t start(struct rt_audio_device *audio, int stream) 51{ 52    struct temp_sound *sound = RT_NULL; 53 54    RT_ASSERT(audio != RT_NULL); 55    sound = (struct temp_sound *)audio->parent.user_data; (void)sound; 56 57    return RT_EOK; 58} 59 60static rt_err_t stop(struct rt_audio_device *audio, int stream) 61{ 62    struct temp_sound *sound = RT_NULL; 63 64    RT_ASSERT(audio != RT_NULL); 65    sound = (struct temp_sound *)audio->parent.user_data; (void)sound;   66 67    return RT_EOK; 68} 69 70rt_size_t transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size) 71{ 72    struct temp_sound *sound = RT_NULL; 73 74    RT_ASSERT(audio != RT_NULL); 75    sound = (struct temp_sound *)audio->parent.user_data; (void)sound; 76 77    return size; 78} 79 80static void buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) 81{ 82    struct temp_sound *sound = RT_NULL; 83 84    RT_ASSERT(audio != RT_NULL); 85    sound = (struct temp_sound *)audio->parent.user_data; 86 87    /** 88     *               TX_FIFO 89     * +----------------+----------------+ 90     * |     block1     |     block2     | 91     * +----------------+----------------+ 92     *  \  block_size  / 93     */ 94    info->buffer      = sound->tx_fifo; 95    info->total_size  = TX_DMA_FIFO_SIZE; 96    info->block_size  = TX_DMA_FIFO_SIZE / 2; 97    info->block_count = 2; 98} 99100static struct rt_audio_ops ops =101{102    .getcaps     = getcaps,103    .configure   = configure,104    .init        = init,105    .start       = start,106    .stop        = stop,107    .transmit    = transmit,108    .buffer_info = buffer_info,109};110111static int rt_hw_sound_init(void)112{113    rt_uint8_t *tx_fifo = RT_NULL;114    static struct temp_sound sound = {0};115116    /* 分配 DMA 搬运 buffer */117    tx_fifo = rt_calloc(1, TX_DMA_FIFO_SIZE);118    if(tx_fifo == RT_NULL)119    {120        return -RT_ENOMEM;121    }122123    sound.tx_fifo = tx_fifo;124125    /* 注册声卡放音驱动 */126    sound.device.ops = &ops;127    rt_audio_register(&sound.device, "sound0", RT_DEVICE_FLAG_WRONLY, &sound);128129    return RT_EOK;130}131INIT_DEVICE_EXPORT(rt_hw_sound_init);

上面是整个audio驱动的架子,没有如何和硬件相关的代码,但是添加到项目中,是可以在shell中使用list_device命令看到 sound0 驱动的。如果我们将第一章中的代码配合的话是可以播放 wav 音频,当然由于没有硬件相关代码是不会出声音的。

我们先来分析下这段代码:

1、rt_hw_sound_init 函数是驱动的入口,用于注册audio框架,在这个里面,我们分配了 audio dma 需要的buffer,并将 实现的音频相关的ops注册到sound0音频设备中。调用这个函数后就可以在list_device中看到sound0驱动了。

2、那么接下来有疑问了struct rt_audio_ops ops这个结构体中的几个函数分别是干什么的如何编写。那么笔者给大家慢慢道来!

3、由于 audio 相关的配置和设置的参数比较多,所以这里我们将配置和获取参数分别分成了2个 ops 函数来实现,分别为 getcaps 和 configure。getcaps 用于获取 audio 的能力,例如硬件通道数,当前采样率,采样深度,音量,configure 函数用于实现设置通道数,当前采样率,采样深度,音量。

4、init ops函数,主要用于实现 芯片的 i2s(与外部codec进行音频数据通信) i2c(控制外部codec的采样率,mute脚,当然部分codec内置的是不需要这个的,还有部分比较低端一点的codec也是不会有i2c控制的,这个根据大家外部接的芯片来确定),当然还需要配置 dma 和 dma 中端。还有控制 mute 的gpio引脚。

5、start ops 函数主要是用于启动 dma 和 关mute 相关的处理的。

6、stop ops 函数主要是用于关闭 dma 和 开mute 相关的处理的。

7、transmit 主要是用于触发数据的搬运,为什么说是触发搬运呢?其实上层代码向音频设备写入音频数据并不会直接写入到驱动中,也就是不会直接调用transmit这个底层函数用于将缓冲区的数据传递到 dma 的buffer中,那么transmit会在什么时候调用呢?上面的驱动并不会触发驱动的搬运也就是这个函数,其实我们可以看到 audio 框架中有一个函数 rt_audio_tx_complete(&sound->device); 这个函数就是用于通知搬运的,那么我们再来梳理下这个段逻辑:

●上层应用调用 rt_device_write 函数向 audio 写入数据,框架层会将写入的数据缓存到内部的一个buffer(静态内存池中的一个节点,默认配置为2k数据)

●上层写入超过2k的数据会阻塞等待

●第一次使用 rt_device_write 会调用 start ops函数启动 dma搬运,在i2s的dma中断(半空和满中断服务函数中)调用 rt_audio_tx_complete 函数

●rt_audio_tx_complete 表示 dma的 数据搬运完毕了,需要填充下一次的音频数据,这个函数会调用 transmit ops,但是如果是i2s dma循环搬运的数据,dma会自动搬运数据,所以并不需要使用 transmit ops来将音频缓冲区的数据 copy 到驱动的dma中,那么transmit 有什么用呢?第一在部分没有dma循环搬运的芯片上我们可以利用这个函数触发下一个dma搬运或者是cpu搬运,第二这个地方可以用来刷cache的!

8、buffer_info 用于告诉audio框架你的音频驱动缓冲区有多大,有几块,这样上层通过 transmit ops函数的时候就知道给你多少字节数据了!

看了上面的分析我相信你应该了解了基本原理了,和编写方法了。但是这个驱动还是不能出声音,那么我们得想办法实现一个驱动,由于笔者的硬件和大家都不一样,那么小弟想了一个办法。

那就是将音频缓存到文件中,这里我们来做一个虚拟音频驱动,这个驱动并不会出声音,但是会将数据保存层pcm文件。pcm的相关参数和你播放的wav一样这样我们可以用电脑来播放了。这样就避免硬件的差异化。

4. 音频虚拟驱动编写

还是废话不多说,直接上代码。

  1/*  2* File: drv_virtual.c  3*  4* COPYRIGHT (C) 2012-2019, Shanghai Real-Thread Technology Co., Ltd  5*/  6  7#include "drv_virtual.h"  8#include "dfs.h"  9#include "dfs_posix.h" 10 11#define DBG_TAG "drv_virtual" 12#define DBG_LVL DBG_LOG 13#define DBG_COLOR 14#include <rtdbg.h> 15 16#define TX_DMA_FIFO_SIZE (2048) 17 18struct tina_sound 19{ 20    struct rt_audio_device device; 21    struct rt_audio_configure replay_config; 22    int volume; 23    rt_uint8_t *tx_fifo; 24    int fd; 25    struct rt_thread thread; 26    int endflag; 27}; 28 29static rt_err_t getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) 30{ 31    rt_err_t ret = RT_EOK; 32    struct tina_sound *sound = RT_NULL; 33 34    RT_ASSERT(audio != RT_NULL); 35    sound = (struct tina_sound *)audio->parent.user_data; (void)sound; 36 37    switch(caps->main_type) 38    { 39    case AUDIO_TYPE_QUERY: 40    { 41        switch (caps->sub_type) 42        { 43        case AUDIO_TYPE_QUERY: 44            caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER; 45            break; 46 47        default: 48            ret = -RT_ERROR; 49            break; 50        } 51 52        break; 53    } 54 55    case AUDIO_TYPE_OUTPUT: 56    { 57        switch(caps->sub_type) 58        { 59        case AUDIO_DSP_PARAM: 60            caps->udata.config.channels   = sound->replay_config.channels; 61            caps->udata.config.samplebits = sound->replay_config.samplebits; 62            caps->udata.config.samplerate = sound->replay_config.samplerate; 63            break; 64 65        default: 66            ret = -RT_ERROR; 67            break; 68        } 69 70        break; 71    } 72 73    case AUDIO_TYPE_MIXER: 74    { 75        switch (caps->sub_type) 76        { 77        case AUDIO_MIXER_QUERY: 78            caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE; 79            break; 80 81        case AUDIO_MIXER_VOLUME: 82            caps->udata.value = sound->volume; 83            break; 84 85        case AUDIO_MIXER_LINE: 86            break; 87 88        default: 89            ret = -RT_ERROR; 90            break; 91        } 92 93        break; 94    } 95 96    default: 97        ret = -RT_ERROR; 98        break; 99    }100101    return ret;102}103104static rt_err_t configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)105{106    rt_err_t ret = RT_EOK;107    struct tina_sound *sound = RT_NULL;108109    RT_ASSERT(audio != RT_NULL);110    sound = (struct tina_sound *)audio->parent.user_data; (void)sound;111112    switch(caps->main_type)113    {114    case AUDIO_TYPE_MIXER:115    {116        switch(caps->sub_type)117        {118        case AUDIO_MIXER_VOLUME:119        {120            int volume = caps->udata.value;121            sound->volume = volume;122            break;123        }124125        default:126            ret = -RT_ERROR;127            break;128        }129130        break;131    }132133    case AUDIO_TYPE_OUTPUT:134    {135        switch(caps->sub_type)136        {137        case AUDIO_DSP_PARAM:138        {139            int samplerate;140141            samplerate = caps->udata.config.samplerate;142            sound->replay_config.samplerate = samplerate;143            LOG_I("set samplerate = %d", samplerate);144            break;145        }146147        case AUDIO_DSP_SAMPLERATE:148        {149            int samplerate;150151            samplerate = caps->udata.config.samplerate;152            sound->replay_config.samplerate = samplerate;153            LOG_I("set samplerate = %d", samplerate);154            break;155        }156157        case AUDIO_DSP_CHANNELS:158        {159            break;160        }161162        default:163            break;164        }165166        break;167    }168169    default:170        break;171    }172173    return ret;174}175176static void virtualplay(void *p)177{178    struct tina_sound *sound = (struct tina_sound *)p; (void)sound;179180    while(1)181    {182        /* tick = TX_DMA_FIFO_SIZE/2 * 1000ms / 44100 / 4 ≈ 5.8 */183        rt_thread_mdelay(6);184        rt_audio_tx_complete(&sound->device);185186        if(sound->endflag == 1)187        {188            break;189        }190    }191}192193static int thread_stack[1024] = {0};194195static rt_err_t init(struct rt_audio_device *audio)196{197    struct tina_sound *sound = RT_NULL;198199    RT_ASSERT(audio != RT_NULL);200    sound = (struct tina_sound *)audio->parent.user_data; (void)sound;201202    LOG_I("sound init");203204    return RT_EOK;205}206207static rt_err_t start(struct rt_audio_device *audio, int stream)208{209    struct tina_sound *sound = RT_NULL;210    rt_err_t ret = RT_EOK;211212    RT_ASSERT(audio != RT_NULL);213    sound = (struct tina_sound *)audio->parent.user_data; (void)sound;214215    LOG_I("sound start");216217    ret = rt_thread_init(&sound->thread, "virtual", virtualplay, sound, &thread_stack, sizeof(thread_stack), 1, 10);218    if(ret != RT_EOK)219    {220        LOG_E("virtual play thread init failed");221        return (-RT_ERROR);222    }223    rt_thread_startup(&sound->thread);224225    sound->endflag = 0;226227    sound->fd = open("/tmp/virtual.pcm", O_CREAT | O_RDWR, 0666);228229    return RT_EOK;230}231232static rt_err_t stop(struct rt_audio_device *audio, int stream)233{234    struct tina_sound *sound = RT_NULL;235236    RT_ASSERT(audio != RT_NULL);237    sound = (struct tina_sound *)audio->parent.user_data; (void)sound;238239    LOG_I("sound stop");  240241    sound->endflag = 1;242243    close(sound->fd);244    sound->fd = -1;245246    return RT_EOK;247}248249rt_size_t transmit(struct rt_audio_device *audio, const void *wb, void *rb, rt_size_t size)250{251    struct tina_sound *sound = RT_NULL;252253    RT_ASSERT(audio != RT_NULL);254    sound = (struct tina_sound *)audio->parent.user_data; (void)sound;255256    return write(sound->fd, wb, size);257}258259static void buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)260{261    struct tina_sound *sound = RT_NULL;262263    RT_ASSERT(audio != RT_NULL);264    sound = (struct tina_sound *)audio->parent.user_data;265266    /**267     *               TX_FIFO268     * +----------------+----------------+269     * |     block1     |     block2     |270     * +----------------+----------------+271     *  \  block_size  /272     */273    info->buffer      = sound->tx_fifo;274    info->total_size  = TX_DMA_FIFO_SIZE;275    info->block_size  = TX_DMA_FIFO_SIZE / 2;276    info->block_count = 2;277}278279static struct rt_audio_ops ops =280{281    .getcaps     = getcaps,282    .configure   = configure,283    .init        = init,284    .start       = start,285    .stop        = stop,286    .transmit    = transmit,287    .buffer_info = buffer_info,288};289290static int rt_hw_sound_init(void)291{292    rt_uint8_t *tx_fifo = RT_NULL;293    static struct tina_sound sound = {0};294295    /* 分配 DMA 搬运 buffer */296    tx_fifo = rt_calloc(1, TX_DMA_FIFO_SIZE);297    if(tx_fifo == RT_NULL)298    {299        return -RT_ENOMEM;300    }301302    sound.tx_fifo = tx_fifo;303304    /* 配置 DSP 参数 */305    {306        sound.replay_config.samplerate = 44100;307        sound.replay_config.channels   = 2;308        sound.replay_config.samplebits = 16;309        sound.volume                   = 60;310        sound.fd                       = -1;311        sound.endflag                  = 0;312    }313314    /* 注册声卡放音驱动 */315    sound.device.ops = &ops;316    rt_audio_register(&sound.device, "sound0", RT_DEVICE_FLAG_WRONLY, &sound);317318    return RT_EOK;319}320INIT_DEVICE_EXPORT(rt_hw_sound_init);

根据第二部分的分析,相信你也能看懂这部分代码,这个驱动的根本思想是利用 virtualplay 线程模拟 i2s dma进行数据的自动搬运!

最终文件会保存到 /tmp/virtual.pcm 中,注意这里有点是 virtualplay 函数延时了6ms是为了模拟dma buffer中 1k 数据搬运(播放)需要消耗的时间,tick = TX_DMA_FIFO_SIZE/2 * 1000ms / 44100 / 4 ≈ 5.8ms  所以我们得要求文件写入比较快,这里笔者利用了ramfs来实现文件系统,经过实际测试如果写入sd卡或者flash会非常的慢,所以还是建议使用 ramfs 保证 20Mbytes 以上的大小,当然可以使用 qemu 来测试~

那么小弟就分析到这里,更加多的信息请加入 qq 群: 690181735 讨论,有更多更专业RT-Thread audio相关资料等着你!

RT-Thread线上/下活动

1、RT-Thread开发者大会报名】深圳站马上开始!2019年RT-Thread开发者大会已经登入了成都、上海,马上将去到我们最后一站深圳,大会内容包含:RT-Thread在中高端智能领域的应用、一站式RTT开发工具、打造IoT极速开发模式等干货演讲,期待您的参与!

立即报名

2、RT-Thread音频大会】12月14日,LiveVideoStack联合RT-Thread在LiveVideoStackCon音视频技术大会推出嵌入式与音频开发专题会议,本专题将讨论音频设备开发方案、实战经验,涉及智能音箱、TWS耳机等产品的技术实践。

长按识别二维码扫码报名

报名链接:http://sz2019.livevideostack.com/track/106

#题外话# 喜欢RT-Thread不要忘了在GitHub上留下你的STAR哦,你的star对我们来说非常重要!链接地址:https://github.com/RT-Thread/rt-thread

你可以添加微信17775982065为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群

RT-Thread

让物联网终端的开发变得简单、快速,芯片的价值得到最大化发挥。Apache2.0协议,可免费在商业产品中使用,不需要公布源码,无潜在商业风险。

长按二维码,关注我们

点击“阅读原文”报名开发者大会

论坛热贴 | RT-Thread音频驱动开发(一)相关推荐

  1. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  2. Linux音频驱动开发概括

    原址 1.嵌入式音频系统硬件连接 下图所示的嵌入式设备使用IIS将音频数据发送给编解码器.对编解码器的I/O寄存器的编程通过IIC总线进行. 2.音频体系结构-ALSA ALSA是Advanced L ...

  3. 嵌入式Linux音频驱动开发

    1.嵌入式音频系统硬件连接 下图所示的嵌入式设备使用IIS将音频数据发送给编解码器.对编解码器的I/O寄存器的编程通过IIC总线进行. 2.音频体系结构-ALSA ALSA是Advanced Linu ...

  4. 基于imx8mq的NAU8822音频驱动开发

    Nau8822音频驱动 说明:此驱动是基于imx8mq soc,内核版本为4.19.35. 1. 将新唐官方提供的音频解码驱动文件nau8822.c.nau8822.h添加到sound\soc\cod ...

  5. LINUX音频驱动架构

    https://www.jianshu.com/p/0f5fe8f51b66 简介 LINUX下音频驱动开发,要遵循标准的ALSA架构, 下面分别从硬件架构.软件架构.驱动程序,3个方面分析. 硬件架 ...

  6. 竹林蹊径:深入浅出Windows驱动开发(china-pub预订中)

    竹林蹊径:深入浅出Windows驱动开发(china-pub预订中) 基本信息 作者: 张佩   马勇   董鉴源 出版社:电子工业出版社 ISBN:9787121125553 内容简介 本书是作者根 ...

  7. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  8. linux设备驱动开发专业论坛www.linuxdriver.cn诚征斑竹

    www.linuxdriver.cn诚征斑竹,一起繁荣Linux设备驱动开发专业论坛的人气,共同致力于提高中国工程师的Linux设备驱动开发水平,普及Linux设备驱动开发知识.[url]www.li ...

  9. MTK 驱动(60)---Audio驱动开发之音频链路

    Audio驱动开发之音频链路 [元器件说明] 本文中使用的 Codec 芯片为 ALC5677. [音频链路模型] 一个常见的音频链路如 图1 所示,包含 音频输入.ADC.DSP.DAC.音频输出 ...

最新文章

  1. 模板模式(template)解析例子
  2. SQLSERVER 2008 R2 事务日志已满
  3. 概率统计:第四章 随机变量的数字特征
  4. MySQL删除普通用户
  5. 净误差与遗漏为负值的含义_巴丹吉林沙漠湖泊水位变化及地下水净补给量
  6. 汇编语言——《分支与循环程序设计》实验任务书
  7. OOAD 面向对象分析与设计
  8. 2019年广西大学硕士研究生入学《数据结构与程序设计(817)》考试大纲
  9. 阿里云负载不支持 WebSocket 协议与 WSS 和 Nginx 配置问题
  10. [导入]设置wap服务器
  11. php支持的协议与封装协议
  12. 二:Unity 3D 脚本介绍
  13. 国遥新天地java_三维空间信息系统-国遥新天地.PDF
  14. 全方位教你怎么看懂英国的车牌
  15. 高通MSM8998芯片参考资料免费下载
  16. 微信公众号开发-使用微信网页授权进行登录并加上过滤器判断是否已登录
  17. 查看服务器GPU型号
  18. 计算机图形学 学习笔记(七):二维图形变换:平移,比例,旋转,坐标变换等
  19. 外贸电子商务网站的网络广告推广方案
  20. python画图y轴在右侧_python画图怎么添加汉字的坐标轴

热门文章

  1. 3.1 react的空标签
  2. K8S CNI之:利⽤ ipvlan + host-local 打通容器与宿主机的平⾏⽹络
  3. 2018年第四阶段组队训练赛第七场
  4. 办公应用计算机二级科目怎么选,全国计算机二级考试科目怎么选择?
  5. 单片机c语言左移的作用,单片机c语言中的左移右移
  6. window权限 及c++实现 【网摘】
  7. Acer笔记本宏碁Aspire系列蜂鸟S40-51原装出厂Windows10系统工厂模式恢复原厂OEM系统
  8. python写的推箱子小游戏
  9. SQL DATEPART()函数
  10. 完整的Web前端开发学习路线图