一、项目简介
本项目使用Funpack第四季第1期活动的板卡,CY8CKIT-149开发套件,PSoC 4100S Plus主控,完成了任务二。
只需要通过触摸主控,发送信号的BLE芯片,连接电脑以实现控制音量等HID功能。
二、硬件资源
开发环境:
赛普拉斯支持两种开发环境
PSOC Creator 4.4
ModusToolbox,在vscode里面的应用市场下载
开发板:CY8CKIT-149-4100S PLUS
M0+芯片:CY8C4147AZI-S475
BLE芯片:CYBLE-022001-00
三、探索过程
刚开始寄过来的开发板默认使用了CE220891_CapSense_with_Breathing_LED的Demo以做展示。
环境问题,导致无法编译。
四、实现任务流程
1.规划硬件外设:
触摸按键(CSX) * 3
触摸滑条(CSX) * 1
EZI2C从机 * 1
I2C主机 * 1
通讯IO
M0_EZI2C_SCL[P30] -- BLE_I2C_MASTER_SCL[P51]
M0_EZI2C_SDA[P31] -- BLE_I2C_MASTER_SDA[P50]
CSX区别于CSD按键,可以少用一个IO,相对来说,抗干扰性更弱
CSX软件设置如图所示,Mutual-cap则对应CSD
2.程序架构:
BLE HID服务配置:键盘模式
CapSense触摸检测
I2C通信传输触摸数据
HID报告发送
3.使用介绍
上电后,会亮3个指示灯
开发板上电后,LED指示灯会闪烁表示正在广播
在电脑的蓝牙设置中搜索并连接设备(显示为BLE Keyboard)
连接成功后,LED指示灯常亮
使用触摸按键和滑条控制电脑:
左按键:音量加
右按键:音量减
中间按键:亮度调节
滑条:控制屏幕页面上下滚动
4.参考Demo:
E224820_PSoC4_CapSense_Slider_Gesture
BLE_HID_Keyboard
5.遇到的问题:
HID函数添加延时也无法解决,HID一直会发送指令,不会松开,暂时无法查明原因,希望各位大佬能给点意见。
6.技术细节
通过CapSense_DecodeWidgetGestures函数,获取当前滑条的手势,返回一个状态值,再根据状态值传送对应的EZI2C参数和LED操作事件
/* 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[BUTTON_STATUS_INDEX2] = 0x01;
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[BUTTON_STATUS_INDEX2] = 0x02;
break;
}
LED11_Write(CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID) ? LED_ON : LED_OFF );
// LED_12_Write(CapSense_IsWidgetActive(CapSense_BUTTON1_WDGT_ID) ? LED_ON : LED_OFF );
LED13_Write(CapSense_IsWidgetActive(CapSense_BUTTON2_WDGT_ID) ? LED_ON : LED_OFF );
/* Calculate the button status mask and update the I2C buffer
bit0= BTN0 status, bit1 = BTN1 status, bit2 = BTN2 status */
for(uint8 widgetID = 0; widgetID < TOTAL_CAPSENSE_BUTTONS; widgetID++)
{
if(CapSense_IsWidgetActive(widgetID))
{
SET_BIT(buttonStatus, widgetID);
}
else
{
CLEAR_BIT(buttonStatus, widgetID);
}
}
使用for循环遍历,获取触摸按键状态,再根据滑条调整LED的PWM占空比,最后EZI2C发送滑环百分比和按键值
/* Calculate the button status mask and update the I2C buffer
bit0= BTN0 status, bit1 = BTN1 status, bit2 = BTN2 status */
for(uint8 widgetID = 0; widgetID < TOTAL_CAPSENSE_BUTTONS; widgetID++)
{
if(CapSense_IsWidgetActive(widgetID))
{
SET_BIT(buttonStatus, widgetID);
}
else
{
CLEAR_BIT(buttonStatus, widgetID);
}
}
/* Checks if the linear slidere was touched and that the slider should be changed */
if(CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID) != CapSense_SLIDER_NO_TOUCH && PWM_ReadCompare() != 0)
{
/* Changes the compare value based on the slider position */
PWM_WriteCompare(PWM_SCALAR * CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID) + 1);
}
i2cBuffer[SLIDER_PERCENT_INDEX] = CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
i2cBuffer[BUTTON_STATUS_INDEX1] = buttonStatus;
BLE这边参考了大佬的代码,放到等待BLE连接成功的大循环里面。实现了IIC轮询只读数据标志位,来获取从机的数据,实现事件传递。做完事件,需要清除标志,避免被重复触发。
static uint8 buffer[BUFFER_SIZE];
void ReceiveData(void)
{
static uint8 sliderPercentValue = 0;
static uint8 touchKeyValue = 0;
static uint8 sliderGestureValue = 0;
/* Read entire data buffer from the slave device */
IIC_I2CMasterReadBuf(I2C_SLAVE_ADDRESS, buffer, BUFFER_SIZE,
IIC_I2C_MODE_COMPLETE_XFER);
while (0u == (IIC_I2CMasterStatus() & IIC_I2C_MSTAT_RD_CMPLT))
{
CyBle_ProcessEvents();
}
sliderPercentValue = buffer[SLIDER_PERCENT_INDEX];
sliderGestureValue = buffer[BUTTON_STATUS_INDEX2];
if(sliderGestureValue == SLIDER_GESTURE_RIGHT)
{
SendKeyboard(PAGE_UP);
send_key_board_flag = 1;
sliderGestureValue = 0;
}
else if(sliderGestureValue == SLIDER_GESTURE_LEFT)
{
SendKeyboard(PAGE_DOWN);
send_key_board_flag = 1;
sliderGestureValue = 0;
}
touchKeyValue = buffer[BUTTON_STATUS_INDEX1];
if(touchKeyValue)
{
if(touchKeyValue & 0x01)
{
send_key_board_flag = 1;
SendKeyboard(VOLUME_UP);
touchKeyValue = 0;
}
if(touchKeyValue & 0x02)
{
send_key_board_flag = 1;
SendKeyboard(VOLUME_DOWN);
touchKeyValue = 0;
}
if(touchKeyValue & 0x04)
{
send_key_board_flag = 1;
SendKeyboard(LIGHT_DOWN);
touchKeyValue = 0;
}
}
}
HID发送,参考了BLEdemo里面的发送asciil函数。使用了全局标志控制,避免事件被重复触发,对常用的区块进行了函数封装。HID和IIC一样使用了判断标志位来轮询HID的状态,以进行数据传输到电脑。
uint8 send_key_board_flag = 0;
void SendKeyboard(uint8 SimKey)
{
static uint8 keyboard_data[KEYBOARD_DATA_SIZE]={0};
CYBLE_API_RESULT_T apiResult;
keyboard_data[2u] = SimKey;
if(!send_key_board_flag)
{
return;
}
send_key_board_flag = 0;
if(CyBle_GattGetBusyStatus() == CYBLE_STACK_STATE_FREE)
{
apiResult = CyBle_HidssGetCharacteristicValue(CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX, CYBLE_HIDS_PROTOCOL_MODE, sizeof(protocol), &protocol);
SendHidNotification(keyboard_data);
if(apiResult == CYBLE_ERROR_OK)
{
memset(keyboard_data, 0, KEYBOARD_DATA_SIZE);
SendHidNotification(keyboard_data);
}
if(apiResult != CYBLE_ERROR_OK)
{
DBG_PRINTF("HID notification API Error: %x \r\n", apiResult);
keyboardSimulation = DISABLED;
}
}
}
CYBLE_API_RESULT_T SendHidNotification(uint8_t* data)
{
CYBLE_API_RESULT_T result;
if(protocol == CYBLE_HIDS_PROTOCOL_MODE_BOOT)
{
result = CyBle_HidssSendNotification(cyBle_connHandle,
CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HIDS_BOOT_KYBRD_IN_REP,
KEYBOARD_DATA_SIZE,
data);
}
else
{
result = CyBle_HidssSendNotification(cyBle_connHandle,
CYBLE_HUMAN_INTERFACE_DEVICE_SERVICE_INDEX,
CYBLE_HUMAN_INTERFACE_DEVICE_REPORT_IN,
KEYBOARD_DATA_SIZE,
data);
}
return result;
}
感谢大家的观看,也欢迎在评论区交流,指出我的不足。感谢电子森林和得捷电子的大力支持本次活动。