找回密码
 立即注册
查看: 136|回复: 4

【学习记录1】AIcube模块化编程,实现TLV320AIC23B任意频率正弦波输出

[复制链接]

9

主题

28

回帖

183

积分

注册会员

积分
183
发表于 2026-9-15 08:34:52 | 显示全部楼层 |阅读模式
充分利用AIcube的模块化编程,USB,CDC,I2S,I2C等基础功能都不用自己编写代码,尤其是是IO口的变更以及设置,在AIcube中分分钟就可以简单解决了,比之前要分别设置还容易遗漏出错,确实是方便太多了。

    第一步 AIcube做框架和底层驱动。
    打开AICUBE,谋划勾选需要的资源,需要的勾选即可。 我的这个测试需要的资源只需要USB、时钟、I2C和I2S即可。
         截图202609150802061551.jpg
    1.时钟设置
         截图202609150803565096.jpg
    这里为了省事,选择了40Mhz,这个频率会涉及到TLV320AIC23B的采样率,如果想准确请选择40.96Mhz,选择40Mhz,采样率将降低为31.25Khz,而不是32KHZ。
    2.配置USB
         截图202609150808363763.jpg
    这里选择USB不停电下载,是为了下载固件方便。
    3.设置I2C总线
         截图202609150810542700.jpg
    I2C总线设置相对简单,注意 设置I2C时钟分频,根据器件特性和实际工作频率,设置相应的分频,这个决定了I2C总线的速度,速度过快有些器件不支持,甚至PCB布线不良也要降低速度。
    4.I2S总线设置
         截图202609150813536738.jpg
    I2S总线设置相对复杂,有个坑是I2S时钟分频要设置为40,I2S主时钟输出分频要设置为5,这样可以让采样率实际工作在31.25KHZ,但是AIcube显示的采样率为3.91khz是不对的,设置的时候记住显示的采样率*8就是实际的采样率。
    数据格式按图上设置就可以。
    5.IO口配置
         截图202609150818425000.jpg
    根据自己的原理图选择相应的端口就可以。
    6.端口特性设置
    端口特性设置可以在上面的图形化IO口设置中设置,不过我更喜欢在PORT端口及IO中断中设置。
    I2S用到了P14/P15端口,在P1端口中设置两端口开漏,并上拉。
         截图202609150829163021.jpg
         截图202609150829464008.jpg
    I2S用到的P2口,设置相应端口模式
         截图202609150831126704.jpg

    上面设置完成后,创建keil文件即可。
    第二步,填充代码。
在AIcube做好的框架下填充相应的功能代码即可。

回复

使用道具 举报 送花

9

主题

28

回帖

183

积分

注册会员

积分
183
 楼主| 发表于 2026-9-15 08:39:36 | 显示全部楼层
这个学习项目实现的功能是通过串口发送指令VOLR***VOLL***FREQ******。也就是左声道音量3位数字,右声道音量3位数字,频率6位数字,单片机接收指令后,执行音量和频率值。
这是main.c的文件内容
//<<AICUBE_USER_HEADER_REMARK_BEGIN>>
////////////////////////////////////////
// 在此添加用户文件头说明信息  
// 文件名称: main.c
// 文件描述:
// 文件版本: V1.0
// 修改记录:
//   1. (2026-09-05) 创建文件
////////////////////////////////////////
//<<AICUBE_USER_HEADER_REMARK_END>>


#include "config.h"                     //默认已包含stdio.h、intrins.h等头文件


//<<AICUBE_USER_INCLUDE_BEGIN>>
// 在此添加用户头文件包含
#include "tlv320aic23.h"
//<<AICUBE_USER_INCLUDE_END>>


//<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
// 在此添加用户全局变量定义、用户宏定义以及函数声明  

/*************        变量定义        **************/
u16        dac_L, dac_R;                //左后声道DAC值

//======= 测试音正弦波相关 ==========
#define        DEMO_16                2                //1: 使用16点正弦波表, 2: 使用32点正弦波表 (生成1kHz正弦波)

#if (DEMO_16 == 1)
// 16点正弦波表
u16 code T_SINE_16[16] = {
    32768, 45014, 55395, 62332, 64768, 62332, 55395, 45014,
    32768, 20522, 10141, 3204, 768, 3204, 10141, 20522
};
#else
// 32点正弦波表
u16 code T_SINE_32[32] = {
    32768, 39011, 45014, 50547, 55395, 59376, 62332, 64153,
    64768, 64153, 62332, 59376, 55395, 50547, 45014, 39011,
    32768, 26525, 20522, 14989, 10141, 6160, 3204, 1383,
    768, 1383, 3204, 6160, 10141, 14989, 20522, 26525
};
#endif

u16  TestTonePhase;     // 测试音相位累加器(16位)
bit  B_TestTone;        // 测试音标志, 1:播放测试音
u16  ToneFreq;          // 当前测试音频率 (Hz)
u16  ToneStepInc;       // 相位累加步进值

