Solution: Multi-mode Mouse¶
重要
此例程仅存在于特殊版本的SDK中,如有需要请联系Panchip。
1.1 概述¶
本文主要介绍PAN1080 鼠标解决方案的设计,方案主体包含发送端与接收端两部分,其中接收端仅为rf模式服务,发送端可以根据实际使用选择BLE模式/2.4G私有模式/USB模式进行交互,其中:
BLE模式下仅蓝牙工作,交互对象可以是PC或其他支持蓝牙的设备。
2.4G私有模式仅private rf工作,接收端为usb dongle,接收端需设置为相同工作频点,rf接收数据并上报给usb发送。
USB模式仅SOC自带usb模块工作。
1.2 系统框图¶
1.2.1 软件工作流程:¶
其中组包模块包含一个1ms的定时器,用于定时采集sensor/qdec/kscan的实时数据并完成鼠标数据包组包操作,最后将数据发送给收发模块的发送端。
其中收发模块中BLE模式的发送端收到软件给出的数据,判别BLE连接状态,在连接时将数据发送给接收端,否则进行其他异常处理,BLE模式下接收端为pc或者支持蓝牙的设备。
其中收发模块中2.4G私有模式的发送端收到软件给出的数据并发送给dongle端,dongle端收到数据并回复ack数据包给发送端,dongle端跳频,发送端收到数据后跳频。若未回复,发送端进行重传,其中2.4G私有模式下接收端仅为usb dongle。
其中收发模块中USB模式的发送端收到软件给出的数据并在下一个usb sof开始发送,其中USB模式下接收端为pc或者其他host设备。
1.3 硬件环境¶
PAN1080 EVB板子两个,其中一个作为发送端,一个接收端(dongle)
P30_UART0_TX、P31_UART0_RX (uart0 log打印)
P46_SWD_CLK、P47_SWD_DAT (swd下载口)
USB:P02_USB_DM、P03_USB_DP,其中usb线对应关系如下
红线:电源正极(接线上的标识为:+5V或VCC)
白线:DM数据线(标识为:Data-或USB Port -)
绿线:DP数据线(标识为:Data+或USB Port +)
黑线:接地(标识为:GROUND或GND)
Secure CRT(串口打印窗口)
带BLE功能电脑
usb抓包工具,bushound 或者usbmonitor
上报率测试工具,MouseTest.exe(仅部分电脑可以达到上报率1KHz)
逻辑分析仪
1.4 软件环境¶
测试源文件目录
模拟发送端(EVB):..\BLE-Group\PAN1080\pan1080-dk-internal\01_SDK\zephyr\samples_panchip\solutions\model_mouse
鼠标:..\BLE-Group\PAN1080\pan1080-dk-internal\01_SDK\zephyr\samples_panchip\solutions\mouse_entity
模拟接收端(EVB):..\BLE-Group\PAN1080\pan1080-dk-internal\01_SDK\zephyr\samples_panchip\solutions\usb_dongle
Dongle:..\BLE-Group\PAN1080\pan1080-dk-internal\01_SDK\zephyr\samples_panchip\solutions\dongle_entity
1.5 软件功能点¶
功能点描述 |
工作模式 |
备注 |
---|---|---|
实体鼠标常规功能 |
rf-freq-hop mode |
此模式下sensor、key、qdec均支持,2.4G模式已支持,支持对码、跳频、重传。 |
实体dongle常规功能 |
rf-freq-hop mode |
此模式下2.4G模块和USB模块均打开,支持对码、跳频。 |
2.4G-ONLY上报率 |
rf-only in band mode |
此模式下仅2.4G模块打开,BLE模块和USB模块不工作,鼠标上报固定画圈数据 |
USB-ONLY上报率 |
usb-only mode |
此模式下仅USB模块打开,BLE模块和2.4G模块不工作,鼠标上报固定画圈数据 |
BLE-ONLY上报率 |
ble-only mode |
此模式下仅BLE模块打开,USB模块和2.4G模块不工作,鼠标上报固定画圈数据 |
2.4G-ONLY私有模式重传功能 |
rf-enhance in band mode |
此模式下仅2.4G模块打开,测试模式已支持(支持对码功能) |
2.4G-ONLY 私有模式跳频功能 |
rf-freq-hop in band mode |
此模式下仅2.4G模块打开,测试模式已支持(支持对码功能) |
电池电量检测 |
ble-usb mode或者rf-usb mode |
待支持 |
模式切换 |
ble-usb mode或者rf-usb mode |
待支持 |
RGB呼吸灯(扩展) |
暂不支持 |
待支持 |
1.6 性能指标¶
支持有线最大1KHz上报率,2.4G 1KHz,蓝牙130Hz。
通信距离(待定)
1.7 测试用例¶
1.7.1 编译命令¶
mouse端编译下载命令:
mouse entity rf-freq-hop mode:
脚本位置: .\sdk_quick_build_samples\solutions\mouse_entity.bat
说明: 此case下2.4G模式使能,rf lib使能,上电默认对码,跳频、重传功能默认使能,sensor、按键、滚轮功能已实现,固定1ms查询一次sensor、按键、滚轮是否有数据上报,测试时移动鼠标观测PC端鼠标是否正常移动、按键、滚轮是否正常。
mouse rf-only in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\model_mouse_prf_only.bat
说明: 此case下rf lib使能,仅rf工作发送数据,rf工作在带内,发送固定画圈数据。此case作为测试case固定画圈。
mouse usb-only mode:
脚本位置: .\sdk_quick_build_samples\solutions\model_mouse_usb_only.bat
说明: 此case下无lib使能,发送固定画圈数据。此case作为测试case固定画圈。
mouse ble-only mode:
脚本位置: .\sdk_quick_build_samples\solutions\model_mouse_ble_only.bat
说明: 此case下ble lib使能,发送固定画圈数据。此case作为测试case固定画圈。
mouse rf-enhance in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\model_mouse_prf_enhance.bat
说明: 此case下,rf使用enhance模式并在1ms内出现无应答时重传。此case作为测试case固定画圈。
mouse rf-freq-hop in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\model_mouse_prf_freq_hop.bat
说明: 此case下,rf使用enhance模式并在1ms内出现无应答时重传,1ms跳频一次。此case作为测试case固定画圈。
dongle端编译命令:
dongle entity rf-freq-hop mode:
脚本位置: .\sdk_quick_build_samples\solutions\dongle_entity.bat
说明: 此case下2.4G模式使能,rf lib使能,上电默认对码,跳频功能默认使能,与mouse entity rf-freq-hop mode配对使用,rf接收数据并通过usb上报给PC,显示鼠标操作。
mouse rf-only in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\usb_dongle_prf_retransmission.bat
说明: 此case下仅测试模式专用,rf工作接收数据,rf工作在带内,rf判断接收数据得第一byte是否于前一包数据的id一致,一致则不发送给usb。
mouse rf-enhance in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\usb_dongle_prf_enhance.bat
说明: 此case下rf收到数据后上报给usb端并给tx端发送ack包,不切换频点。
mouse rf-freq-hop in band mode:
脚本位置: .\sdk_quick_build_samples\solutions\usb_dongle_prf_freq_hop.bat
说明: 此case下rf收到数据后上报给usb端并给tx端发送ack包,同时切换频点。
1.7.2 测试case¶
1.7.2.1 实体鼠标基础功能及上报率¶
测试流程
执行鼠标端mouse entity rf-freq-hop mode编译命令,下载程序。
执行dongle端dongle entity rf-freq-hop mode编译命令,下载程序。
打开设备管理器,查看是否有新鼠标设备插入。
等待对码完成,移动鼠标或按下按键或滚动滚轮,查看PC上鼠标是否有操作。
打开bushoud工具及MouseTest.exe工具,移动鼠标,测试上报率。
保持移动,反复测试鼠标上报率,观测是否稳定。
测试结果
Report数据如下图所示:
Report1数据如下图所示:
循环测试,可保持稳定
1.7.2.2 rf-only带内测试模式上报率¶
测试流程
执行鼠标端mouse rf-only in band mode编译命令,下载程序。
执行dongle端mouse rf-only in band mode编译命令,下载程序。
发送端按住复位不释放,dongle端USB插入等待设备在pc设备管理器上显示。
dongle 端复位,释放发送端reset,这里主要是需要等待dongle端的usb完成初始化,避免被rf中断打断。
打开bushound或者Mouse Test工具,测试上报率情况。
测试tx发送两次数据需要的时间(扩展,需要修改代码,用逻辑分析仪抓取时间间隔)。
扩展测试代码添加
在..\modules\hal\panchip\panplat\pan1080\prf\src\comm_prf.c中,panchip_prf_init函数中打开注释代码
#if 1 // o_pp_phy_drv_ll_rx_phy_en PRI_RF_WRITE_REG_VALUE(PRI_RF, TEST_MUX02, TST_MUX_SELECT_09, 0x32); SYS->P2_MFP |= SYS_MFP_P21_LL_DBG09; // o_pp_phy_drv_ll_tx_phy_en PRI_RF_WRITE_REG_VALUE(PRI_RF, TEST_MUX02, TST_MUX_SELECT_10, 0x31); SYS->P0_MFP |= SYS_MFP_P04_LL_DBG10; // pp_acc_addr_match PRI_RF_WRITE_REG_VALUE(PRI_RF, TEST_MUX02, TST_MUX_SELECT_08, 0x33); SYS->P2_MFP |= SYS_MFP_P20_LL_DBG08; // i_phy_drv_ll_rx_clk PRI_RF_WRITE_REG_VALUE(PRI_RF, TEST_MUX03, TST_MUX_SELECT_12, 0x35); SYS->P2_MFP |= SYS_MFP_P26_LL_DBG12; // i_phy_drv_ll_rx_data PRI_RF_WRITE_REG_VALUE(PRI_RF, TEST_MUX03, TST_MUX_SELECT_13, 0x34); SYS->P1_MFP |= SYS_MFP_P16_LL_DBG13; #endif
编译后逻辑分析仪抓取时间间隔
测试结果
rf模式使用特定电脑上(实验室台式机)的Mouse Test工具观测上报率,最大上报率约为1KHz
两次发送的时间总耗时约为400us:
$$ 单次发送时间 = pre_tx + tx + post_tx $$
$$ 两次发送时间 = 单次发送时间 * 2 + software_delay(上图中两次高电平中的低电平时间,软件设置为160us,可缩短) $$
1.7.2.3 usb-only模式鼠标上报率¶
测试流程
执行鼠标端mouse usb-only mode编译命令,下载程序。
鼠标端断开usb,观察是否触发usb plug out中断。
鼠标端插入usb,观察是否触发usb plug in中断。
将GPIO P23杜邦线接地,先不让usb上报数据。
GPIO P23杜邦线接高电平,打开MouseTest.exe工具,测试usb模式下上报率。
多次循环执行usb插拔以测试能否正确识别USB。
将GPIO P23杜邦线接地,等待电脑灭屏。
在串口显示USB isr in: Suspend evt信息后,拉高P23,观察电脑是否亮屏并自动画圈。
多次执行步骤7,8,观察是否每次都能亮屏画圈。
测试结果
断开usb,鼠标不动作,触发plug out中断。
插入usb,触发plug in中断,最大上报率为1KHz。
循环插拔usb,可正确识别usb设备。
1.7.2.4 ble-only模式鼠标上报率¶
测试流程
执行鼠标端mouse ble-only mode编译命令,下载程序。
打开电脑“开始->设置->设备->蓝牙和其他设备->添加蓝牙和其他设备”,找寻设备名称为pan ble mouse的蓝牙设备,点击连接。或者打开nrfConnect app搜寻pan ble mouse设备,连接此设备,至配对成功。
打开MouseTest.exe工具,测试ble模式下上报率。
关闭PC蓝牙开关,后重新打开蓝牙,能自动重连
测试结果
Report数据如下图所示,最大上报率为135Hz:
反复开关蓝牙,可正确重连设备。
1.7.2.5 rf-enhance模式带重传跳频的上报率¶
测试流程
执行鼠标端mouse rf-freq-hop in band mode编译命令,下载程序。
执行dongle端mouse rf-freq-hop in band mode编译命令,下载程序。
发送端按住复位不释放,dongle 端复位,再释放发送端reset,这里主要是需要等待dongle端的usb完成初始化,避免被rf中断打断。
打开bushound或者Mouse Test工具测试上报率情况。
测试是否能在1ms内重传完成。
测试跳频机制是否正常。测试case中tx端每间隔1ms改变一次频点,如果在1ms内发送一次数据后未正常收到ack,那么此时频点不改变,同时再次发送与第一次发送的数据相同的包,收到ack后跳频。rx端在接收数据并发送完成数据后改变频率。
对码说明
滚码数据目前使用INFO区MAC数据,请使用PANLINK先行下载MAC地址, 否则滚码相同会导致多个测试板同时测试时通信冲突。
默认上电即可进行对码功能,使用流程无特别差异,鼠标如果未进行对码,则上电时处于发送对码请求状态。
清除对码在PAN1080EVB板上为Key2按键(P05),请确保跳线帽正常连接。
USB dongle无按键清除滚码功能,每次上电时1S时间内可以接收重新对码,USB dongle如需重新对码,确保待配对鼠标处于未对码状态,dongle重新上电即可重新进行对码流程。
测试结果
此模式为带重传跳频机制的上报,在mouse test工具上最大上报率约为1KHz
跳频重传功能正常:
上图中00为发送端的tx,01为发送端rx,02为接收端tx,03为接收端rx,从图中可见发送端的第三个波形在第一次发送时没有ack发出,rx在异常后,tx发送端在很短时间内紧接着又发送了一次数据(数据相同,因为未对发送数据作重新载入)并成功接收了数据,第二包数据发送时tx和rx的频点均未发生改变,及至下一次发送时频点才发生改变。
1.8 开发说明¶
本章节主要介绍PAN1080 实体鼠标方案的API功能,分为mouse发送端及dongle接收端分开介绍,下文中统一tx表示发送端,rx表示接收端,以下API接口按照实际执行先后顺序来介绍。
1.8.2 数据结构说明¶
sensor数据结构
struct sensor_data_t {
uint8_t motion; //上报标志,置位表示有上报
uint8_t observation; //绝对移动坐标
uint8_t x_delta_l; //x坐标低字节
uint8_t x_delta_h; //x坐标高字节
uint8_t y_delta_l; //y坐标低字节
uint8_t y_delta_h; //y坐标高字节
};
payload数据结构
struct pkt_detect_t {
int8_t key_value; //按键
int16_t x_value; //x坐标
int16_t y_value; //y坐标
int16_t roll_value; //滚轮
int8_t header; //头,用于判别鼠标或按键
int8_t sequence; //识别id,用于判别重传等信息
int16_t reserved; //保留
};
private radio 增强型帧结构
其中signal包括:
1.8.3 TX-配置时钟¶
在zephyr\samples_panchip\solutions\mouse_entity目录下添加“mouse_entity.overlay”用以修改时钟相关参数。
&dpll {
clock-mult = <3>; /* <3> means output 48MHz <4> means output 64MHz */
};
&rcc {
clock-frequency-system = <DT_FREQ_M(48)>;
ahb-prescaler = <1>; //ahb 不分频
apb1-prescaler = <2>; //apb1 2分频
apb2-prescaler = <2>; //apb2 2分频
};
并在zephyr\samples_panchip\solutions\CMakeLists.txt启用此配置
set(DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/mouse_entity.overlay")
1.8.4 TX-启用对码、跳频、重传功能¶
在zephyr\samples_panchip\solutions\mouse_entity目录下添加“overlay-rf_freq_hop_test.conf”用以修改跳频重传相关配置参数及RF模式,phy速率相关配置,并在编译时包含此配置文件。
# Enable Panchip Private RF or not
CONFIG_ENABLE_COMM_PRF=y
CONFIG_ENABLE_COMM_USB_PANCHIP=n
CONFIG_ENABLE_PANCHIP_PRF=y
#use mouse test flow
CONFIG_USE_MOUSE_RF_ONLY_TEST_FLOW=y
CONFIG_USE_MOUSE_USB_ONLY_TEST_FLOW=n
CONFIG_USE_MOUSE_BLE_ONLY_TEST_FLOW=n
#use private rf enhance mode
CONFIG_PRF_WORK_MODE_SEL_ENHANCE=y
#use private rf 297 mode
CONFIG_PRF_MODE_SEL_NORDIC=n
#use private rf extern channel
CONFIG_USE_PRF_OUT_OF_BAND=n
#private rf 2M phy select
CONFIG_SELECT_PHY_2M_MODE=y
#use private rf frequency hop function
CONFIG_USE_PRF_FREQUENCY_HOP=y
1.8.5 对码流程说明¶
鼠标对码工作流程
dongle对码工作流程
对码通信流程
1.8.6 TX-实体鼠标API接口¶
接口总览
void panchip_mouse_timer_handler(void); //1ms定时器查询是否有数据
static void panchip_report_pkt_detect(void); //数据组包逻辑
static void panchip_get_report_value(void); //prf组包发送逻辑
void event_tx_fun(void); //prf tx中断处理逻辑(重传)
void event_rx_fun(void); //ack 处理逻辑(同步、跳频)
void event_rx_timeout_fun(void); //超时处理(重传、异常处理)
void event_crc_err_fun(void); //错包处理(重传、异常处理)
panchip_mouse_timer_handler:hardware timer中断,检测上一包数据是否结束
panchip_report_pkt_detect 组包实现如下:
//识别ID,用于识别是否为重传包,是否需要过滤上报
mouse_report_data.sequence = (report_identify_flag << 4) | report_test_id;
if (report_identify_flag > 0xf) {
report_identify_flag = 0;
}
if (report_test_id > 0xf) {
report_test_id = 0;
}
//按键检测
panchip_key_get_value(&mouse_report_data.key_value);
//sensor检测
panchip_sensor_get_value(&mouse_report_data.x_value, &mouse_report_data.y_value);
//滚轮检测
panchip_qdec_get_value(&mouse_report_data.roll_value);
panchip_get_report_value prf组包发送实现如下:
//连续丢失6包后覆盖最老的一包数据
if (buf_pkt_cnt > 2) {
buf_pkt_cnt = 2;
/* Discard the first packet of data */
memcpy(tx_payload.data, tx_payload.data + data_cnt, data_cnt * 2);
memcpy(tx_payload.data + buf_pkt_cnt * data_cnt,
tmp_report_data->report_data, data_cnt);
} else { //当前包包含前一次丢失的数据
memcpy(tx_payload.data + buf_pkt_cnt * data_cnt,
tmp_report_data->report_data, data_cnt);
}
//检测是否重传中
if (!repeat_id) {
//检测是否为频点不匹配
if (no_ack_cnt >= 20) {
channel_sel++;
channel_sel = (channel_sel % channel_size); //轮询频点,寻找dongle匹配频点
ana_prf_ldo_en();
PHY_SetChConfig((channel_num[channel_sel] - 2402) / 2); //切换频点
no_ack_cnt = 0;
} else if (channel_change_num != 0xff) { //检测是否为dongle发起的切换频点
if (resend_cnt >= 3) { //以当前频点连续发送3次数据确保dongle端已经知道发送端已收到切频点通知
PHY_SetChConfig(channel_change_num); //切换频点
channel_change_num = 0xff;
resend_cnt = 0;
} else {
resend_cnt++;
}
}
}
//设置发送数据
panchip_prf_set_data(&tx_payload);
//启动prf发送
panchip_prf_trx_start();
//获取当前tick
prf_ready_time = sys_clock_tick_get_32();
void event_rx_fun(void) tx端ack 处理实现如下:
if (rx_payload.data_length) {
//获取同步标志
tmp_sync_flag = (uint8_t)rx_payload.data[0];
//获取频点
rec_channel_num = (uint8_t)rx_payload.data[4];
}
if (tmp_sync_flag == 0xa5) { //满足同步条件
sync_flag = tmp_sync_flag;
//获取usb sof起始至dongle rx的时间,单位为us
tx_rec_tmr_cnt = (rx_payload.data[3] << 16);
tx_rec_tmr_cnt |= (rx_payload.data[2] << 8);
tx_rec_tmr_cnt |= rx_payload.data[1];
//判断时间 + rx done至rx ready的时间是否超出1ms
if (tx_rec_tmr_cnt + RX_DONE_TO_RX_READY >= 1000) {
tx_rec_tmr_cnt = 1000 * 2 - tx_rec_tmr_cnt - RX_DONE_TO_RX_READY;
} else {
tx_rec_tmr_cnt = 1000 - tx_rec_tmr_cnt - RX_DONE_TO_RX_READY;
}
//判断剩余时间是否满足一次重传的时间
if (tx_rec_tmr_cnt >= 200) {
tx_rec_tmr_cnt = PAN_US_CNT * tx_rec_tmr_cnt;
//重新设置下一次timer中断来临的时间,用于与usb sof同步
TIMER_Reset(TIMER0);
TIMER_SetCmpValue(TIMER0, tx_rec_tmr_cnt);
TIMER_Start(TIMER0);
}
}
//判断收到的频点是否为频点列表中的频点
if ((rec_channel_num != 0xa5) && ((rec_channel_num == channel_num_rec[0])
|| (rec_channel_num == channel_num_rec[1])
|| (rec_channel_num == channel_num_rec[2])
|| (rec_channel_num == channel_num_rec[3]))) {
//获取下次切换的频点
channel_change_num = rec_channel_num;
}
void event_rx_timeout_fun(void) tx端超时处理实现如下:
//非重传timeout
if (!repeat_id) {
repeat_id++;
//计算当前时间与rf ready的时间差
if (cur_tick <= prf_ready_time) {
cur_tick = cur_tick + 0xffffff - prf_ready_time;
} else {
cur_tick -= prf_ready_time;
}
/* ready_to_tx_time + transfer fixed time */
ready_to_rx_time = PAN_PRF_READY_TO_TX_TIME + PAN_PRF_TRANS_FIXED_TIME;
/* payload time */
ready_to_rx_time += PAN_PRF_TRANS_PAYLOAD_TIME * (buf_pkt_cnt + 1);
/* tx_to_rx time + timeout + extra_time */
ready_to_rx_time += (PAN_PRF_TX_TO_RX_TIME + PAN_PRF_RX_TIMEOUT_TIME + PAN_PRF_TIMEOUT_EXTRA_TIME);
//判断rf ready时间至timeout的时间是否超出预期值,超出可能发生地址match错误,不要重传
if (cur_tick <= PAN_TICKS_PER_US * ready_to_rx_time) {
panchip_prf_trx_start();
}
} else {
//清除重传id,最多重传1次
repeat_id = 0;
//下一次发送需要带上当前发送的数据
buf_pkt_cnt++;
//无应答次数加1
no_ack_cnt++;
}
void event_crc_err_fun 错包处理实现如下:
//非重传timeout
if (!repeat_id) {
repeat_id++;
panchip_prf_trx_start();
} else {
//清除重传id,最多重传1次
repeat_id = 0;
//下一次发送需要带上当前发送的数据
buf_pkt_cnt++;
}
1.8.7 RX-启用对码、跳频、重传功能¶
在zephyr\samples_panchip\solutions\dongle_entity目录下添加“overlay-retrans_freq_hop_test.conf”用以修改跳频重传相关配置参数及RF模式,phy速率相关配置,并在编译时包含此配置文件。
#enable usb
CONFIG_USB_DONGLE_MOUSE=y
#use private rf 297 mode
CONFIG_PRF_MODE_SEL_NORDIC=n
#use private rf extern channel
CONFIG_USE_PRF_OUT_OF_BAND=n
#private rf phy select
CONFIG_SELECT_PHY_2M_MODE=y
#enable private radio lib
CONFIG_ENABLE_PANCHIP_PRF=y
CONFIG_USE_MOUSE_TEST_FLOW=y
#use private rf enhance mode
CONFIG_PRF_WORK_MODE_SEL_ENHANCE=y
#use private rf frequency hop function
CONFIG_USE_PRF_FREQUENCY_HOP=y
1.8.8 RX-实体鼠标API接口¶
接口总览
void panchip_usb_sof_handler(void); //usb sof中断处理
static void panchip_get_report_value(void); //usb数据发送逻辑
void event_tx_fun(void); //tx中断处理逻辑(跳频)
void event_rx_fun(void); //ack 处理逻辑(同步)
void put_sw_buff_q(uint8_t *dat); //缓存buffer填充
uint8_t get_sw_buff_q(uint32_t *buf_cnt); //缓存buffer下标获取
void rx_data_process(panchip_prf_payload_t *rx_payload) //数据处理
void event_rx_timeout_fun(void); //超时处理
void event_crc_err_fun(void); //错包处理
panchip_usb_sof_handler usb sof中断处理实现如下:
sync_cnt++;
//判断是否到时间同步,每1s同步一次
if (sync_cnt >= PAN_MS_CNT) {
sync_flag = 0xa5;
//判断每1s的收包数目是否少于990包
if (start_report_flag && (total_rec_pkt_1s < HOP_PKT_NUM_IN_ONE_SEC)) {
channel_change_preview++;
//连续3s内收包数少于990包触发跳频
if (channel_change_preview >= HOP_CONTINUES_SEC) {
channel_change_flag = true;
}
} else {
channel_change_preview = 0;
}
total_rec_pkt_1s = 0;
//获取当前timer时间
sync_remain_cnt = TIMER0->CNT;
}
//判断缓存buffer中是否有数据,有数据并且上一包数据usb已完成上报触发usb write操作
if (start_report_flag && usb_send_finish) {
if (!is_swbuff_empty()) {
k_sem_give(&sem_usb);
}
}
panchip_get_report_value rx获取数据实现如下:
//等待usb上报完成
while (!usb_send_finish) {
}
//屏蔽中断,确保此函数操作过程中不会被其他中断打断
k_spinlock_key_t key = k_spin_lock(&lock);
//获取缓存buffer的下标
ret_tmp = get_sw_buff_q(&buf_r_idx);
if (ret_tmp == 0) {
usb_send_finish = false;
usb_report_data[0] = sw_report_data[buf_r_idx + 0];
usb_report_data[1] = sw_report_data[buf_r_idx + 2];
usb_report_data[2] = sw_report_data[buf_r_idx + 4];
usb_report_data[3] = sw_report_data[buf_r_idx + 6];
//写usb数据
WRITE_REG(USB->INDEX, 1);
USB_Write((uint32_t)1, 4, (void *)(usb_report_data));
WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
}
//释放中断屏蔽
k_spin_unlock(&lock, key);
event_tx_fun rx端ack发送实现如下:
//是否需要跳频
if (channel_change_flag) {
//频点切换数据发送次数超过1次
if (channel_num_send_time > 1) {
channel_sel++;
channel_sel = (channel_sel % channel_size);
//切换频点
PHY_SetChConfig(tx_payload.data[4]);
//清除标志
channel_num_send_time = 0;
channel_change_flag = false;
channel_change_preview = 0;
/* start_report_flag = 0; */
}
}
//启动发送接收
panchip_prf_trx_start();
event_rx_fun rx端接收数据处理实现如下:
//设置ack数据
//判断是否需要同步
if (sync_flag == 0xa5) {
//获取当前时间
tmp_cnt = TIMER0->CNT;
//计算sof至rx的时间
if (tmp_cnt < sync_remain_cnt) {
tmp_cnt = tmp_cnt + 0xffffff - sync_remain_cnt;
} else {
tmp_cnt -= sync_remain_cnt;
}
/* (sys_apb1_clock / 1000000); */
//将同步信号及us时间发送给tx
tmp_cnt = tmp_cnt / PAN_US_CNT;
tx_payload.data[0] = sync_flag;
tx_payload.data[1] = tmp_cnt & 0xff;
tx_payload.data[2] = (tmp_cnt >> 8) & 0xff;
tx_payload.data[3] = (tmp_cnt >> 16) & 0xff;
//判断是否需要跳频
if (channel_change_flag) {
uint8_t tmp_ch = channel_sel;
tmp_ch++;
tmp_ch = (tmp_ch % channel_size);
//计算下一次的频点
tx_payload.data[4] = (channel_num[tmp_ch] - 2402) / 2;
}
sync_flag = 0;
sync_cnt = 0;
} else {
//识别id,区分同步信号
tx_payload.data[0]++;
if (tx_payload.data[0] >= 0xa0) {
tx_payload.data[0] = 0;
}
//区分频点信号
if (!channel_change_flag) {
tx_payload.data[4] = 0xa5;
}
}
//接收数据处理
//获取rf数据
panchip_prf_set_ack_data(&tx_payload);
//获取数据长度
rx_payload.data_length = panchip_prf_data_rec(&rx_payload);
//频点发送次数
if (channel_change_flag) {
channel_num_send_time++;
}
if (!rx_payload.data_length) {
LOG_WRN("no data rcvd");
} else {
//rx数据处理,识别重传重复数据,放入缓存buffer
rx_data_process(&rx_payload);
pkt_rec_flag = false;
//缓存5包数据后启动usb上报
if (!start_report_flag) {
if (sw_buf_w_cnt > CONFIG_MODULE_REPORT_SIZE * BUFFER_STORE_CNT) {
uint8_t start_report_data[4];
start_report_flag = 1;
start_report_data[0] = sw_report_data[0];
start_report_data[1] = sw_report_data[2];
start_report_data[2] = sw_report_data[4];
start_report_data[3] = sw_report_data[6];
WRITE_REG(USB->INDEX, 1);
USB_Write((uint32_t)1, 4, (void *)(start_report_data));
WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
usb_send_finish = true;
k_sem_give(&sem_usb);
}
}
last_rec_pkt_num = receive_pkt_num;
}
put_sw_buff_q:缓存buffer填充
get_sw_buff_q:获取缓存buffer下标
rx_data_process:接收数据处理实现如下:
//计算当前收到的数据中包含多少包数据
/* tx packet length is multiple of 11 */
receive_pkt_num = rx_payload->data_length / CONFIG_MODULE_REPORT_SIZE;
//将数据放入缓存变量
for (uint8_t i = 0; i < receive_pkt_num; i++) {
memcpy((void *)(transfer_report_data.report_data + i * CONFIG_MODULE_REPORT_SIZE),
(void *)(rx_payload->data + i * CONFIG_MODULE_REPORT_SIZE),
CONFIG_MODULE_REPORT_SIZE);
}
//识别ID,判断当前包是否已经存放过buffer
for (uint8_t i = 0; i < receive_pkt_num; i++) {
uint8_t cur_sequence = transfer_report_data.report_data[i * CONFIG_MODULE_REPORT_SIZE + 9] & 0xf;
already_stored = false;
for (uint8_t j = 0; j < last_rec_pkt_num; j++) {
if (report_test_id[j] == cur_sequence) {
already_stored = true;
break;
}
}
if (already_stored) {
//过滤
continue;
} else {
uint8_t cur_rec_id;
if (!pkt_rec_flag) {
//计算收包数
total_rec_pkt_1s++;
pkt_rec_flag = true;
}
//存放识别id
report_test_id[i] = cur_sequence;
//获取当前识别id
cur_rec_id = (transfer_report_data.report_data[i * CONFIG_MODULE_REPORT_SIZE + 9] & 0xf0) >> 4;
report_identify_flag[i] = cur_rec_id;
//将数据放入缓存buffer
put_sw_buff_q(transfer_report_data.report_data + i * CONFIG_MODULE_REPORT_SIZE);
data_ready = true;
if (start_report_flag) {
//释放usb上报线程信号量,触发上报
k_sem_give(&sem_usb);
}
}
}
event_rx_timeout_fun:重新启动收发
event_crc_err_fun:重新启动收发