当前文档版本为 v0.4.1,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

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 系统框图

image

鼠标方案系统框图

1.2.1 软件工作流程:

image

鼠标方案软件流程图

  • 其中组包模块包含一个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 硬件环境

  1. PAN1080 EVB板子两个,其中一个作为发送端,一个接收端(dongle)

    • P30_UART0_TX、P31_UART0_RX (uart0 log打印)

    • P46_SWD_CLK、P47_SWD_DAT (swd下载口)

  2. USB:P02_USB_DM、P03_USB_DP,其中usb线对应关系如下

    • 红线:电源正极(接线上的标识为:+5V或VCC)

    • 白线:DM数据线(标识为:Data-或USB Port -)

    • 绿线:DP数据线(标识为:Data+或USB Port +)

    • 黑线:接地(标识为:GROUND或GND)

  3. Secure CRT(串口打印窗口)

  4. 带BLE功能电脑

  5. usb抓包工具,bushound 或者usbmonitor

  6. 上报率测试工具,MouseTest.exe(仅部分电脑可以达到上报率1KHz)

  7. 逻辑分析仪

1.4 软件环境

  1. 测试源文件目录

    • 模拟发送端(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 性能指标

  1. 支持有线最大1KHz上报率,2.4G 1KHz,蓝牙130Hz。

  2. 通信距离(待定)

1.7 测试用例

1.7.1 编译命令

mouse端编译下载命令

  1. mouse entity rf-freq-hop mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\mouse_entity.bat
    

    说明: 此case下2.4G模式使能,rf lib使能,上电默认对码,跳频、重传功能默认使能,sensor、按键、滚轮功能已实现,固定1ms查询一次sensor、按键、滚轮是否有数据上报,测试时移动鼠标观测PC端鼠标是否正常移动、按键、滚轮是否正常。

  2. mouse rf-only in band mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\model_mouse_prf_only.bat
    

    说明: 此case下rf lib使能,仅rf工作发送数据,rf工作在带内,发送固定画圈数据。此case作为测试case固定画圈。

  3. mouse usb-only mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\model_mouse_usb_only.bat
    

    说明: 此case下无lib使能,发送固定画圈数据。此case作为测试case固定画圈。

  4. mouse ble-only mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\model_mouse_ble_only.bat
    

    说明: 此case下ble lib使能,发送固定画圈数据。此case作为测试case固定画圈。

  5. mouse rf-enhance in band mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\model_mouse_prf_enhance.bat
    

    说明: 此case下,rf使用enhance模式并在1ms内出现无应答时重传。此case作为测试case固定画圈。

  6. 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端编译命令:

  1. 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,显示鼠标操作。

  2. mouse rf-only in band mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\usb_dongle_prf_retransmission.bat
    

    说明: 此case下仅测试模式专用,rf工作接收数据,rf工作在带内,rf判断接收数据得第一byte是否于前一包数据的id一致,一致则不发送给usb。

  3. mouse rf-enhance in band mode:

    脚本位置:
    .\sdk_quick_build_samples\solutions\usb_dongle_prf_enhance.bat
    

    说明: 此case下rf收到数据后上报给usb端并给tx端发送ack包,不切换频点。

  4. 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 实体鼠标基础功能及上报率

测试流程

  1. 执行鼠标端mouse entity rf-freq-hop mode编译命令,下载程序。

  2. 执行dongle端dongle entity rf-freq-hop mode编译命令,下载程序。

  3. 打开设备管理器,查看是否有新鼠标设备插入。

  4. 等待对码完成,移动鼠标或按下按键或滚动滚轮,查看PC上鼠标是否有操作。

  5. 打开bushoud工具及MouseTest.exe工具,移动鼠标,测试上报率。

  6. 保持移动,反复测试鼠标上报率,观测是否稳定。

测试结果

  1. Report数据如下图所示:

    image

    Bushoud Report 数据包

  2. Report1数据如下图所示:

    image

    MouseTest上报率

  3. 循环测试,可保持稳定

1.7.2.2 rf-only带内测试模式上报率

测试流程

  1. 执行鼠标端mouse rf-only in band mode编译命令,下载程序。

  2. 执行dongle端mouse rf-only in band mode编译命令,下载程序。

  3. 发送端按住复位不释放,dongle端USB插入等待设备在pc设备管理器上显示。

  4. dongle 端复位,释放发送端reset,这里主要是需要等待dongle端的usb完成初始化,避免被rf中断打断。

  5. 打开bushound或者Mouse Test工具,测试上报率情况。

  6. 测试tx发送两次数据需要的时间(扩展,需要修改代码,用逻辑分析仪抓取时间间隔)。

扩展测试代码添加

  1. 在..\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
    
  2. 编译后逻辑分析仪抓取时间间隔

测试结果

  1. rf模式使用特定电脑上(实验室台式机)的Mouse Test工具观测上报率,最大上报率约为1KHz

  2. 两次发送的时间总耗时约为400us:

    image

    RF Only模式下2.4G上报率

$$ 单次发送时间 = pre_tx + tx + post_tx $$

$$ 两次发送时间 = 单次发送时间 * 2 + software_delay(上图中两次高电平中的低电平时间,软件设置为160us,可缩短) $$

1.7.2.3 usb-only模式鼠标上报率

测试流程

  1. 执行鼠标端mouse usb-only mode编译命令,下载程序。

  2. 鼠标端断开usb,观察是否触发usb plug out中断。

  3. 鼠标端插入usb,观察是否触发usb plug in中断。

  4. 将GPIO P23杜邦线接地,先不让usb上报数据。

  5. GPIO P23杜邦线接高电平,打开MouseTest.exe工具,测试usb模式下上报率。

  6. 多次循环执行usb插拔以测试能否正确识别USB。

  7. 将GPIO P23杜邦线接地,等待电脑灭屏。

  8. 在串口显示USB isr in: Suspend evt信息后,拉高P23,观察电脑是否亮屏并自动画圈。

  9. 多次执行步骤7,8,观察是否每次都能亮屏画圈。

测试结果

  1. 断开usb,鼠标不动作,触发plug out中断。

  2. 插入usb,触发plug in中断,最大上报率为1KHz。

  3. 循环插拔usb,可正确识别usb设备。

1.7.2.4 ble-only模式鼠标上报率

测试流程

  1. 执行鼠标端mouse ble-only mode编译命令,下载程序。

  2. 打开电脑“开始->设置->设备->蓝牙和其他设备->添加蓝牙和其他设备”,找寻设备名称为pan ble mouse的蓝牙设备,点击连接。或者打开nrfConnect app搜寻pan ble mouse设备,连接此设备,至配对成功。

  3. 打开MouseTest.exe工具,测试ble模式下上报率。

  4. 关闭PC蓝牙开关,后重新打开蓝牙,能自动重连

测试结果

  1. Report数据如下图所示,最大上报率为135Hz:

    image

    BLE Only模式下上报率

  2. 反复开关蓝牙,可正确重连设备。

1.7.2.5 rf-enhance模式带重传跳频的上报率

测试流程

  1. 执行鼠标端mouse rf-freq-hop in band mode编译命令,下载程序。

  2. 执行dongle端mouse rf-freq-hop in band mode编译命令,下载程序。

  3. 发送端按住复位不释放,dongle 端复位,再释放发送端reset,这里主要是需要等待dongle端的usb完成初始化,避免被rf中断打断。

  4. 打开bushound或者Mouse Test工具测试上报率情况。

  5. 测试是否能在1ms内重传完成。

  6. 测试跳频机制是否正常。测试case中tx端每间隔1ms改变一次频点,如果在1ms内发送一次数据后未正常收到ack,那么此时频点不改变,同时再次发送与第一次发送的数据相同的包,收到ack后跳频。rx端在接收数据并发送完成数据后改变频率。

对码说明

  1. 滚码数据目前使用INFO区MAC数据,请使用PANLINK先行下载MAC地址, 否则滚码相同会导致多个测试板同时测试时通信冲突。

  2. 默认上电即可进行对码功能,使用流程无特别差异,鼠标如果未进行对码,则上电时处于发送对码请求状态。

  3. 清除对码在PAN1080EVB板上为Key2按键(P05),请确保跳线帽正常连接。

  4. USB dongle无按键清除滚码功能,每次上电时1S时间内可以接收重新对码,USB dongle如需重新对码,确保待配对鼠标处于未对码状态,dongle重新上电即可重新进行对码流程。

测试结果

  1. 此模式为带重传跳频机制的上报,在mouse test工具上最大上报率约为1KHz

  2. 跳频重传功能正常:

    image

    RF增强型模式下2.4G上报率

上图中00为发送端的tx,01为发送端rx,02为接收端tx,03为接收端rx,从图中可见发送端的第三个波形在第一次发送时没有ack发出,rx在异常后,tx发送端在很短时间内紧接着又发送了一次数据(数据相同,因为未对发送数据作重新载入)并成功接收了数据,第二包数据发送时tx和rx的频点均未发生改变,及至下一次发送时频点才发生改变。

1.8 开发说明

本章节主要介绍PAN1080 实体鼠标方案的API功能,分为mouse发送端及dongle接收端分开介绍,下文中统一tx表示发送端,rx表示接收端,以下API接口按照实际执行先后顺序来介绍。

1.8.1 TRX-流程说明

鼠标及dongle实现流程如下:

image

鼠标实现流程图

image

dongle实现流程图

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 增强型帧结构

image

帧结构

其中signal包括:

image

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 对码流程说明

  • 鼠标对码工作流程

    mouse_pair

    鼠标对码流程图

  • dongle对码工作流程

    dongle_pair

    Dongle 对码工作流程

  • 对码通信流程

    pair_transaction

    对码通信交互流程图

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:重新启动收发