Funpack4-1 基于CY8CKIT-149开发板实现的蓝牙触控定制键盘设计
该项目使用了CY8CKIT-149,实现了基于蓝牙的触控定制键盘设计的设计,它的主要功能为:使用CY8CKIT-149开发板片上两款单片机,组合完成触摸动作采集,并映射至定制化键值,最后通过蓝牙模组将键值发送至电脑.。
标签
Funpack活动
BLE HID
CY8CKIT-149
蓝牙定制键盘
EZ-BLE
bigjiong
更新2025-04-09
38

一、项目描述

1.1 项目介绍

本项目基于CY8CKIT-149 PSoC 4200M开发板,设计并实现了一款具有蓝牙功能的触控定制键盘。该键盘通过电容式触控技术实现按键输入,利用板载蓝牙模块实现无线数据传输,可自定义按键布局和功能映射,适用于特殊应用场景或个性化输入需求。

传统机械键盘存在体积大、噪音高、易损坏等问题,而基于PSoC的电容触控键盘具有轻薄、静音、耐用等优势。结合蓝牙无线传输技术,本设计实现了更加灵活的人机交互方式,可广泛应用于智能家居控制、工业HMI界面、游戏外设等领域。

1.2 设计思路

本项目的核心设计思路包括:

  1. 电容触控检测:利用PSoC芯片内置的CapSense模块检测用户触摸输入
  2. 蓝牙无线传输:通过板载蓝牙模块将按键信息无线传输至接收设备
  3. 功能可定制化:可通过修改源代码的方式,改变按键对应关系
  4. 低功耗设计:利用PSoC的低功耗特性,实现休眠唤醒机制延长续航

系统采用分层架构设计,硬件层负责信号采集,驱动层处理底层通信,应用层实现业务逻辑和用户界面,各层之间通过清晰接口耦合,提高了系统的可维护性和扩展性。

信息用到了两个单片机

一个单片机作为触摸识别控制器,识别触摸手势,并通过IIc slave接口被动向蓝牙模块iic master发送触摸动作数据

此处参考了两个主要示例工程

CE224820_PSoC4_CapSense_Slider_Gestures以及CE210709_CapSense_Linear_Slider_and_Buttons

主体工程以CE224820_PSoC4_CapSense_Slider_Gestures为基础,统合了CE210709_CapSense_Linear_Slider_and_Buttons工程中有关触摸按键检测的相关代码.


ez-ble模块代码基于BLE_HID_Keyboard代码改动而来

通过三个IIC字节数据传送触摸信息


第0字节数据开机为0,表示无动作,滑条每滑动一次,此字节数据加一;蓝牙模组端通过第0字节数据的变化可得知有滑条滑动事件发生.


第一字节,对应了三个触摸按键中哪个被最后按下,以区分滑条滑动最终对应的控制项.


第二字节对应的是滑条左右滑动的方向,与第一字节一起组合生成最终的按键键值.

企业微信截图_17436848308780.png

二、硬件介绍

2.1 主要硬件组件

  1. CY8CKIT-149开发板
    • PSoC 4200M微控制器(48MHz ARM Cortex-M0核心)
    • 32KB Flash, 4KB SRAM
    • 集成CapSense电容感应模块
    • 板载BLE蓝牙模块(CYBLE-012001-00)
  2. 外围电路
    • 开发板板载触控面板(3个按键,一个滑条)
    • 状态指示灯LED

2.2 硬件连接示意图

企业微信截图_17436838808167.png

三、软件设计

3.1 系统流程图

触控流程图

企业微信截图_17436862468442.png

蓝牙流程图

企业微信截图_17436865198089.png

3.2 主要代码片段及说明

3.2.1 触摸按键检测及led控制代码


