DSP

高通CSR QCC300x和RDA 5856软件音频流走向和系统架构

2019-07-13 17:00发布

前几天有朋友问, CSR和RDA软件, 假设在上层用一个while(1), 会不会死掉; 另外, A2DP音频流数据是如何到DSP的. 还有CSR和RDA CPU架构是如何? 这些都是不错的问题, 因为这对于study现已成熟产品的系统架构有帮助.

文章目录

1. 上层使用while(1)会不会死掉.

分为两方面来讨论, 一方面是CSR, 另一方面是RDA, GO!
方法: 当音乐开始播放后, 进入while(1).

1.1 CSR

以ADK2.5.1为例, 在以下函数最后添加一行: void handleA2DPStartStreaming(uint16 DeviceId, uint16 StreamId, a2dp_status_code status) { ... while(1); } 实验结果: 蓝牙不会断开连接, 但声音不出来, 串口发AT指令无作用.
之前以为会死掉,并断开连接, 但实际上只是死掉, 不会断开连接.
分析: CSR上层的代码全部在void app_handler(Task task, MessageId id, Message message);
上层代码死在while(1)中, 底层系统可能在等app_handler回调完成. 但是Controller可能是独立运行的, 故和手机的交互还在, 所以没有表现出断开连接.

1.2 RDA

void app_a2dp_msg_handle(COS_EVENT *ev) { case RDABT_A2DP_SEND_SEP_START_IND_MSG: { stateManagerEnterA2dpStreamingState(); while(1); } break; case RDABT_A2DP_SEND_SEP_PAUSE_IND_MSG: .... } 结果: 串口程序不响应, 无声音, 没有断开连接, 但是大约1分钟后, 程序重启了. 自然, 蓝牙连接也断开重连了.
分析: 软件task(任务)实现架构和A2DP/串口数据流向如下:
BAL_TaskInit生成了许多子任务, 主要如下图所示:
串口中断服务是独立的. MOD_APPMOD_BTMOD_MEDMOD_MULTIMOD_SYSA2DP开始信息串口buffer串口AT指令BAL_TaskInitMainTaskBTTaskMMCTaskMultimediaTaskSysTask串口中断ISR
  • 通过上诉分析, 程序在处理A2DP消息时, APP层MainTask程序停住.
  • 当user通过串口工具发送串口指令时, 串口指令依次经过串口中断 -> SysTask -> MainTask. 因为MainTask已经被while(1)停住,所以MainTask是收不到从SysTask发来的消息的. 所以串口是没有反应的,
  • 1分钟以后重启应该是因为watchdog起作用了.

2 底层A2DP 音频是如何运行到DSP的.

2.1 CSR

查询上层没有音频数据的接口, 并且, CSR没有开放AVDTP层的代码. 所以, 猜测是在AVDTP层直接发送到DSP了. CSR的DSP又叫Kalimba.
后来找到一片文章, 具体在哪不记得了, 说音频数据直接从GAVDP到DSP了.
下次找到补上.

2.2 RDA

两个CPU(应该是虚拟的), 一个是XCPU, 一个是BTCPU.
音频数据是从BTCPU开始的, 先到共享内存,再到XCPU的BTtask, 经过MainTask, 最后到MMCTask, 并送入DSP. MOD_APPMOD_BTMOD_MEDA2DP_STREAMSTACK MSGBT MSGA2DP STREAM调用解码器XCPUMainTaskBTTaskMMCTaskBTCPUSharedMemoryMMC_AudioSBCDecVocDSP 如上, BT Task会不停的从shared memory取数据, 然后把数据发给MMCtask, 最后调用解码器解码SBC数据, 最后送给DSP.

3. CPU架构

3.1 CSR

以QCC 3008为例, CSR是80 MHz RISC CPU and 80 MHz Qualcomm® Kalimba™DSP.
也就是说, HCI层以上协议栈和APP都是跑RISC CPU的. 暴露给APP的, 是单线程架构.
至于底层系统是如何写的, 因为不可见, 所以是不确定.

