Просмотр исходного кода

update:1、增加main.lua: 主程序入口,仅加载初始化模块
2、audio_drv.lua: 管理音频设备初始化与控制
3、cc_app.lua: 实现完整通话业务逻辑以及通话录音

13917187172 4 месяцев назад
Родитель
Сommit
0d3d79284f

+ 58 - 0
module/Air8000/demo/cc/audio_drv.lua

@@ -0,0 +1,58 @@
+--[[
+@module  audio_drv
+@summary 音频设备管理模块,负责音频设备的初始化和控制(仅使用exaudio扩展库)
+@version 2.0
+@date    2025.10.23
+@author  陈媛媛
+@usage
+本模块提供以下功能:
+1、定义所有硬件引脚常量
+2、使用exaudio扩展库初始化音频设备
+]]
+
+-- 引入exaudio库
+local exaudio = require("exaudio")
+
+-- exaudio配置参数
+local audio_configs = {
+    model = "es8311",         -- dac类型: "es8311"
+    i2c_id = 0,               -- i2c_id: 可填入0,1 并使用pins 工具配置对应的管脚
+    pa_ctrl = 162,            -- 音频放大器电源控制管脚
+    dac_ctrl = 164,           -- 音频编解码芯片电源控制管脚
+    dac_delay = 3,            -- DAC启动前冗余时间(单位100ms)
+    pa_delay = 100,           -- DAC启动后延迟打开PA的时间(单位1ms)
+    dac_time_delay = 100,     -- 播放完毕后PA与DAC关闭间隔(单位1ms)
+    bits_per_sample = 16,     -- 采样位深
+    pa_on_level = 1           -- PA打开电平 1:高 0:低
+}
+
+
+exaudio.vol(70)            -- 喇叭音量
+exaudio.mic_vol(65)        -- 麦克风音量
+
+-- 初始化音频设备
+local function initAudioDevice()
+
+    -- 使用exaudio.setup统一配置音频设备
+    log.info("audio_drv", "使用exaudio.setup初始化音频设备")
+    if exaudio.setup(audio_configs) then
+        log.info("audio_drv", "exaudio.setup初始化成功")
+    else
+        log.error("audio_drv", "exaudio.setup初始化失败")
+        return false
+    end
+    
+    -- log.info("audio_drv", "Audio device initialized using exaudio only")
+    return true
+end
+
+-- 获取音频通道ID(保留用于兼容性)
+local function getMultimediaId()
+    return 0  -- 返回默认值0
+end
+
+-- 导出接口
+return {
+    initAudioDevice = initAudioDevice,
+    getMultimediaId = getMultimediaId
+}

+ 246 - 0
module/Air8000/demo/cc/cc_app.lua

