ALSA声卡09_从零编写之参数设置_学习笔记

发布者:ShiningSmile最新更新时间:2024-07-16 来源: elecfans关键字:参数设置  s3c2440 手机看文章 扫描二维码
随时随地手机看文章

1、参数设置分析

(1)open: soc_pcm_open 依次调用cpu_dai, dma, codec_dai, machine的open或startup函数


只在dma的open函数里添加参数相关的代码


(2)SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params 依次调用machine,codec_dai,cpu_dai,platform(dma)的hw_params函数
    
在uda1341.c, s3c2440-iis.c里实现hw_params函数(把裸板程序里面的相关代码移过来)


(s3c2440-dma.c 主要涉及数据传输,在下一节实现hw_params函数)

(3)打开声卡的时候会调用到machine部分的dma.c的snd_pcm_ops的dma_open函数)


2、open函数

(1)s3c2440_dma.c(Platform)

static int s3c2440_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
    int ret;
    /* 设置属性 */
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
snd_soc_set_runtime_hwparams(substream, &s3c2440_dma_hardware);
    }
return 0;
}

(2)snd_pcm_hardware结构体

static const struct snd_pcm_hardware s3c2440_dma_hardware = {
.info= SNDRV_PCM_INFO_INTERLEAVED | //数据的排列方式(左右左右左右还是左左左右右右)
   SNDRV_PCM_INFO_BLOCK_TRANSFER |
   SNDRV_PCM_INFO_MMAP |
   SNDRV_PCM_INFO_MMAP_VALID |
   SNDRV_PCM_INFO_PAUSE |
   SNDRV_PCM_INFO_RESUME,
.formats= SNDRV_PCM_FMTBIT_S16_LE |  //所支持的音频数据格式
   SNDRV_PCM_FMTBIT_U16_LE |
   SNDRV_PCM_FMTBIT_U8 |
   SNDRV_PCM_FMTBIT_S8,
.channels_min= 2,//通道数
.channels_max= 2,
.buffer_bytes_max= 128*1024,
.period_bytes_min= PAGE_SIZE,
.period_bytes_max= PAGE_SIZE*2,
.periods_min= 2,
.periods_max= 128,
.fifo_size= 32,
};

3、hw_params函数(硬件参数)

(1)uda1341.c(codec)

static int uda1341_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
    /* 根据params的值,设置UDA1341的寄存器 
     * 比如时钟设置,格式
     */
    /* 为了简单, 在uda1341_init_regs里就设置好时钟、格式等参数 */
    return 0;
}

Uda1341的初始化

static void uda1341_init_regs(struct snd_soc_codec *codec)
{


/* GPB 4: L3CLOCK */
/* GPB 3: L3DATA */
/* GPB 2: L3MODE */
    *gpbcon &= ~((3<<4) | (3<<6) | (3<<8));
    *gpbcon |= ((1<<4) | (1<<6) | (1<<8));

    uda1341_write_reg(codec, UDA1341_STATUS0, 0x40 | STAT0_SC_384FS | STAT0_DC_FILTER); // reset uda1341
    uda1341_write_reg(codec, UDA1341_STATUS1, STAT1_ADC_ON | STAT1_DAC_ON);


    uda1341_write_reg(codec, UDA1341_DATA00, DATA0_VOLUME(0x0)); // maximum volume
    uda1341_write_reg(codec, UDA1341_DATA01, DATA1_BASS(0)| DATA1_TREBLE(0));
    uda1341_write_reg(codec, UDA1341_DATA10, 0);  // not mute
}

驱动程序函数uda1341_soc_probe调用uda1341_init_regs函数



/* 所有寄存器的默认值 */
static const char uda1341_reg[UDA1341_REG_NUM] = {
    /* DATA0 */
    0x00, 0x40, 0x80,
    
/* Extended address registers */
0x04, 0x04, 0x04, 0x00, 0x00, 0x00,


    /* data1 */
    0x00,
    
    /* status regs */
    0x00, 0x83,
};

probe函数

static int uda1341_soc_probe(struct snd_soc_codec *codec)
{
    int ret;
    uda1341_init_regs(codec);
    return ret;
}

(2)s3c2440_iis.c(Platform)

static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
    /* 根据params设置IIS控制器 */


    int tmp_fs;
    int i;
    int min = 0xffff;
    int pre = 0;
    unsigned int fs;
    struct clk *clk = clk_get(NULL, 'pclk');

