1.现象
如题,在调试多个52832高频率通信时,每隔一段时间会有载荷长度为0的 NRF_ESB_EVENT_RX_RECEIVED
事件发生,52832接收处理部分代码如下:
void rfEventHandler(nrf_esb_evt_t const * p_event)
{
uint8_t i;
switch (p_event->evt_id)
{
case NRF_ESB_EVENT_TX_SUCCESS:
SEGGER_RTT_printf(0,"TX SUCCESS EVENT\r\n");
break;
case NRF_ESB_EVENT_TX_FAILED:
(void) nrf_esb_flush_tx();
(void) nrf_esb_start_tx();
NVIC_SystemReset();
SEGGER_RTT_printf(0,"TX FAILED EVENT\r\n");
break;
case NRF_ESB_EVENT_RX_RECEIVED:
//SEGGER_RTT_printf(0,"RX RECEIVED EVENT\r\n");
if (nrf_esb_read_rx_payload(&rx_payload) == NRF_SUCCESS)
{
SEGGER_RTT_printf(0,"RX EVENT pipe:%d cnt:%d noack:%d pid:%d rssi:%d data:%s\r\n",rx_payload.pipe,rcvCnt++,rx_payload.noack,rx_payload.pid,rx_payload.rssi,rx_payload.data);
//通道检测,判断数据有效性,
switch(rx_payload.pipe)
{
case 0:
SEGGER_RTT_printf(0,"receive PIPE 0 data\r\n");
//0通道的直接转发,有效性由stm32来处理
break;
case 1:
case 2:
SEGGER_RTT_printf(0,"receive PIPE 1 or 2 data\r\n");
//传感器数据 数据有效性检查
if(sensorDataDispose() == 0)
{
return; //无效直接返回,不处理
}
break;
}
//转发有效数据到串口
for(i=0; i<rx_payload.length; i++)
{
rf_rx_buf[i]=rx_payload.data[i];
}
rf_rx_len=rx_payload.length;
for(i=0; i<rf_rx_len; i++)
{
app_uart_put(rf_rx_buf[i]);
}
rf_rx_len=0;
nrf_esb_flush_rx();
}
break;
}
}
发送模块每隔50ms发送50个字节
接收模块在j-link打印出异常日志如下:
0> RX EVENT pipe:0 cnt:5618 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5619 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5620 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5621 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5622 noack:1 pid:1 rssi:50 data:
0> RX EVENT pipe:0 cnt:5623 noack:1 pid:1 rssi:49 data:
0> RX EVENT pipe:0 cnt:5624 noack:1 pid:1 rssi:50 data:
0> RX EVENT pipe:0 cnt:5625 noack:1 pid:1 rssi:49 data:
0> RX EVENT pipe:0 cnt:5626 noack:1 pid:1 rssi:49 data:
0> RX EVENT pipe:0 cnt:5627 noack:1 pid:1 rssi:50 data:
0> RX EVENT pipe:0 cnt:5628 noack:1 pid:1 rssi:50 data:
0> RX EVENT pipe:0 cnt:5629 noack:1 pid:1 rssi:50 data:
0> RX EVENT pipe:0 cnt:5630 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5631 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5632 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
0>
0> RX EVENT pipe:0 cnt:5633 noack:1 pid:1 rssi:29 data:01234567890123456789012345678901234567890123456789
其中data:
后为空的即为上述字节长度为0的接收事件。
2.问题排查
2.1对比实验
取其中两个模块,作为一收一发,pipe0地址设置成与其他不同,测试通信发现没有上述问题。
增加一个模块,设置地址与前两个相同,三个模块上电,一发两收,问题复现,
再增加1到3个模块,1发收到,问题复现,且随着模块增加,问题出现频率上升。
初步确定是多模块通信出现问题,继续下一步排查。
2.2 查看代码
重新查看写的程序,发现在发送时将载荷成员变量设置为false,也就是需要ack,猜想是否多个模块回复ack导致的问题,于是把noack设置为true
原来的设置,直接看图
修改之后的设置:
tx_payload.noack = true;
发现现象依然存在,按道理说不应该呀,百思不得其解。
2.3 硬件干扰排查
同事建议考虑下硬件问题,是否为信号干扰。但是我想在两个模块能正常通信的前提下,可以排除硬件问题,随后为了以防万一,还为无线模块增加了铝箔屏蔽,漏出天线,如下图
编写简单串口转无线,无线转串口程序,进行试验,统计丢包率,排查是否为干扰。数据记录如下:
两个模块 每次50个字节 (都包裹屏蔽铝箔)
发送 200460
接收 200380
丢包率 0.03%
三个模块 每次50个字节 (其中有1个没有包裹屏蔽铝箔)
发送 221364
接收 221554
丢包率 不丢反而多出来了,多出来比率为 0.08%
四个模块 每次50个字节 (其中有2个没有包裹屏蔽铝箔)
发送 215332
接收 217653
丢包率 不丢反而多出来了,多出来比率为 1.07%
三个模块 每次50个字节 (都包裹屏蔽铝箔)
发送 218868
接收 219023
丢包率 不丢反而多出来了,多出来比率为 0.07%
想不通的是,大于2个模块居然会有接收数据包大于发送数据包的现象,想不通。。。
至此已耗时3天,领导已不太满意进度,让我实在不行先放放这个问题。
我感觉不搞明白,不太舒服,于是晚上加班继续查找原因。
由于应用层程序已经确认过多次,没有问题,于是查看底层SDK程序,梳理无线中断到应用层接收的流程与逻辑(具体流程下一篇博文写出)。
3.仿真跟踪
取一个模块不断发送,3个模块同时接收,然后在接收模块仿真跟踪接收程序,发现上边虽然更改了需要ack,但是接收端仍然在回复ack,
射频接收中断中,判断是否需要ack是由两个条件来判断的,
if ((m_config_local.selective_auto_ack == false) || ((m_rx_payload_buffer[1] & 0x01) == 1))
{
ack = true;
}
if (ack)
{
......
发ack
}
{
......
不发ac’
}
或条件之后((m_rx_payload_buffer[1] & 0x01) == 1))
中m_rx_payload_buffer[1] 是接收的发送时设置的noack
字段,我在发送时将其设置为true
,所以,没有问题。
而前者(m_config_local.selective_auto_ack == false)
中selective_auto_ack
字段在无线初始化时设置其为false会导致无论发送段noack
是否为真,接收端都会回复ack。也就是说我在发送端修改的noack
没有生效,所有通信仍然在自动ack。
于是在初始化时将selective_auto_ack
设置为true
,
/**************************************************************************************
函数功能: 无线发送初始化
入 参:
返回结果:
作 者:
时 间: 2021年2月26日10:22:15
**************************************************************************************/
uint32_t initRfTx( void )
{
uint32_t err_code;
// uint8_t base_addr_0[4] = {0xE7, 0xE7, 0xE7, 0xE7};
// uint8_t base_addr_1[4] = {0xC2, 0xC2, 0xC2, 0xC2};
// uint8_t addr_prefix[8] = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8 };
nrf_esb_config_t nrf_esb_config = NRF_ESB_DEFAULT_CONFIG;
nrf_esb_config.payload_length = 0x40; //更改为64字节
nrf_esb_config.protocol = NRF_ESB_PROTOCOL_ESB_DPL;
nrf_esb_config.retransmit_delay = 600;
nrf_esb_config.bitrate = NRF_ESB_BITRATE_2MBPS;
nrf_esb_config.event_handler = rfEventHandler;
nrf_esb_config.mode = NRF_ESB_MODE_PTX;
nrf_esb_config.selective_auto_ack = false;
err_code = nrf_esb_init(&nrf_esb_config);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_0(flashConfigData.obj.baseAddr0);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_1(flashConfigData.obj.baseAddr1);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_prefixes(flashConfigData.obj.addrPrefix, NRF_ESB_PIPE_COUNT);
VERIFY_SUCCESS(err_code);
return err_code;
}
继续利用之前编写的串口透传做实验,结果如下:
修改 nrf_esb_config.selective_auto_ack = true;后
四个模块 每次50个字节 (其中有3个包裹屏蔽铝箔)
发送 323804
接收 323804
没有丢包,完美
问题解决。
回到SDK中 对selective_auto_ack
注释,
bool selective_auto_ack; //!< Enable or disable selective auto acknowledgement. When this feature is disabled, all packets will be acknowledged ignoring the noack field.
我感觉翻译过来的意思是:使能或失能自动恢复,当失能的话,所有的数据包将回复noack字段 自动回复。
也就是说该字段设置为false
时,无论在发送端noack
真还是假,接收端都会自动回复!
开始在查看初始化程序时我就注意到了这个字段,但是依靠selective_auto_ack
的字面含义,我理解为只有设置为真,才会自动回复,所以造成了这个问题。哪能想到他是反逻辑,真是乌龙!!!
最后我将接受段的这个字段设置为true
,这样在接收端收到的数据默认不回复ack,只有发送端设置noack
为false
才会回复。
思考
看源码真有用!当遇到问题实在解决不了的话,就查看sdk源码,能够了解每一个字段的含义,说不定就像我一样找到了问题。