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

Solution: Multimode Keyboard Dongle

重要

此例程仅存在于特殊版本的SDK中,如有需要请联系Panchip。

1 功能概述

此sample为pan108xxb1(32pin芯片)在实体接收器板下的应用

具体支持的feature如下:

  • 2.4G模式(PRF增强型模式)

    1. 跳频:在信号质量不好(连续RX_LOST_PKT_LIMIT = 9ms收不到数据)/对码前在8个频点(每个频点RX_FREQ_HOP_MS=9ms)进行跳频

    2. 对码:上电跳频找到dongle端的频点后,通信互发对端的MAC地址后2字节,之后切换到私有地址进行通信

    3. 数据:数据跟随方案,对键盘数据进行解析上报

    4. 性能:配合键盘端性能

  • EMI测试

  • USB DFU升级

2 环境要求

  • board: pan108xxb1(芯片型号)接收器(带usb)/pan108xxb1(芯片型号)evb开发板(调试使用)

3 编译和烧录

例程位置:zephyr\samples_panchip\solutions\multimode_keyboard_dongle

使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍

4 演示说明

芯片全部擦除还原默认状态,准备好烧录multimode_keyboard的接收器

4.1 初始对码

  1. 初始对码时,初始键盘板(全擦除后烧录)先上电,长按FN+W,键盘绿色灯快速闪动时插入接收器,键盘由绿灯快闪变为绿灯常亮

  2. 之后键盘未强制对码时,接收器可以一直保持,键盘端重新上电可以与接收器通信

4.2 强制对码

键盘端进入强制对码时,需要重新插拔接收器,键盘端快速闪动时插入接收器,键盘由绿灯快闪变为绿灯常亮

5 开发说明

5.1 架构说明

multimode_keyboard_dongle基于zephyr架构,进行多线程编程,线程静态初始化后,根据优先级进行先后初始化,之后各个线程运行至while(1)等待相应的信号量,以此通过控制信号量控制各个线程的调度关系

架构中应用层主要包含

  • 4个线程

    • 跳频

    • 对码

    • RF发包

    • USB发包

  • 2种中断

    • USB中断

    • PRF中断

  • USB EP3接口(VENDOR DFU):通过USB中断进入进行DFU升级,EMI测试

5.2 线程说明

线程定义方式如下

/**
 * @brief Statically define and initialize a thread.
 *
 * The thread may be scheduled for immediate execution or a delayed start.
 *
 * Thread options are architecture-specific, and can include K_ESSENTIAL,
 * K_FP_REGS, and K_SSE_REGS. Multiple options may be specified by separating
 * them using "|" (the logical OR operator).
 *
 * The ID of the thread can be accessed using:
 *
 * @code extern const k_tid_t <name>; @endcode
 *
 * @param name Name of the thread.
 * @param stack_size Stack size in bytes.
 * @param entry Thread entry function.
 * @param p1 1st entry point parameter.
 * @param p2 2nd entry point parameter.
 * @param p3 3rd entry point parameter.
 * @param prio Thread priority.
 * @param options Thread options.
 * @param delay Scheduling delay (in milliseconds), zero for no delay.
 *
 *
 * @internal It has been observed that the x86 compiler by default aligns
 * these _static_thread_data structures to 32-byte boundaries, thereby
 * wasting space. To work around this, force a 4-byte alignment.
 *
 */
