TLV320AIC3204 CODEC裸机驱动

一、TLV320AIC3204录音测试

/**
  * Copyright (c) 2019 Nuclei Limited. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
  * not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "ns_sdk_hal.h"
#include "psram_cfg_x16.h"

#define I2C0_SPEED             (400000)

// Define page size, extended to 2K for X16 1K bank wrap issue test
#define PAGESIZE 2048

#define TLV320AIC3204_ADDRESS (0x18)

#define SAI0_S0_BASE_A    SAI0_S0_CFG_A_BASE
#define SAI0_S0_BASE_B    SAI0_S0_CFG_B_BASE

#define ARR_SIZE_B    (160)
#define ARR_SIZE_A    2*(2*16000*2)
#define FIFO_RDAT    0x14

volatile uint16_t *SAI0_S0_A_Send_buffer = (volatile uint16_t *)0x88000000;
volatile uint16_t *SAI0_S0_B_Receive_buffer = (volatile uint16_t *)0x89000000;

uint8_t state0 = 0;
//#define CONTINUOS_MODE 1

void UDMA0_IRQHandler(void)
{
	static int count = 0;
	static int hcount = 0;
    if (UDMA_PA2M_GetITStatus(SAI0_SAI_0_B_RX_DMA_DMA_IRQ, PA2M_HTRANS_IRQ_STAT) == SET) {
       UDMA_PA2M_ClearITStatus(SAI0_SAI_0_B_RX_DMA_DMA_IRQ, PA2M_HTRANS_IRQ_CLEAR_STAT);
       hcount++;
    }
    if (UDMA_PA2M_GetITStatus(SAI0_SAI_0_B_RX_DMA_DMA_IRQ, PA2M_FTRANS_IRQ_STAT) == SET) {
       UDMA_PA2M_ClearITStatus(SAI0_SAI_0_B_RX_DMA_DMA_IRQ, PA2M_FTRANS_IRQ_CLEAR_STAT);
       if (count > ARR_SIZE_A/ARR_SIZE_B) {
    	   UDMA_Cmd(SAI0_SAI_0_B_RX_DMA_DMA_CH, DISABLE);
       	   state0 = 1;
       	   printf("fint = %d\r\n", count);
       	   printf("hint = %d\r\n", hcount);
       }
       else {
    	   memcpy((uint16_t *)&(SAI0_S0_A_Send_buffer[count*ARR_SIZE_B]), (uint16_t *)SAI0_S0_B_Receive_buffer, ARR_SIZE_B*2);
    	   UDMA_Cmd(SAI0_SAI_0_B_RX_DMA_DMA_CH, ENABLE);
       }
       count++;
    }

    if (UDMA_PA2M_GetITStatus(SAI0_SAI_0_A_TX_DMA_DMA_IRQ, PA2M_FTRANS_IRQ_STAT) == SET) {
       state0 = 2;
       UDMA_Cmd(SAI0_SAI_0_A_TX_DMA_DMA_CH, DISABLE);
       SAI_Config(SAI0_S0_CFG_A,DISABLE);
       SAI_Config(SAI0_S0_CFG_B,DISABLE);
       printf("playback int\r\n");
       UDMA_PA2M_ClearITStatus(SAI0_SAI_0_A_TX_DMA_DMA_IRQ, PA2M_FTRANS_IRQ_CLEAR_STAT);
    }

}
/**
 * @brief Test PSRAM by writing and reading data.
 *
 * @param argc Number of command - line arguments.
 * @param argv Array of command - line arguments.
 */
void psram_test()
{
    uint8_t *write = malloc(PAGESIZE);
    if (write == NULL) {
        printf("out of memory\n");
        return;
    }
    uint8_t *read = malloc(PAGESIZE);
    if (read == NULL) {
        free(read);
        printf("out of memory\n");
        return;
    }
    uint16_t value = 0;
    bool isX16 = false;
    printf("Reg dump:\n");
    // Disable XIP mode
    PSRAM_XIP_Disable();

    for (int i = 0; i < 9; i++) {
        SPI_Psram_RegRead(i, (uint8_t *)&value);
        printf("MR[%d %d] = %02x, %02x\n", i, i + 1, value & 0xff, (value >> 8) & 0xff);
    }
    if ((value & (0x01 << 6)) != 0) {
        isX16 = true;
    }

    // Enable XIP mode
    PSRAM_XIP_Enable();
    // Write & Read test
    for (int i = 0; i < PAGESIZE; i++) {
        write[i] = (i / 256) + (i & 0xff) ;
    }
    memset(read, 0x00, PAGESIZE);
    memcpy((void*)QSPI_XIP1_MEM_BASE, write, PAGESIZE);
    memcpy(read, (void*)QSPI_XIP1_MEM_BASE, PAGESIZE);
    if (0 == memcmp(write, read, PAGESIZE)) {
        printf("%d line XIP W/R test pass\n", isX16 ? 16 : 8);
    } else {
        printf("%d line XIP data cmp failed\n", isX16 ? 16 : 8);
        printf("data dump:\n");
        for(int i = 0; i < PAGESIZE; i++) {
            if (i % 16 == 0)
                printf("\n%p: ", &read[i]);
            printf("%02x ", read[i]);
        }
        printf("\n");
    }
    free(write);
    free(read);
}

/**
  * \brief cofigure the I2C1 interfaces
  */