@@ -0,0 +1,246 @@
+--[[
+@module  cc_app
+@summary 通话业务逻辑模块,实现4种通话场景的处理和通话录音功能
+@version 1.0
+@date    2025.10.21
+@author  陈媛媛
+@usage
+本模块提供以下功能:
+1. 设置 ACTIVE_SCENARIO 变量选择需要的场景(1-4)
+2. 其他场景自动禁用,不会影响程序运行
+3. 每个场景都有独立的状态处理逻辑
+4. 实现通话录音功能
+
+支持的场景:
+[场景1] 呼入立即挂断(响铃3次后自动拒接)
+[场景2] 呼入自动接听(响2声)+10秒后主动挂断
+[场景3] 呼入自动接听(响2声)+等待对方挂断
+[场景4] 主动呼出电话+等待对方挂断
+
+
+注意事项:
+1. 设置ACTIVE_SCENARIO选择要启用的场景(1-4)
+2. 场景4会主动拨打电话(修改为自己测试时要拨打的电话号码)
+3. 所有通话都会被录音(需确保存储空间足够)
+]]
+
+-- 引入音频设备模块
+local audio_drv = require "audio_drv"
+
+-- ====================== 配置区域 ======================
+-- 设置当前激活的场景(1-4),注释掉不需要的场景
+-- local ACTIVE_SCENARIO = 1  -- 场景1:呼入立即挂断
+ local ACTIVE_SCENARIO = 2  -- 场景2:呼入自动接听,10秒后主动挂断
+--local ACTIVE_SCENARIO = 3  -- 场景3:呼入自动接听,等待对方挂断
+-- local ACTIVE_SCENARIO = 4  -- 场景4:主动呼出,等待对方挂断
+
+-- 全局状态变量
+local call_counter = 0                 -- 响铃计数器(用于场景1-3)
+local caller_number = ""               -- 来电号码(用于场景1-3)
+local is_connected = false             -- 通话连接状态标志(用于场景2)
+local outgoing_number = "10000"  -- 呼出号码(用于场景4),修改为自己测试时要拨打的电话号码
+
+-- ====================== 录音功能 ======================
+-- 创建音频数据缓冲区
+local up1 = zbuff.create(6400,0)      -- 上行数据保存区1
+local up2 = zbuff.create(6400,0)      -- 上行数据保存区2
+local down1 = zbuff.create(6400,0)    -- 下行数据保存区1
+local down2 = zbuff.create(6400,0)    -- 下行数据保存区2
+
+-- 音频数据回调函数
+local function recordCallback(is_dl, point)
+    if is_dl then
+        log.info("录音", "下行数据,位于缓存", point+1, "缓存1数据量", down1:used(), "缓存2数据量", down2:used())
+    else
+        log.info("录音", "上行数据,位于缓存", point+1, "缓存1数据量", up1:used(), "缓存2数据量", up2:used())
+    end
+    log.info("通话质量", cc.quality())
+    -- 可以在初始化串口后,通过uart.tx来发送走对应的zbuff即可
+end
+
+-- 启用通话录音
+local function enableRecording()
+    cc.record(true, up1, up2, down1, down2)
+    cc.on("record", recordCallback)
+    log.info("cc_app", "通话录音已启用")
+end
+
+-- 获取所有缓冲区
+local function getRecordingBuffers()
+    return {
+        up1 = up1,
+        up2 = up2,
+        down1 = down1,
+        down2 = down2
+    }
+end
+
+-- ====================== 场景处理函数 ======================
+
+-- 场景1:呼入立即挂断(响铃3次后)
+local function handle_scenario1(status)
+    if status == "INCOMINGCALL" then
+        -- 获取来电号码
+        caller_number = cc.lastNum() or "未知号码"
+        call_counter = call_counter + 1
+        
+        log.info("场景1", "收到来电,号码:", caller_number, "响铃次数:", call_counter)
+        
+        -- 响铃3声后拒接
+        if call_counter >= 3 then
+            log.info("场景1", "拒接来电")
+            cc.hangUp(0)
+            call_counter = 0  -- 重置计数器
+        end
+    elseif status == "HANGUP_CALL_DONE" then
+        log.info("场景1", "挂断完成")
+        call_counter = 0
+    end
+end
+
+-- 场景2挂断回调函数
+local function scenario2_hangup_callback()
+    log.info("场景2", "10秒通话结束,主动挂断")
+    cc.hangUp(0)
+    is_connected = false
+end
+
+-- 场景2:呼入自动接听,10秒后主动挂断
+local function handle_scenario2(status)
+    if status == "INCOMINGCALL" then
+        -- 获取来电号码
+        caller_number = cc.lastNum() or "未知号码"
+        call_counter = call_counter + 1
+        
+        log.info("场景2", "收到来电,号码:", caller_number, "响铃次数:", call_counter)
+        
+        -- 响铃2声后自动接听
+        if call_counter >= 2 then
+            log.info("场景2", "自动接听来电")
+            cc.accept(0)
+            call_counter = 0  -- 重置计数器
+        end
+    elseif status == "ANSWER_CALL_DONE" then
+        log.info("场景2", "接听完成,等待通话建立")
+    elseif status == "SPEECH_START" then
+        -- 语音通话真正开始
+        if not is_connected then
+            log.info("场景2", "通话已建立,开始计时")
+            is_connected = true
+            
+            -- 创建10秒后挂断的定时器
+            sys.timerStart(scenario2_hangup_callback, 10000)  -- 10秒后执行挂断
+            log.info("场景2", "10秒挂断定时器创建成功")
+        end
+    elseif status == "HANGUP_CALL_DONE" or status == "DISCONNECTED" then
+        log.info("场景2", "通话结束")
+        is_connected = false
+        
+        -- 取消挂断定时器
+        sys.timerStop(scenario2_hangup_callback)
+        log.info("场景2", "已取消挂断定时器")
+        
+        call_counter = 0  -- 重置计数器
+    end
+end
+
+-- 场景3:呼入自动接听,等待对方挂断
+local function handle_scenario3(status)
+    if status == "INCOMINGCALL" then
+        -- 获取来电号码
+        caller_number = cc.lastNum() or "未知号码"
+        call_counter = call_counter + 1
+        
+        log.info("场景3", "收到来电,号码:", caller_number, "响铃次数:", call_counter)
+        
+        -- 响铃2声后自动接听
+        if call_counter >= 2 then
+            log.info("场景3", "自动接听来电")
+            cc.accept(0)
+            call_counter = 0  -- 重置计数器
+        end
+    elseif status == "SPEECH_START" then
+        -- 语音通话真正开始
+        log.info("场景3", "电话已接通,电话号码:", caller_number)
+    elseif status == "DISCONNECTED" then
+        -- 对方挂断通话
+        log.info("场景3", "通话结束对方挂断")
+        call_counter = 0  -- 重置计数器
+    end
+end
+
+-- 场景4:主动呼出,等待对方挂断
+local function handle_scenario4(status)
+    if status == "CONNECTED" then
+        log.info("场景4", "呼叫接通")
+    elseif status == "DISCONNECTED" then
+        log.info("场景4", "通话结束(对方挂断)")
+    elseif status == "MAKE_CALL_FAILED" then
+        log.info("场景4", "呼叫失败")
+    end
+end
+
+-- 场景4拨号函数
+local function dial_for_scenario4()
+    log.info("场景4", "开始拨打", outgoing_number)
+    cc.dial(0, outgoing_number)
+end
+
+-- ====================== 主事件处理器 ======================
+sys.subscribe("CC_IND", function(status)
+    log.info("CC状态", status)
+    
+    -- 根据激活的场景调用对应的处理函数
+    if ACTIVE_SCENARIO == 1 then
+        handle_scenario1(status)
+    elseif ACTIVE_SCENARIO == 2 then
+        handle_scenario2(status)
+    elseif ACTIVE_SCENARIO == 3 then
+        handle_scenario3(status)
+    elseif ACTIVE_SCENARIO == 4 then
+        handle_scenario4(status)
+    end
+    
+    -- 所有场景都需要处理的通用状态
+    if status == "READY" then
+        sys.publish("CC_READY")  -- 发布系统就绪事件
+        
+        -- 场景4:电话系统就绪后自动拨号
+        if ACTIVE_SCENARIO == 4 then
+            sys.timerStart(dial_for_scenario4, 1000)  -- 延迟1秒拨号
+        end
+    elseif status == "HANGUP_CALL_DONE" or status == "MAKE_CALL_FAILED" or status == "DISCONNECTED" then
+        
+        audio.pm(0,audio.STANDBY)
+        -- audio.pm(0,audio.SHUTDOWN)   --低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
+    end
+end)
+
+-- ====================== 电话系统初始化 ======================
+local function init_cc()
+    -- 初始化音频设备(使用exaudio)
+    audio_drv.initAudioDevice()
+    
+    -- 等待电话系统就绪
+    sys.waitUntil("CC_READY")
+    
+    -- 初始化电话功能
+    cc.init(audio_drv.getMultimediaId())
+    
+    -- 启用通话录音(录音功能在cc_app中)
+    enableRecording()
+    
+    log.info("cc_app", "电话系统初始化完成")
+end
+
+-- 启动初始化任务
+sys.taskInit(init_cc)
+
+log.info("cc_app", "通话业务逻辑模块加载完成,当前场景:", ACTIVE_SCENARIO)
+
+-- 导出录音相关功能(如果需要被其他模块使用)
+-- return {
+--     enableRecording = enableRecording,
+--     getRecordingBuffers = getRecordingBuffers,
+--     recordCallback = recordCallback
+-- }