入口函数映射寄存器(出口函数取消映射)
    /*gpecon   = ioremap(0x56000040, 4);//0x56000040是物理地址,大小是4字节
    iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));*/
    /* 配置GPIO用于IIS */ 

    *gpecon &= ~((3<<0) | (3<<2) | (3<<4) | (3<<6) | (3<<8));
    *gpecon |= ((2<<0) | (2<<2) | (2<<4) | (2<<6) | (2<<8));
    
    
    /* bit[9] : Master clock select, 0-PCLK
     * bit[8] : 0 = Master mode
     * bit[7:6] : 10 = Transmit mode
     * bit[4] : 0-IIS compatible format
     * bit[2] : 384fs, 确定了MASTER CLOCK之后, fs = MASTER CLOCK/384
     * bit[1:0] : Serial bit clock frequency select, 32fs
     */
     寄存器映射(写入一个结构体后再映射)

static volatile struct s3c2440_iis_regs *iis_regs;

struct s3c2440_iis_regs {
    unsigned int iiscon ; 
    unsigned int iismod ; 
    unsigned int iispsr ; 
    unsigned int iisfcon; 
    unsigned int iisfifo; 
};
    iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));//寄存器映射

    if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)//每个采样值占据的位数(是8位还是16位根据params决定)
        iis_regs->iismod = (2<<6) | (0<<4) | (1<<3) | (1<<2) | (1);
    else if (params_format(params) == SNDRV_PCM_FORMAT_S8)
        iis_regs->iismod = (2<<6) | (0<<4) | (0<<3) | (1<<2) | (1);
    else
        return -EINVAL;//其他值是错误的
    struct clk *clk = clk_get(NULL, 'pclk');

PCLK=clk_get_rate(clk);最后要用clk_put(clk);
    /* Master clock = PCLK/(n+1)
     * fs = Master clock / 384//fs是采样率
     * fs = PCLK / (n+1) / 384
     */
    fs = params_rate(params);//采样频率根据params得到
    for (i = 0; i <= 31; i++)
    {
        tmp_fs = clk_get_rate(clk)/384/(i+1);
        if (ABS(tmp_fs, fs) < min)
        {
            min = ABS(tmp_fs, fs);
            pre = i;
        }
    }
    iis_regs->iispsr = (pre << 5) | (pre);


    /*
     * bit15 : Transmit FIFO access mode select, 1-DMA
     * bit13 : Transmit FIFO, 1-enable
     */
    iis_regs->iisfcon = (1<<15) | (1<<13);
    
    /*
     * bit[5] : Transmit DMA service request, 1-enable
     * bit[1] : IIS prescaler, 1-enable
     */
    iis_regs->iiscon = (1<<5) | (1<<1) ;


    clk_put(clk);
    
    return 0;
}
(3)s3c2440_dma.c(Platform)

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);
    
    /* 根据params设置DMA */
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);


    /* s3c2440_dma_new分配了很大的DMA BUFFER
     * params决定使用多大
     */
runtime->dma_bytes            = totbytes;
    playback_dma_info.buffer_size = totbytes;
    playback_dma_info.period_size = params_period_bytes(params);


    return 0;
}

4、uda1341.c

codec部分

static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {
    .probe = uda1341_soc_probe,
    /* UDA1341的寄存器不支持读操作,只支持写操作
     * 要知道某个寄存器的当前值,
     * 只能在写入时保存起来(cache)
     */
.reg_cache_size = sizeof(uda1341_reg), //存放的是寄存器的值(cache有多大,看看寄存器个数)
.reg_word_size = sizeof(u8),//每个寄存器占的数据位数
.reg_cache_default = uda1341_reg,//默认值
.reg_cache_step = 1,
.read  = uda1341_read_reg_cache,//读寄存器的函数
.write = uda1341_write_reg,  /* 写寄存器 */
};
(1)读寄存器(支持寄存器的读操作的编解码芯片可用读寄存器函数)

/*
 * The codec has no support for reading its registers except for peak level...
 */

对于uda1341智能在cache中读出来
static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *cache = codec->reg_cache;//对于uda1341智能在cache中读出来


if (reg >= UDA1341_REG_NUM)//寄存器的个数大于某个值,返回-1
return -1;
return cache[reg];
}
(2)写寄存器

static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 *cache = codec->reg_cache;//把写寄存器的值写到cache里面去(备份下来,因为uda1341不允许读寄存器操作)


    /* 先保存 */