//======= 命令解析相关(extern声明,定义在usblib.c) ==========
extern u8   ComBuf[64];      // 命令接收缓冲区
extern u8   ComLen;          // 已接收命令长度
extern bit  B_CmdReceived;   // 收到完整命令标志

void        I2S_config(void);
void        SetHeadPhoneVolume(u16 vol);
void        SetHeadPhoneVolumeL(u16 vol);
void        SetHeadPhoneVolumeR(u16 vol);
void        SetHeadPhoneVolumeLR(u16 volL, u16 volR);
void        TestTone_Start(u16 freq);          // 启动测试音
u16        StringToU16(u8 *str, u8 len);   // 字符串转数字辅助函数声明

       
//<<AICUBE_USER_GLOBAL_DEFINE_END>>



////////////////////////////////////////
// 项目主函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void main(void)
{
    //<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
    // 在此添加用户主函数初始化代码

    //<<AICUBE_USER_MAIN_INITIAL_END>>

    SYS_Init();

    //<<AICUBE_USER_MAIN_CODE_BEGIN>>
    // 在此添加主函数中运行一次的用户代码  
                delay_ms(300);

    AIC23_Init();                // void 函数,直接调用
                delay_ms(50);
    AIC32_InitSet();                // void 函数,直接调用
                delay_ms(50);
    SetHeadPhoneVolume(120);  // 实际调用耳机音量设置

                TestTone_Start(1000);  // 启动1kHz测试音
                P10 =0;
               
       
    //<<AICUBE_USER_MAIN_CODE_END>>

    while (1)
    {
        //<<AICUBE_USER_MAIN_LOOP_BEGIN>>
    // 在此添加主函数中用户主循环代码  
   
    if(B_CmdReceived)
    {
        u16 volL, volR, freq;
        u8 i;
        bit  B_VolLErr = 0;      // 左声道音量越界标志
        bit  B_VolRErr = 0;      // 右声道音量越界标志
        bit  B_FreqErr = 0;      // 频率越界标志
        
        volL = 120;   // 默认左声道音量
        volR = 120;   // 默认右声道音量
        freq = 1000;  // 默认频率
        
        // 解析命令字符串
        i = 0;
        while(i < ComLen)
        {
            // 解析VOLL(左声道音量, 后面3位数字)
            if(ComBuf == 'V' && ComBuf[i+1] == 'O' &&
               ComBuf[i+2] == 'L' && ComBuf[i+3] == 'L')
            {
                u8 len = 0;
                i += 4;
                // 读取3位数字
                while(ComBuf >= '0' && ComBuf <= '9' && len < 3)
                {
                    len++;
                    i++;
                }
                volL = StringToU16(&ComBuf[i-len], len);
                // 越界检测:音量最大127
                if(volL > 127)
                {
                    B_VolLErr = 1;  // 左声道音量越界
                    volL = 127;     // 限制到最大值
                }
            }
            // 解析VOLR(右声道音量, 后面3位数字)
            else if(ComBuf == 'V' && ComBuf[i+1] == 'O' &&
                    ComBuf[i+2] == 'L' && ComBuf[i+3] == 'R')
            {
                u8 len = 0;
                i += 4;
                // 读取3位数字
                while(ComBuf >= '0' && ComBuf <= '9' && len < 3)
                {
                    len++;
                    i++;
                }
                volR = StringToU16(&ComBuf[i-len], len);
                // 越界检测:音量最大127
                if(volR > 127)
                {
                    B_VolRErr = 1;  // 右声道音量越界
                    volR = 127;     // 限制到最大值
                }
            }
            // 解析FREQ(频率, 后面6位数字, 单位Hz)
            else if(ComBuf == 'F' && ComBuf[i+1] == 'R' &&
                    ComBuf[i+2] == 'E' && ComBuf[i+3] == 'Q')
            {
                u8 len = 0;
                i += 4;
                // 读取6位数字
                while(ComBuf >= '0' && ComBuf <= '9' && len < 6)
                {
                    len++;
                    i++;
                }
                freq = StringToU16(&ComBuf[i-len], len);
                // 越界检测:频率最大16000Hz(采样率32K的一半)
                if(freq > 16000)
                {
                    B_FreqErr = 1;  // 频率越界
                    freq = 16000;   // 限制到最大值
                }
                if(freq < 20)
                {
                    B_FreqErr = 1;  // 频率低于最小值
                    freq = 20;      // 限制到最小值
                }
            }
            else
            {
                i++;
            }
        }
        
        // 设置左右声道音量(仅当无越界错误时执行,或者始终执行的限制值)
        SetHeadPhoneVolumeLR(volL, volR);
        
        // 设置测试音频率
        ToneFreq = freq;
        ToneStepInc = (u16)((unsigned long)freq * 65536UL / 32000UL);
        TestTonePhase = 0;
        
        // 根据越界情况发送响应
        if(B_VolLErr && B_VolRErr)
        {
            USB_SendData("VOLL/VOLR超出音量范围(0~127)\r\n", 28);
        }
        else if(B_VolLErr)
        {
            USB_SendData("VOLL超出音量范围(0~127)\r\n", 24);
        }
        else if(B_VolRErr)
        {
            USB_SendData("VOLR超出音量范围(0~127)\r\n", 24);
        }
        
        if(B_FreqErr)
        {
            USB_SendData("FREQ超出范围(20~16000Hz)\r\n", 24);
        }
        
        if(!B_VolLErr && !B_VolRErr && !B_FreqErr)
        {
            USB_SendData("CMD OK\r\n", 8);
        }
        
        // 清除命令标志和缓冲区
        B_CmdReceived = 0;
        ComLen = 0;
    }
   
        //<<AICUBE_USER_MAIN_LOOP_END>>
    }
}