void I2C_Config(void)
{
    I2C_InitTypeDef init = {0};
    init.duty = I2C_50_DUTY;
    init.clk = I2C0_SPEED;
    init.mode = I2C_MASTER_MODE;
    init.scl_pull = ENABLE;
    init.sda_pull = ENABLE;
    I2C_Init(I2C0, &init);
}

void tlv320aic_select_page(uint8_t page_num)
{
    uint32_t timeout = 0xFFFFFFFF;
    I2C_ACK(I2C0, DISABLE);
    I2C_Start(I2C0);
    I2C_Send_SlaveAddr(I2C0, TLV320AIC3204_ADDRESS, I2C_DIR_WRITE, timeout);
    /* wait until I2C recv ack */
    while (!I2C_Get_Status(I2C0, I2C_STATUS_ADDR_ACK)) {}

    I2C_Clear_Status(I2C0, I2C_STATUS_ADDR_ACK);
    I2C_Write(I2C0, 0x00, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Write(I2C0, page_num, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Stop(I2C0);
}

void tlv320aic_reset()
{
    uint32_t timeout = 0xFFFFFFFF;
    I2C_ACK(I2C0, DISABLE);
    I2C_Start(I2C0);
    I2C_Send_SlaveAddr(I2C0, TLV320AIC3204_ADDRESS, I2C_DIR_WRITE, timeout);
    /* wait until I2C recv ack */
    while (!I2C_Get_Status(I2C0, I2C_STATUS_ADDR_ACK)) {}

    I2C_Clear_Status(I2C0, I2C_STATUS_ADDR_ACK);
    I2C_Write(I2C0, 0x01, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Write(I2C0, 0x01, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Stop(I2C0);
}

void tlv320aic_write_reg(uint8_t reg, uint8_t val)
{
    uint32_t timeout = 0xFFFFFFFF;
    I2C_ACK(I2C0, DISABLE);
    I2C_Start(I2C0);
    I2C_Send_SlaveAddr(I2C0, TLV320AIC3204_ADDRESS, I2C_DIR_WRITE, timeout);
    /* wait until I2C recv ack */
    while (!I2C_Get_Status(I2C0, I2C_STATUS_ADDR_ACK)) {}

    I2C_Clear_Status(I2C0, I2C_STATUS_ADDR_ACK);
    I2C_Write(I2C0, reg, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Write(I2C0, val, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Stop(I2C0);
}

void tlv320aic_read_reg(uint8_t reg, uint8_t *dat)
{
	uint8_t temp = 0;
    uint32_t timeout = 0xFFFFFFFF;

    I2C_ACK(I2C0, DISABLE);
    I2C_Start(I2C0);
    I2C_Send_SlaveAddr(I2C0, TLV320AIC3204_ADDRESS, I2C_DIR_WRITE, timeout);
    /* wait until I2C recv ack */
    while (!I2C_Get_Status(I2C0, I2C_STATUS_ADDR_ACK)) {}

    I2C_Clear_Status(I2C0, I2C_STATUS_ADDR_ACK);
    I2C_Write(I2C0, reg, timeout);
    /* wait until I2C recv ack */
    while (I2C_Get_Status(I2C0, I2C_STATUS_ACK)) {}

    I2C_Restart(I2C0);
    I2C_Send_SlaveAddr(I2C0, TLV320AIC3204_ADDRESS, I2C_DIR_READ, timeout);
    /* wait until I2C recv ack */
    while (!I2C_Get_Status(I2C0, I2C_STATUS_ADDR_ACK)) {}

    I2C_Clear_Status(I2C0, I2C_STATUS_ADDR_ACK);
    I2C_ACK(I2C0, ENABLE);
    I2C_ACK(I2C0, DISABLE);
    I2C_Read(I2C0, dat, timeout);
    I2C_Stop(I2C0);

}

#if 0
void init_recording()
{
    // ========== 切换到 Page 0 ==========
    tlv320aic_select_page(0);
    tlv320aic_reset();
    delay_1ms(10);

    // ========== 时钟配置 (无 PLL,CODEC_CLKIN = MCLK = 8.192 MHz) ==========
    tlv320aic_write_reg(0x04, 0x00);  // CODEC_CLKIN = MCLK, PLL输入 = MCLK
    tlv320aic_write_reg(0x05, 0x00);  // PLL 关闭


    // 采样率 16 kHz 配置:fs = MCLK / (NADC × MADC × AOSR)
    // 8,192,000 / (1 × 2 × 256) = 16,000 Hz
    tlv320aic_write_reg(0x12, 0x81);  // NADC = 1, 使能
    tlv320aic_write_reg(0x13, 0x82);  // MADC = 2, 使能
    tlv320aic_write_reg(0x14, 0x80);  // AOSR = 256 (0x00代表256,提供更好的SNR)

    // ========== 音频接口配置 (I²S 从机,16 位) ==========
    tlv320aic_write_reg(0x1B, 0x00);  // I²S 格式,16 位,BCLK/WCLK 从机
    tlv320aic_write_reg(0x1C, 0x00);  // 数据偏移 = 0
    tlv320aic_write_reg(0x27, 0x00);  // 数据路径设置

    // ========== ADC 配置 ==========
    tlv320aic_write_reg(0x3D, 0x01);  // PRB_R1 处理块

    // ========== 切换到 Page 1(模拟控制) ==========
    tlv320aic_select_page(1);

    // 电源和参考配置
    tlv320aic_write_reg(0x01, 0x08);  // 使能主模拟电源
    tlv320aic_write_reg(0x02, 0x01);  // 参考快速充电
    tlv320aic_write_reg(0x7B, 0x01);  // 使能参考充电
    delay_1ms(100);  // 等待参考电压稳定(至少40ms)

    // MICBIAS 配置 (2.5V for WMM7027ATSN1)
    tlv320aic_write_reg(0x33, 0x60);  // MICBIAS 使能,2.5V (bit[6]=1, bit[5:4]=10)

    // ========== 模拟输入路由(差分:IN2_L/IN2_R 到左 PGA,IN1_R/IN1_L 到右 PGA) ==========
    // 左声道
    tlv320aic_write_reg(0x34, 0x10);  // 左正:IN2_L 10 kΩ
    tlv320aic_write_reg(0x36, 0x10);  // 左负:IN2_R 10 kΩ (实现差分)

    // 右声道
    tlv320aic_write_reg(0x37, 0x40);  // 右正:IN1_R 10 kΩ
    tlv320aic_write_reg(0x39, 0x40);  // 右负:IN1_L 10 kΩ (实现差分)

    // ========== MicPGA 增益配置 ==========
    // MEMS麦克风通常需要一定增益,建议从20dB开始测试
    tlv320aic_write_reg(0x3B, 0x28);  // 左 PGA 增益 = 20 dB (0x28 = 40, 每步0.5dB)
    tlv320aic_write_reg(0x3C, 0x28);  // 右 PGA 增益 = 20 dB

    // 使能 MicPGA
    tlv320aic_write_reg(0x47, 0x32);  // MicPGA 启动延时 3.1 ms
    tlv320aic_write_reg(0x01, 0x0A);  // 使能左右 MicPGA (bit[1]=1左,bit[3]=1右)
    delay_1ms(10);  // 等待启动

    // ========== 切换回 Page 0 ==========
    tlv320aic_select_page(0);

    // ========== 使能和解除静音 ADC ==========
    tlv320aic_write_reg(0x51, 0xC0);  // 上电左右 ADC (bit[7]=1左,bit[6]=1右)
    tlv320aic_write_reg(0x52, 0x00);  // 解除 ADC 静音

    // 设置 ADC 音量 (0dB = 0x00, 范围-12dB到20dB)
    tlv320aic_write_reg(0x53, 0x00);  // 左 ADC 音量 0 dB
    tlv320aic_write_reg(0x54, 0x00);  // 右 ADC 音量 0 dB

    delay_1ms(10);  // 等待ADC稳定
}
#endif

#if 0//LR OK有底噪电流声
void init_recording()
{
    // ========== 切换到 Page 0 ==========
    tlv320aic_select_page(0);
    tlv320aic_reset();
    delay_1ms(10);

    // ========== 时钟配置 (无 PLL,CODEC_CLKIN = MCLK = 8.192 MHz) ==========
    tlv320aic_write_reg(0x04, 0x00);  // CODEC_CLKIN = MCLK, PLL输入 = MCLK
    tlv320aic_write_reg(0x05, 0x00);  // PLL 关闭


    // 采样率 16 kHz 配置:fs = MCLK / (NADC × MADC × AOSR)
    // 8,192,000 / (1 × 2 × 256) = 16,000 Hz
    tlv320aic_write_reg(0x12, 0x81);  // NADC = 1, 使能
    tlv320aic_write_reg(0x13, 0x82);  // MADC = 2, 使能
    tlv320aic_write_reg(0x14, 0x80);  // AOSR = 256 (0x00代表256,提供更好的SNR)

    // ========== 音频接口配置 (I²S 从机,16 位) ==========
    tlv320aic_write_reg(0x1B, 0x00);  // I²S 格式,16 位,BCLK/WCLK 从机
    tlv320aic_write_reg(0x1C, 0x00);  // 数据偏移 = 0
    tlv320aic_write_reg(0x27, 0x00);  // 数据路径设置

    // ========== ADC 配置 ==========
    tlv320aic_write_reg(0x3D, 0x01);  // PRB_R1 处理块

    // ========== 切换到 Page 1(模拟控制) ==========
    tlv320aic_select_page(1);

    // 电源和参考配置
    tlv320aic_write_reg(0x01, 0x08);  // 使能主模拟电源
    tlv320aic_write_reg(0x02, 0x01);  // 参考快速充电
    tlv320aic_write_reg(0x7B, 0x01);  // 使能参考充电
    delay_1ms(100);  // 等待参考电压稳定(至少40ms)

    // MICBIAS 配置 (2.5V for WMM7027ATSN1)
    tlv320aic_write_reg(0x33, 0x60);  // MICBIAS 使能,2.5V (bit[6]=1, bit[5:4]=10)

    // ========== 模拟输入路由(差分:IN2_L/IN2_R 到左 PGA,IN1_R/IN1_L 到右 PGA) ==========
    // 左声道
    tlv320aic_write_reg(0x34, 0x10);  // 左正:IN2_L 10 kΩ
    tlv320aic_write_reg(0x36, 0x10);  // 左负:IN2_R 10 kΩ (实现差分)

    // 右声道
    tlv320aic_write_reg(0x37, 0x10);  // 右正:IN1_R 10 kΩ
    tlv320aic_write_reg(0x39, 0x10);  // 右负:IN1_L 10 kΩ (实现差分)

    // ========== MicPGA 增益配置 ==========
    // MEMS麦克风通常需要一定增益,建议从20dB开始测试
    tlv320aic_write_reg(0x3B, 0x10);  // 左 PGA 增益 = 20 dB (0x28 = 40, 每步0.5dB)
    tlv320aic_write_reg(0x3C, 0x10);  // 右 PGA 增益 = 20 dB

    // 使能 MicPGA
    tlv320aic_write_reg(0x47, 0x32);  // MicPGA 启动延时 3.1 ms
    tlv320aic_write_reg(0x01, 0x0A);  // 使能左右 MicPGA (bit[1]=1左,bit[3]=1右)
    delay_1ms(10);  // 等待启动

    // ========== 切换回 Page 0 ==========
    tlv320aic_select_page(0);

    // ========== 使能和解除静音 ADC ==========
    tlv320aic_write_reg(0x51, 0xC0);  // 上电左右 ADC (bit[7]=1左,bit[6]=1右)
    tlv320aic_write_reg(0x52, 0x00);  // 解除 ADC 静音

    // 设置 ADC 音量 (0dB = 0x00, 范围-12dB到20dB)
    tlv320aic_write_reg(0x53, 0x00);  // 左 ADC 音量 0 dB
    tlv320aic_write_reg(0x54, 0x00);  // 右 ADC 音量 0 dB

    delay_1ms(10);  // 等待ADC稳定
}
#endif

//LR OK
void init_recording()
{
    // ========== 切换到 Page 0 ==========
    tlv320aic_select_page(0);
    tlv320aic_reset();
    delay_1ms(10);

    // ========== 时钟配置 (无 PLL,CODEC_CLKIN = MCLK = 8.192 MHz) ==========
    tlv320aic_write_reg(0x04, 0x00);  // CODEC_CLKIN = MCLK, PLL输入 = MCLK
    tlv320aic_write_reg(0x05, 0x00);  // PLL 关闭


    // 采样率 16 kHz 配置:fs = MCLK / (NADC × MADC × AOSR)
    // 8,192,000 / (1 × 2 × 256) = 16,000 Hz
    tlv320aic_write_reg(0x12, 0x81);  // NADC = 1, 使能
    tlv320aic_write_reg(0x13, 0x82);  // MADC = 2, 使能
    tlv320aic_write_reg(0x14, 0x80);  // AOSR = 256 (0x00代表256,提供更好的SNR)

    // ========== 音频接口配置 (I²S 从机,16 位) ==========
    tlv320aic_write_reg(0x1B, 0x00);  // I²S 格式,16 位,BCLK/WCLK 从机
    tlv320aic_write_reg(0x1C, 0x00);  // 数据偏移 = 0
    tlv320aic_write_reg(0x27, 0x00);  // 数据路径设置

    // ========== ADC 配置 ==========
    tlv320aic_write_reg(0x3D, 0x01);  // PRB_R1 处理块

    // ========== 切换到 Page 1(模拟控制) ==========
    tlv320aic_select_page(1);

    // 电源和参考配置
    tlv320aic_write_reg(0x01, 0x08);  // 使能主模拟电源
    tlv320aic_write_reg(0x02, 0x01);  // 参考快速充电
    tlv320aic_write_reg(0x7B, 0x01);  // 使能参考充电
    delay_1ms(100);  // 等待参考电压稳定(至少40ms)

    // MICBIAS 配置 (2.5V for WMM7027ATSN1)
    tlv320aic_write_reg(0x33, 0x60);  // MICBIAS 使能,2.5V (bit[6]=1, bit[5:4]=10)

    // ========== 模拟输入路由(差分:IN2_L/IN2_R 到左 PGA,IN1_R/IN1_L 到右 PGA) ==========
    // 左声道
    tlv320aic_write_reg(0x34, 0x10);  // 左正:IN2_L 10 kΩ
    tlv320aic_write_reg(0x36, 0x10);  // 左负:IN2_R 10 kΩ (实现差分)

    // 右声道
    tlv320aic_write_reg(0x37, 0x10);  // 右正:IN1_R 10 kΩ
    tlv320aic_write_reg(0x39, 0x10);  // 右负:IN1_L 10 kΩ (实现差分)

    // ========== MicPGA 增益配置 ==========
    // MEMS麦克风通常需要一定增益,建议从20dB开始测试
    tlv320aic_write_reg(0x3B, 0x8);  // 左 PGA 增益 = 20 dB (0x28 = 40, 每步0.5dB)
    tlv320aic_write_reg(0x3C, 0x8);  // 右 PGA 增益 = 20 dB

    // 使能 MicPGA
    tlv320aic_write_reg(0x47, 0x32);  // MicPGA 启动延时 3.1 ms
    tlv320aic_write_reg(0x01, 0x0A);  // 使能左右 MicPGA (bit[1]=1左,bit[3]=1右)
    delay_1ms(10);  // 等待启动

    // ========== 切换回 Page 0 ==========
    tlv320aic_select_page(0);

    // ========== 使能和解除静音 ADC ==========
    tlv320aic_write_reg(0x51, 0xC0);  // 上电左右 ADC (bit[7]=1左,bit[6]=1右)
    tlv320aic_write_reg(0x52, 0x00);  // 解除 ADC 静音

    // 设置 ADC 音量 (0dB = 0x00, 范围-12dB到20dB)
    tlv320aic_write_reg(0x53, 0x00);  // 左 ADC 音量 0 dB
    tlv320aic_write_reg(0x54, 0x00);  // 右 ADC 音量 0 dB

    tlv320aic_write_reg(0x55, 0x04); // 使能ADC自动静音检测和信号处理

    delay_1ms(10);  // 等待ADC稳定
}

// 1. 读取并显示所有关键寄存器
void dump_tlv320_registers(void)
{
    uint8_t val;

    printf("\n========== TLV320AIC3204 寄存器转储 ==========\n");

    // Page 0 - 数字控制
    tlv320aic_select_page(0);
    printf("\n[Page 0 - 数字控制]\n");

    tlv320aic_read_reg(0x01, &val);
    printf("0x01 (复位): 0x%02X\n", val);

    tlv320aic_read_reg(0x04, &val);
    printf("0x04 (时钟源): 0x%02X (期望 0x00 = MCLK)\n", val);

    tlv320aic_read_reg(0x05, &val);
    printf("0x05 (PLL): 0x%02X (期望 0x00 = 禁用)\n", val);

    tlv320aic_read_reg(0x12, &val);
    printf("0x12 (NADC): 0x%02X (期望 0x81)\n", val);

    tlv320aic_read_reg(0x13, &val);
    printf("0x13 (MADC): 0x%02X (期望 0x82)\n", val);

    tlv320aic_read_reg(0x14, &val);
    printf("0x14 (AOSR): 0x%02X (期望 0x00=256 或 0x80=128)\n", val);

    tlv320aic_read_reg(0x1B, &val);
    printf("0x1B (接口): 0x%02X (期望 0x00 = I²S, 16bit, Slave)\n", val);

    tlv320aic_read_reg(0x3D, &val);
    printf("0x3D (PRB): 0x%02X (期望 0x01)\n", val);

    tlv320aic_read_reg(0x51, &val);
    printf("0x51 (ADC使能): 0x%02X (期望 0xC0) ", val);
    if (val == 0xC0) printf("✓\n"); else printf("❌\n");

    tlv320aic_read_reg(0x52, &val);
    printf("0x52 (ADC静音): 0x%02X (期望 0x00) ", val);
    if (val == 0x00) printf("✓\n"); else printf("❌\n");

    tlv320aic_read_reg(0x53, &val);
    printf("0x53 (左ADC音量): 0x%02X\n", val);

    tlv320aic_read_reg(0x54, &val);
    printf("0x54 (右ADC音量): 0x%02X\n", val);

    // Page 1 - 模拟控制
    tlv320aic_select_page(1);
    printf("\n[Page 1 - 模拟控制]\n");

    tlv320aic_read_reg(0x01, &val);
    printf("0x01 (电源): 0x%02X (期望 0x0A = PGA使能) ", val);
    if (val == 0x0A) printf("✓\n"); else printf("❌\n");

    tlv320aic_read_reg(0x33, &val);
    printf("0x33 (MICBIAS): 0x%02X (期望 0x60 = 2.5V) ", val);
    if (val == 0x60) printf("✓\n"); else printf("❌\n");

    tlv320aic_read_reg(0x34, &val);
    printf("0x34 (左+路由): 0x%02X (期望 0x10 = IN2_L)\n", val);

    tlv320aic_read_reg(0x36, &val);
    printf("0x36 (左-路由): 0x%02X (期望 0x10 = IN2_R)\n", val);

    tlv320aic_read_reg(0x37, &val);
    printf("0x37 (右+路由): 0x%02X (期望 0x40 = IN1_R)\n", val);

    tlv320aic_read_reg(0x39, &val);
    printf("0x39 (右-路由): 0x%02X (期望 0x40 = IN1_L)\n", val);

    tlv320aic_read_reg(0x3B, &val);
    printf("0x3B (左PGA增益): 0x%02X (每0.5dB = %d.%d dB)\n",
           val, val/2, (val%2)*5);

    tlv320aic_read_reg(0x3C, &val);
    printf("0x3C (右PGA增益): 0x%02X (每0.5dB = %d.%d dB)\n",
           val, val/2, (val%2)*5);

    tlv320aic_select_page(0);
    printf("========================================\n\n");
}

void tlv320aic_init_recording_16k(void)
{
    // 1. 切换到 Page 0 (控制寄存器)
    tlv320aic_select_page(0);
    tlv320aic_reset();
    delay_1ms(10); // 等待复位完成

    // ---------------------------------------------------------
    // 2. 时钟配置 (关键修改:使用 BCLK 作为 PLL 源)
    // 目标: fs = 16kHz, AOSR = 128 (高质量)
    // BCLK = 512kHz -> PLL -> ADC_MOD_CLK = 2.048MHz
    // ---------------------------------------------------------

    // P0_R4: 时钟源选择
    // bit 1-0 = 01 (PLL_CLKIN = BCLK, range 512k-20M)
    // bit 3-2 = 01 (CODEC_CLKIN = PLL_CLK)
    tlv320aic_write_reg(0x04, 0x05);

    // P0_R5: PLL 设置 P & R
    // PLL Power Up (bit 7=1), P=1 (bit 6-4=001), R=4 (bit 3-0=0100)
    // PLL_IN = 512k * 4 = 2.048MHz
    tlv320aic_write_reg(0x05, 0x94);

    // P0_R6: PLL 设置 J
    // J=40 (0x28). VCO = 2.048M * 40 = 81.92MHz
    tlv320aic_write_reg(0x06, 0x28);

    // P0_R7/R8: PLL 设置 D (D=0000)
    tlv320aic_write_reg(0x07, 0x00);
    tlv320aic_write_reg(0x08, 0x00);

    // P0_R18: NADC 设置
    // Power Up (bit 7=1), NADC=4 (bit 6-0=4). Clk = 81.92M / 4 = 20.48MHz
    tlv320aic_write_reg(0x12, 0x84);

    // P0_R19: MADC 设置
    // Power Up (bit 7=1), MADC=10 (bit 6-0=10). Clk = 20.48M / 10 = 2.048MHz
    tlv320aic_write_reg(0x13, 0x8A);

    // P0_R20: AOSR 设置
    // AOSR = 128. fs = 2.048M / 128 = 16kHz (Bingo!)
    tlv320aic_write_reg(0x14, 0x80);

    // ---------------------------------------------------------
    // 3. 音频接口配置 (I2S, 16-bit, Slave)
    // ---------------------------------------------------------
    tlv320aic_write_reg(0x1B, 0x00);  // I2S, 16bit, BCLK/WCLK 输入(Slave)
    tlv320aic_write_reg(0x1C, 0x00);  // Data Offset = 0

    // ---------------------------------------------------------
    // 4. ADC 处理块 (PRB)
    // ---------------------------------------------------------
    tlv320aic_write_reg(0x3D, 0x01);  // PRB_R1 (标准录音处理)

    // ---------------------------------------------------------
    // 5. 模拟电源与 MICBIAS (Page 1)
    // ---------------------------------------------------------
    tlv320aic_select_page(1);

    tlv320aic_write_reg(0x01, 0x08);  // 禁用弱AVDD连接 (根据LDO配置调整)
    tlv320aic_write_reg(0x02, 0x01);  // 启用 AVDD LDO
    tlv320aic_write_reg(0x0A, 0x00);  // Common Mode 0.9V
    tlv320aic_write_reg(0x7B, 0x01);  // 参考电压充电 40ms

    delay_1ms(50); // 等待模拟电源稳定

    // [重点] WMM7027ATSN1 需要 >1.6V. 设置为 2.5V
    // bit 6=1 (Enable), bit 5-4=10 (2.5V), bit 3=0 (Source=AVDD)
    tlv320aic_write_reg(0x33, 0x60);

    // ---------------------------------------------------------
    // 6. 输入路由 (差分输入配置)
    // ---------------------------------------------------------
    // 左声道: IN2_L (+) 和 IN2_R (-) -> 10kΩ
    tlv320aic_write_reg(0x34, 0x10);  // P1_R52: Left MicPGA(+) 选 IN2_L (10k)
    tlv320aic_write_reg(0x36, 0x10);  // P1_R54: Left MicPGA(-) 选 IN2_R (10k)

    // 右声道: IN1_R (+) 和 IN1_L (-) -> 10kΩ
    // 注意:AIC3204 右声道 PGA 正端只能选 IN1_R, 负端选 IN1_L
    tlv320aic_write_reg(0x37, 0x40);  // P1_R55: Right MicPGA(+) 选 IN1_R (10k)
    tlv320aic_write_reg(0x39, 0x40);  // P1_R57: Right MicPGA(-) 选 IN1_L (10k)

    // ---------------------------------------------------------
    // 7. 增益与使能
    // ---------------------------------------------------------
    // 设置 MicPGA 增益 (可选调整)
    // 0x00 = 0dB, 0x1E = 15dB, 0x3C = 30dB
    // 建议先给一点增益,MEMS 麦克风如果不加增益声音可能偏小
    tlv320aic_write_reg(0x3B, 0x14);  // Left PGA Gain = 10dB
    tlv320aic_write_reg(0x3C, 0x14);  // Right PGA Gain = 10dB

    tlv320aic_select_page(0); // 切回 Page 0 准备开启 ADC

    // 开启 ADC
    tlv320aic_write_reg(0x51, 0xC0);  // Power Up Left & Right ADC
    tlv320aic_write_reg(0x52, 0x00);  // Unmute ADC
}

void init_playback()
{
    // 切换到 Page 0
    tlv320aic_select_page(0);
    tlv320aic_reset();
    delay_1ms(10);  // 等待重置完成

    // 时钟配置(无 PLL,CODEC_CLKIN = MCLK)
    tlv320aic_write_reg(0x04, 0x00);  // CODEC_CLKIN = MCLK, PLL 输入 = MCLK
    tlv320aic_write_reg(0x05, 0x00);  // PLL 关闭
    tlv320aic_write_reg(0x0B, 0x81);  // NDAC = 1, 启用
    tlv320aic_write_reg(0x0C, 0x82);  // MDAC = 2, 启用(调整为匹配 fs = MCLK / (NDAC * MDAC * DOSR))
    tlv320aic_write_reg(0x0D, 0x00);  // DOSR MSB = 0
    tlv320aic_write_reg(0x0E, 0x80);  // DOSR LSB = 128

    // 音频接口配置(I²S 从机,16 位)
    tlv320aic_write_reg(0x1B, 0x00);  // I²S 格式,16 位,BCLK/WCLK 从机
    tlv320aic_write_reg(0x1C, 0x00);  // 数据偏移 = 0
    tlv320aic_write_reg(0x27, 0x00);  // 接口模式
    tlv320aic_write_reg(0x28, 0x00);  // 字长 16 位
    tlv320aic_write_reg(0x29, 0x00);  // WCLK 输入

    // DAC 配置(PRB_P1,数字音量 0 dB)
    tlv320aic_write_reg(0x3C, 0x01);  // PRB_P1 处理块
    tlv320aic_write_reg(0x3F, 0xC0);  // 启用左右 DAC
    tlv320aic_write_reg(0x40, 0x00);  // 软步进
    tlv320aic_write_reg(0x41, 0x00);  // 左 DAC 音量 0 dB
    tlv320aic_write_reg(0x42, 0x00);  // 右 DAC 音量 0 dB

    // 切换到 Page 1(模拟控制)
    tlv320aic_select_page(1);

    // 电源和参考配置
    tlv320aic_write_reg(0x01, 0x08);  // 启用主模拟电源
    tlv320aic_write_reg(0x02, 0x01);  // 参考快速充电
    tlv320aic_write_reg(0x0A, 0x00);  // 输入/输出 CM = 0.9V
    tlv320aic_write_reg(0x7B, 0x01);  // 启用参考充电
    delay_1ms(1000);  // 等待参考电压稳定(~1s)

    // 输出路由(左 DAC 到 LOL,右 DAC 到 LOR)
    tlv320aic_write_reg(0x0E, 0x02);  // LOL 路由:左 DAC
    tlv320aic_write_reg(0x0F, 0x02);  // LOR 路由:右 DAC

    // 输出驱动器配置(增益 0 dB,启用 LOL/LOR)
    tlv320aic_write_reg(0x09, 0x30);  // 启用 LOL/LOR 电源
    tlv320aic_write_reg(0x12, 0x06);  // LOL 增益 0 dB
    tlv320aic_write_reg(0x13, 0x06);  // LOR 增益 0 dB
    tlv320aic_write_reg(0x14, 0x00);  // 无耳机启动(仅线输出)
    delay_1ms(10);  // 等待启动

    // 切换回 Page 0
    tlv320aic_select_page(0);
}

/**
  * \brief main
  *
  * \return int
  */
int main(void)
{
    printf("TVL320AIC TEST\n\r");
    uint8_t val = 0;

	// Initial PSRAM
    PSRAM_Mode_X16_Init();
    psram_test();

    #ifdef MISC_HAS_I2C1_HAS_CLK
    i2c0_clk_en(ENABLE);
    #endif
    #ifdef MISC_HAS_I2C1_RST
    i2c0_set_rst(DISABLE);
    i2c0_set_rst(ENABLE);
    #endif

    #ifdef CFG_SIMULATION
    #ifdef MISC_HAS_I2C1_CLK_DIV
    i2c0_clk_div(0);
    #endif
    #endif

    /* configure I2C */
    I2C_Config();

	#ifdef MISC_HAS_SAI0_HAS_CLK
	sai0_clk_en(ENABLE);
	#endif
	#ifdef MISC_HAS_UDMA0_HAS_CLK
	udma0_clk_en(ENABLE);
	udma0_set_rst(DISABLE);
	udma0_set_rst(ENABLE);
	#endif
	#ifdef MISC_HAS_SAI0_RST
	sai0_set_rst(DISABLE);
	sai0_set_rst(ENABLE);
	#endif

    __enable_irq();

    SAI_InitTypeDef sai_init_t = {0};
    SAI_FrameInitTypeDef sai_frame_t = {0};
    SAI_SlotInitTypeDef sai_slot_t = {0};
    SAI_ClockInitTypeDef sai_clock_t = {0};

    printf("Write Page 0, Register 0x04 0x1, then read back...\n");
    tlv320aic_select_page(0);
    tlv320aic_reset();
    tlv320aic_write_reg(0x04, 0x01);
    tlv320aic_read_reg(0x04, &val);

    if (val == 0x01) {
        printf("I2C register read/write OK!\n");
    } else {
        printf("I2C communication abnormal (read back 0x%02X)\n", val);
    }


#if 1//Record
    init_recording();
    uint8_t micbias_reg = 0;
    tlv320aic_select_page(1);
    tlv320aic_read_reg(0x33, &micbias_reg);
    printf("MICBIAS Reg = 0x%02X\n", micbias_reg);

    //SAI0 A Record
    UDMA_PAM2MTypeDef UDMA_PAM2MStruct2={0};
    UDMA_PAM2MStruct2.UDMA_TransEn = PA2M_TRANS_ENABLE;
    UDMA_PAM2MStruct2.UDMA_DstBaseAddr = ADDR32(SAI0_S0_B_Receive_buffer);
    UDMA_PAM2MStruct2.UDMA_SrcBaseAddr = ADDR32(SAI0_S0_BASE_B + FIFO_RDAT);
    UDMA_PAM2MStruct2.UDMA_BufferSize = ARR_SIZE_B*sizeof(uint16_t);//ARR_SIZE;//*sizeof(uint16_t);
    UDMA_PAM2MStruct2.UDMA_DstInc = PA2M_MDNA_ENABLE;
    UDMA_PAM2MStruct2.UDMA_SrcInc = PA2M_MSNA_DISABLE;
    UDMA_PAM2MStruct2.UDMA_Width = PA2M_MDWIDTH_16BIT;//PA2M_MDWIDTH_8BIT;
    UDMA_PAM2MStruct2.UDMA_Mode = PA2M_MODE_CONTINUOUS;
    UDMA_PAM2MStruct2.UDMA_PER_SEL = UDMA_SEL_SAI0_SAI_0_B_RX_DMA;

    UDMA_PAM2MStruct2.UDMA_SrcBaseAddr_H = (uint32_t)(ADDR64(SAI0_S0_BASE_B + FIFO_RDAT) >> 32) ;
    UDMA_PAM2MStruct2.UDMA_DstBaseAddr_H = (uint32_t)(ADDR64(SAI0_S0_B_Receive_buffer) >> 32) ;

    UDMA_PAM2M_Init(SAI0_SAI_0_B_RX_DMA_DMA_CH, &UDMA_PAM2MStruct2);

    /* DMA interrupt request */
    ECLIC_Register_IRQ(UDMA0_IRQn, ECLIC_NON_VECTOR_INTERRUPT,
                    ECLIC_LEVEL_TRIGGER, 1, 0,
                    UDMA0_IRQHandler);
    UDMA_PA2M_ITConfig(SAI0_SAI_0_B_RX_DMA_DMA_IRQ, PA2M_FTRANS_IRQ_EN, ENABLE);

    sai_init_t.Synchro = SAI_AUDIOCTRL_SYNCEN_INNER_SYNCHRONOUS;//SAI_AUDIOCTRL_SYNCEN_ASYNCH;//SAI_AUDIOCTRL_SYNCEN_INNER_SYNCHRONOUS;
    sai_init_t.ClockPolarity = SAI_AUDIOCTRL_CKPOL_NEGEDGE;
    sai_init_t.PortNum = SAI_AUDIOCTRL_PORTN_PORT0_ENABLE;
    sai_init_t.SAIProtocol = SAI_AUDIOCTRL_PRTCFG_FREE_PROTOCOL;
    sai_init_t.MonoStereoMode = SAI_AUDIOCTRL_MONO_DISABLE;
    sai_init_t.FirstBit = SAI_GEN_CFG0_LSBFIRST_DISABLE;//SAI_GEN_CFG0_LSBFIRST_ENABLE;
    sai_init_t.ClockStrobingEdge = SAI_GEN_CFG0_CKSTR_ENABLE;
    sai_init_t.TriState = SAI_GEN_CFG0_TRIS_DISABLE;
    sai_init_t.AudioMode = SAI_AUDIOCTRL_MODE_MASTER_RX;
    sai_init_t.Syncin = SAI_AUDIOCTRL_SYNCIN_NOSYNCIN;
    sai_init_t.Syncout = SAI_AUDIOCTRL_SYNCOUT_ENABLE;

    sai_frame_t.FrameLength = SAI_GEN_CFG0_FRL(31);//SAI_GEN_CFG0_FRL(15);
    sai_frame_t.ActiveFrameLength = SAI_GEN_CFG0_FSALL(15);//SAI_GEN_CFG0_FSALL(7);
    sai_frame_t.FSDefinition = SAI_GEN_CFG0_FSDEF_ENABLE;
    sai_frame_t.FSPolarity = SAI_GEN_CFG0_FSPOL_DISABLE;
    sai_frame_t.FSOffset = SAI_GEN_CFG0_FSOFF_ENABLE;//;

    sai_slot_t.FirstBitOffset = SAI_GEN_CFG1_FBOFF(0);
    sai_slot_t.SlotSize = SAI_GEN_CFG1_SLOTSZ_16B;//SAI_GEN_CFG1_SLOTSZ_DATASIZE;
    sai_slot_t.SlotNumber = SAI_GEN_CFG1_NBSLOT(1);//SAI_GEN_CFG1_NBSLOT(1);
    sai_slot_t.SlotActive = SAI_GEN_CFG1_SLOTEN_ACTIVE_ALL;//SAI_GEN_CFG1_SLOTEN_ACTIVE_0 | SAI_GEN_CFG1_SLOTEN_ACTIVE_1;
    sai_slot_t.DataSize = SAI_GEN_CFG1_DS_16;//SAI_GEN_CFG1_DS_8;
    sai_slot_t.RestoreSize = SAI_FIFOCTRL_FTH_SIZE_EMPTY;//SAI_FIFOCTRL_FTH_SIZE_8;
    sai_slot_t.DmaState = SAI_FIFOCTRL_DMAEN_ENABLE;
    SAI_Init(SAI0_S0_CFG_B, &sai_init_t,&sai_frame_t,&sai_slot_t);

    /* SAI1 A reg init */
    sai_init_t.AudioMode = SAI_AUDIOCTRL_MODE_MASTER_TX;
    sai_init_t.Syncin = SAI_AUDIOCTRL_SYNCIN_NOSYNCIN;
    sai_init_t.Syncout = SAI_AUDIOCTRL_SYNCOUT_ENABLE;
    SAI_Init(SAI0_S0_CFG_A, &sai_init_t,&sai_frame_t,&sai_slot_t);

    sai_clock_t.Mckdiv = 12 << 2;//kestrel2 12 = 16KHz kestrel 48 = 16KHz
    sai_clock_t.NoDivider = 0;
    sai_clock_t.OverSamplingRatio = 1;
    SAI_ClockInit(SAI0_S0_CFG_B, &sai_clock_t);

    sai_clock_t.Mckdiv = 12 << 2;//kestrel2 12 = 16KHz kestrel 48 = 16KHz
    sai_clock_t.NoDivider = 0;
    sai_clock_t.OverSamplingRatio = 1;
    SAI_ClockInit(SAI0_S0_CFG_A, &sai_clock_t);

    /* SAI0_S0_A start */
    SAI_Config(SAI0_S0_CFG_A,ENABLE);
    SAI_Config(SAI0_S0_CFG_B,ENABLE);

//    dump_tlv320_registers();

    while(1) {
    	if(state0 == 0)
    		delay_1ms(10);
    	else
    	{
    		state0 = 0;
    		break;
    	}
    }

    for(int i=0; i<100; i++) {
        if(i%8==0) printf("\n");
        printf("0x%x ", SAI0_S0_B_Receive_buffer[i]);
    }
    printf("\r\n");

    printf("recording end\r\n");
#endif

    delay_1ms(5000);
    delay_1ms(5000);
    while(1) {}
}

录音到PSRAM数组,然后memory窗口导出二进制PCM数据到AUDACITY

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值