涂鸦智能暖风机软件实现之利用TC309实现触摸按键控制功能

Posted 三明治开发社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了涂鸦智能暖风机软件实现之利用TC309实现触摸按键控制功能相关的知识,希望对你有一定的参考价值。


前言

前面我们已经实现了暖风机的面板显示功能,本文将实现暖风机的触摸按键控制功能。


一、智能暖风机的整体功能设定

  • 智能暖风机的设定功能如下表,我们将整个暖风机拆分成几个模块,逐个实现功能;暖风机在离线工作时需要通过机体的触摸按键对暖风机进行控制,故本文将实现此部分功能。
功能说明
开关触摸按键:1个
按键控制,app控制
控制暖风机开关。
目前开发的开启有三种方式:
1.App面板控制
2:按键控制
3:定时控制
模式触摸按键:1个
按键控制,app控制
1:风扇功能:只吹风,不制热。
2:1档加热:风扇+加热1
3:2档加热:风扇+加热2(档位更高)
定时触摸按键:1个
按键控制,app控制
倒计时默认枚举值有cancel, 1h, 2h, 3h, 4h, 5h, 6h,7h, 8h。
倒计时功能针对暖风机开关。
app暖风机倒计时剩余时间。
灯光触摸按键:1个
按键控制,app控制
4种照明模式:
1 rgb1
2 rgb2
3 rgb3
4 rgb4
设温触摸按键:1个
按键控制,app控制
温度设置区间为15-40℃,客户可以自行设定温度。
温度显示硬件:断码显示
按键控制,app控制
只是在设定的时候显示
设备上和当前温度显示复用,app单独显示
设备:温度设定时,显示设定温度,误操作3秒后显示当前室温
摇头触摸按键:1个
按键可控制,app可控制。
开/关
待机记忆按键,app,主动操作开关键关机为待机状态。
再开启后恢复上一次设置:
温度设置:上一次设置
温度显示:当前环境温度
灯光模式:上一次设置
设备定时关:默认关闭
app定时关:默认关闭。
app定时开:上一次设置。
断电记忆断电后为断电状态,再上电恢复上一次设置:
开关状态:默认关
温度设置:上一次设置
温度显示:当前环境温度
灯光模式:上一次设置
设备定时关:默认关闭
app定时关:默认关闭。
app定时开:上一次设置。

二、触摸按键控制暖风机的方案制定

1.触摸按键原理

本案例中触摸按键采集芯片选择TC309,TC309 是一个 9 按键电容传感装置,本案例中只使用了六个按键。

  • 按键采集电路如下:

在这里插入图片描述

  • TC309芯片使用说明

    TC309 是一个 9 按键电容传感装置。该装置可以作为一个 9 键控制器。按键按下后CX的相应通道将会输入低电平,此时0x08和0x09寄存器中相应按键位也会为0,读取寄存器的值就可以判断是哪个按键按下。

 可以控制 9 个按键
 自动灵敏度校正
 系统低成本
 I2C 输出方式
管脚描述:
在这里插入图片描述
在这里插入图片描述
器件地址:

地址 (A[6:0])40H
读命令 (A[6:0]+RWB)81H
写命令 (A[6:0]+RWB)80H

在这里插入图片描述
在这里插入图片描述

读操作:

在这里插入图片描述
TC309 的默认读寄存器地址为 08H,如果操作过其他寄存器,再要读取08H地址数据需要重新访问08H,即通过IIC向TC309发送0x80(0x40地址写操作) 和0x08,之后再进行读操作。

  • TC309控制寄存器描述
    A
    在这里插入图片描述
    在这里插入图片描述

  • 按下按键后芯片的INT引脚会拉低,所以我们采用外部中断去采集INT引脚的变化,一次下降沿说明按键按下一次,中断发生后发送信号量,按键读取键值进程将会运行,采集到是哪个按键按下,从而对暖风机进行控制。

