电容触摸按键座椅控制器【开源】
本帖最后由 DoDoTech 于 2021-9-7 08:55 编辑几年前的电容触摸座椅控制器项目,已经设计到小批量阶段,最后被客户坑了,我也是阿莫论坛老坛友了,回馈论坛,希望有需要的朋友可以用到。
方案优点:
[*]电容触摸开机自动重新校准
[*]电容触摸在高灵敏度的情况下,还能保证有汗渍、水滴不误触,这有点儿牛
[*]轻触、按住不放功能
[*]低功耗模式,设备轻触启动
[*]弹簧式触摸感应,非常方便安装,外壳要求非常低,量产及其方便
[*]触摸部分和控制部分分开,使用CAN网络连接,易于扩展,可以一个触摸模块控制多个模块
原理图:
实际效果视频:
代码:
#include "system.h"
uint8_t CAP_sensorDeltas;
uint8_t TouchKeyNum = 0;
UINT8 PowerKeyCount = 0;
UINT16 PowerKeyTimerOut = 0;
void App_CAP1298Task(void)
{
// uint8_t stateMask = 0;
uint8_t i = 0;
if(gSysStateFlag.bits.b_CAP1298InitFlag == 0)
{
CAP_init();
if(CAP_error == CAP_I2C_error_None)
{
gSysStateFlag.bits.b_CAP1298InitFlag = 1;
}
}
else
{
//if(gSysStateFlag.bits.b_CAP1298Flag == 1)
//{
//-----------------------------------------
// Read the current state mask value
//-----------------------------------------
TouchKeyNum = CAP_getSensorStatus();
if (CAP_error)
{
DebugUart_Printf(DEBUG_CAP1298Info,"CAP_getSensorStatus error = 0x%2x",CAP_error);
}
if (TouchKeyNum)
{
CAP_resetSensorStatus();
//DebugUart_Printf(DEBUG_CAP1298Info,"TouchKeyNum = 0x%2x\r\n",TouchKeyNum);
}
// -----------------------------------------
// Read all sensor values
// -----------------------------------------
CAP_getSensorDeltas(CAP_sensorDeltas);
if (CAP_error)
{
DebugUart_Printf(DEBUG_CAP1298Info,"CAP_getSensorDeltas error = 0x%2x",CAP_error);
}
DebugUart_Printf(DEBUG_CAP1298Info,"CAP_getSensorDeltas:");
for (i = 0; i < NUMBER_OF_SENSORS; i++)
{
DebugUart_Printf(DEBUG_CAP1298Info,"0x%2x ",CAP_sensorDeltas);
}
//DebugUart_Printf(DEBUG_CAP1298Info,"\r\n");
//}
if(gSysStateFlag.bits.b_KeyChangeFlag == 1)
{
gSysStateFlag.bits.b_KeyChangeFlag = 0;
if(SystemKeyValue == 0x01)
{
if(SystemKeyStatus == 1)//按下
{
LED1_R();
PowerKeyCount ++;
PowerKeyTimerOut = 1000;//1s
DebugUart_Printf(1,"PowerKeyCount:%d\r\n",PowerKeyCount);
if(PowerKeyCount == 2)
{//powerkey按键点击两次
PowerKeyTimerOut = 0;
PowerKeyCount = 0;
if(gSysStateFlag.bits.b_PowerStateFlag == 1)
{
LEDPowerOFF();
}
else
{
LEDPowerON();
}
}
BEEP_ON();
}
else
{
if(gSysStateFlag.bits.b_PowerStateFlag == 1)
{
LED1_W();
}
else
{
LED1_OFF();
}
}
}
if(gSysStateFlag.bits.b_PowerStateFlag == 1)
{
if(SystemKeyValue == 0x02)
{
DebugUart_Printf(1,"Key2:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED2_R();
gD0_0x392.bits.b_key2 = 1;
BEEP_ON();
}
else
{
LED2_W();
gD0_0x392.bits.b_key2 = 0;
}
}
else if(SystemKeyValue == 0x04)
{
DebugUart_Printf(1,"Key3:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED3_R();
gD0_0x392.bits.b_key3 = 1;
BEEP_ON();
}
else
{
LED3_W();
gD0_0x392.bits.b_key3 = 0;
}
}
else if(SystemKeyValue == 0x08)
{
DebugUart_Printf(1,"Key4:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED4_R();
gD0_0x392.bits.b_key4 = 1;
BEEP_ON();
}
else
{
LED4_W();
gD0_0x392.bits.b_key4 = 0;
}
}
else if(SystemKeyValue == 0x20)
{
DebugUart_Printf(1,"Key5:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED5_R();
gD0_0x392.bits.b_key5 = 1;
BEEP_ON();
}
else
{
LED5_W();
gD0_0x392.bits.b_key5 = 0;
}
}
else if(SystemKeyValue == 0x80)
{
DebugUart_Printf(1,"Key6:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED6_R();
gD0_0x392.bits.b_key6 = 1;
BEEP_ON();
}
else
{
LED6_W();
gD0_0x392.bits.b_key6 = 0;
}
}
else if(SystemKeyValue == 0x40)
{
DebugUart_Printf(1,"Key7:%d\r\n",SystemKeyStatus);
if(SystemKeyStatus == 1)//按下
{
LED7_R();
gD0_0x392.bits.b_key7 = 1;
BEEP_ON();
}
else
{
LED7_W();
gD0_0x392.bits.b_key7 = 0;
}
}
}
}
}
}系统框图:系统比较简单,但也非常可靠,一个电容触摸板+一个控制器+电机
效果及实物图:触摸属于外观部分,通过扣件固定在扶手部分,只有黑色部分才露出来,四周的用真皮包边,非常漂亮。控制器部分用的是通用的外壳。
支持楼主开源 支持,有空看一下 買這種高級的座椅,就是為了享受,用觸摸按鍵,控制的時候還要自己爬起身盯著面板才能控制,真的無力吐槽
討厭各種觸摸控制的電器 讨厌触摸按键! 除了不用的人看起来好看外,没有任何好处! 难用到爆! 支持楼主。 dukelec 发表于 2021-9-6 14:47
買這種高級的座椅,就是為了享受,用觸摸按鍵,控制的時候還要自己爬起身盯著面板才能控制,真的無力吐槽
...
我只是分享技术,传统按键也会有寿命和含无法密封的问题。电容触摸和实体按键,各有优缺点,看应用场合了。 本帖最后由 dukelec 于 2021-9-6 23:03 编辑
DoDoTech 发表于 2021-9-6 21:48
我只是分享技术,传统按键也会有寿命和含无法密封的问题。电容触摸和实体按键,各有优缺点,看应用场合了 ...
手機需要防水吧,需要壽命吧,你看哪個手機的音量鍵是觸摸的
不是說觸摸不好,但是要做好才行,最起碼的防誤觸功能要有,要有模擬實體按鍵的震動反饋,有了這些,你圖片中的操控盤就可以躺着不用起身看就可以操控了,手摸到按鍵的時候,通過震動交互讓人感覺摸到了實體按鍵,然後繼續按壓會增大電容,模擬實體按鍵按下,如果放一個水杯在上面,或者手臂搭在上面,還要檢測出不是手指,不予動作,這些能做到位再說,否則就應該老老實實用機械按鍵 can接口是必须的么?成本有点高 akey3000 发表于 2021-9-7 00:35
can接口是必须的么?成本有点高
CAN接口主要是可靠性高,项目后续有很多房车车灯,娱乐系统的联动。CAN接口后续会易于拓展。 dukelec 发表于 2021-9-6 22:58
手機需要防水吧,需要壽命吧,你看哪個手機的音量鍵是觸摸的
不是說觸摸不好,但是要做好才行,最起碼的 ...
我只是分享技术,不是讨论产品。这个触摸控制器的需求是房车厂家提供的。你有一万个理由不喜欢这个设计,但是依然有那么多触摸按键的产品。应用场景不同,需求就不一样。
这个设计是有考虑防止误触的。正常情况是不需要控制,座椅一直在那里。表面就是一整面黑色一体,非常漂亮。原始座椅的设计应该是弱化这个按键的存在(我猜测),只有在开始按键(感应)到就会亮按键,但不会动作,只有连续轻触2次才行,所以你的杯子什么的放上面,也不会触发调整座椅。在房车环境里,触摸按键可以营造科技感的效果。按键会有声音反馈但是没震动反馈,后台设置是有默认的记忆模式。客户对这个设计非常满意,但是因为侵权原因,最终项目没有量产。
开源工程还是只是原理图? whatcanitbe 发表于 2021-9-7 09:14
开源工程还是只是原理图?
原理图代码都有,原理也有 外壳要专门开模吗? DoDoTech 发表于 2021-9-10 12:51
原理图代码都有,原理也有
没看到CAN通信部分。只是代码片段?
干扰环境电容式触摸屏要做好不容易 whatcanitbe 发表于 2021-9-10 16:31
没看到CAN通信部分。只是代码片段?
CAN部分很简单,自定义2个ID,就可以了。驱动都是通用的,用的官方代码。
#ifndefDRIVER_CAN_H
#defineDRIVER_CAN_H
#include "type.h"
void CAN1_Init(void);
void CAN_Disable(void);
UINT8 CAN1_SendMessage(UINT32 id, UINT8 *memory, UINT8 Length);
void CAN_RXBufferSet(void);
#endif
#include "system.h"
CanTxMsg TxMessage;
CanRxMsg RxMessage;
void CAN_SetRxBuffer(UINT8 buffer, UINT32 id, UINT32 mask)
{
CAN_FilterInitTypeDefCAN_FilterInitStructure;
UINT32 ID_FDR = 0,Mask_FDR = 0;
ID_FDR = (id<<21) & 0xFFE00000;
Mask_FDR = (mask<<21) & 0xFFE00000;
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber = buffer; /*指定待初始化的过滤器(0~13)*/
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; /*过滤器模式 CAN_FILTERMODE_MASK(标识符屏蔽位模式)CAN_FILTERMODE_LIST(列表模式)*/
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; /*过滤器位宽*/
CAN_FilterInitStructure.CAN_FilterIdHigh = (UINT16)(ID_FDR>>16);
CAN_FilterInitStructure.CAN_FilterIdLow = (UINT16)ID_FDR; /*过滤器标识符*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (UINT16)(Mask_FDR>>16);
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (UINT16)Mask_FDR; /*过滤器屏蔽标识符*/
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 1; /*FIFO Num 1*/
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; /*使能或是失能过滤器*/
CAN_FilterInit(&CAN_FilterInitStructure);
}
void CAN1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDefCAN_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure CAN RX(PA11) pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;/*上拉输入*/
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure CAN TX(PA12) pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;/*复用推挽输出*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
//CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;
//72MHz//72000/2/6/((1+9+2))=500k(1+9)/(1+9+2)=83%
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
CAN_InitStructure.CAN_Prescaler = 6;
CAN_Init(CAN1, &CAN_InitStructure);
//设置接收buf
CAN_SetRxBuffer(0,0x392,0xfff);
/* Enable CAN1 RX1 interrupt IRQ channel */
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX1_IRQn;//FIFO 1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP1, ENABLE);//FIFO 1 not empty interrupt
//错误中断配置
NVIC_InitStructure.NVIC_IRQChannel = CAN1_SCE_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_ERR, ENABLE); //错误中断使能
// CAN_ITConfig(CAN1, CAN_IT_EWG, ENABLE); //警告错误
// CAN_ITConfig(CAN1, CAN_IT_EPV, ENABLE); //被动错误
CAN_ITConfig(CAN1, CAN_IT_BOF, ENABLE); //离线中断
// CAN_ITConfig(CAN1, CAN_IT_LEC, ENABLE); //错误种类
}
UINT8 CAN1_SendMessage(UINT32 id, UINT8 *memory, UINT8 Length)
{
UINT8 Sendmail_num =CAN_TxStatus_NoMailBox;
UINT8 SendCount = 0;
UINT8 value = 1;
if(Length > 8) Length = 8;
/* Transmit */
TxMessage.StdId = id;
TxMessage.ExtId = id;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.IDE = CAN_ID_STD; //标准帧
TxMessage.DLC = Length;
memcpy(TxMessage.Data,memory,TxMessage.DLC);
while(1)
{//只有3个发送邮箱,所以一次只能发送三帧数据,如果满了可以等待一段时间再发
Sendmail_num = CAN_Transmit(CAN1, &TxMessage);
if(Sendmail_num == CAN_TxStatus_NoMailBox)
{//邮箱满
delay_ms(1);
SendCount ++;
if(SendCount == 10)//10ms
{
SendCount = 0;
value = 0;
break;
}
}
else
{
value = 1;
break;
}
}
return value;
}
//------------------------中断处理------------------------------
//接收中断处理
void CAN1_RX1_IRQHandler(void)
{
CAN_Receive(CAN1, CAN_FIFO1, &RxMessage);
if((RxMessage.FMI == 0) && (RxMessage.StdId == 392))
{
//DebugUart_Printf(1,"CAN:0x500\r\n");
}
}
//错误中断处理
void CAN1_SCE_IRQHandler(void)
{
//错误中断
if(CAN_GetITStatus(CAN1,CAN_IT_ERR) == SET)
{
CAN_ClearITPendingBit(CAN1,CAN_IT_ERR);
//DebugUart_Printf(DEBUG_ErrInfo,":Ext CAN ERROR:INT\r\n");
}
//离线中断
if(CAN_GetITStatus(CAN1,CAN_IT_BOF) == SET)
{
CAN_ClearITPendingBit(CAN1,CAN_IT_BOF);
//DebugUart_Printf(DEBUG_ErrInfo,":Ext CAN ERROR:buf off\r\n");
//通过软件手动的从离线状态中恢复
gSysStateFlag.bits.b_CanBusOffFlag = 1;
}
// if(CAN_GetITStatus(CAN1,CAN_INT_WE) == SET)
// {
// //DebugUart_Printf(DEBUG_ErrInfo,":Ext CAN ERROR:error warning 96\r\n");
// }
// if(CAN_GetITStatus(CAN1,CAN_INT_PE) == SET)
// {
// //DebugUart_Printf(DEBUG_ErrInfo,":Ext CAN ERROR:error warning 128\r\n");
// }
// if(CAN_GetITStatus(CAN1,CAN_INT_ET) == SET)
// {
// //DebugUart_Printf(DEBUG_ErrInfo,":Ext CAN ERROR:error active\r\n");
// }
}
是按摩椅?的控制器吗?
页:
[1]