3.2 RDA

3.2.1 CPU

RDA5856Q32软件上有两个MCU, 一个是BTCPU, 一个是XCPU, 这个在代码上和debug工具上都可见. 不过,正如前面所说, 两个CPU应该是虚拟的,应该是实时操作系统创建了两个进程而已. 规格书并没有说是两个MCU, 如下:
104MHz RISC MCU and 104MHz Voice Co-Processor(VoC) DSP core

3.2.2 XPCU和BTCPU内存交互

此节分析交互情况, 并分析为何应该是虚拟CPU, 而实际就是两进程.
3.2.2.1 XCPU从共享内存获取BT CPU数据:
BOOL rdabt_get_xcpu_msg(COS_EVENT *event) { if(g_rdabt_sharemem.xcpu_head==g_rdabt_sharemem.xcpu_tail) return FALSE; else { *event = g_rdabt_sharemem.xcpu_event[g_rdabt_sharemem.xcpu_tail]; g_rdabt_sharemem.xcpu_event[g_rdabt_sharemem.xcpu_tail++].nEventId = 0; } if(g_rdabt_sharemem.xcpu_tail == RDABT_SHAREMEM_SIZE) g_rdabt_sharemem.xcpu_tail = 0; return TRUE; } 获取到之后的操作: while(rdabt_get_xcpu_msg(&ev)) { if(ev.nEventId == RDABT_SCO_DATA_IND) { COS_EVENT event = {0}; hci_sco_data_msg_t *packet = (hci_sco_data_msg_t*)ev.nParam1; //hal_HstSendEvent(0xff, 0x20160300); //hal_HstSendEvent(0xff, ev.nParam1); event.nEventId = RDABT_SCO_DATA_RES; event.nParam1 = packet->data; //rdabt_send_msg_to_bt(&event); memcpy(MMC_SCOGetRXBuffer(packet->flag), packet->data, packet->length); MMC_SCOReceiveData(packet->length); } else { if(ev.nEventId == RDABT_USER_BT_CLOSE_MSG) { hal_HstSendEvent(1, 0x04191515); rdabt_antenna_off(); } COS_SendEvent(MOD_APP, &ev , COS_WAIT_FOREVER, COS_EVENT_PRI_NORMAL);//在这里发到APP上层去了. } }
3.2.2.1 BTCPU从共享内存获取XCPU数据:
BOOL rdabt_get_bt_msg(COS_EVENT *event) { if(g_rdabt_sharemem.bt_head==g_rdabt_sharemem.bt_tail) return FALSE; else { *event = g_rdabt_sharemem.bt_event[g_rdabt_sharemem.bt_tail]; g_rdabt_sharemem.bt_event[g_rdabt_sharemem.bt_tail++].nEventId = 0; } if(g_rdabt_sharemem.bt_tail == RDABT_SHAREMEM_SIZE) g_rdabt_sharemem.bt_tail = 0; return TRUE; } 获取到数据后, 调用执行协议栈接口. u_int8 BThost_Sched(u_int32 num_interations) { u_int8 status = 0; COS_EVENT ev; //SYS_SendEvent2(0x2015a000); //EDRV_TRACE(EDRV_BTD_TRC, 0, "BThost_Sched"); #ifndef UART2_HCI do{ status = RDABT_Execute(1); }while(0); #endif if(rdabt_get_bt_msg(&ev)) { rdabt_handle_app_msg(&ev); //里面的接口就是直接执行BT协议栈的API了. status |= 1; } return status; } 综上, BT CPU并不是一个独立的CPU, 实际上应该是用的BT CPU进程. 这个进程可能是底层创建的僵尸进程. 所以和其他进程分离, 并且交互使用共享内存的方式. 以上全部都是个人理解, 如有错误, 请指正, 谢谢!