2.软件方案设定

  • 触摸按键嵌入式方案设定
    前面已经实现了暖风机的一些功能,现在要实现触摸按键控制功能,实现离线状态时对暖风机的控制功能。
    通信引脚设定:

    SDA1	| P26	
    SCL1	| P24
    

    外部中断引脚设定:

    INT	| P9	
    

    要实现的功能:

按键功能
开关按一次暖风机开关状态切换
灯光灯光模式切换
设温设置要加热到的温度 操作时面板显示设置温度
定时关闭设定定时关闭的时间 面板显示定时时间
档位在设定温度大于室温时可以调整档位
摇头摇头开关

三、功能实现

1.代码实现

  • 在之前开发的基础上增加程序,增加tc309.c文件和其头文件、增加interrupt.c文件,目前整个工程的文件结构如下:
├── src	
|    ├── tuya_drive
|    |  	└── b3950
|    |    		└── b3950.c           //温度传感器驱动相关
|    |  	└── tm1650
|    |    		└──  tm1650.c           
|    |  	└── tc309					//按键驱动相关
|    |    		└── tc309.c    
|    |  	└── interrupt				//外部中断
|    |    		└── interrupt.c    
|    |  	└── gpio_control		//gpio控制相关
|    |    		└── gpio_control.c    
|    |  	└── timer
|    |    		└── timer.c           //定时器相关
|    ├── tuya_device.c             //应用层入口文件
|    ├── tuya_thread.c             //主要线程处理文件
|    └── tuya_dp_process.c            //dp数据触发的主要应用文件
|
├── include				//头文件目录
|    ├── tuya_drive_h
|    |  	└── b3950_h
|    |    		└── b3950.h        
|    |  	└── tm1650_h
|    |    		└──  tm1650.h           
|    |  	└── tc309_h					
|    |    		└── tc309.h    
|    |  	└── interrupt_h				
|    |    		└── interrupt.h    
|    |  	└── gpio_control_h
|    |    		└── gpio_control.h        
|    |  	└── timer_h
|    |    		└── timer.h           
|    ├── tuya_device.h           
|    ├── tuya_thread.h       
|    └── tuya_dp_process.h       
|
└── output              //编译产物

tc309.c代码及说明如下:

#include "tc309.h"
#include "uni_log.h"
#include "soc_i2c.h"
/*============================ PROTOTYPES ====================================*/

/*============================ IMPLEMENTATION ================================*/
static UCHAR_T senset[20] = {0x04,0x15,0x25,0x36,0x47,0x58,0x68,0x79,0x8a,0x9b,0xac,0xbc,0xcd,0xde,0xef,0xff};   //sensitivity set


STATIC UCHAR_T gTc309InitFlag = FALSE;
/**
 * @brief: TC309 IIC delay proc
 * @param {none} 
 * @retval: none
 */
STATIC VOID vTc309Delay(USHORT_T usTime)
{       
    vSocI2CDelay(usTime);
}

/**
 * @brief: TC309 IIC start transport
 * @param {none}
 * @retval: OPERATE_LIGHT
 */
STATIC int vTc309Start(VOID)
{
    vSocI2CSclSet();
    vSocI2CSdaSet();

    vTc309Delay(2);

    if(!ucSocI2CSdaInputRead()) {    /* confirm SDA status is ready */
        return -1;
    }

    vSocI2CSdaReset();
    vTc309Delay(2);

    if(ucSocI2CSdaOutputRead()) {    /* confirm SDA status is set correct */
        return -1;
    }

    return 0;
}

/**
 * @brief: TC309 IIC stop transport
 * @param {none}
 * @retval: none
 */
STATIC VOID vTc309Stop(VOID)
{
    vSocI2CSdaReset();
    vSocI2CSclReset();

    vSocI2CSclSet();
    vTc309Delay(2);

    vSocI2CSdaSet();
    vTc309Delay(2);
}

/**
 * @brief: TC309 IIC wait ACK
 * @param {SDA_IO -> SDA pin}
 * @param {SCL_IO -> SCL pin}
 * @retval: OPERATE_LIGHT
 */
