DoDoTech 发表于 2021-9-6 12:48:50

电容触摸按键座椅控制器【开源】

本帖最后由 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;
                                        }                              
                              }                        
                        }      
                }                        
      }
}系统框图:系统比较简单,但也非常可靠,一个电容触摸板+一个控制器+电机
效果及实物图:触摸属于外观部分,通过扣件固定在扶手部分,只有黑色部分才露出来,四周的用真皮包边,非常漂亮。控制器部分用的是通用的外壳。








honami520 发表于 2021-9-6 13:05:50

支持楼主开源

newcanking 发表于 2021-9-6 13:56:31

支持,有空看一下

dukelec 发表于 2021-9-6 14:47:16

買這種高級的座椅,就是為了享受,用觸摸按鍵,控制的時候還要自己爬起身盯著面板才能控制,真的無力吐槽

討厭各種觸摸控制的電器

tiger_wu 发表于 2021-9-6 19:31:21

讨厌触摸按键! 除了不用的人看起来好看外,没有任何好处! 难用到爆!

HJJ2008 发表于 2021-9-6 19:38:50

支持楼主。

DoDoTech 发表于 2021-9-6 21:48:12

dukelec 发表于 2021-9-6 14:47
買這種高級的座椅,就是為了享受,用觸摸按鍵,控制的時候還要自己爬起身盯著面板才能控制,真的無力吐槽

...

我只是分享技术,传统按键也会有寿命和含无法密封的问题。电容触摸和实体按键,各有优缺点,看应用场合了。

dukelec 发表于 2021-9-6 22:58:20

本帖最后由 dukelec 于 2021-9-6 23:03 编辑

DoDoTech 发表于 2021-9-6 21:48
我只是分享技术,传统按键也会有寿命和含无法密封的问题。电容触摸和实体按键,各有优缺点,看应用场合了 ...

手機需要防水吧,需要壽命吧,你看哪個手機的音量鍵是觸摸的

不是說觸摸不好,但是要做好才行,最起碼的防誤觸功能要有,要有模擬實體按鍵的震動反饋,有了這些,你圖片中的操控盤就可以躺着不用起身看就可以操控了,手摸到按鍵的時候,通過震動交互讓人感覺摸到了實體按鍵,然後繼續按壓會增大電容,模擬實體按鍵按下,如果放一個水杯在上面,或者手臂搭在上面,還要檢測出不是手指,不予動作,這些能做到位再說,否則就應該老老實實用機械按鍵

akey3000 发表于 2021-9-7 00:35:32

can接口是必须的么?成本有点高

DoDoTech 发表于 2021-9-7 06:13:23

akey3000 发表于 2021-9-7 00:35
can接口是必须的么?成本有点高

CAN接口主要是可靠性高,项目后续有很多房车车灯,娱乐系统的联动。CAN接口后续会易于拓展。

DoDoTech 发表于 2021-9-7 08:53:04

dukelec 发表于 2021-9-6 22:58
手機需要防水吧,需要壽命吧,你看哪個手機的音量鍵是觸摸的

不是說觸摸不好,但是要做好才行,最起碼的 ...

我只是分享技术,不是讨论产品。这个触摸控制器的需求是房车厂家提供的。你有一万个理由不喜欢这个设计,但是依然有那么多触摸按键的产品。应用场景不同,需求就不一样。
这个设计是有考虑防止误触的。正常情况是不需要控制,座椅一直在那里。表面就是一整面黑色一体,非常漂亮。原始座椅的设计应该是弱化这个按键的存在(我猜测),只有在开始按键(感应)到就会亮按键,但不会动作,只有连续轻触2次才行,所以你的杯子什么的放上面,也不会触发调整座椅。在房车环境里,触摸按键可以营造科技感的效果。按键会有声音反馈但是没震动反馈,后台设置是有默认的记忆模式。客户对这个设计非常满意,但是因为侵权原因,最终项目没有量产。

whatcanitbe 发表于 2021-9-7 09:14:05

开源工程还是只是原理图?

DoDoTech 发表于 2021-9-10 12:51:20

whatcanitbe 发表于 2021-9-7 09:14
开源工程还是只是原理图?

原理图代码都有,原理也有

bipengjiang 发表于 2021-9-10 14:58:53

外壳要专门开模吗?

whatcanitbe 发表于 2021-9-10 16:31:41

DoDoTech 发表于 2021-9-10 12:51
原理图代码都有,原理也有

没看到CAN通信部分。只是代码片段?

lb0857 发表于 2021-9-10 16:32:28

干扰环境电容式触摸屏要做好不容易

DoDoTech 发表于 2021-9-10 17:32:19

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


H2O123 发表于 2021-9-10 19:31:22

是按摩椅?的控制器吗?
页: [1]
查看完整版本: 电容触摸按键座椅控制器【开源】