+ 60 - 99
module/Air8000/demo/cc/main.lua

@@ -1,111 +1,72 @@
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "ccdemo"
-VERSION = "1.0.0"
-log.style(1)
-
-
--- sys库是标配
-sys = require("sys")
-
+--[[
+@module  main
+@summary LuatOS语音通话应用主入口,负责加载功能模块
+@version 1.0
+@date    2025.07.16
+@author  陈媛媛
+@usage
+本demo演示的核心功能为:
+1、音频设备初始化与控制
+2、完整通话业务逻辑处理(5种通话场景),详情如下:
+-呼入,挂断,挂断消息识别打印;
+-呼入,接听,接听消息识别打印,录音,主动挂断,挂断消息识别打印;
+-呼入,接听,接听消息识别打印,录音,对方挂断,挂断消息识别打印;
+-呼出,对方接通,接听消息识别打印,建立通话后一段时间,对方主动挂断,挂断消息识别打印;
+-通话中给对方播放音频功能(暂不支持,待补充)
+3、通话状态监控与日志记录
+4、支持Air8000系列和Air780EHV等硬件平台
+
+更多说明参考本目录下的readme.md文件
+]]
+
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "VOICE_CALL_DEMO"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
 if wdt then
-    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
-    --wdt.init(9000)--初始化watchdog设置为9s
-    --sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
-end
-local up1 = zbuff.create(6400,0)        --上行数据保存区1
-local up2 = zbuff.create(6400,0)        --上行数据保存区2
-local down1 = zbuff.create(6400,0)      --下行数据保存区1
-local down2 = zbuff.create(6400,0)      --下行数据保存区2
-local cnt = 0
-
-gpio.setup(24, 1, gpio.PULLUP)          -- i2c工作的电压域
-gpio.setup(164, 1, gpio.PULLUP) -- es8311电源使能脚
-gpio.setup(162, 1, gpio.PULLUP) -- 喇叭pa功放脚
-pins.setup(80, "I2C0_SCL")      --复用pin80为i2c0_scl
-pins.setup(81, "I2C0_SDA")      --复用pin81为i2c0_sda
-
-local multimedia_id = 0         -- 音频通道 0
-local i2c_id = 0            -- i2c_id 0
-local i2s_id = 0            -- i2s_id 0
-local i2s_mode = 0          -- i2s模式 0 主机 1 从机
-local i2s_sample_rate = 16000   -- 采样率
-local i2s_bits_per_sample = 16      -- 数据位数
-local i2s_channel_format = i2s.MONO_R        -- 声道, 0 左声道, 1 右声道, 2 立体声
-local i2s_communication_format = i2s.MODE_LSB    -- 格式, 可选MODE_I2S, MODE_LSB, MODE_MSB
-local i2s_channel_bits = 16     -- 声道的BCLK数量
-
-local pa_pin = 162       -- 喇叭pa功放脚
-local pa_on_level = 1       -- PA打开电平 1 高电平 0 低电平
-local pa_delay = 100        -- 在DAC启动后,延迟多长时间打开PA,单位1ms
-local power_pin = 164         -- es8311电源使能脚
-local power_on_level = 1        -- 电源控制IO的电平,默认拉高
-local power_delay = 3       -- 在DAC启动前插入的冗余时间,单位100ms
-local power_time_delay = 100        -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
-
-local voice_vol = 70        -- 喇叭音量
-local mic_vol = 65          -- 麦克风音量
-
-local function record(is_dl, point)
-    if is_dl then
-        log.info("下行数据,位于缓存", point+1, "缓存1数据量", down1:used(), "缓存2数据量", down2:used())
-    else
-        log.info("上行数据,位于缓存", point+1, "缓存1数据量", up1:used(), "缓存2数据量", up2:used())
-    end
-    log.info("通话质量", cc.quality())
-    -- 可以在初始化串口后,通过uart.tx来发送走对应的zbuff即可
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
-sys.subscribe("CC_IND", function(state)
-    if state == "READY" then
-        sys.publish("CC_READY")
-    elseif state == "INCOMINGCALL" then
-        cnt = cnt + 1
-        if cnt > 3 then
-            cc.accept(0)   --自动接听
-        end
-    elseif state == "HANGUP_CALL_DONE" or state == "MAKE_CALL_FAILED" or state == "DISCONNECTED" then
-        audio.pm(0,audio.STANDBY)
-        -- audio.pm(0,audio.SHUTDOWN)   --低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
-    end
-end)
-
-function cc_setup()
-
-    cc.record(true, up1, up2, down1, down2)
 
