前几天有朋友问, 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生成了许多子任务, 主要如下图所示:
串口中断服务是独立的.
- 通过上诉分析, 程序在处理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.
如上, 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;
event.nEventId = RDABT_SCO_DATA_RES;
event.nParam1 = packet->data;
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);
}
}
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;
#ifndef UART2_HCI
do{
status = RDABT_Execute(1);
}while(0);
#endif
if(rdabt_get_bt_msg(&ev))
{
rdabt_handle_app_msg(&ev);
status |= 1;
}
return status;
}
综上, BT CPU并不是一个独立的CPU, 实际上应该是用的BT CPU进程. 这个进程可能是底层创建的僵尸进程. 所以和其他进程分离, 并且交互使用共享内存的方式.
以上全部都是个人理解, 如有错误, 请指正, 谢谢!