#define K_THREAD_DEFINE(name, stack_size,                                \
			entry, p1, p2, p3,                               \
			prio, options, delay)                            \
	K_THREAD_STACK_DEFINE(_k_thread_stack_##name, stack_size);	 \
	struct k_thread _k_thread_obj_##name;				 \
	STRUCT_SECTION_ITERABLE(_static_thread_data, _k_thread_data_##name) = \
		Z_THREAD_INITIALIZER(&_k_thread_obj_##name,		 \
				    _k_thread_stack_##name, stack_size,  \
				entry, p1, p2, p3, prio, options, delay, \
				NULL, name);				 	 \
	const k_tid_t name = (k_tid_t)&_k_thread_obj_##name

根据线程定义,定义了如下几个线程

5.2.1 FREQ_HOP线程

跳频线程,内部计算丢包数,达到阈值进行跳频,收到数据结束跳频

#define FREQ_HOP_THREAD_PRIORITY                2
#define FREQ_HOP_THREAD_STACKSIZE               1024

K_THREAD_DEFINE(freq_hop, FREQ_HOP_THREAD_STACKSIZE, thread_freq_hop, NULL, NULL, NULL,
		FREQ_HOP_THREAD_PRIORITY, 0, 0);

5.2.2 PRF_PAIR线程

配对线程,上电在公共地址和私有地址进行切换接收,公共地址收到对码数据后,切换为私有地址,私有地址收到数据后直接退出对码维持私有地址,不再切换地址

#define PAIR_THREAD_PRIORITY            2
#define PAIR_THREAD_STACKSIZE           1024

K_THREAD_DEFINE(pair, PAIR_THREAD_STACKSIZE, thread_pair, NULL, NULL, NULL,
		PAIR_THREAD_PRIORITY, 0, 0);

5.2.3 PRF线程

2.4G主线程,接收数据,判断数据seq并为USB准备数据

#define PRF_THREAD_PRIORITY             2
#define PRF_THREAD_STACKSIZE            1024

K_THREAD_DEFINE(prf, PRF_THREAD_STACKSIZE, thread_prf, NULL, NULL, NULL,
		PRF_THREAD_PRIORITY, 0, 0);

5.2.4 USB线程

USB线程,USB插入PC时进入,获取组包并且上报

#define USB_THREAD_PRIORITY                     1
#define USB_THREAD_STACKSIZE            		1024

K_THREAD_DEFINE(usb, USB_THREAD_STACKSIZE, thread_usb, NULL, NULL, NULL,
		USB_THREAD_PRIORITY, 0, 0);

5.3 RF中断说明

接收器为PRF RX端,增强型模式会在RX后自动转入TX, 中断中主要处理信号量sem_prf_isr的给出及再次开启rx,rx_lost_cnt的计数

5.4 主要数据结构说明

5.4.1 枚举状态

5.4.1.1 配对状态、地址类型
enum prf_pair_stat_t {
	prf_pair_default,
	prf_pair_start,
	prf_pair_comm,
	prf_pair_end,
};

enum pair_addr_type_t {
	prf_pair_public,
	prf_pair_private,
};
5.4.1.2 2.4G RF状态
enum prf_trx_stat_t {
	prf_idle_stat,
	prf_tx_done_stat,
	prf_rx_done_stat,
	prf_rx_timeout_stat,
	prf_rx_crc_err_stat,
	prf_rx_pid_err_stat,
};
5.4.1.3 跳频状态
enum prf_freq_hop_stat_t {
	freq_hop_disconnect_stat,
	freq_hop_done_stat,
};

5.4.2 全局结构

5.4.2.1 组包ring_buf(zephyr)
/**
 * @brief A structure to represent a ring buffer
 */
struct ring_buf {
	uint32_t head;	 /**< Index in buf for the head element */
	uint32_t tail;	 /**< Index in buf for the tail element */
	union ring_buf_misc {
		struct ring_buf_misc_item_mode {
			uint32_t dropped_put_count; /**< Running tally of the
						     * number of failed put
						     * attempts.
						     */
		} item_mode;
		struct ring_buf_misc_byte_mode {
			uint32_t tmp_tail;
			uint32_t tmp_head;
		} byte_mode;
	} misc;
	uint32_t size;   /**< Size of buf in 32-bit chunks */

	union ring_buf_buffer {
		uint32_t *buf32; /**< Memory region for stored entries */
		uint8_t *buf8;
	} buf;
	uint32_t mask;   /**< Modulo mask if size is a power of 2 */

	struct k_spinlock lock;
};
5.4.2.2 packet格式结构体
struct keyboard_pkt_detect_t {
	uint8_t key_event;
	uint8_t key_value[KEYBOARD_KEY_SIZE];
	uint8_t header;
	uint8_t sequence;
	uint8_t rate_pkt_index;
	uint8_t reserved;
};
5.4.2.3 配对信息结构体
struct pair_ctrl_t {
	enum prf_pair_stat_t prf_pair_stat;
	bool prf_pair_timeout;
	bool paired_flag;
	uint8_t pair_own_addr[2];
	uint8_t pair_peer_addr[2];
	uint8_t pair_cnt;
	bool pair_saved_flag;
	enum pair_addr_type_t pair_addr_type;
};

6 RAM/Flash资源使用情况

Memory region         Used Size  Region Size  %age Used
FLASH:       58440 B       256 KB     22.29%
SRAM:       29192 B        50 KB     57.02%