ALSA声卡10_从零编写之数据传输_学习笔记

发布者:CelestialSoul最新更新时间:2024-07-10 来源: elecfans关键字:数据传输  s3c2440 手机看文章 扫描二维码
随时随地手机看文章

1、引言

(1)应用程序使用声卡的时候,数据流程是:应用程序把数据发送给驱动,驱动把数据发送给硬件声卡,声卡把数据转换成声音数据播放出去。

(2)可以使用两种方式发送数据

第一种:app发数据,等驱动处理完后再发下一段(处理完再发下一段就会导致声音会断断续续 )

第二种:应用程序不断地发数据,驱动程序不断地取数据,不断地发给硬件。解决了声音断续的问题,但是要创建一个非常大的缓冲区(在驱动程序里面申请的 ,称其为buffer)



一个采样点的数据包括左声道数据和右声道数据


这里hw_ptr是指针( 更新是指指针向后移)


2、怎么写驱动(s3c2440_dma.c(platform))

(1)负责数据传输的是平台部分里面的DMA文件,修改s3c2440_dma.c



3、分配/释放buffer

创建声卡时,s3c2440_dma_new函数被调用,函数里分配dma buffer.销毁声卡时,释放dma buffer


(1)分配DMA BUFFER

static int s3c2440_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;


int ret = 0;


    /* 1. 分配DMA BUFFER */
if (!card->dev->dma_mask)
card->dev->dma_mask = &dma_mask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);


if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {//播放功能
   playback_dma_info.virt_addr = (unsigned int)dma_alloc_writecombine(pcm->card-   >dev, s3c2440_dma_hardware.buffer_bytes_max,
  &playback_dma_info.phy_addr, GFP_KERNEL);//分配DMA BUFFER   //virt_addr 是buffer的虚拟地址,buffer_bytes_max是分配缓冲区的大小,phy_addr是所分配缓冲区的物理地址
        if (!playback_dma_info.virt_addr)//如果虚拟地址为空,返回错误
        {
            return -ENOMEM; //返回没有内存的错误标志
        }
        playback_dma_info.buf_max_size = s3c2440_dma_hardware.buffer_bytes_max;//分配成功,记录分配buffer的大小,尽管分配128K,但用到多少是app决定的 

    //把buffer信息告诉alsa的核心层
    buf->dev.type = SNDRV_DMA_TYPE_DEV;
    buf->dev.dev = pcm->card->dev;
    buf->private_data = NULL;
        buf->area = playback_dma_info.virt_addr;//播放相关的dma  buffer  的信息的结构体
        buf->bytes = playback_dma_info.buf_max_size;//buf的大小
}//


return ret;
}

(2)释放DMA BUFFER

static void s3c2440_dma_free(struct snd_pcm *pcm)
{
dma_free_writecombine(pcm->card->dev, playback_dma_info.buf_max_size,
     (void *)playback_dma_info.virt_addr, playback_dma_info.phy_addr);//释放的是playback(播放)部分的dma buffer(buffer大小,buffer的虚拟地址,buffer的物理地址)
}



4、request_irq

(1)open函数

static int s3c2440_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
    int ret;
    /* 注册中断 */
    ret = request_irq(IRQ_DMA2,s3c2440_dma2_irq, IRQF_DISABLED, 'myalsa for playback',substream);
    if (ret)   //IRQ_DMA2是中断号,s3c2440_dma2_irq是中断处理函数, IRQF_DISABLED是标志(当发生中断时,在中断处理过程中,中断是保持屏蔽的), 'myalsa for playback'是中断名字,substream是device ID 
    {
        printk('request_irq error!n');
        return -EIO;
    }


return 0;
}

(2)中断请求函数(数据传输成功后,更新hw_ptr等信息)