void LED_Control()
{
if (CapSense_IsWidgetActive(CapSense_BTN0_WDGT_ID))
{
i2cBuffer[1u] = 0;
}

if (CapSense_IsWidgetActive(CapSense_BTN1_WDGT_ID))
{
i2cBuffer[1u] = 1;
}

if (CapSense_IsWidgetActive(CapSense_BTN2_WDGT_ID))
{
i2cBuffer[1u] = 2;
}

switch (i2cBuffer[1u])
//switch (i2cBuffer[0u] % 3)
{
case 0:
LED_11_Write( LED_ON );
LED_12_Write( LED_OFF );
LED_13_Write( LED_OFF );
break;

case 1:
LED_11_Write( LED_OFF );
LED_12_Write( LED_ON );
LED_13_Write( LED_OFF );
break;

case 2:
LED_11_Write( LED_OFF );
LED_12_Write( LED_OFF );
LED_13_Write( LED_ON );
break;

default:
i2cBuffer[1u] = 0;
break;
}

if (i2cBuffer[2u] == 0)
{
LED_7_Write( LED_ON );
LED_8_Write( LED_OFF );
}
else
{
LED_8_Write( LED_ON );
LED_7_Write( LED_OFF );
}

CapSense_Sleep();
}

这段代码检测了最后触摸的按键实体,并将状态保持在最后触摸的状态,直到有新的不同的按键被触摸.

3.2.2 触控调滑动检测代码


            /* Process data */
CapSense_ProcessAllWidgets();

/* Updates the selected timestamp */
timeStampUpdate();

/* Stores the current detected gesture */
detectedGesture = CapSense_DecodeWidgetGestures(CapSense_LINEARSLIDER0_WDGT_ID);

/* Turns a specific LED on or off depending on the gesture */
switch(detectedGesture){
case CapSense_ONE_FINGER_FLICK_RIGHT:
/* If LED is on turn it off, or if off turn it on */
Right_LED_Write((Right_LED_Read() == LED_ON) ? LED_OFF: LED_ON);

i2cBuffer[2u] = 0;

i2cBuffer[0u] ++;
break;
case CapSense_ONE_FINGER_SINGLE_CLICK:
/* If LED is on turn it off, or if off turn it on */
PWM_WriteCompare((PWM_ReadCompare() != 0) ? 0: HALF_POWER);
break;
case CapSense_ONE_FINGER_FLICK_LEFT:
/* If LED is on turn it off, or if off turn it on */
Left_LED_Write((Left_LED_Read() == LED_ON) ? LED_OFF: LED_ON);

i2cBuffer[2u] = 1;

i2cBuffer[0u] ++;
break;
}

这段代码检测了触控条的左右滑动,并相应的翻转了对应的LED的电平,且同步更新IIC寄存器数值。

3.2.3 BLE IIC数据采集及按键发送