////////////////////////////////////////
// 系统初始化函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void SYS_Init(void)
{
    EnableAccessXFR();                  //使能访问扩展XFR
    AccessCodeFastest();                //设置最快速度访问程序代码
    AccessIXramSpeed(1);                //设置访问内部XDATA的速度(如果不使能DMA,可将参数设置为0)
    IAP_SetTimeBase();                  //设置IAP等待参数,产生1us时基

    //<<AICUBE_USER_PREINITIAL_CODE_BEGIN>>
    // 在此添加用户预初始化代码  
    //<<AICUBE_USER_PREINITIAL_CODE_END>>

    PORT1_Init();                       //P1口初始化
    PORT2_Init();                       //P2口初始化
    PORT3_Init();                       //P3口初始化
    PORT5_Init();                       //P5口初始化
    CLK_Init();                         //时钟模块初始化
    I2C_Init();                         //I2C初始化
    I2S_Init();                         //I2S初始化
    delay_ms(1);
    USBLIB_Init();                      //USB库初始化
    delay_ms(1);

    //<<AICUBE_USER_INITIAL_CODE_BEGIN>>
    // 在此添加用户初始化代码
                //I2S_config();               
    //<<AICUBE_USER_INITIAL_CODE_END>>

    EnableGlobalInt();                  //使能全局中断
    USBLIB_WaitConfiged();              //等待USB完成配置
}

////////////////////////////////////////
// 微秒延时函数
// 入口参数: us (设置延时的微秒值)
// 函数返回: 无
////////////////////////////////////////
void delay_us(uint16_t us)
{
    do
    {
        NOP(34);                        //(MAIN_Fosc + 500000) / 1000000 - 6
    } while (--us);
}


////////////////////////////////////////
// 毫秒延时函数
// 入口参数: ms (设置延时的毫秒值)
// 函数返回: 无
////////////////////////////////////////
void delay_ms(uint16_t ms)
{
    uint16_t i;

    do
    {
        i = MAIN_Fosc / 6000;
        while (--i);
    } while (--ms);
}


//<<AICUBE_USER_FUNCTION_IMPLEMENT_BEGIN>>
// 在此添加用户函数实现代码



//========================================================================
// 函数: void TestTone_Start(u16 freq)
// 描述: 启动测试音, 持续输出不停止
// 参数: freq - 测试音频率 (Hz)
// 返回: none.
// 版本: VER1.0
// 备注: 采样率32000Hz, 生成正弦波
//       相位累加器步进 = freq*65536/32000
//========================================================================
void TestTone_Start(u16 freq)
{
    TestTonePhase = 0;      // 相位清零
    ToneFreq = freq;        // 保存频率
    ToneStepInc = (u16)((unsigned long)freq * 65536UL / 32000UL);  // 计算步进
    B_TestTone    = 1;      // 启动测试音
}


//========================================================================
// 函数: u16 StringToU16(u8 *str, u8 len)
// 描述: 将字符串转换为16位无符号整数(替代atoi)
// 参数: str - 字符串指针
//       len - 数字长度
// 返回: 转换后的数值
//========================================================================
u16 StringToU16(u8 *str, u8 len)
{
    u16 val = 0;
    u8 i;
   
    for(i = 0; i < len; i++)
    {
        if(str >= '0' && str <= '9')
        {
            val = val * 10 + (str - '0');
        }
        else
        {
            break;
        }
    }
   
    return val;
}



//<<AICUBE_USER_FUNCTION_IMPLEMENT_END>>



这是完整的项目文件
TLVAIC23B学习.zip (1.13 MB, 下载次数: 94)
回复

使用道具 举报 送花

9

主题

28

回帖

183

积分

注册会员

积分
183
 楼主| 发表于 2026-9-15 08:41:34 | 显示全部楼层
这是实际的输出波形
示波器正弦波形.jpg
回复

使用道具 举报 送花

9

主题

772

回帖

5311

积分

论坛元老

积分
5311
发表于 2026-9-15 11:30:06 | 显示全部楼层
能当DAC用,输出直流吗?
回复

使用道具 举报 送花

9

主题

28

回帖

183

积分

注册会员

积分
183
 楼主| 发表于 2026-9-15 17:24:06 | 显示全部楼层
网*** 发表于 2026-9-15 11:30
能当DAC用,输出直流吗?

输出有电容隔离呢,怎么输出直流呀
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-8 03:08 , Processed in 0.073388 second(s), 61 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表