-    pm.power(pm.LDO_CTL, false)  --开发板上ES8311由LDO_CTL控制上下电
-    sys.wait(100)
-    pm.power(pm.LDO_CTL, true)  --开发板上ES8311由LDO_CTL控制上下电
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-    i2c.setup(i2c_id,i2c.FAST)      --设置i2c
 
-    i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-    audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
-    audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311", i2cid = i2c_id, i2sid = i2s_id})    --通道0的硬件输出通道设置为I2S
 
-    audio.vol(multimedia_id, voice_vol)
-    audio.micVol(multimedia_id, mic_vol)
-
-    cc.init(multimedia_id) --初始化电话功能
-
-    sys.waitUntil("CC_READY")
-    sys.wait(10000)
-    cc.dial(0,"158xxxxxxxx") --拨打电话
-end
-cc.on("record", record)--注册cc事件回调
-sys.taskInit(cc_setup) --初始化cc
 
--- sys.taskInit(function()
---     while 1 do
---         -- 打印内存状态, 调试用
---         sys.wait(1000)
---         log.info("lua", rtos.meminfo())
---         log.info("sys", rtos.meminfo("sys"))
---     end
--- end)
+-- 仅加载必要的功能模块
+require "audio_drv"  -- 音频设备管理模块
+require "cc_app"        -- 通话业务逻辑模块
 
 -- 用户代码已结束---------------------------------------------