*******************************************************************************/int main()
{
CyGlobalIntEnable;

uint8 last_key_state;
uint8 key_state;

uint8 send_flag = 0;

#if (DEBUG_UART_ENABLED == ENABLED)
UART_DEB_Start();
#endif /* (DEBUG_UART_ENABLED == ENABLED) */
DBG_PRINTF("BLE HID Keyboard Example Project \r\n");

Disconnect_LED_Write(LED_OFF);
Advertising_LED_Write(LED_OFF);
CapsLock_LED_Write(LED_OFF);

/* Start CYBLE component and register generic event handler */
CyBle_Start(AppCallBack);
WDT_Start();

/* Begin I2C master component operation */
I2CHW_Start();

#if (BAS_MEASURE_ENABLE != 0)
ADC_Start();
#endif /* BAS_MEASURE_ENABLE != 0 */

while(1)
{
/* CyBle_ProcessEvents() allows BLE stack to process pending events */
CyBle_ProcessEvents();

/* To achieve low power in the device */
LowPowerImplementation();

/*
if (User_Key_Read())
{
User_Led_Write(LED_ON);
}
else
{
User_Led_Write(LED_OFF);
}
*/

/* Read entire data buffer from the slave device */
I2CHW_I2CMasterReadBuf(I2C_SLAVE_ADDRESS, i2cBuffer, I2C_BUF_SIZE,
I2CHW_I2C_MODE_COMPLETE_XFER);
while (0u == (I2CHW_I2CMasterStatus() & I2CHW_I2C_MSTAT_RD_CMPLT))
{
CyBle_ProcessEvents();
}

//key_state = User_Key_Read();

/*
key_state = i2cBuffer[1u];
if (last_key_state && key_state == 0)
*/

key_state = i2cBuffer[0u];
if (last_key_state != key_state)
{
User_Led_Write( User_Led_Read() ? LED_ON : LED_OFF);

send_flag = 1;
}
last_key_state = key_state;

if((CyBle_GetState() == CYBLE_STATE_CONNECTED) && (suspend != CYBLE_HIDS_CP_SUSPEND))
{
if(mainTimer != 0u)
{
mainTimer = 0u;
#if (BAS_SIMULATE_ENABLE != 0)
SimulateBattery();
CyBle_ProcessEvents();
#endif /* BAS_SIMULATE_ENABLE != 0 */
#if (BAS_MEASURE_ENABLE != 0)
MeasureBattery();
CyBle_ProcessEvents();
#endif /* BAS_MEASURE_ENABLE != 0 */
if(keyboardSimulation == ENABLED && send_flag)
{
//SimulateKeyboard();
send_flag = 0;

uint8 ble_key = i2cBuffer[1] * 2 + i2cBuffer[2];
SendKeyboard(ble_key);
}
}
/* Store bonding data to flash only when all debug information has been sent */
#if(CYBLE_BONDING_REQUIREMENT == CYBLE_BONDING_YES)
#if (DEBUG_UART_ENABLED == ENABLED)
if((cyBle_pendingFlashWrite != 0u) &&
((UART_DEB_SpiUartGetTxBufferSize() + UART_DEB_GET_TX_FIFO_SR_VALID) == 0u))

#else
if(cyBle_pendingFlashWrite != 0u)
#endif /* (DEBUG_UART_ENABLED == ENABLED) */
{
CYBLE_API_RESULT_T apiResult;

apiResult = CyBle_StoreBondingData(0u);
(void)apiResult;
DBG_PRINTF("Store bonding data, status: %x \r\n", apiResult);
}
#endif /* CYBLE_BONDING_REQUIREMENT == CYBLE_BONDING_YES */
}
}
}

这段代码包含了IIC寄存器数据读取,以及是否有按键按下指令的判断,外加按键发送函数的调用

3.2.4 实际按键发送函数


#define KEY_SYSRQ 0x46 // Keyboard Print Screen
#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock
#define KEY_PAUSE 0x48 // Keyboard Pause
#define KEY_INSERT 0x49 // Keyboard Insert
#define KEY_HOME 0x4a // Keyboard Home
#define KEY_PAGEUP 0x4b // Keyboard Page Up
#define KEY_DELETE 0x4c // Keyboard Delete Forward
#define KEY_END 0x4d // Keyboard End
#define KEY_PAGEDOWN 0x4e // Keyboard Page Down
#define KEY_RIGHT 0x4f // Keyboard Right Arrow
#define KEY_LEFT 0x50 // Keyboard Left Arrow
#define KEY_DOWN 0x51 // Keyboard Down Arrow
#define KEY_UP 0x52 // Keyboard Up Arrow

const uint8 keyList[] = {KEY_PAGEUP, KEY_PAGEDOWN, KEY_HOME, KEY_END, KEY_UP, KEY_DOWN};