STATIC int vTc309WaitAck(VOID)
{
    vSocI2CSclReset();
    vTc309Delay(1);

    vSocI2CSclSet();
    vTc309Delay(1);
    if(ucSocI2CSdaOutputRead()) {     /* receive ACK NG */
        return -1;
    }
    
    vTc309Delay(5);
    return 0;
}

/**
 * @brief: TC309 IIC send byte
 * @param {UCHAR_T ucSendByte -> send one byte}
 * @retval: none
 */
STATIC VOID vTc309SendOneByte(UCHAR_T ucSendByte)
{
    UCHAR_T i = 0;

    for(i = 0; i < 8; i++)
    {
        vSocI2CSclReset();
        vTc309Delay(2);

        if(ucSendByte & 0x80) {
            vSocI2CSdaSet();
        } else {
            vSocI2CSdaReset();
        }

        vSocI2CSclSet();
        vTc309Delay(2);

        ucSendByte <<= 1;
   }
}

/**
 * @brief: TC309 write data proc
 * @param {*pBuffer -> write data buf}
 * @param {NumByteToWrite -> write data len}
 * @retval: OPERATE_LIGHT 
 */
STATIC int vTc309WritePage(UCHAR_T *pBuffer, USHORT_T usNumByteToWrite)
{
    int opRet = 0;

    if( NULL == pBuffer ) {
        PR_ERR("TC309 write data is invalid!");
        return -1;
    }
 
    opRet = vTc309Start();     /* start transport */
    if( 0 != opRet ) {
        PR_ERR("IIC is busy!");
        return -1;
    }
    
    while(usNumByteToWrite --) {
        
        vTc309SendOneByte(*pBuffer++);
        vTc309WaitAck();
    }

    vTc309Stop();
    vTc309Delay(1000);

    return 0;
}

unsigned char i2c_read_byte()//读一个字节
{
    vSocI2CSclReset();
    unsigned char i,k;
    for(i=0;i<8;i++)
    {
    vTc309Delay(2);
    vSocI2CSclSet();//上升沿时,IIC 设备将数据放在 sda 线上,并在高电平期间数据已经稳定,可以接收啦
    // i2c_delay();
    k=(k<<1)|vSocI2CSdaget();
    vSocI2CSclReset();
    }
    return k;
}


void i2c_read_direct(UCHAR_T dev_addr,UCHAR_T * dest_buf,UCHAR_T len)
{
    char i;
    vTc309Start();//启动
    vTc309SendOneByte((dev_addr<<1)+1);//发送发送从设备地址 读操作
    vTc309WaitAck();//等待从设备的响应
    dest_buf[0]=i2c_read_byte();//获取数据
    for(i=1;i<len;i++)
    {
    i2c_sendack();
    dest_buf[i]=i2c_read_byte();
    }
    i2c_sendnack();
    vTc309Stop();//停止
}

void i2c_sendack() //应答 SCL 在高电平期间,SDA 被从设备拉为低电平表示应答
{
    vSocI2CSdaReset();
    vTc309Delay(1);
    vSocI2CSclSet();
    vTc309Delay(2);
    vSocI2CSclReset();
    vSocI2CSdaSet();

}
void i2c_sendnack() //应答 SCL 在高电平期间,SDA 被从设备拉为低电平表示应答
{
    vSocI2CSdaSet();
    vTc309Delay(1);
    vSocI2CSclSet();
    vTc309Delay(2);

    vSocI2CSclReset();

}

//int UserTc309Init(IN TC309_PIN_T* pTc309Init)
int UserTc309Init()
{
    int opRet = -1;

    if(gTc309InitFlag != FALSE) {
        PR_NOTICE("Tc309 already init!");
        return 0;
    }
    I2C_PIN_T tI2CConfig = {
        .ucSdaPin = 15,
        .ucSclPin = 17,
    };
    opRet = opSocI2CInit(&tI2CConfig);          /* SDA&SCL GPIO INIT */
    if(opRet != 0){
        PR_ERR("Tc309 I2C init error!");
        return -1;
    }

    gTc309InitFlag = TRUE;

    return 0;
}