if (reg >= UDA1341_REG_NUM)//寄存器个数为12
return -1;
cache[reg] = value;//保存值


    /* 再写入硬件 */
    
    /* 对于EA(扩展寄存器),需要调用2次l3_write */先把EA地址值作为数据发送出去,再把ED作为数据值发送出去
    if ((reg >= UDA1341_EA000) && (reg <= UDA1341_EA110))
    { //左边的参数是地址(data0),右边的参数是数据  (uda1341_reg_addr[reg]对应的是扩展地址),最高两位或上1
        l3_write(UDA1341_DATA0_ADDR, uda1341_reg_addr[reg] | UDA1341_EXTADDR_PREFIX);
        l3_write(UDA1341_DATA0_ADDR, value | UDA1341_EXTDATA_PREFIX);
    }
    else
    {
        l3_write(uda1341_reg_addr[reg], value | uda1341_data_bit[reg]);//我们访问某个寄存器的时候,数据值要或上某一些位,才能定位到uda1341的某一类下的某个寄存器
    }


    return 0;
}

dai部分

static struct snd_soc_dai_driver uda1341_dai = {
.ops = &uda1341_dai_ops,
};

static const struct snd_soc_dai_ops uda1341_dai_ops = {
.hw_params = uda1341_hw_params,
};


5、uda1341硬件分析

(1)2440通过3条线连接uda1341,想写uda1341的寄存器,肯定需要地址和数据,L3MODE等于0时表示线L3DATA线上传输的是地址,为1时传输的是数据。L3CLOCK表示每一个时钟 传输1位。


(2)看芯片手册(UDA1341TS.pdf)有多少个寄存器

数据位7~2代表设备地址(表示uda1341),数据位0和1表示地址


(3)L3接口

先发出地址,再发出数据。地址里bit7~bit2用于表示uda1341,数据位0和1表示访问哪类寄存器,有3类


data0类能访问多少个寄存器

可认为第1、2、3三行代表的是寄存器,第4、5行不是寄存器,是扩展地址,发出的数据是data0这1类,并且前面两个数据是11的话,后面的3位表示扩展地址。


想访问某个扩展地址,如访问EA,先是地址,后是数据(数据是扩展寄存器的地址(高两位是1))

下面的1、2、3、4、5、6、7代表寄存器,对于data0这1类有9个寄存器



data1这1类只有1个

status这1类,先发出地址,再发出数据,如果数据的最高位是0,选第1行数据,否则选第2行数据。


所以uda1341会根据第2个周期的某些位来分辨访问哪些寄存器,共有3类12个寄存器。


关键字:参数设置  s3c2440 引用地址:ALSA声卡09_从零编写之参数设置_学习笔记

上一篇:ALSA声卡08_从零编写之框架_学习笔记
下一篇:ALSA声卡12_从零编写之添加音量控制_学习笔记

推荐阅读最新更新时间:2024-11-02 10:32