static irqreturn_t s3c2440_dma2_irq(int irq, void *devid) //左边参数是中断号,右边是设备ID
{
    struct snd_pcm_substream *substream = devid;//在open函数调用的请求中断函数 request_irq中的给中断处理函数s3c2440_dma2_irq的参数substream
        
    /* 更新状态信息 */  当传输完一个DMA后,发生中断
    playback_dma_info.dma_ofs += playback_dma_info.period_size;//偏移地址向后移一个period
    if (playback_dma_info.dma_ofs >= playback_dma_info.buffer_size)//如果偏移地址超过buffer 的范围
        playback_dma_info.dma_ofs = 0;//就指向buffer的开头
    
    /* 更新hw_ptr等信息,(驱动部分)
     * 并且判断:如果buffer里没有数据了,则调用trigger来停止DMA 
     */

传完一个DMA,
    snd_pcm_period_elapsed(substream);  


    if (playback_dma_info.be_running)
    {
        /* 如果还有数据,be_running是1表明在运行
         * 1. 加载下一个period 
         * 2. 再次启动DMA传输
         */
        load_dma_period();
        s3c2440_dma_start();
    }


    return IRQ_HANDLED;
}



5、 s3c2440_dma_hw_params(准备DMA传输的参设设置)

static int s3c2440_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long totbytes = params_buffer_bytes(params);//所使用的buffer大小由应用程序传进来的参数params决定的
    
    /* 根据params设置DMA */
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);


    /* s3c2440_dma_new(驱动程序里面)分配了很大的DMA BUFFER,应用程序会用多大是由应用程序决定的,传进来的参数params
     * params决定使用多大
     */
runtime->dma_bytes            = totbytes;//记录所使用的大小,以后会用到,应用程序写到尾部时会返回来,尾部在哪里由尾部决定的。
    playback_dma_info.buffer_size = totbytes;//buffer_size
    playback_dma_info.period_size = params_period_bytes(params);//period_size,buffer里面,一次DMA传输是以period传输的


    return 0;
}


6、prepare函数(准备DMA传输的数据)


static int s3c2440_dma_prepare(struct snd_pcm_substream *substream)
{
    /* 准备DMA传输 */

    /* 复位各种状态信息(清零) */
    playback_dma_info.dma_ofs = 0;//偏移值设置为0,以后DMA传输从开始处开始
    playback_dma_info.be_running = 0;//表示DMA尚未启动,还没运行
    
    /* 加载第1个period */
    load_dma_period();


return 0;
}





return ret;
}


7、加载需要传输的数据

/* 数据传输: 源,目的,长度 */
static void load_dma_period(void)
{       
/* 把源,目的,长度告诉DMA */
dma_regs->disrc      = playback_dma_info.phy_addr + playback_dma_info.dma_ofs;       /* 源的物理地址 */物理地址+偏移地址
dma_regs->disrcc     = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */
dma_regs->didst      = 0x55000010;        /* 目的的物理地址 */IIC控制器
dma_regs->didstc     = (0<<2) | (1<<1) | (1<<0); /* 目的位于APB总线, 目的地址不变 */


    /* bit22: 1-noreload *表示传完一段数据后会重新加载某些值,自己启动DMA,在我们的中断程序是自己启动DMA的,所以不让它自动下载,因而把它设置为1,自动加载的话在中断函数还没有处理前就自动传输数据/解决播放存在杂音的问题
dma_regs->dcon       = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(playback_dma_info.period_size/2);  /* 使能中断,单个传输,硬件触发 */period_size长度,每次传输不是传输整个buffer,是传输里面的某个period,period_size大小。buffer_size是应用程序所使用buffer的大小,buf_max_size是驱动程序分配的buffer大小,应用程序只可能使用里面的一部分。除以2是因为(1<<20)也就是2个字节,因而以字节为单位所以要除以2。也就是传输多少次,每次传输2个字节。
}



8、dma_trigger函数(触发DMA传输,传输完成后,产生中断,进入中断处理函数s3c2440_dma2_irq)



static int s3c2440_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;


    /* 根据cmd启动或停止DMA传输 */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        /* 启动DMA传输 */
        playback_dma_info.be_running = 1;//还有数据的状态信息
        s3c2440_dma_start();
break;


case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        /* 停止DMA传输 */
        playback_dma_info.be_running = 0;//传完数据的状态信息
        s3c2440_dma_stop();
break;


default:
ret = -EINVAL;
break;
}

9、dma_pointer函数(表示下一次DMA传输的位置(每一次DMA传输一个period),hw_ptr指针是根据DMA位置来确定的)

