Funpack4-1 基于CY8CKIT-149的蓝牙PC控制器
该项目使用了CY8CKIT-149板卡,实现了蓝牙PC控制器的设计,它的主要功能为:板卡与PC蓝牙连接,使板卡模拟PC键盘,控制PC。
标签
嵌入式系统
Funpack活动
CY8CKIT-149
蓝牙控制器
反正都一样
更新2025-04-08
25

image.png

项目介绍

这里是我参加Funpack第四季第一期活动的任务总结报告,我所完成的是任务二,采集滑动条和按键状态,使用BLE连接并调节手机或电脑音量控制,屏幕亮度,上下翻页,要求三个按键分别控制不同的对象。

项目实施步骤

1. 准备软件与工具

  • 访问官网,下载必要软件:
    • SDK 开发包:包含示例代码和开发所需的库文件。
  • 安装下载的软件,并确保电脑能识别连接的开发板。

2. 硬件连接与设备识别

  • 将开发板通过 USB 线连接到电脑。
  • 打开“设备管理器”,确认开发板(如“J-Link”)已正确识别。如未识别,请检查连接或安装驱动程序。

3. 熟悉示例代码

  • 打开 SDK,浏览蓝牙连接和 CapSense 示例代码。
  • 在 IDE 中打开示例项目,配置开发板型号,编译并烧录代码,观察运行效果。

4. 修改代码以满足需求

  • 根据任务需求,修改蓝牙数据传输和 CapSense 功能的代码。
  • 调试代码,优化性能,确保功能正常运行。

硬件介绍

CY8CKIT-149 是一款基于 PSoC™ 4100S Plus 的原型开发套件,适用于评估和开发 PSoC™ 4100S Plus 器件。

  • 核心芯片:PSoC™ 4100S Plus,带 Arm Cortex-M0+ 内核,128KB 闪存,16KB SRAM。
  • CapSense™:支持自电容和互电容,包含三键按键板和六段滑块板。
  • 接口
    • 所有 GPIO 通过扩展头引出。
    • EZ-BLE 模块,带蓝色 LED,支持 BLE 开发。
    • USB Micro-B 连接器,用于供电和编程。
  • 编程与调试
    • 板载 KitProg2 编程器/调试器,支持 USB-UART 和 UART-I2C 桥接。
    • 5 引脚编程接头,可用于外部编程。
  • 其他特性
    • 4 MHz ECO 晶体和 32.768 kHz WCO 晶体。
    • 用户 LED 和按钮,用于开发和调试。
    • 复位按钮和电源指示 LED。

软件介绍

未命名绘图.jpg

该开发板有多种开发方式,arduino,NCS,SEGGER Embedded Studio,我则是使用的第三种。

Nordic 官方提供了多个示例程序,其中与本项目相关的有以下三个:

  1. BLE_HID_Keyboard
    该示例程序使设备通过蓝牙连接到 PC,并模拟成键盘。在此基础上,我们可以修改代码,以发送自定义信息到 PC。
  2. CE220891_CapSense_with_Breathing_LED
    此示例程序专注于板卡上的 CapSense 触摸模块应用,并包含两个主控之间的通信部分。它通过 I2CHW 发送触摸数据,可用于实现触摸模块的功能。
  3. CE210709_EZ-BLE_Peripheral
    该示例程序展示了蓝牙主控如何处理来自另一个主控的消息,并通过蓝牙将数据发送给连接的设备。原例程是将触摸位置发送到官方 app,但我们可以修改其功能。

大概思路是通过综合这三个示例程序,我们可以实现以下功能:
当 CapSense 模块被触发后,PSoC™ 4100S 通过 I2CHW 向蓝牙主控发送触摸数据。蓝牙主控接收到数据后,将其转换为相应的按键功能,并通过蓝牙发送给 PC。这样,CapSense 模块的触摸事件就能在 PC 上以键盘按键的形式呈现。

这种方案充分利用了现有的示例程序,降低了开发难度,同时实现了所需的功能。

            case PROCESS_DATA:

/* Process data on all the enabled widgets */
CapSense_ProcessAllWidgets();

/* Controls LEDs Status based on the result of Widget processing. */
LED_Control();

#if !ENABLE_TUNER

/*If tuner is not enabled expose the CapSense slider centroid position and button status to
EZ-BLE Module on CY8CKIT-149 PSoC 4100S Plus Prototyping Kit via I2C interface*/

/*Update the I2C buffer with slider centroid position*/
i2cBuffer[SLIDER_CENTROID_INDEX] = (uint8) CapSense_GetCentroidPos(CapSense_SLD_WDGT_ID);

/*Calculate the button status mask and update the I2C buffer
bit0= BTN0 status, bit1 = BTN1 status, bit2 = BTN2 status*/
for(widgetID = 0; widgetID < TOTAL_CAPSENSE_BUTTONS ; widgetID++)
{
if(CapSense_IsWidgetActive(widgetID))
{
SET_BIT(buttonStatus, widgetID);
}
else
{
CLEAR_BIT(buttonStatus, widgetID);
}
}
i2cBuffer[BUTTON_STATUS_INDEX1] = buttonStatus;

// if(){
//
// }
// temp_i2cBuffer[0]=
#endif
// CyDelay(1500);
SW_Tx_UART_PutString("SLIDER:");
SW_Tx_UART_PutHexInt(i2cBuffer[SLIDER_CENTROID_INDEX]);
SW_Tx_UART_PutCRLF();
SW_Tx_UART_PutString("BUTTON:");
SW_Tx_UART_PutHexInt(i2cBuffer[BUTTON_STATUS_INDEX1]);
SW_Tx_UART_PutCRLF();
/* Set the device state to SENSOR_SCAN */
currentState = SENSOR_SCAN;
break;

