单片机中面向对象的思维

发布于:2025-06-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言:

        面向对象的编程方式用在C语言中,听起来是不是很怪。从C语言入门开始,老师就和你说过,C语言是面向过程的,你现在却要用它来实现面向对象操作。可能是太久没谈对象了,想要new一个对象出来解闷。开玩笑的,面向对象自然有面向对象的好处,C语言是面向过程的语言,他的小弟C++是半面向对象,半面向过程的。所以我们使用C语言类比C++的方式实现面向对象的操作。面向对象三大考点,封装,继承,多态。有的时候我们在操作的时候会面临重复的操作,你有想到过面向对象的特点嘛,考虑面向对象的特点是不是就可以避免重复的操作。

一、面向对象特点与面向过程的类比

        面向过程的代码就是便于直观的查看整体流程,而面向对象的则需要你能够将你所需要的部分抽象出来,但是不管是整么个方式,程序猿最重要的还是代码能跑就行,不然就得你,我的朋友跑了。C++中对象class(类)包含的的成员属性,成员函数,C语言是没有这个叫法的,但是我们可以通过struct结构体成员类比出来,通过void*万能指针实现成员函数的调用。

二、总结:你就得有抽象思维,只要够抽象就不怕找不到对象。

三、接下来我以按键为例简单展示一下单片机中的面向对象思维。

3.1 先简单说一下按键功能:

        我们通常在单片机中用按键作为外部触发事件,按下按键,实现一段操作,松开按键实现另外的操作,每次按键的触发都会存在 “消抖” 问题,每次消抖都是在重复一样的操作,这个时候我就想到了,既然是重复的操作,那干嘛写那么多重复的函数呢,所以我就想到了面向对象的多态。只要创建对象,改写一下他的成员函数就可以了。下面是我原先按键的代码(面向过程的方式消抖)。