void SendKeyboard(uint8 send_key)
{
static uint8 keyboard_data[KEYBOARD_DATA_SIZE]={0,0,0,0,0,0,0,0};
CYBLE_API_RESULT_T apiResult;
static uint8 simKey;
uint8 i;

if(CyBle_GattGetBusyStatus() == CYBLE_STACK_STATE_FREE)
{
//simKey = SIM_KEY_MIN + send_key;
if(send_key > 5)
{
send_key = 5;
}
simKey = keyList[send_key];
keyboard_data[2u] = simKey;

apiResult = CyBle_HidssGetCharacteristicValue(CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HIDS_PROTOCOL_MODE, sizeof(protocol), &protocol);
if(apiResult == CYBLE_ERROR_OK)
{
DBG_PRINTF("HID notification: ");
for(i = 0; i < KEYBOARD_DATA_SIZE; i++)
{
DBG_PRINTF("%2.2x,", keyboard_data[i]);
}
DBG_PRINTF("\r\n");

if(protocol == CYBLE_HIDS_PROTOCOL_MODE_BOOT)
{
apiResult = CyBle_HidssSendNotification(cyBle_connHandle, CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HIDS_BOOT_KYBRD_IN_REP, KEYBOARD_DATA_SIZE, keyboard_data);
}
else
{
apiResult = CyBle_HidssSendNotification(cyBle_connHandle, CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HUMAN_INTERFACE_DEVICE_REPORT_IN, KEYBOARD_DATA_SIZE, keyboard_data);
}

if(apiResult == CYBLE_ERROR_OK)
{
keyboard_data[2u] = 0u; /* Set up keyboard data*/
if(protocol == CYBLE_HIDS_PROTOCOL_MODE_BOOT)
{
apiResult = CyBle_HidssSendNotification(cyBle_connHandle, CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HIDS_BOOT_KYBRD_IN_REP, KEYBOARD_DATA_SIZE, keyboard_data);
}
else
{
apiResult = CyBle_HidssSendNotification(cyBle_connHandle, CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HUMAN_INTERFACE_DEVICE_REPORT_IN, KEYBOARD_DATA_SIZE, keyboard_data);
}
}
if(apiResult != CYBLE_ERROR_OK)
{
DBG_PRINTF("HID notification API Error: %x \r\n", apiResult);
keyboardSimulation = DISABLED;
}
}
}
}

这段代码用数组存放了实际的键值,通过数组索引获取实际需要发送的按键,并通过蓝牙协议接口进行发送.

四、功能展示

4.1 触控键盘外观

按键1

8ba8c6b8-942a-4b15-8fa9-1ffa2b30db38.jpg

按键2

39b4c195-550d-4de1-886e-31b0f9e06826.jpg

按键3

28bbc841-9e9f-42d7-bd9e-42393c0359ce.jpg

右滑

4646a8a6-200f-4964-8d27-81a53cc1efad.jpg

左滑

92734a3b-ca73-41b1-b5a3-d02434becec9.jpg

4.2 蓝牙连接状态

企业微信截图_17436878427421.png

4.3 按键测试演示

翻页控制

企业微信截图_1743688287253.png

home/end控制

企业微信截图_17436883007640.png

up/down控制

企业微信截图_17436883225269.png

五、项目难题与解决方案

5.1 蓝牙模组IIC数据获取及判断逻辑

问题描述:初期采用读寄存器与写寄存器结合方式实现两个单片机之间的状态同步,后来发现此方案不太行得通

解决方案

  1. 采用i2cBuffer[0u]自增的方式通知蓝牙模组触摸动作的发生,此方案可实现数据单向流动,避免了冲突的发生

六、心得体会

6.1 项目收获

通过本项目,我获得了以下宝贵经验:

  1. PSoC开发技能:掌握了PSoC Creator开发环境和PSoC芯片的图形化开发能力
  2. CapSense技术:深入理解了电容式触控的原理和优化方法
  3. 蓝牙协议栈:实践了BLE协议栈的应用和优化技巧
  4. 系统集成能力:提升了硬件设计、嵌入式开发和无线通信的综合能力
  5. 问题解决能力:通过调试和优化培养了系统性解决问题的能力

6.2 改进建议

  1. 软件方面
    • 开发跨平台配置工具
    • 实现宏按键和复杂脚本功能
    • 添加用户配置云同步功能
  2. 功能扩展
    • 音量控制及屏幕亮度控制需要consumer类型的reportmap支持,后期考虑加入此功能.
    • 支持多设备快速切换

6.3 对电子森林平台建议

  1. 增加更多PSoC相关教程和案例分享
  2. 提供硬件设计检查工具
  3. 建立项目协作功能方便团队开发
  4. 增加专家答疑板块
  5. 举办定期的线上技术分享会

七、结论

本项目成功实现了基于CY8CKIT-149开发板的蓝牙触控定制键盘,验证了PSoC芯片在HMI应用中的优势。通过整合CapSense技术和BLE无线通信,创造了一种新型的人机交互方式。项目过程中解决的各类技术难题为后续开发积累了宝贵经验,也为相关应用提供了可参考的实现方案。

未来,我们将继续优化产品性能,扩展应用场景,探索商业化可能性,使这项技术能够真正服务于特定需求的用户群体。

附件下载
eetree_4_2.zip
团队介绍
个人开发者
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号