/* 返回结果是frame(单位是frame) */
static snd_pcm_uframes_t s3c2440_dma_pointer(struct snd_pcm_substream *substream)
{
return bytes_to_frames(substream->runtime, playback_dma_info.dma_ofs);//playback_dma_info.dma_ofs是DMA传输的偏移地址,如果偏移地址超出buffer的范围,就应该回到开始的位置,假如一个period里面有3个frame,如果位置指向period开始处,返回值是3,指向右边是6.也就是
}

10、总结

(1)创建声卡时导致 s3c2440_dma_new函数被调用

(2)s3c2440_dma_new函数里面分配DMA  BUFFER(分配最大的,128K,但并不表示一定要用完(根据应用程序参数决定))

(3)应用程序打开设备,播放声音的时候,在open函数(s3c2440_dma_open),设置属性,注册中断;在close函数中,释放中断

(4)设置参数(在s3c2440_dma_hw_params函数中),驱动程序分配了很大一块空间,确定buffer_size(应用程序传进来的params参数决定的),period_size(buffer里是逐个peroid传输的)

一次DMA传输是以period传输的

(5)准备DMA传输(s3c2440_dma_prepare),加载第一个period(load_dma_period)

(6)启动DMA 传输(s3c2440_dma_trigger),一个period传输完后会产生中断)调用中断处理函数(s3c2440_dma2_irp更新偏移地址。更新alsa驱动的其他状态信息。如果还有数据,再次加载period,再次启动


关键字:数据传输  s3c2440 引用地址:ALSA声卡10_从零编写之数据传输_学习笔记

上一篇:ARM的堆栈方式
下一篇:u-boot-2011.06移植

推荐阅读最新更新时间:2024-11-10 13:01

BootLoader —— S3C2440
先来看看扬创的bootloader的实现方法: 首先:在汇编中初始化堆栈,中断向量表,MMU,时钟,串口等,然后跳到C语言的Main函数。这部分代码小于4K,放在block0。这个Main函数用来将第二段代码拷备到DRAM中并执行。 其次:进入第二段代码。第二段代码也是先在汇编中初始化堆栈,中断向量表等,然后跳到C语言的Main函数。这部分代码就不用有4K限制了,具体大小由第一段代码决定,因为它本身由第一段代码来搬运。这个Main函数用来显示开机图片以及进度条。然后视串口接收信息运行带有USB下载NK的Eboot或是读取NK映像,启动WINCE系统。 最后:进入第三段代码。这段代码用于通过USB将PC上的NK.nb0或
[单片机]
s3c2440裸机-norflash3-uboot中操作norflash
前提: norflash初始化正常,能够正常从nor上执行。 cpu总是从0地址读取指令执行程序。当cpu设置成nor启动时,0地址对应nor。cpu从nand启动时,0地址对应sram。 1.读取norFlash 我们将板子设为nor启动,那么0地址对应nor,我们先将uboot烧写到nor中。我们先看下这款NorFlash的手册,找到操作flash的命令表: 下面简单的举一些例子: 1)复位(reset): 往任何一个地址写入F0即可。 2)读ID: 很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte),我们这款norflash数据位宽为16bit。下面我们按照nor手册上的命令表尝
[单片机]
S3C2440中断控制器
S3C2440A 中的中断控制器接受来自 60 个中断源的请求。提供这些中断源的是内部外设,如 DMA 控制器、 UART、IIC等等。在这些中断源中,UARTn、AC97 和EINTn中断对于中断控制器而言是 或 关系。 当从内部外设和外部中断请求引脚收到多个中断请求时,中断控制器在仲裁步骤后请求ARM920T 内核的FIQ 或IRQ。 中断控制器所用到的专业寄存器,其详细的使用请看数据手册 SRCPND 中断标志(挂起)寄存器 写1时 对应位中断源有中断请求,写0时,对应位中断源无中断请求 INTPND 中断服务(挂起)寄存器 写1时 对应位中断源被响应,写0时,对应位中断源未被响应 。。。。
[单片机]
<font color='red'>S3C2440</font>中断控制器
六 ARM9(S3C2440)的LCD显示-理论知识
今天和大家一起讨论一下S3C2440的LCD显示的问题,希望大家能够多多指教,我说的不对的地方希望大家及时帮我改正,以使我可以增长知识,才能不至于给别人的学习带来不便,呵呵 下面先看一下我从别人那转过来的一篇文章,我觉得很有用,看完之后我再说一下自己对这一块的了解,也可以先看看我的理解(下面红字标出)再看开始的这篇文章,因为我说的更通俗,O( _ )O哈哈~。 1. LCD工作的硬件需求: 要使一块LCD正常的显示文字或图像,不仅需要LCD驱动器,而且还需要相应的LCD控制器。在通常情况下,生产厂商把LCD驱动器会以COF/COG的 形式与LCD玻璃基板制作在一起,而LCD控制器则是由外部的电路来实现,现在很多
[单片机]
S3C2440开发板-LCD基础(源代码)
首先了解TFT LCD的时序,每个VSYNC信号表示一帧数据的开始,每个HSYNC表示一行数据的开始,无论这些数据是否有效,每个VCLK表示正在传输一个像素的数据,无论它是否有效。VSPW称为垂直同步信号的脉宽,VBPD称为垂直同步信号的后肩,VFPD称为垂直同步信号的前肩。HSPW称为水平同步信号的脉宽,HBPD称为水平同步信号的后肩,HFPD称为水平同步信号的前肩。查看时序图,VSYNC信号有效时,表示一帧数据的开始,VSPW表示VSYNC信号的脉冲宽度为(VSPW+1)个HSYNC信号周期,即(VSYNC+1)行,这(VSPW+1)行的数据无效。VSYNC信号脉冲之后,还要经过(VBPD+1)个HSYNC信号周期,有效的行数
[单片机]
<font color='red'>S3C2440</font>开发板-LCD基础(源代码)
基于S3C2440的Ethercat实现
  1 引言   工业以太网由于低成本、易于组网和具有相当高的数据传输速率、资源共享能力强以及易于Internet连接等特点,以太网(Ethernet)指的是由Xerox公司创建并由Xerox、Intel和DEC公司联合开发的基带局域网规范,是当今现有局域网采用的最通用的通信协议标准。以太网络使用CSMA/CD(载波监听多路访问及冲突检测)技术,并以10M/S的速率运行在多种类型的电缆上。以太网与IEEE802·3系列标准相类似。以太网最早由Xerox(施乐)公司创建,在1980年,DEC、lntel和Xerox三家公司联合开发成为一个标准。以太网是应用最为广泛的局域网,包括标准的以太网(10Mbit/s)、快速以太网(100Mb
[单片机]
基于<font color='red'>S3C2440</font>的Ethercat实现
s3c2440裸机-I2c编程-1-i2c协议
1.硬件电路 I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。如下图: SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,需通过上拉电阻接电源VCC.当总线空闲时.两根线都是高电平。 I2C 总线标准模式下速度可以达到 100Kb/S,快速模式下可以达到 400Kb/S。SDA 和 SCL 这两根线必须要接一个上拉电阻,一般是 4.7K。 2.i2c协议规则 传输过程如下: 主控发送start讯号(S) 主控发送从设备地址(slave dev addr) 主控发送方向(W/R) 从设备应答(ack) 主控(or从设备)发送数据(data) 从设备(or主控)应答(ack) … 主控发送
[单片机]
ARM-Linux s3c2440 之中断分析(一)
硬件篇: S3C2440 是arm920T架构,先温习一下s3c2440中的中断控制器原理和相关硬件构架。 中断控制器(InterruptControler): S3c2440A的中断控制器有60个中断源,如DMA中断,UART中断,IIC中断等,60个中断源在寄存器中用相应的位来表示。当有多个中断要求到来时,经过仲裁过程后,中断控制器向CPU请求FIQ或者IRQ中断。仲裁过程根据硬件中的优先级模块来决定,其结果最后写进中断未决(intterrupt pending)寄存器中,通过中断未决寄存器的值可以清楚哪个中断发生了。 S3c2440中断控制器流程图: 挂起 中断模式(InterruptMode): AR
[单片机]
ARM-Linux <font color='red'>s3c2440</font> 之中断分析(一)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved