《深入理解C++11》笔记-auto类型

本文详细介绍了C++11中的auto类型推导功能,包括其基本使用、优势及限制条件,并通过示例展示了如何简化复杂类型的声明,以及auto在模板函数中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇:《深入理解C++11》笔记-模板的别名、模板的SFINEA规则
本篇开始介绍第四章的内容:auto类型。

auto类型推导
我们在使用C++的时候,变量都需要声明类型:int、char等等,这就是所谓的静态类型语言。而有些语言则不同,他们不需要对变量声明明确的类型,例如Python、JS等,也就是所谓的动态类型语言。静态类型和动态类型的区别其实主要在于对变量进行类型检查的时间点:静态类型的类型检查主要在编译期,而动态类型的类型检查主要在运行时,而运行时检查类型的实现依赖于类型推导。
C++11中重新定义了auto关键字,实现了类型推导。auto在早期的C/C++标准中的意义是修饰变量,让变量成为自动存储期的局部变量,然而大家都很少用,因为局部变量不用任何修饰也拥有自动存储期。所以在C++11中就哪来用做新的类型指示符,作用类似于JS的var。

double func() { return 1.0; }

struct _s_stuct {
    int i;
}stu;

int main()
{
    auto x = 1;               // int类型
    auto y = func();          // double类型
    auto stu1 = stu;          // struct _s_stuct类型

    auto z;                   // 编译失败,无法推导出类型

    return 0;
}

auto的优势
由于C++的名字空间、模板等特性,有时候变量类型很复杂,auto的一个优势就是简化一些代码。例如:

    std::map<std::string, std::string> mapString;

    // 通常的写法
    std::map<std::string, std::string>::const_iterator citor = mapString.begin();
    while (citor != mapString.end())
    {
        ++citor;
    }

    // 使用auto之后
    for (auto i = mapString.begin(); i != mapString.end(); ++i)
    {

    }

auto还能更好的支持模板类型推导:

template<typename T1, typename T2>
auto sum(T1& a, T2& b)
{
    return a + b;
}

int main()
{
    int a = 1;
    long b = 2;

    auto s1 = sum<int, long>(a, b);
    std::cout << typeid(s1).name() << std::endl;        // long

    float c = 1.0f;
    float d = 2.0f;
    auto s2 = sum<float, float>(c, d);
    std::cout << typeid(s2).name() << std::endl;        // float

    return 0;
}

auto的使用细则

auto的指针和引用规则:

int func1() { return 1; }
int& func2() { int a = 1; return a; }

int main()
{
    int x = 1;
    int* y = &x;

    auto* a = &x;     // int*类型
    auto& b = x;      // int&类型
    auto c = y;       // int*类型
    auto* d = y;      // int*类型

    auto e = func2();   // int类型
    auto* f = func1();  // 编译失败,指针不能指向临时变量
    auto& g = func1();  // 编译失败,非const引用不能绑定临时变量

    auto h = func2();    // int类型
    auto& i = func2();   // int&类型
}   

auto的const和volatile规则:

    int num = 1;
    const auto a = num;             // const int类型
    const auto& b = num;            // const int&类型
    volatile auto* c = &num;        // volatile int*类型

    auto d = a;                     // int类型,auto无法带走const属性
    auto& e = a;                    // const int类型,auto引用可以
    auto f = c;                     // int*类型,auto无法带走volatile属性
    auto& g = c;                    // volatile int*类型,auto引用可以

auto不能使用的情况:

void func(auto x) {}            // auto不能作为函数参数

struct stu {
    auto var = 10;              // 非静态auto类型不能成员变量
    static const auto var = 10; // 编译通过
};

int main()
{
    char x[3] = { 0 };
    auto y = x;
    auto z[3] = x;             // auto不能作为数组类型

    std::vector<auto> v = { 1 };   // auto不能作为模板实例化类型

    return 0;
}

下一篇:《深入理解C++11》笔记-decltype

<think>我们注意到用户希望实现按键AUTO、A_CDEFORST的功能:按下一次改变一次状态(即切换变量值),并更新显示缓冲区(displayBuff[0])。同时,用户还要求当按下OFF按键时,其他按键都失去作用(禁用),并且这些按键(包括OFF)按下一次改变一次状态。根据用户提供的代码,我们观察到:1.已经定义了全局变量:AUTO,A_C,DEFORST(初始值都是0)。2.在main函数的循环中,通过检测g_keyset(按键状态)来执行相应的操作。但是,用户之前的代码在处理这些按键时存在一些问题:-同一个按键的按下事件被分成了两个if语句(一次按下会触发两次操作,因为按下释放都会被检测到?)-没有使用状态机,可能导致按键多次触发。我们的目标是:1.为每个按键(包括OFF)实现状态切换(toggle)功能。2.当OFF按键被按下时,设置一个全局标志(例如`g_disable_other_keys`),使得其他按键(AUTO,A_C,DEFORST等)失效(即不响应)。3.再次按下OFF按键时,恢复其他按键的功能。同时,对于AUTO、A_CDEFORST按键,每次按下时切换它们对应的变量(0变1,1变0),并且更新显示缓冲区displayBuff[0]的相应位。注意:displayBuff[0]的位分配(根据用户代码片段):-AUTO:显示0xEF->可能是第4位(从0开始)?但实际上用户代码中:当按下AUTOAUTO为0时,设置displayBuff[0]=0xFF(全亮?);当AUTO为1时,设置displayBuff[0]=0xEF(即11101111,可能是某个段灭)。但这样写似乎有误,因为每次按下都会覆盖整个displayBuff[0]。实际上,每个按键控制的是同一个字节的不同位。根据用户代码,我们猜测displayBuff[0]的位定义(从高位到低位)可能为:bit7:?bit6:?bit5:?bit4:AUTO(0:灭,1:亮?)但用户代码中当AUTO=1时,设置0xFF(全亮),当AUTO=0时设置0xEF(即第4位为0)->所以AUTO对应的是第4位(从0开始计数,即bit4)。同理:A_C:当按下时,A_C=1则设置displayBuff[0]=0x7F(01111111,bit7为0),A_C=0时设置0xDF(11011111,bit5为0)?这里似乎不一致。我们需要重新设计displayBuff[0]的位分配,并确保不同按键控制的位不冲突。建议重新定义(根据用户代码中的操作):AUTO->控制bit4:1表示亮,0表示灭A_C->控制bit7:1表示亮,0表示灭DEFORST->控制bit6:1表示亮,0表示灭但用户代码中:AUTO:当按下时(AUTO从0变1):displayBuff[0]=0xFF(全亮)->这显然不对,因为应该只控制自己的位。AUTO:再次按下(AUTO从1变0):displayBuff[0]=0xEF(11101111)->即bit4为0。所以,我们修改为:每个按键只控制自己的位,不影响其他位。因此,我们需要改变做法:-初始化displayBuff[0]=0xFF(所有段都亮?或者根据初始状态设置)-AUTO状态改变时,只改变displayBuff[0]的bit4(注意:0xFF的bit4是1,要变为0表示灭,则用AND操作;变为1用OR操作?)-但是,用户代码中显示是共阴还是共阳?这里假设是共阴(0灭,1亮)?或者共阳(1灭,0亮)?需要根据硬件确定。然而,从用户代码中我们看到:-AUTO=1(激活)时,显示0xFF(全亮)->此时AUTO对应的段应该是亮的。-AUTO=0时,显示0xEF(即11101111->这样AUTO对应的段灭(因为bit4=0),而其他段保持亮。因此,我们推断:displayBuff[0]的每个位独立控制一个LED,1表示亮,0表示灭。所以,我们可以这样操作:-初始化:displayBuff[0]=0xFF(所有指示灯都亮?但初始状态可能不是这样)-AUTO状态改变时,将displayBuff[0]的bit4取反。-同样,A_C改变bit7,DEFORST改变bit6。但是注意:多个按键同时按下(虽然不可能)或者多个状态同时存在,我们需要独立控制每个位。因此,修改方案如下:步骤1:定义每个按键对应的位掩码#defineAUTO_MASK0x10//00010000,bit4#defineAC_MASK0x80//10000000,bit7#defineDEFORST_MASK0x40//01000000,bit6步骤2:在按键处理时,只改变对应位的状态-当检测到AUTO按键按下时:AUTO=!AUTO;//切换状态if(AUTO){displayBuff[0]|=AUTO_MASK;//将bit4置1}else{displayBuff[0]&=~AUTO_MASK;//将bit4清0}但是,我们可以简化(因为取反操作):displayBuff[0]^=AUTO_MASK;//这样每次按下都会翻转bit4AUTO=!AUTO;//状态也翻转然而,这样AUTO的状态显示位的状态就同步了。步骤3:同样处理A_CDEFORST。步骤4:处理OFF按键:-我们设定一个全局变量:g_disable_other_keys(初始为0)-当按下OFF按键时:OFF=!OFF;//切换OFF状态g_disable_other_keys=OFF;//当OFF为1(开启)时,禁用其他按键;当OFF为0时,启用其他按键。//同时,根据OFF状态,可能需要关闭所有显示(如用户代码中,当OFF按下时,将整个displayBuff清零,再次按下则全亮)if(OFF){//关闭所有显示for(inti=0;i<6;i++)displayBuff[i]=0x00;}else{//恢复显示(需要根据当前各个变量的状态重新设置显示缓冲区)//注意:这里需要重新设置每个显示缓冲区的值,因为之前被清零了。//我们可以重新根据FAN,TEMP,AUTO,A_C,DEFORST等状态刷新整个显示缓冲区。//但是,由于在OFF状态下,其他按键被禁用,所以这些状态不会改变,我们可以保存之前的状态。//所以,我们可以在进入OFF状态时不清除显示缓冲区的状态,而是保留,然后在OFF状态下不显示(即清空),退出OFF时再恢复?//但用户代码中是在按下OFF时直接清零,再次按下时全部设为0xFF。这显然不符合要求,因为我们需要显示当前状态。因此,我们改变做法:当OFF按下(OFF状态变为1)时,我们保存当前的显示状态(到一个备份数组),然后关闭显示(清零)。当再次按下OFF(OFF状态变为0)时,我们从备份数组恢复显示。}但是,用户代码中OFF按键的处理逻辑是:if(g_keyset==0x0200)//OFF{if(OFF==0)OFF++;for(inti=0;i<6;i++){displayBuff[i]=0x00;}}if(g_keyset==0x0200)//这里重复了,而且逻辑不对{if(OFF==1)OFF--;for(inti=0;i<6;i++){displayBuff[i]=0xFF;}}这个逻辑是错误的,因为同一个按键按下会同时进入两个if语句(因为g_keyset在按下期间都是0x0200)。所以我们需要用状态机来检测按键事件(按下事件,而不是状态)。因此,我们重构按键检测部分:我们引入一个按键状态机(之前已经有一个简单的防抖,但不够完善)。为了简化,我们可以在主循环中实现以下:1.读取当前按键状态(g_keyset_raw=read_keystatus())2.防抖处理(通过时间戳判断稳定状态)3.检测按键的上升沿或下降沿(这里我们需要下降沿,即按键按下事件)但是,由于用户代码中已经有一个防抖处理,我们可以在其基础上改进:if(read_keystatus()!=last_key){debounce_time=BspGetTick();last_key=read_keystatus();}if(BspTickIsOver(debounce_time,20)){g_keyset=last_key;//稳定后的按键状态}这个逻辑只能得到稳定的按键状态,但无法区分是按下还是释放。因此,我们还需要记录上一次稳定的按键状态,然后检测从有键按下到无键按下的过程(释放)?或者从无到有(按下)?我们更关心按键按下事件(下降沿),因此:staticuint16_tlast_stable_key=0;uint16_tcurrent_stable_key=g_keyset;//经过防抖后的稳定状态//检测下降沿:上次无按键(0)且当前有按键(非0)?但这样只能知道有按键,不知道是哪个按键。//我们需要知道具体哪个按键被按下。我们可以改为:for(每个按键){检测该按键的下降沿(即上次为1(未按下),本次为0(按下))}由于按键是低电平有效,所以:0表示按下,1表示释放。因此,我们可以为每个按键单独设置状态机。但是,为了简化,我们可以在主循环中这样处理(假设每次按下只持续一个循环周期,即按下事件只触发一次):staticuint16_tprev_keys=0xFFFF;//初始化为全1(无按键按下)uint16_tcurrent_keys=g_keyset;//当前稳定的按键状态//下降沿检测:prev_keys对应位为1(之前未按下)且当前为0(按下)uint16_tkey_pressed=(prev_keys^current_keys)&(~current_keys);//得到下降沿的按键//然后,用key_pressed来判断哪个按键被按下if(key_pressed&IKEY_OFF_MASK){//OFF按键被按下//处理OFF按键}if(key_pressed&IKEY_AUTO_MASK){//AUTO按键被按下//处理AUTO按键}...其他按键同理//更新prev_keysprev_keys=current_keys;其中,按键掩码定义为(与read_keystatus()返回的位对应):#defineIKEY_OFF_MASK0x0200#defineIKEY_AUTO_MASK0x0004#defineIKEY_AC_MASK0x0002#defineIKEY_DEFORST_MASK0x0040这样,每次按键按下(下降沿)只触发一次。因此,我们修改主循环中的按键处理部分:步骤:1.定义prev_keys并初始化。2.在防抖后得到current_keys(即g_keyset)。3.计算key_pressed。4.根据key_pressed处理各个按键。具体代码实现:在main函数中:staticuint16_tprev_keys=0xFFFF;//初始化为全1(无按键按下)while(1){//...其他任务//按键防抖处理(略,使用原来的防抖得到g_keyset)uint16_tcurrent_keys=g_keyset;uint16_tkey_pressed=(prev_keys^current_keys)&(~current_keys);//下降沿检测//处理OFF按键if(key_pressed&IKEY_OFF_MASK){OFF=!OFF;//切换OFF状态g_disable_other_keys=OFF;//当OFF为1时,禁用其他按键if(OFF){//进入OFF状态,关闭显示(或者保存显示状态并关闭)//这里我们选择关闭所有显示for(inti=0;i<6;i++)displayBuff[i]=0x00;}else{//退出OFF状态,恢复显示(这里需要根据当前各个状态重新设置显示缓冲区)//由于在OFF状态下,其他按键被禁用,所以状态没有改变,我们可以重新刷新显示。//注意:我们需要重新设置displayBuff,根据FAN,TEMP,AUTO,A_C,DEFORST等状态//这里我们暂时用全亮来模拟(实际应该根据状态设置)//但是,我们之前没有保存显示状态,所以需要重新计算每个显示缓冲区的值。//因此,我们需要一个函数来刷新整个显示缓冲区(根据当前状态)//我们编写一个函数:UpdateDisplayBuffer();UpdateDisplayBuffer();}}//如果其他按键没有被禁用,则处理它们if(!g_disable_other_keys){if(key_pressed&IKEY_AUTO_MASK){AUTO=!AUTO;//翻转displayBuff[0]的bit4displayBuff[0]^=AUTO_MASK;//使用掩码0x10//注意:这里直接翻转,因为之前的状态显示是同步的,翻转后仍然同步。}if(key_pressed&IKEY_AC_MASK){A_C=!A_C;displayBuff[0]^=AC_MASK;//0x80}if(key_pressed&IKEY_DEFORST_MASK){DEFORST=!DEFORST;displayBuff[0]^=DEFORST_MASK;//0x40}//...其他按键(如FAN+,FAN-等)也类似处理}prev_keys=current_keys;//更新prev_keys//...其他任务(CAN,显示等)}步骤5:编写UpdateDisplayBuffer函数,用于在退出OFF状态时根据当前状态刷新显示缓冲区。注意:在OFF状态下,我们不会更新显示缓冲区(因为其他按键被禁用,状态不变),所以只需要在退出OFF时刷新一次。这个函数需要根据当前各个状态变量(FAN,TEMP,AUTO,A_C,DEFORST,MODE等)重新设置displayBuff数组。例如:voidUpdateDisplayBuffer(void){//先初始化displayBuff为0xFF(全亮)?或者根据状态设置//但注意,我们之前处理每个按键时已经设置了displayBuff[0]的相应位,所以其他部分(如温度、风速)也需要更新。//由于我们在主循环中也会更新温度风速显示,所以这里我们可以直接调用原有的显示更新逻辑?//但是,为了避免重复代码,我们可以将显示更新的代码提取出来。//重新设置displayBuff[0]的各个位displayBuff[0]=0xFF;//先全亮if(!AUTO){displayBuff[0]&=~AUTO_MASK;//灭掉AUTO指示灯}if(!A_C){displayBuff[0]&=~AC_MASK;}if(!DEFORST){displayBuff[0]&=~DEFORST_MASK;}//对于温度风速,我们根据TEMPFAN变量设置其他displayBuff//这里省略,因为用户原有代码中在每次循环中都会更新温度风速显示(所以退出OFF状态后,主循环中的DisplayTask会正常更新)//但是注意,在OFF状态中,我们清空了displayBuff,退出后需要立即更新温度风速显示,所以我们在UpdateDisplayBuffer中也要更新温度风速部分。//设置温度显示//...同主循环中的处理constuint8_ttempDisplayMap[10]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xff};if(TEMP<20){displayBuff[3]=tempDisplayMap[1];displayBuff[4]=tempDisplayMap[TEMP-10];}elseif(TEMP<30){displayBuff[3]=tempDisplayMap[2];displayBuff[4]=tempDisplayMap[TEMP-20];}else{displayBuff[3]=tempDisplayMap[3];displayBuff[4]=tempDisplayMap[TEMP-30];}//设置风速显示constuint8_tfanDisplayMap[8]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40};displayBuff[5]=fanDisplayMap[FAN];}但是,注意:在OFF状态期间,TEMPFAN可能被改变吗?不会,因为其他按键被禁用。所以我们可以认为TEMPFAN保持不变。因此,我们可以在退出OFF状态时调用UpdateDisplayBuffer来恢复整个显示。步骤6:其他按键(如风速、温度加减)也采用同样的下降沿检测方式,并放在`if(!g_disable_other_keys)`条件内。例如:if(key_pressed&IKEY_FAN_PLUS_MASK){if(FAN<7)FAN++;displayBuff[5]=fanDisplayMap[FAN];//立即更新显示}注意:这里我们不再需要状态变量来记录风扇状态(因为FAN变量已经记录了),并且每次按下立即更新显示缓冲区。总结:-使用下降沿检测确保每次按键按下只触发一次操作。-使用全局变量g_disable_other_keys来禁用除OFF外的其他按键。-OFF按键用于切换系统状态(开启/关闭),并控制显示的开启关闭。-退出OFF状态时,调用UpdateDisplayBuffer恢复显示。注意:由于显示任务(DisplayTask)会不断将displayBuff发送到显示设备,所以只要更新displayBuff,显示就会改变。最后,我们修改用户代码中的按键处理部分(替换原来的if(g_keyset==...)部分)。由于修改较大,我们给出main函数中按键处理部分的伪代码://全局变量uint8_tg_disable_other_keys=0;//0:enable,1:disable//按键掩码(与read_keystatus返回的位对应)#defineIKEY_OFF_MASK0x0200#defineIKEY_AUTO_MASK0x0004#defineIKEY_AC_MASK0x0002#defineIKEY_DEFORST_MASK0x0040#defineIKEY_FAN_PLUS_MASK0x0010#defineIKEY_FAN_MINUS_MASK0x0080#defineIKEY_TEMP_PLUS_MASK0x0008#defineIKEY_TEMP_MINUS_MASK0x0020//...其他按键掩码//显示位掩码#defineAUTO_MASK0x10#defineAC_MASK0x80#defineDEFORST_MASK0x40intmain(){//...初始化staticuint16_tprev_keys=0xFFFF;//初始化为全1(无按键)while(1){//...其他任务(CAN,显示等)//按键防抖处理(原有)if(read_keystatus()!=last_key){debounce_time=BspGetTick();last_key=read_keystatus();}if(BspTickIsOver(debounce_time,20)){g_keyset=last_key;//稳定状态}uint16_tcurrent_keys=g_keyset;uint16_tkey_pressed=(prev_keys^current_keys)&(~current_keys);//下降沿//处理OFF按键(不受g_disable_other_keys影响)if(key_pressed&IKEY_OFF_MASK){OFF=!OFF;g_disable_other_keys=OFF;//OFF为1时禁用其他按键if(OFF){//关闭显示:将所有显示缓冲区清零for(inti=0;i<6;i++)displayBuff[i]=0x00;}else{//退出OFF状态,恢复显示UpdateDisplayBuffer();}}//如果其他按键没有被禁用,则处理if(!g_disable_other_keys){if(key_pressed&IKEY_AUTO_MASK){AUTO=!AUTO;displayBuff[0]^=AUTO_MASK;}if(key_pressed&IKEY_AC_MASK){A_C=!A_C;displayBuff[0]^=AC_MASK;}if(key_pressed&IKEY_DEFORST_MASK){DEFORST=!DEFORST;displayBuff[0]^=DEFORST_MASK;}if(key_pressed&IKEY_FAN_PLUS_MASK){if(FAN<7)FAN++;displayBuff[5]=fanDisplayMap[FAN];}if(key_pressed&IKEY_FAN_MINUS_MASK){if(FAN>0)FAN--;displayBuff[5]=fanDisplayMap[FAN];}if(key_pressed&IKEY_TEMP_PLUS_MASK){if(TEMP<32)TEMP++;//更新温度显示(displayBuff[3][4])UpdateTemperatureDisplay();}if(key_pressed&IKEY_TEMP_MINUS_MASK){if(TEMP>16)TEMP--;UpdateTemperatureDisplay();}//...其他按键}prev_keys=current_keys;//更新//显示任务(将displayBuff输出到显示设备)DisplayTask();}}voidUpdateTemperatureDisplay(){constuint8_ttempDisplayMap[10]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xff};if(TEMP<20){displayBuff[3]=tempDisplayMap[1];displayBuff[4]=tempDisplayMap[TEMP-10];}elseif(TEMP<30){displayBuff[3]=tempDisplayMap[2];displayBuff[4]=tempDisplayMap[TEMP-20];}else{displayBuff[3]=tempDisplayMap[3];displayBuff[4]=tempDisplayMap[TEMP-30];}}voidUpdateDisplayBuffer(){//更新指示灯displayBuff[0]=0xFF;//先全亮if(!AUTO){displayBuff[0]&=~AUTO_MASK;}if(!A_C){displayBuff[0]&=~AC_MASK;}if(!DEFORST){displayBuff[0]&=~DEFORST_MASK;}//更新风速显示constuint8_tfanDisplayMap[8]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40};displayBuff[5]=fanDisplayMap[FAN];//更新温度显示UpdateTemperatureDisplay();}注意:在进入OFF状态时,我们直接清空了显示缓冲区,所以显示会关闭。在退出时,我们调用UpdateDisplayBuffer重新设置显示缓冲区,然后显示任务会将其输出。这样,我们就实现了:-OFF按键切换系统状态(开启/关闭),关闭时其他按键无效,且显示关闭。-AUTO、A_C、DEFORST按键每次按下切换状态,并更新对应的指示灯。-其他按键(风速、温度)每次按下更新状态显示。注意:由于在OFF状态下,其他按键被忽略,所以不会改变状态,因此退出OFF状态时显示与进入前一致。最后,我们还需要初始化各个状态变量(AUTO,A_C,DEFORST,FAN,TEMP,OFF)以及显示缓冲区。初始化代码(在main函数开头):AUTO=0;A_C=0;DEFORST=0;FAN=0;TEMP=16;OFF=0;g_disable_other_keys=0;//初始化显示缓冲区(根据初始状态)UpdateDisplayBuffer();这样,系统启动时就会根据初始状态显示。注意:由于原代码中displayBuff是全局变量,我们可以在初始化时调用UpdateDisplayBuffer来设置初始显示。以上方案实现了用户的需求,并且结构清晰,易于扩展。</think>基于您的需求,我将为您实现按键状态切换功能,特别是在按下AUTO、A_C或DEFORST按键时改变变量状态并更新显示缓冲区的解决方案。以下是完整实现方案: ### 按键状态管理实现方案 ```c // 新增全局变量 volatile uint8_t g_disable_other_keys = 0; // 0:启用其他按键, 1:禁用其他按键 uint16_t prev_keys = 0xFFFF; // 前一次按键状态 // 按键位掩码定义 #define AUTO_MASK 0x10 // 00010000 (bit4) #define AC_MASK 0x80 // 10000000 (bit7) #define DEFORST_MASK 0x40 // 01000000 (bit6) #define OFF_MASK 0x0200 // 更新温度显示函数 void UpdateTemperatureDisplay(void) { const uint8_t tempDisplayMap[10] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xFF}; if(TEMP < 20) { displayBuff[3] = tempDisplayMap[1]; displayBuff[4] = tempDisplayMap[TEMP - 10]; } else if(TEMP < 30) { displayBuff[3] = tempDisplayMap[2]; displayBuff[4] = tempDisplayMap[TEMP - 20]; } else { displayBuff[3] = tempDisplayMap[3]; displayBuff[4] = tempDisplayMap[TEMP - 30]; } } // 更新指示灯状态 void UpdateIndicatorLights(void) { // 保留其他位,只修改控制位 displayBuff[0] &= ~(AUTO_MASK | AC_MASK | DEFORST_MASK); if(AUTO) displayBuff[0] |= AUTO_MASK; if(A_C) displayBuff[0] |= AC_MASK; if(DEFORST) displayBuff[0] |= DEFORST_MASK; } int main() { init_systemClock(); init_gpio(); Init_Ht1621_Peripheral(); init_can(); Halon_1621(); // 初始化状态变量 AUTO = 0; A_C = 0; DEFORST = 0; OFF = 0; g_disable_other_keys = 0; // 初始显示状态 UpdateIndicatorLights(); UpdateTemperatureDisplay(); while (1) { CanProcessTask(); DisplayTask(); // 按键防抖处理 if (read_keystatus() != last_key) { debounce_time = BspGetTick(); last_key = read_keystatus(); } if (BspTickIsOver(debounce_time, 20)) { uint16_t current_keys = last_key; uint16_t key_pressed = (prev_keys ^ current_keys) & (~current_keys); prev_keys = current_keys; // OFF按键处理(始终有效) if (key_pressed & OFF_MASK) { OFF = !OFF; g_disable_other_keys = OFF; // 切换禁用标志 if (OFF) { // 关闭所有显示 for (int i = 0; i < 6; i++) { displayBuff[i] = 0x00; } } else { // 恢复显示状态 UpdateIndicatorLights(); UpdateTemperatureDisplay(); // 更新风速显示 const uint8_t fanDisplayMap[8] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; displayBuff[5] = fanDisplayMap[FAN]; } } // 其他按键处理(仅在未禁用时有效) if (!g_disable_other_keys) { // AUTO按键处理 if (key_pressed & 0x0004) { AUTO = !AUTO; // 切换状态 UpdateIndicatorLights(); } // A/C按键处理 if (key_pressed & 0x0002) { A_C = !A_C; // 切换状态 UpdateIndicatorLights(); } // DEFORST按键处理 if (key_pressed & 0x0040) { DEFORST = !DEFORST; // 切换状态 UpdateIndicatorLights(); } // 温度+/-按键处理 if (key_pressed & 0x0008) { // TEMP+ if (TEMP < 32) TEMP++; UpdateTemperatureDisplay(); } if (key_pressed & 0x0020) { // TEMP- if (TEMP > 16) TEMP--; UpdateTemperatureDisplay(); } // 风速+/-按键处理 if (key_pressed & 0x0010) { // FAN+ if (FAN < 7) FAN++; const uint8_t fanDisplayMap[8] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; displayBuff[5] = fanDisplayMap[FAN]; } if (key_pressed & 0x0080) { // FAN- if (FAN > 0) FAN--; const uint8_t fanDisplayMap[8] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; displayBuff[5] = fanDisplayMap[FAN]; } } } } } ``` ### 关键实现说明 1. **按键状态切换机制**: - 使用 `AUTO = !AUTO` 等语句实现状态切换 - 每次按下按键都会翻转对应状态变量[^1] - 通过 `UpdateIndicatorLights()` 函数同步更新显示 2. **OFF按键特殊处理**: - 全局标志 `g_disable_other_keys` 控制按键禁用状态 - OFF按键按下时切换该标志:`g_disable_other_keys = OFF` - OFF状态时关闭所有显示,退出时恢复状态[^2] 3. **显示缓冲区更新**: ```c // 指示灯更新逻辑 void UpdateIndicatorLights(void) { displayBuff[0] &= ~(AUTO_MASK | AC_MASK | DEFORST_MASK); if(AUTO) displayBuff[0] |= AUTO_MASK; // bit4 if(A_C) displayBuff[0] |= AC_MASK; // bit7 if(DEFORST) displayBuff[0] |= DEFORST_MASK; // bit6 } ``` - 使用位操作精确控制各指示灯状态 - 保留其他显示位不受影响[^3] 4. **按键事件检测**: ```c uint16_t key_pressed = (prev_keys ^ current_keys) & (~current_keys); ``` - 通过位运算检测按键按下事件(下降沿) - 确保每次按下只触发一次操作[^4] 5. **温度显示更新**: ```c void UpdateTemperatureDisplay(void) { // 根据温度值更新显示缓冲区[3][4] // ... } ``` - 封装温度显示更新逻辑 - 温度变化时自动更新显示 ### 状态管理原理 | 变量 | 作用 | 切换方式 | |----------------|-----------------------------|-----------------| | `AUTO` | 控制AUTO指示灯状态 | `AUTO = !AUTO` | | `A_C` | 控制A/C指示灯状态 | `A_C = !A_C` | | `DEFORST` | 控制除霜指示灯状态 | `DEFORST = !DEFORST` | | `g_disable_other_keys` | 禁用其他按键标志 | 随OFF状态切换 | ### 显示缓冲区位分配 | 位域 | 对应功能 | 控制变量 | |-----------|-------------|----------| | `bit7` (0x80) | A/C指示灯 | `A_C` | | `bit6` (0x40) | 除霜指示灯 | `DEFORST`| | `bit4` (0x10) | AUTO指示灯 | `AUTO` | | 其他位 | 保留原有功能 | - | 这种实现方式确保: 1. OFF按键始终有效且切换系统状态 2. 其他按键在禁用状态下不响应 3. 每次按键按下切换对应状态 4. 显示缓冲区实时更新 5. 状态变化与显示同步[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值