int UserTc309Set_Sensitivity(UCHAR_T level)
{
        int opRet = 0;
        UCHAR_T IIC_Sendbuf[5] = {0};
        IIC_Sendbuf[0] = 0x80 ;        
        IIC_Sendbuf[1] = 0x00;
        IIC_Sendbuf[2] = senset[level];
        IIC_Sendbuf[3] = senset[level];
        opRet = vTc309WritePage(IIC_Sendbuf, 4);
        IIC_Sendbuf[0] = 0x80 ;        
        IIC_Sendbuf[1] = 0x08;
        opRet = vTc309WritePage(IIC_Sendbuf, 2);
    return 0;
}

+上面是驱动tc309程序的代码,使用说明:

//tc309初始化
    UserTc309Init();
 //设置按键灵敏度
    UserTc309Set_Sensitivity(7);


后面在按键读取进程中执行读取存有键值的寄存器即可判断是哪个按键按下,读取程序如下:
uint8_t *buffer = (uint8_t *)Malloc(2*sizeof(uint8_t));
 //tc309  read register 0x08 0x09
 i2c_read_direct(0x40,buffer,2);
key_value = (buffer[0] << 4) & 0x1ff;
key_value |= (buffer[1] >> 4) & 0x0f;
根据key_value就可以知道哪个按键按下
#define KEY0    0xff
#define KEY1    0x17f
#define KEY2    0x1bf
#define KEY3    0x1df
#define KEY4    0x1ef
#define KEY5    0x1f7
#define KEY6    0x1fb
#define KEY7    0x1fd
#define KEY8    0x1fe

interrupt.c增加的代码及说明如下:每次INT外部中断触发即发送信号量,采集按键值的线程获取到信号量将进行采集键值并控制暖风机进行相应的动作。

#include "interrupt.h"
#include "tuya_dp_process.h"
#define KEY_INT 9

void key_interrupt_init(void)
{
    //SW1下降沿触发
    tuya_pin_irq_init(KEY_INT, TUYA_PIN_MODE_IN_IRQ_FALL, tuya_key_irq_cb, NULL);

}

//按键SW1中断回调函数
void tuya_key_irq_cb(void *arg)
{
    tuya_hal_semaphore_post(g_key_trigger_binsemap);
}

tuya_thread.c增加的代码及说明如下:里面的led功能实现和断电记忆功能将在后续文章实现