u-boot-2016.11移植到S3C2440之第一阶段修改(2)
1、u-boot-2016.11的第一个启动的文件为archarmlibvectors.S,系统上电时,产生复位异常,从vectors.S中的reset跳转到start.S (archarmcpuarm920t),在该文件中修改系统时钟代码,并添加上icache的启动代码加快启动速度(添加到屏蔽IRQ中断代码之后): /******************初始化系统时钟********************/ ldr r0,=0x4c000014 mov r1,#0x05 /*FCLK:HCLK:PCLK = 1:4:8 = 400M:100M:50M*/ str r1, mrc p15, 0
[单片机]
u-boot-2016.11移植到<font color='red'>S3C2440</font>之第一阶段修改(2)
S3C2440 OpenJtag
C:UsersAdministratorDesktop oflash.exe leds.bin +---------------------------------------------------------+ | Flash Programmer v1.5.4 for OpenJTAG of www.100ask.net | | OpenJTAG is a USB to JTAG & RS232 tool based FT2232 | | This programmer supports both of S3C24X0 & S3C6410 | | Author: Email/MSN(thisway.diy@1
[单片机]
DS1621在Linux下的IIC接口驱动设计
  IIC总线作为一种申行传输总线,其使用连线少,结构简单,是一种应用广泛的高性能总线方式。而Linux作为一个源代码公开、易于裁剪的操作系统,非常适合于嵌入式系统的应用。Linux操作系统下的嵌入式设备驱动,通过IIC总线,实现ARM与外围模块间的协同工作,有着广泛的应用。   1 IIC总线协议以及选用芯片功能   1.1 IIC总线的特点以及工作协议   IIC串行总线由两根信号线组成:一根双向传输的数据线SDA;另一根是时钟线SCL。IIC总线通过简单的结构即能实现半双工的同步数据传输。   IIC总线采用一主多从的运行机制,在同一时间只能有一台设备作为主设备,总线的运行由主设备控制,主设备控制数据的传送起始信号、
[单片机]
DS1621在Linux下的IIC接口驱动设计
S3C2440 从NANDFLASH启动的设计原理与过程
S3C2440有两种启动方式:NORFLASH启动与NANDFLASH启动;但一般都只从NANDFLASH启动,既然是从这个地方启动,那必须具备两个条件: 1 这个里面必须得有程序 2 必须得有内存去跑这段程序 第一个条件,我们必须通过JTAG下载程序到NANDFLASH,假设下载了一个LED测试程序在里面了; 第二个条件,要跑这段程序必须有内存,可这个时候SDRAM还没有初始化,不能跑,怎么办?S3C2440里面有一个4k空间的SRAM,当芯片复位的时候会通过外部引脚首先判断使用哪种启动方式,如果是NANDFLASH启动方式,S3C2440的Nand Flash控制器有一个特殊的功能,在S3C2440上电后,Na
[单片机]
<font color='red'>S3C2440</font> 从NANDFLASH启动的设计原理与过程
s3c2440的UART功能测试
/* 文件名称:UART.c /* 实现功能:最基本的UART发送与接收 /* 作者:无jianqi /* 版本:1.0 #include 2440addr.h //包含2440相关寄存器的设置 #include def.h //四个LED对应GPB5.6.7.8。 #define LED1 5 #define LED2 6 #define LED3 7 #define LED4 8 #define BAUD 115200 //波特率 #define Bit(x) (1《x) //将某位置位 #define Output(x) (1《2*x) //将对应IO置为输出 #
[单片机]
S3C2440地址空间的分配及启动过程
一、S3C2440地址空间的分配 1. s3c2440A 的存储器控制器有以下特性: l 大小端(通过软件选择) l 地址空间:每个bank有128M 的字节(总共1G字节/8个banks) l 可编程的访问位宽,bank0(16/32 位),其他bank(8/16/32 位) l 共8个存储器banks l 6 个是ROM,SRAM 等类型存储器bank l 2 个是可以作为ROM、SRAM、SDRAM 等存储器bank l 7 个固定的存储器bank起始地址 l 最后一个bank 的起始地址可调整 l 最后两个bank 大小可编程 l 所有存储器bank的访问周期可编程 l 总线访问周期可通过插入外部
[单片机]
<font color='red'>S3C2440</font>地址空间的分配及启动过程
基于S3C2440的nRF2401的接口电路和驱动程序设计
通信频率为2.4GHz 的ISM 频段,由于其免许可证、波长较短、天线的尺寸小、外围器件少等优点,适合于近距离无线通信。将ARM9 与nRF24E1 结合, 可以缩小设备体积, 降低系统功耗, 减少设备间连线困难等问题。针对ARM9 芯片S3C2440 的特点设计了对nFR24E1 的接口电路和驱动程序。对于在狭小空间中,有设备之间的数据共享要求的系统是一种有效解决途径。 2.4GHz 无线设备的使用,免去了系统之间连线的烦恼。一方面可以降低设备的成本,另一方面就是可以简化设备的安装。 特别是对于一些运动部件的实时测量,借助两个无线传输设备,可以将一部分测量设备做到运动部件上,另一部分安装在附近,就可以将运动部件的
[单片机]
基于<font color='red'>S3C2440</font>的nRF2401的接口电路和驱动程序设计
S3C2440存储控制器、SDRAM原理、内存初始化
NandFlash和NorFlash都是Flash的一种,都是散存,都是磁盘存储介子,但是NandFlash一般比较大,而NorFlash都比较小,并且NorFlash比较贵,并且NorFlash写的速度比较慢,但读的速度比较快,而NandFlash读的速度比较慢,写的速度比较快。 NOR Flash是总线型设备,可在芯片内执行(XIP,eXecute In Place),应用程序可以直接在FIash闪存内运行,不必再把代码读到系统RAM中;而NAND Flash则需I/O接口,因此使用时需要写入驱动程序     SDRAM:Synchronous Dynamic Random Access Memory,同步动态随机存储器
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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