--- 结尾总是这一句
 sys.run()
--- sys.run()之后后面不要加任何语句!!!!!
+-- sys.run()之后不要加任何语句!!!!!

+ 250 - 0
module/Air8000/demo/cc/pins_air8000.json

@@ -0,0 +1,250 @@
+{
+  "model": "Air8000",
+  "pins": [
+    [
+      1,
+      "USB_BOOT",
+      ""
+    ],
+    [
+      2,
+      "VBUS",
+      ""
+    ],
+    [
+      14,
+      "PWR_KEY",
+      ""
+    ],
+    [
+      16,
+      "UART1_TXD",
+      ""
+    ],
+    [
+      17,
+      "UART1_RXD",
+      ""
+    ],
+    [
+      18,
+      "I2S_BCLK",
+      ""
+    ],
+    [
+      19,
+      "I2S_LRCK",
+      ""
+    ],
+    [
+      20,
+      "I2S_DIN",
+      ""
+    ],
+    [
+      21,
+      "I2S_DOUT",
+      ""
+    ],
+    [
+      22,
+      "I2S_MCLK",
+      ""
+    ],
+    [
+      23,
+      "GPIO20",
+      ""
+    ],
+    [
+      24,
+      "GPIO21",
+      ""
+    ],
+    [
+      25,
+      "LCD_CLK",
+      ""
+    ],
+    [
+      26,
+      "LCD_CS",
+      ""
+    ],
+    [
+      27,
+      "LCD_RST",
+      ""
+    ],
+    [
+      28,
+      "LCD_SDA",
+      ""
+    ],
+    [
+      29,
+      "LCD_RS",
+      ""
+    ],
+    [
+      30,
+      "GPIO2",
+      ""
+    ],
+    [
+      31,
+      "GPIO1",
+      ""
+    ],
+    [
+      35,
+      "CAN_STB",
+      ""
+    ],
+    [
+      36,
+      "CAN_TXD",
+      ""
+    ],
+    [
+      37,
+      "CAN_RXD",
+      ""
+    ],
+    [
+      38,
+      "SPI1_SCLK",
+      ""
+    ],
+    [
+      39,
+      "SPI1_MISO",
+      ""
+    ],
+    [
+      40,
+      "SPI1_MOSI",
+      ""
+    ],
+    [
+      41,
+      "SPI1_CS",
+      ""
+    ],
+    [
+      43,
+      "WAKEUP6",
+      ""
+    ],
+    [
+      44,
+      "WAKEUP0",
+      ""
+    ],
+    [
+      46,
+      "DBG_TXD",
+      ""
+    ],
+    [
+      47,
+      "DBG_RXD",
+      ""
+    ],
+    [
+      48,
+      "UART11_RXD",
+      ""
+    ],
+    [
+      49,
+      "UART11_TXD",
+      ""
+    ],
+    [
+      52,
+      "GPIO153",
+      ""
+    ],
+    [
+      53,
+      "GPIO147",
+      ""
+    ],
+    [
+      54,
+      "GPIO146",
+      ""
+    ],
+    [
+      55,
+      "GPIO141",
+      ""
+    ],
+    [
+      56,
+      "GPIO140",
+      ""
+    ],
+    [
+      59,
+      "UART12_RXD",
+      ""
+    ],
+    [
+      60,
+      "UART12_TXD",
+      ""
+    ],
+    [
+      66,
+      "I2C1_SCL",
+      ""
+    ],
+    [
+      67,
+      "I2C1_SDA",
+      ""
+    ],
+    [
+      73,
+      "GPIO162",
+      ""
+    ],
+    [
+      74,
+      "GPIO164",
+      ""
+    ],
+    [
+      80,
+      "I2C0_SCL",
+      ""
+    ],
+    [
+      81,
+      "I2C0_SDA",
+      ""
+    ],
+    [
+      82,
+      "GPIO17",
+      ""
+    ],
+    [
+      83,
+      "GPIO16",
+      ""
+    ],
+    [
+      96,
+      "GPIO160",
+      ""
+    ],
+    [
+      98,
+      "GPIO3",
+      ""
+    ]
+  ]
+}

