在對協(xié)議棧在 Bluenrg2 芯片上采用 SPI 作為 HCI 的數(shù)據(jù)傳輸進行測試的時候,發(fā)現(xiàn)存在丟包問題。當進行大吞吐連續(xù)傳輸時,可以發(fā)現(xiàn)協(xié)議棧收到的字節(jié)數(shù)少于測試APP發(fā)送的字節(jié)數(shù)。
首先需要找到丟包的位置,有多個可能:①在HCI層傳輸上報給協(xié)議棧上層的過程丟包;②在HCI層與芯片進行SPI通信時丟包;③在芯片接收和上報的過程丟包(按說可能性不大)。
使用在每一層計數(shù)接收到的數(shù)據(jù)的字節(jié)數(shù),進行比較的方式確定產(chǎn)生丟包的位置。
1 在HCI層傳輸上報給協(xié)議棧上層的過程丟包
在 HCI 層的 SPI 初始化hci_driver_init()處也設置一個打印當前接收的字節(jié)數(shù)的定時任務:
HCI_SPI_ACL_recv_count = 0;
k_timer_init(&HCI_SPI_count_work, HCI_SPI_count_timeout, NULL);
k_timer_start(&HCI_SPI_count_work, K_SECONDS(30), K_SECONDS(30));
void HCI_SPI_count_timeout(struct k_timer *timer)
{
printf("HCI SPI ACL recv count timeout: %dn", HCI_SPI_ACL_recv_count);
}
在HCI層的接收函數(shù)hci_driver_init_loop()處,對ACL數(shù)據(jù)包進行判斷和計數(shù)。
switch(data[0])
{
case HCI_EVENT_PKT:
buf = bt_buf_get_controller_tx_evt();
break;
case HCI_ACLDATA_PKT:
buf = bt_buf_get_controller_tx_acl();
HCI_SPI_ACL_recv_count += ret;
// printk("ACL: ");
// for (int i = 0; i < ret; ++i)
// {
// printk("%02x:",data[i]);
// }
// printk("n");
break;
default:
return;
}
測試
手機連接BLE模塊,發(fā)送間隔設定為1ms,數(shù)據(jù)包大小20字節(jié),測試得到打印結果。比較發(fā)現(xiàn),協(xié)議棧上層的計數(shù)比HCI層接收處的數(shù)據(jù)計數(shù)更少,在HCI層傳輸上報給協(xié)議棧上層的過程有丟包。
檢查HCI層接收數(shù)據(jù)和上報的代碼,發(fā)現(xiàn)當數(shù)據(jù)傳輸量很大,MCU來不及處理時,協(xié)議棧上層的接收隊列會堆積最后爆滿,HCI 層申請 buffer 的時候可能失敗,此時本次從芯片處接收到的數(shù)據(jù)就會被丟棄。
解決方案是在開始一次 SPI 接收之前,判斷當前的緩沖區(qū)是否還有空間,有空間才接收。一般來說(事實上最后發(fā)現(xiàn)這個芯片好像并不是這樣的),芯片收到的數(shù)據(jù)如果一直沒有被HCI層接收,芯片端的緩沖區(qū)滿了之后,芯片會暫停數(shù)據(jù)傳輸服務。這樣可以使發(fā)送端的傳輸暫停,等待MCU完成處理后再繼續(xù)傳輸,避免丟包。
if (bt_buf_reserve_size(BT_BUF_ACL_IN) == 0)
{
printk("HCI ACL BUFFER EMPTY rn");
return;
}
int ret = HCI_TL_SPI_Receive(data, len); //ret: bytes num Recv
再次測試發(fā)現(xiàn),HCI層接收到的字節(jié)數(shù)和協(xié)議棧上層接收的字節(jié)數(shù)一致,但手機端發(fā)送的字節(jié)數(shù)和協(xié)議棧上層接收的字節(jié)數(shù)還是不一致,丟包還是存在。
2 在芯片接收和上報的過程丟包
在 SPI 的接收函數(shù)HCI_TL_SPI_Receive()處也加入一個計數(shù)HCI_SPI_ACL_recv_count,計數(shù)從芯片處接收到的全部數(shù)據(jù),包括包頭等;在HCI層將數(shù)據(jù)包塞入緩沖區(qū)之后,加入一個計數(shù)HCI_ACL_buf_recv_count,計數(shù)buffer緩沖區(qū)內(nèi)的數(shù)據(jù)字節(jié)數(shù)(不包括包頭)。
四個計數(shù)分別是:
HCI_SPI_recv_count_timeout: 從芯片處通過SPI接收到的全部數(shù)據(jù)包(包括包頭)
HCI SPI ACL recv count timeout: HCI層接收到的ACL數(shù)據(jù)包的數(shù)據(jù)(僅數(shù)據(jù),不包括包頭等)
HCI_ACL_buf_recv_count_timeout: HCI層寫入緩沖區(qū)后,緩沖區(qū)內(nèi)的data字段里的數(shù)據(jù)(僅數(shù)據(jù),不包括包頭等)
app_count: 協(xié)議棧上層接收到的數(shù)據(jù)(僅數(shù)據(jù),不包括包頭等)
因為藍牙啟動的過程中也有一系列數(shù)據(jù)交互,為了確保計數(shù)的準確性,加入一個開始計數(shù)的HCISPIFlag,HCISPIFlag為true時才開始計數(shù)。當HCI層接收到手機端發(fā)送的99:99:99:99數(shù)據(jù)包(該數(shù)據(jù)包不會上報給協(xié)議棧上層)時,HCISPIFlag轉(zhuǎn)為true。
ACL數(shù)據(jù)包的包頭為12字節(jié),例如測試數(shù)據(jù)包的內(nèi)容:
02:01:281b00:17:00:04:00:520b00:00:01:02:03:04:05:06:07:08:09:00:01:02:03:04:05:06:07:08:09
開始計數(shù)的命令判斷:
bool HCIcountCmdCheck(uint8_t *buf) {
uint8_t cmd[4] = {0x99, 0x99, 0x99, 0x99};
for (int i = 0; i < 4; ++i)
{
if (buf[i + 12] != cmd[i]) {
return false;
}
}
return true;
}
HCI層SPI接收處的計數(shù):
if(byte_count > 0)
{
/* avoid to read more data than the size of the buffer /
if (byte_count > size)
{
byte_count = size;
}
for(len = 0; len < byte_count; len++)
{
rt_spi_transfer(ble_spi, &char_00, (uint8_t )&read_char, 1);
buffer[len] = read_char;
}
HCI_SPI_recv_count += len;
// ACL pack received count
if (HCISPIFlag)
{
if (buffer[0] == HCI_ACLDATA_PKT) {
HCI_SPI_ACL_recv_count += (len - 12);
}
}
}
測試
手機連接BLE模塊,發(fā)送間隔設定為1ms,數(shù)據(jù)包大小20字節(jié),測試得到打印結果:
[00:26:45.218]收←◆Connected
[00:27:05.152]收←◆HCI_SPI_recv_count_timeout: 660
HCI SPI ACL recv count timeout: 0
HCI_ACL_buf_recv_count_timeout: 0
[00:27:05.302]收←◆app count timeout: 0
[00:27:08.899]收←◆HCI count start
app count start
[00:27:35.134]收←◆HCI_SPI_recv_count_timeout: 302404
HCI SPI ACL recv count timeout: 188580
HCI_ACL_buf_recv_count_timeout: 188580
[00:27:35.284]收←◆app count timeout: 189840
[00:28:05.116]收←◆HCI_SPI_recv_count_timeout: 720932
HCI SPI ACL recv count timeout: 450160
HCI_ACL_buf_recv_count_timeout: 450160
[00:28:05.267]收←◆app count timeout: 451560
[00:28:35.096]收←◆HCI_SPI_recv_count_timeout: 1143556
HCI SPI ACL recv count timeout: 714300
HCI_ACL_buf_recv_count_timeout: 714300
[00:28:35.246]收←◆app count timeout: 715480
[00:29:05.081]收←◆HCI_SPI_recv_count_timeout: 1579780
HCI SPI ACL recv count timeout: 986940
HCI_ACL_buf_recv_count_timeout: 986940
[00:29:05.231]收←◆app count timeout: 988100
[00:29:35.066]收←◆HCI_SPI_recv_count_timeout: 1726692
HCI SPI ACL recv count timeout: 1078760
HCI_ACL_buf_recv_count_timeout: 1078760
[00:29:35.215]收←◆app count timeout: 1078760
手機APP端:
手機端發(fā)送 55276個包,共1105504字節(jié),其中20字節(jié)的測試數(shù)據(jù)包 55275個,共1105500字節(jié)。
協(xié)議棧上層收到1078760字節(jié)數(shù)據(jù),即53938個數(shù)據(jù)包; HCI層接收到的 ACL 數(shù)據(jù)包的數(shù)據(jù)字節(jié)數(shù)和 HCI 層寫入緩沖區(qū)的data字段里的數(shù)據(jù)字節(jié)數(shù),與協(xié)議棧上層的一致(最終一致,中間定時器打印的count數(shù)不一致是因為緩沖區(qū)的數(shù)據(jù)還未被取出)。從芯片處通過SPI接收到的全部數(shù)據(jù)包為1726692字節(jié),其中660字節(jié)為啟動階段傳輸。
ACL 數(shù)據(jù)包的包頭為12字節(jié),發(fā)送的命令 ACL 包為16字節(jié),測試ACL數(shù)據(jù)包為32字節(jié)。則實際接收到的測試ACL數(shù)據(jù)包為(1726692 - 660 - 16) / 32 = 53938個,與協(xié)議棧上層的一致。
對比發(fā)現(xiàn),在芯片接收手機數(shù)據(jù)和上報的過程中發(fā)生了丟包。一般來說,芯片收到的數(shù)據(jù)如果一直沒有被HCI層接收,芯片端的緩沖區(qū)滿了之后,芯片會阻止發(fā)送端(手機)繼續(xù)發(fā)送數(shù)據(jù)。
-
接收機
+關注
關注
8文章
1177瀏覽量
53377 -
SPI接口
+關注
關注
0文章
258瀏覽量
34317 -
BLE技術
+關注
關注
0文章
28瀏覽量
5835 -
MCU芯片
+關注
關注
3文章
246瀏覽量
11355 -
RTThread
+關注
關注
7文章
132瀏覽量
40776
發(fā)布評論請先 登錄
相關推薦
評論