上面代码的中间部分,就是关键的4100S主控通过I2CHW发送给蓝牙蓝牙主控触摸数据。可以看到该变量前一个是滑动触摸的位置信息,后一个则是三个按钮被按下的信息。

image.png

BLE_HID_Keyboard的例程,main函数前面都是相关的蓝牙初始化部分,例程帮我们坐好了,不用管它,其次要自己加上对I2CHW的初始化,因为要用到。

image.png

在蓝牙事件中,将原来的函数屏蔽了,加上自己的处理触摸数据的部分。

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();
}

通过 I2C 从从设备(触摸模块)读取数据到 i2cBuffer使用 I2CHW_I2CMasterReadBuf 发起读取请求,并通过 I2CHW_I2CMasterStatus 检查读取是否完成。在读取完成之前,调用 CyBle_ProcessEvents() 处理 BLE 事件,以确保 BLE 通信不会被阻塞。

sliderValue = i2cBuffer[0];
if (sliderValue != 0x00ff)
{
if (sliderValue >= 0x0043)
{
page_up();
}
else if (sliderValue <= 0x0028)
{
page_down();
}
}
if (prevSliderValue != sliderValue)
{
// SendDataOverCapSenseSliderNotification(sliderValue);
}
prevSliderValue = sliderValue;

i2cBuffer 中读取滑块的值(i2cBuffer[0])。如果滑块值不是默认值(0x00ff),根据滑块值的范围执行相应的操作:如果滑块值大于或等于 0x0043,调用 page_up() 函数,如果滑块值小于或等于 0x0028,调用 page_down() 函数。如果滑块值发生变化(prevSliderValue != sliderValue),可以发送滑块通知(注释掉的代码)。更新 prevSliderValue 为当前滑块值。

temp_buttonValue = i2cBuffer[2];
if (temp_buttonValue != 0x0000)
{
if (temp_buttonValue == 0x0001)
{
sound_up();
}
else if (temp_buttonValue == 0x0002)
{
sound_down();
}
else if (temp_buttonValue == 0x0004)
{
light_up();
}
}

i2cBuffer 中读取按钮的状态(i2cBuffer[2])。如果按钮状态不是默认值(0x0000),根据按钮值执行相应的操作:如果按钮值为 0x0001,调用 sound_up() 函数。如果按钮值为 0x0002,调用 sound_down() 函数。如果按钮值为 0x0004,调用 light_up() 函数。

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

if(CapsKey == 1u)
{
keyboard_data[2u] = CAPS_LOCK;
}
keyboard_data[3u] = SimKey;

if(CyBle_GattGetBusyStatus() == CYBLE_STACK_STATE_FREE)
{
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*/
keyboard_data[3u] = 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;
}
}
}
}

void page_up(){
SendKeyboard(0u, PAGE_UP);
}
void page_down(){
SendKeyboard(0u, PAGE_DOWN);
}
void sound_up(){
SendKeyboard(0u, SOUND_HIGH);
}
void sound_down(){
SendKeyboard(0u, SOUND_LOW);
}
void light_up(){
SendKeyboard(0u, LIGHT_HIGH);
}
void light_down(){
SendKeyboard(0u, LIGHT_LOW);
}

SendKeyboard借鉴一个大佬的代码,HandleCapSense 函数中,根据触摸模块的输入(滑块和按钮状态),调用相应的辅助函数(如 page_upsound_up 等),从而实现触摸事件到按键事件的映射。这样,触摸模块的输入就可以通过蓝牙发送到连接的设备,模拟键盘操作。

滑块与按键触发,再到蓝牙主控发送相应键的大概流程

                    i2cBuffer[SLIDER_CENTROID_INDEX] = (uint8) CapSense_GetCentroidPos(CapSense_SLD_WDGT_ID);

/*Calculate the button status mask and update the I2C buffer
bit0= BTN0 status, bit1 = BTN1 status, bit2 = BTN2 status*/
for(widgetID = 0; widgetID < TOTAL_CAPSENSE_BUTTONS ; widgetID++)
{
if(CapSense_IsWidgetActive(widgetID))
{
SET_BIT(buttonStatus, widgetID);
}
else
{
CLEAR_BIT(buttonStatus, widgetID);
}
}
i2cBuffer[BUTTON_STATUS_INDEX1] = buttonStatus;

此处是滑块,按键被触发后,主控获取到滑块的当前位置,按键的按下情况,将数据存到i2cbuffer中

    /* 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();
}

// if(startNotification & SLIDER_CCCD_NTF_BIT_MASK)
// {
sliderValue = i2cBuffer[0];
if(sliderValue != 0x00ff){
if(sliderValue >= 0x0043){
page_up();
}else if(sliderValue <=0x0028){
page_down();
}
}
if(prevSliderVaule != sliderValue)
{
// SendDataOverCapSenseSliderNotification(sliderValue);
}
prevSliderVaule = sliderValue;
// }

temp_buttonValue = i2cBuffer[2];
if(temp_buttonValue != 0x0000){
if(temp_buttonValue == 0x0001){
sound_up();
}else if(temp_buttonValue == 0x0002){
sound_down();
}else if(temp_buttonValue == 0x0004){
//light_up();
}
}

此处是蓝牙主控中,通过i2chw获取到另一个主控发来的滑块及按键数据,提取后,进行分析,并进行相应的键位发送


效果展示

image.png

总结

在本次活动中,学习了如何使设备模拟蓝牙键盘连接PC。在过程中遇到的问题,通过百度搜索都能找到适合的答案,使自我得到了提升,感谢硬禾学堂平台。

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