/*****************************************************************************
 函 数 名  : KEY1EintProc
 功能描述  : KEY1 消抖延时函数,双边沿中断触发中断后开启20ms定时任务
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY1EintProc(void)
{
    u8 mkeyStatu = KEY_VALUE_RELEASED;
    mkeyStatu=KEY1_STATUS_READ;
    if((mkeyStatu == KEY_VALUE_PRESSED)&&(KEY_VALUE_RELEASED == KEY1LastState))
    {
         DelayMs(20);
        mkeyStatu=KEY1_STATUS_READ;
        if(KEY_VALUE_PRESSED==mkeyStatu)//KEY1 按下
        {
            KEY1LastState=mkeyStatu;
            KEY1_NOTICE_OUT_EN_0;//按键1被拉低后,拉高对应引脚通知安卓端
        }
    }else if((mkeyStatu == KEY_VALUE_RELEASED)&&( KEY_VALUE_PRESSED== KEY1LastState))//松开
    {
         DelayMs(20);
        mkeyStatu=KEY1_STATUS_READ;
        if(KEY_VALUE_RELEASED==mkeyStatu)//KEY1释放
        {
            KEY1LastState=mkeyStatu;
            KEY1_NOTICE_OUT_EN_1;//按键1被拉高后,拉高对应引脚通知安卓端
        }
    }
}
/*****************************************************************************
 函 数 名  : KEY2EintProc
 功能描述  : KEY2 消抖延时函数,双边沿中断触发中断后开启20ms定时任务
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY2EintProc(void)
{
    u8 mkeyStatu = KEY_VALUE_RELEASED;
    mkeyStatu=KEY2_STATUS_READ;
    if((mkeyStatu == KEY_VALUE_PRESSED)&&(KEY_VALUE_RELEASED == KEY2LastState))
    {
         DelayMs(20);
        mkeyStatu=KEY2_STATUS_READ;
        if(KEY_VALUE_PRESSED==mkeyStatu)//KEY2按下
        {
            KEY2LastState=mkeyStatu;
            KEY2_NOTICE_OUT_0;//拉低
        }
    }else if((mkeyStatu == KEY_VALUE_RELEASED)&&( KEY_VALUE_PRESSED== KEY2LastState))//松开
    {
         DelayMs(20);
        mkeyStatu=KEY2_STATUS_READ;
        if(KEY_VALUE_RELEASED==mkeyStatu)
        {
            KEY2LastState=mkeyStatu;
            KEY2_NOTICE_OUT_1;//拉高
        }
    } 
}
/*****************************************************************************
 函 数 名  : KEY3EintProc
 功能描述  : PTT 消抖延时函数,双边沿中断触发中断后开启20ms定时任务
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY3EintProc(void)
{
    u8 mkeyStatu = KEY_VALUE_RELEASED;
    mkeyStatu=KEY3_STATUS_READ;
    if((mkeyStatu == KEY_VALUE_PRESSED)&&(KEY_VALUE_RELEASED == KEY3LastState))
    {
         DelayMs(20);
        mkeyStatu=KEY3_STATUS_READ;
        if(KEY_VALUE_PRESSED==mkeyStatu)//KEY3 按下
        {
            KEY3LastState=mkeyStatu;
             KEY3_NOTICE_OUT_0;//拉低,按下KEY3后对应操作
        }
    }else if((mkeyStatu == KEY_VALUE_RELEASED)&&( KEY_VALUE_PRESSED== KEY3LastState))//松开
    {
         DelayMs(20);
        mkeyStatu=KEY3_STATUS_READ;
        if(KEY_VALUE_RELEASED==mkeyStatu)//KEY3 松开相对操作
        {
            KEY3LastState=mkeyStatu;
            KEY3_NOTICE_OUT_1;//拉高
        }
    }  
}
/*****************************************************************************
 函 数 名  :KEY4EintProc
 功能描述  : key4 消抖延时函数,双边沿中断触发中断后开启20ms定时任务
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY4EintProc(void)
{
    u8 mkeyStatu = KEY_VALUE_RELEASED;
    mkeyStatu=KEY4_STATUS_READ;
    if((mkeyStatu == KEY_VALUE_PRESSED)&&(KEY_VALUE_RELEASED ==KEY4LastState))
    {
         DelayMs(20);
        mkeyStatu=KEY4_STATUS_READ;
        if(KEY_VALUE_PRESSED==mkeyStatu)//
        {
           KEY4LastState=mkeyStatu;
           KEY4_OUT_0;//拉低
        }
    }else if((mkeyStatu == KEY_VALUE_RELEASED)&&( KEY_VALUE_PRESSED==KEY4LastState))//松开
    {
         DelayMs(20);
        mkeyStatu=KEY4_STATUS_READ;
        if(KEY_VALUE_RELEASED==mkeyStatu)//
        {
           KEY4LastState=mkeyStatu;
           KEY4_OUT_1;//拉高
        }
    } 
}

        可以看出在我消抖后的函数中一直在重复一样的内容,比较繁琐,但简单。

3.2 面向对象的操作,就需要我们将它的不变的内容抽象出来形成统一的模板。然后只需改变成员属性就可以了。下面是面向对象的操作方式。

3.2.1首先是整体对象属性

// 按键设备结构体
typedef struct {
    uint8_t (*read)(void);      // 读取状态函数指针
    void (*setState)(uint8_t);  // 读取状态后操作函数
    uint8_t lastState;          // 上次状态
} KEY_Device;

3.2.2建立对象,以及初始化对象函数

//将消抖函数抽象出来
KEY_Device KEY1;
KEY_Device KEY2;
KEY_Device KEY3;
KEY_Device KEY4;
// 初始化PTT设备
void KEY_Device_init(KEY_Device* dev, 
                   uint8_t (*readFunc)(void), 
                   void (*setStateFunc)(uint8_t)) {
    dev->read = readFunc;
    dev->setState = setStateFunc;
    dev->lastState = KEY_VALUE_RELEASED;
}

3.2.3设备对象初始化

/*****************************************************************************
 函 数 名  : KEY_DevicesInit
 功能描述  : 输入设备面向对象初始化
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY_DevicesInit(void)
{
    // 初始化KEY1
    KEY1.read = readKEY1;
    KEY1.setState = KEY1PressFunc;
    KEY1.lastState = KEY_VALUE_RELEASED;
    
    // 初始化KEY2
    KEY2.read = readKEY2;
    KEY2.setState = KEY2PressFunc;
    KEY2.lastState = KEY_VALUE_RELEASED;
    
    // 初始化KEY3
    KEY3.read = readKEY3;
    KEY3.setState = KEY3PressFunc;
    KEY3.lastState = KEY_VALUE_RELEASED;
    
    // 初始化KEY4
    KEY4.read = readKEY4;
    KEY4.setState = KEY4Func;
    KEY4.lastState = KEY_VALUE_RELEASED;
}

3.2.4 抽象出来的通用部分

// 处理KEY事件的通用方法
/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
uint8_t KEY_Device_process(KEY_Device* dev) {
    uint8_t currentState = dev->read();
    
    // 按下检测(上升沿)
    if ((currentState == KEY_VALUE_PRESSED) && (dev->lastState == KEY_VALUE_RELEASED)) {
        DelayMs(20);  // 防抖延时
        if (dev->read() == KEY_VALUE_PRESSED) {
            dev->lastState = KEY_VALUE_PRESSED;
            dev->setState(KEY_VALUE_PRESSED);
            return 1;  // 返回按下事件
        }
    } 
    // 释放检测(下降沿)
    else if ((currentState == KEY_VALUE_RELEASED) && (dev->lastState == KEY_VALUE_PRESSED)) {
        DelayMs(20);  // 防抖延时
        if (dev->read() == KEY_VALUE_RELEASED) {
            dev->lastState = KEY_VALUE_RELEASED;
            dev->setState(KEY_VALUE_RELEASED);
            return 2;  // 返回释放事件
        }
    }
    return 0;  // 无状态变化
}

3.2.5 各自的按键操作函数

/*****************************************************************************
 函 数 名  : readKEY1
 功能描述  : 读取引脚函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
uint8_t readKEY1(void)
{ 
    return KEY1_STATUS_READ;
}
/*****************************************************************************
 函 数 名  : KEY1PressFunc
 功能描述  : 读取按键后操作
 输入参数  : state:高低电平
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY1PressFunc(uint8_t state)
{
    if(state)//KEY1松开后
    {
       KEY1_NOTICE_OUT_EN_1;//通知安卓松开KEY1
    
    }else{  //KEY1 按下
       KEY1_NOTICE_OUT_EN_1; //通知安卓按下KEY1
		
    }
}
/*****************************************************************************
 函 数 名  : readKEY2
 功能描述  : 读取ptt2按键
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
// DMR PTT2 相关函数
uint8_t readKEY2(void)
{ 
    return KEY2_STATUS_READ;
}
/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 读到状态后操作
 输入参数  : state:高低电平
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY2PressFunc(uint8_t state) 
{
    if(state)//Ptt1松开后
    {
       KEY2_NOTICE_OUT_EN_1;//通知安卓松开KEY2
       //PA4 DMR 停止发射
    }else{  //ptt2 按下
       KEY2_NOTICE_OUT_EN_0; //通知安卓按下KEY2 
       //PA4 发射
    }
}
/*****************************************************************************
 函 数 名  : readKEY3
 功能描述  : 读取M6状态
 输入参数  : void
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
// M6 PTT 相关函数
uint8_t readKEY3(void)
{     
    return KEY3_STATUS_READ;
}
/*****************************************************************************
 函 数 名  : KEY3PressFunc
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY3PressFunc(uint8_t state)
{
    if(state)//KYE3松开后
    {
      KEY3_NOTICE_OUT_1; 
       //
    }else{  //KYE3 按下
      KEY3_NOTICE_OUT_0;
       //PA4 发射
    }    
}
/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
// KEY4 相关函数
uint8_t readKEY4(void)
{ 
    return KEY4_STATUS_READ;
}
/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
void KEY4Func(uint8_t state)
{
    //KEY4按下后对应操作
}
/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
// 主处理函数

3.2.6 主函数

/*****************************************************************************
 函 数 名  : KEY_Device_process
 功能描述  : 消抖函数
 输入参数  : u8 power
 返 回 值  : void
 作  者    : Bright
 创建日期 : 20250607
*****************************************************************************/
// 主处理函数
uint8_t ExtiProc(uint8_t pttPort) 
{
    // 使用switch处理不同端口
    switch(pttPort) {
        case KEY1_Port:
            return KEY_Device_process(&KEY1);
            
        case KEY2_Port:
            return KEY_Device_process(&KEY2);
            
        case KEY3_Port:
            return KEY_Device_process(&KEY3);
            
        case KEY4Statu_Port:
            return KEY_Device_process(&KEY4);

        default:
            return 0;  // 未知端口
    }
}
            return KEY_Device_process(&KEY3);
            
        case KEY4Statu_Port:
            return KEY_Device_process(&KEY4);

        default:
            return 0;  // 未知端口
    }
}

四、总体上只要找到找到函数的特点,就能够利用面向对象的思想来实现。面向对象的方式只要将抽象部分写完,后续只需要新建对象,写各自的函数就可以了,整体上看起来比较清爽。

        但无论是是用哪种方式,只要能够跑就行了,怎么简单怎么来,公司怎么要求怎么来,希望以上内容对大家有帮助。