void tc309_keyscan_task(void)
{
    static uint16_t key_value = 0x1ff;
    UserTc309Init();
    UserTc309Set_Sensitivity(7);
    while(1)
    {

    //GET KEY_VALUE
        tuya_hal_semaphore_wait(g_key_trigger_binsemap);
        uint8_t *buffer = (uint8_t *)Malloc(2*sizeof(uint8_t));
        //tc309 read 0x08 0x09
        i2c_read_direct(0x40,buffer,2);
        key_value = (buffer[0] << 4) & 0x1ff;
        key_value |= (buffer[1] >> 4) & 0x0f;
        PR_DEBUG("BUF0:%x",buffer[0]);
        PR_DEBUG("BUF1:%x",buffer[1]);
        PR_DEBUG("key_value:%x",key_value);
        //tc309
        dev_key_s.key_value = key_value;
        Free(buffer);
        //Actions are performed according to key values
        switch (key_value){

        case KEY0:
        {
            //shake key
            if(dp_memory_s.switch_bool)
            {
                timer1_init();
                PR_DEBUG("key_value0");
                if(dp_memory_s.shake_bool == 1){
                    shake_handle(0);
                }else
                {
                    shake_handle(1);
                }
            }

        }
        break;
        case KEY1:
        {
            //relay key
            if(dp_memory_s.switch_bool)
            {
                timer1_init();
                PR_DEBUG("key_value1");
                static uint8_t cur_mode = 0;
                cur_mode = dp_memory_s.relay_mode;
                cur_mode = (cur_mode + 1 < 3) ? (cur_mode + 1) : 0;
                relay_handle(cur_mode);
                PR_DEBUG("relay mode:%d",dp_memory_s.relay_mode);
            }

        }
        break;
        case KEY2:
        {
            //timer close key
            if(dp_memory_s.switch_bool)
            {
                timer1_init();
                PR_DEBUG("key_value2");
                if(dev_key_s.last_time_count && ((timercount - dev_key_s.last_time_count) < 2)){
                    dev_key_s.timer_hour++;
                }
                dev_key_s.last_time_count = timercount;
                if(dev_key_s.timer_hour > 12){
                dev_key_s.timer_hour = 0;
                }
                //Avoid triggering the shutdown directly by pressing the timing button for the first time
                if(dev_key_s.timer_hour > 0){
                    dev_key_s.key_notice = 1;
                }else
                {
                    dev_key_s.key_notice = 0;
                }

                dev_key_s.temp_time_count = timercount + dev_key_s.timer_hour*360;
                timer2_init();
                display_num(dev_key_s.timer_hour);
                display_status(0x04);
            }
        }
        break;
        case KEY3:
        {
            //Set target temperature
            if(dp_memory_s.switch_bool)
            {
                timer1_init();
                PR_DEBUG("key_value3");
                static uint8_t cur_set_temp = 0;
                cur_set_temp = dp_memory_s.set_temper_value;
                cur_set_temp = (cur_set_temp + 1 < 41) ? (cur_set_temp + 1) : 15;
                set_termper_handle(cur_set_temp);
                timer2_init();
                display_num(dp_memory_s.set_temper_value);
                display_status(0x02);
            }
        }
        break;
        case KEY4:
        {
            //Short press:led mode key  long press: Reconfigure the network mode
            if(dp_memory_s.switch_bool)
            {
                timer1_init();
                static uint32_t count = 0;
                while(tuya_gpio_read(9) == 0){
                    count++;
                    if(count > 700000){
                        break;
                    }
                }
                if(count > 700000){
                    count = 0;
                    PR_DEBUG("wf_unc");
                    tuya_iot_wf_gw_unactive();
                }else{
                    PR_DEBUG("key_value4");
                    static uint8_t cur_ledmode = 0;
                    cur_ledmode = dp_memory_s.led_mode;
                    cur_ledmode = (cur_ledmode + 1 < 5) ? (cur_ledmode + 1) : 0;
                    led_handle(cur_ledmode);
                }
                PR_DEBUG("led mode:%d",dp_memory_s.led_mode);
            }            

        }
        break;
        case KEY5:
        {
            //switch key
            timer1_init();
            PR_DEBUG("key_value5");
            if(dp_memory_s.switch_bool == 1){
                user_switch_handle(0);
                tm1650_close();
            }else
            {
                user_switch_handle(1);
            }
        }
        break;
        case KEY6:
        {
            PR_DEBUG("key_value6");
        }
        break;
        case KEY7:
        {
            PR_DEBUG("key_value7");
        }
        break;
        case KEY8:
        {
            PR_DEBUG("key_value8");
        }
        break;
        default:
        break;
        }
        //set timing time
        PR_DEBUG("KEY_HOUR:%d",dev_key_s.timer_hour);
        
        

    

    }
}
  • 此时一个暖风机的触摸按键控制功能就已经实现了,从刚开始的开发到现在终于有了暖风机的框架了,后面我们将实现暖风机的LED控制、断电记忆功能以及云端控制功能。

技术支持

您可以通过以下方法获得涂鸦的支持:

以上是关于涂鸦智能暖风机软件实现之利用TC309实现触摸按键控制功能的主要内容,如果未能解决你的问题,请参考以下文章

涂鸦智能暖风机软件实现之利用TM1650实现显示功能

涂鸦智能暖风机软件实现之利用B3950实现温度采集功能

涂鸦智能暖风机软件实现之云端控制(完结)

涂鸦智能暖风机软件实现之LED驱动和断电记忆功能

涂鸦智能烧水壶软件实现之TS02N触摸按键驱动

涂鸦智能暖风机-FAQ