+ 196 - 0
module/Air8000/demo/cc/readme.md

@@ -0,0 +1,196 @@
+# CC_DEMO 项目说明
+
+## 项目概述
+本项目是基于 Air8000 的语音通信演示demo,实现了基本的语音通话功能,包括音频设备初始化、通话建立、通话管理等核心功能。
+
+## 文件结构
+- main.lua: 主程序入口,仅加载初始化模块
+- audio_drv.lua: 管理音频设备初始化与控制
+- cc_app.lua: 实现完整通话业务逻辑以及通话录音
+
+## 功能说明
+1. **音频设备初始化与控制**:配置并管理ES8311音频编解码芯片和扬声器功放,包括I2C、I2S接口设置及音量控制。
+
+2. **完整通话业务逻辑处理**:实现4种通话场景,包括呼入和呼出的各种情况处理。
+
+按照自己的通话需求启用对应的Lua文件,其余注释掉;
+
+- (1)呼入,主动挂断(响铃3次后自动拒接);
+
+- (2)呼入,自动接听,接听消息识别打印,主动挂断,挂断消息识别打印;
+
+- (3)呼入,自动接听,接听消息识别打印,等待对方挂断,挂断消息识别打印;
+
+- (4)呼出,对方接通,接听消息识别打印,建立通话后一段时间,等待对方挂断,挂断消息识别打印;
+
+3. **通话状态监控与日志记录**:实时记录通话状态变化及相关信息。
+
+## 演示硬件环境
+1、Air8000开发板
+
+![alt text](https://docs.openLuat.com/cdn/image/Air8000%E5%BC%80%E5%8F%91%E6%9D%BF.jpg )
+ 
+ 或者Air8000核心板+AirAUDIO_1010 音频扩展板+喇叭
+
+![alt text]( https://docs.openLuat.com/cdn/image/Air8000%E6%A0%B8%E5%BF%83%E6%9D%BF+1010.jpg)
+
+- 具备volte功能的电话卡插入开发板/核心板的sim卡槽
+
+2、TYPE-C USB数据线一根
+- Air8000开发板/核心板通过 TYPE-C USB 口供电;
+- TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+3、可选AirAudio_1010 配件板一块,Air8000核心板和AirAudio_1010 配件板的硬件接线方式为:
+|  Air8000核心板   | AirAUDIO_1010配件板 |
+| --------------- | -----------------   |
+| 22/I2S_MCLK     | I2S_MCLK            |
+| 18/I2S_BCK      | I2S_BCK             |
+| 19/I2S_LRCK     | I2S_LRCK            |
+| 20/I2S_DIN      | I2S_DIN             |
+| 21/I2S_DOUT     | I2S_DOUT            |
+| 80/I2C0_SCL     | I2C_SCL             |
+| 81/I2C0_SDA     | I2C_SDA             |
+| 82/GPIO17       | PA_EN               |
+| 83/GPIO16       | 8311_EN             |
+| VDD_EXT         | VCC                 |
+| GND             | GND                 |
+
+## 演示软件环境
+1、Luatools下载调试工具 [https://docs.openluat.com/air780epm/common/Luatools/]
+
+2、Air8000 V2014版本固件(理论上,2025年7月26日之后发布的固件都可以)),选择支持Volte功能的1、2、8、13或101、102、108、113号固件。不同版本区别请见https://docs.openluat.com/air8000/luatos/firmware/
+
+3、合宙 LuatIO 工具(GPIO 复用初始化配置)使用说明  https://docs.openluat.com/air780epm/common/luatio/
+
+4、 lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
+
+## 相关软件资料
+1、cc库   https://docs.openluat.com/osapi/core/cc/
+
+2、audio - 多媒体-音频 https://docs.openluat.com/osapi/core/audio/#audiopmidpm_mode
+
+3、CC_IND -- 通话状态变化
+  "READY":通话准备完成,可以拨打电话或者呼入电话了
+  "INCOMINGCALL":有电话呼入
+  "CONNECTED":电话已经接通
+  "DISCONNECTED":电话被对方挂断
+  "SPEECH_START":通话开始
+  "MAKE_CALL_OK":拨打电话请求成功
+  "MAKE_CALL_FAILED":拨打电话请求失败
+  "ANSWER_CALL_DONE":接听电话请求完成
+  "HANGUP_CALL_DONE":挂断电话请求完成
+  "PLAY":开始有音频输出
+
+## 演示核心步骤
+1、搭建好硬件环境
+
+2、demo脚本代码cc_app.lua中的local TEST_PHONE_NUMBER = "10086"  -- 场景4拨打的测试号码,修改为自己测试时要拨打的电话号码
+
+3、Luatools烧录内核固件和修改后的demo脚本代码
+
+4、烧录成功后,自动开机运行
+
+5、运行程序,观察日志输出了解通话状态
+
+- 场景1 呼入立即挂断
+
+    当设备启动并初始化完成后,打印READY和电话系统初始化完成。
+
+    当有来电时,会打印INCOMINGCALL,并开始计数响铃次数。
+
+    响铃3次后,自动拒接来电,打印拒接来电和挂断完成。
+
+    类似以下日志:
+
+    ``` lua
+    I/user.cc_app 通话业务逻辑模块加载完成,当前场景: 1
+    I/user.exaudio_device 使用exaudio.setup初始化音频设备
+    I/user.exaudio_device exaudio.setup初始化成功
+    I/user.CC状态 READY
+    I/user.exaudio_device 通话录音已启用
+    I/user.cc_app 电话系统初始化完成
+    I/user.CC状态 INCOMINGCALL
+    I/user.场景1 收到来电,号码: 139XXXXXXXX 响铃次数: 1
+    I/user.CC状态 INCOMINGCALL
+    I/user.场景1 收到来电,号码: 139XXXXXXXX 响铃次数: 2
+    I/user.CC状态 INCOMINGCALL
+    I/user.场景1 收到来电,号码: 139XXXXXXXX 响铃次数: 3
+    I/user.场景1 拒接来电
+    I/user.CC状态 HANGUP_CALL_DONE
+    I/user.场景1 挂断完成
+    ``` 
+- 场景2 呼入自动接听+10秒后主动挂断
+
+    来电响铃2次后自动接听,通话建立10秒后设备会自动挂断。通话期间持续进行双向录音。
+
+    类似以下日志:
+
+    ``` lua
+    I/user.cc_app 通话业务逻辑模块加载完成,当前场景: 2
+    I/user.exaudio_device 使用exaudio.setup初始化音频设备
+    I/user.exaudio_device exaudio.setup初始化成功
+    I/user.CC状态 READY
+    I/user.exaudio_device 通话录音已启用
+    I/user.cc_app 电话系统初始化完成
+    I/user.CC状态 PLAY
+    I/user.CC状态 INCOMINGCALL   
+    I/user.场景2 收到来电,号码: 139xxxxxxxx 响铃次数: 1  
+    I/user.场景2 收到来电,号码: 139xxxxxxxx 响铃次数: 2  
+    I/user.场景2 自动接听来电   
+    I/user.场景2 接听完成,等待通话建立             
+    I/user.场景2 通话已建立,开始计时  
+    I/user.场景2 10秒挂断定时器创建成功,ID: 2097153
+    I/user.录音 上行数据,位于缓存 1 缓存1数据量 6400 缓存2数据量 0
+    I/user.通话质量 2
+    I/user.CC状态 HANGUP_CALL_DONE
+    I/user.场景2 通话结束
+    I/user.场景2 已取消挂断定时器 
+    
+           
+- 场景3 呼入自动接听+等待对方挂断
+
+    呼入主动接听并等待对方主动挂断。通话期间持续进行双向录音。
+    
+    类似以下日志:
+
+  ``` lua
+    I/user.cc_app 通话业务逻辑模块加载完成,当前场景: 3
+    I/user.exaudio_device 使用exaudio.setup初始化音频设备
+    I/user.exaudio_device exaudio.setup初始化成功
+    I/user.CC状态 READY
+    I/user.exaudio_device 通话录音已启用
+    I/user.cc_app 电话系统初始化完成
+    I/user.CC状态 PLAY
+    I/user.CC状态 INCOMINGCALL   
+    I/user.场景2 收到来电,号码: 139xxxxxxxx 响铃次数: 1  
+    I/user.场景2 收到来电,号码: 139xxxxxxxx 响铃次数: 2  
+    I/user.场景2 自动接听来电  
+    I/user.场景3 电话已接通,电话号码: 139xxxxxxxx
+    I/user.录音 上行数据,位于缓存 1 缓存1数据量 6400 缓存2数据量 0
+    I/user.通话质量 2
+    I/user.CC状态 DISCONNECTED
+    I/user.场景3 通话结束对方挂断
+
+- 场景4 主动呼出预设号码并等待对方挂断。
+  
+  类似以下效果:
+
+  ``` lua
+   I/user.cc_app 通话业务逻辑模块加载完成,当前场景: 4
+   I/user.exaudio_device 使用exaudio.setup初始化音频设备
+   I/user.exaudio_device exaudio.setup初始化成功
+   I/user.CC状态 READY
+   I/user.exaudio_device 通话录音已启用
+   I/user.cc_app 电话系统初始化完成
+   I/user.场景4 开始拨打 139xxxxxxxx
+   I/user.CC状态 MAKE_CALL_OK
+   I/user.CC状态 PLAY
+   I/user.录音 上行数据,位于缓存 1 缓存1数据量 6400 缓存2数据量 0
+   I/user.通话质量 2
+   I/user.CC状态 DISCONNECTED
+   I/user.场景4 通话结束(对方挂断)
+
+
+
+
+