外部事件的處理

1 外部事件的處理

1.1 概述

系統中要監控和管理兩個事件,一是按鍵事件;二是多個外部中斷事件。我們通過事件標誌組來進行處理。
在這裏插入圖片描述

1.2 代碼實現

button.h:

/**
 * @brief 按鍵驅動
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#ifndef BUTTON_H
#define BUTTON_H

#include "appConfig.h"

#define BUTTON_MSG_NR                   10          // 按鍵消息緩存數量
#define BUTTON_SCAN_INTERVAL            20          // 安鍵掃描的時間間隔, ms爲單位

typedef enum {
    ButtonNone,
    ButtonStartStop,
    Button1,
    Button2,
    Button3,
}ButtonId;

typedef enum {
    ButtonDown,
    ButtonUp,
}ButtonState;

typedef void (*ButtonPressCb) (ButtonId id);

void ButtonInit (ButtonPressCb cb);
uint32_t ButtonWaitPress (ButtonId * id);
uint32_t ButtonGetPressed (ButtonId * id);
uint32_t ButtonNoWaitGetPressed (ButtonId * id);

#endif //BUTTON_H

button.c:

/**
 * @brief 按鍵驅動
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include "tinyOS.h"
#include "button.h"
#include "stm32f10x.h"
#include "extevent.h"

// 按鍵存儲消息緩衝區
static tMbox buttonMbox;
static void * buttonMsgBuffer[BUTTON_MSG_NR];

static tTimer buttonTimer;              // 按鍵掃描定時器

static ButtonPressCb buttonPressCb;

/**
 * 初始化按鍵硬件
 */
static void ButtonHalInit (void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
 * 獲取哪一個按鍵按下
 */
static ButtonId ButtonGetWhichPress (void) {
    uint8_t state;
    ButtonId buttonId = ButtonNone;

    state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
    if (state == Bit_SET) {
        buttonId = Button1;
    }

    state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);
    if (state == Bit_SET) {
        buttonId = Button2;
    }

    state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3);
    if (state == Bit_SET) {
        buttonId = Button3;
    }

    state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0);
    if (state == Bit_SET) {
        buttonId = ButtonStartStop;
    }
    

    return buttonId;
}

/**
 * 按鍵定時器掃描處理, 按鍵處理譯碼
 * @param arg
 */
static void timerFunc (void * arg) {
    static ButtonId prePressedButton = ButtonNone;
    static enum {
        NO_DOWN,
        RECHECK_DOWN,
        CONFIRMED_DOWN,
        RECHECK_UP
    } scanState = NO_DOWN;

    // 讀取當前硬件狀態
    ButtonId currPressedButton = ButtonGetWhichPress();

    switch (scanState) {
        case NO_DOWN:
            // 有按鍵按下,進入第1次確認檢查狀態
            if (currPressedButton != ButtonNone) {
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("NO_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);
            }
            break;
        case RECHECK_DOWN:
            if (currPressedButton == ButtonNone) {
                scanState = NO_DOWN;
                DEBUG_PRINT("RECHECK_DOWN -> NO_DOWN:%d\n", currPressedButton);
            } else if (prePressedButton == currPressedButton) {
                // 發消息通知,根據按鍵類型,決定如何處理. 可以考慮從某個配置表中查,這樣可配置多個按鍵事件的通知行爲
                uint32_t notifyOption = (currPressedButton == ButtonStartStop) 
																? tMBOXSendFront : tMBOXSendNormal;
                tMboxNotify(&buttonMbox, (void *)currPressedButton, notifyOption);

                // 通知事件管理器
                if (buttonPressCb != NULL) {
                    buttonPressCb(currPressedButton);
                }

                scanState = CONFIRMED_DOWN;
                DEBUG_PRINT("RECHECK_DOWN -> CONFIRMED_DOWN:%d\n", currPressedButton);
            }
            break;
        case CONFIRMED_DOWN:
            // 進入這個狀態後,要做的是檢查按鍵是否重複按下或者等待按鍵釋.這裏簡單起見,等待按鍵釋放
            if (currPressedButton == ButtonNone) {
                // 發現按鍵釋放?進一步確認。但這裏要return,否則prePressedButton會在後面設置None
                scanState = RECHECK_UP;
                DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_UP:%d\n", currPressedButton);
                return;
            } else if (currPressedButton != prePressedButton) {
                // 發現不一樣的按鍵按下,重新確認
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);
            }
            break;
        case RECHECK_UP:
            if (currPressedButton == ButtonNone) {
                // 確認沒有按鍵按下,已經釋放
                scanState = NO_DOWN;
                DEBUG_PRINT("RECHECK_UP -> NO_DOWN:%d\n", currPressedButton);
            } else if (currPressedButton != prePressedButton) {
                // 發現不一樣的按鍵按下,重新確認是否按下
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("RECHECK_UP -> scanState:%d\n", currPressedButton);
            } else if (currPressedButton == prePressedButton) {
                // 相同的鍵,噢,前一次可能是誤觸發,再次重新檢查
                scanState = CONFIRMED_DOWN;
                DEBUG_PRINT("RECHECK_UP -> CONFIRMED_DOWN:%d\n", currPressedButton);
            }
            break;
    }

    // 記錄當前結果
    prePressedButton = currPressedButton;
}

/**
 * 按鍵初始化
 */
void ButtonInit (ButtonPressCb cb) {
    buttonPressCb = cb;

    ButtonHalInit();

    tMboxInit(&buttonMbox, buttonMsgBuffer, BUTTON_MSG_NR);

    tTimerInit(&buttonTimer, 0, BUTTON_SCAN_INTERVAL / TINYOS_SYSTICK_MS, timerFunc, 0, TIMER_CONFIG_TYPE_SOFT);
    tTimerStart(&buttonTimer);
}

/**
 * 等待任意按鍵按下
 * @param id 按鍵是否按下的狀態
 * @return 非0,有錯誤發生;0無錯誤
 */
uint32_t ButtonWaitPress (ButtonId * id) {
    uint32_t err = 0;

    err = tMboxWait(&buttonMbox, (void **)id, 0);
    return err;
}

/**
 * 無阻塞獲取按下的按鍵
 * @param id
 * @return
 */
uint32_t ButtonNoWaitGetPressed (ButtonId * id) {
    uint32_t err = 0;

    err = tMboxNoWaitGet(&buttonMbox, (void **)id);
    return err;
}


extio.h:

/**
 * @brief 外部IO設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#ifndef EXTIO_H
#define EXTIO_H

typedef enum {
    ExtIOHigh,
    ExtIOLow
}ExtIOState;

typedef enum {
    ExtIOPin0 = 0,
    ExtIOPin1,
    ExtIOPin2,
    ExtIOPin3,
    ExtIOPinEnd,
}ExtIOPin;

typedef enum {
    ExtIntIOPin4,
    ExtIntIOPin5,
    ExtIntIOPin6,
    ExtIntIOPin7,
}ExtIntIOPin;

typedef void (*ExtIntIOCb) (ExtIntIOPin pin);

void ExtIOInit (ExtIntIOCb cb);
void ExtIOSetState (ExtIOPin pin, ExtIOState);
ExtIOState ExtIOGetState (ExtIOPin pin);
void ExtIOSetDir (ExtIOPin pin, uint8_t isInput);
void ExtIOResetInput (void);

#endif //PROJECT_EXTIO_H

extio.c:

/**
 * @brief 外部IO設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include "tinyOS.h"
#include "extio.h"
#include "stm32f10x_gpio.h"             // Keil::Device:StdPeriph Drivers:GPIO

static ExtIntIOCb extIntIOCb;

static uint16_t extioMap[ExtIOPinEnd] = {
        GPIO_Pin_1,     // 對應ExtIOPin0,以下類堆
        GPIO_Pin_2,
        GPIO_Pin_3,
        GPIO_Pin_4
};

/**
 * 初始化外部IO
 */
void ExtIOInit (ExtIntIOCb cb) {
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置GPIO中斷管腳
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource5);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);

    EXTI_InitStructure.EXTI_Line = EXTI_Line5 | EXTI_Line6 | EXTI_Line7 | EXTI_Line8;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    extIntIOCb = cb;
}

/**
 * 設置指定IO引腳狀態
 * @param pin 需配置的IO
 * @param state 配置的狀態值
 */
void ExtIOSetState (ExtIOPin pin, ExtIOState state) {
    tTaskCritical_t critical;
    uint16_t halPin = extioMap[pin];

    critical = tTaskEnterCritical();
    if (state == ExtIOHigh) {
        GPIO_SetBits(GPIOA, halPin);
    } else {
        GPIO_ResetBits(GPIOA, halPin);
    }
    tTaskExitCritical(critical);
}

/**
 * 獲取指定引腳的狀態
 * @param pin 需獲取的引腳
 * @return 引腳的狀態
 */
ExtIOState ExtIOGetState (ExtIOPin pin) {
    uint16_t halPin = extioMap[pin];

    uint8_t state = GPIO_ReadInputDataBit(GPIOA, halPin);
    return (state == Bit_SET) ? ExtIOHigh : ExtIOLow;
}

/**
 * 設置管腳狀態
 * @param pin
 * @param isInput
 */
void ExtIOSetDir (ExtIOPin pin, uint8_t isInput) {
    tTaskCritical_t critical;
    GPIO_InitTypeDef GPIO_InitStructure;
    uint16_t halPin = extioMap[pin];

    critical = tTaskEnterCritical();

    GPIO_InitStructure.GPIO_Pin = halPin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = isInput ? GPIO_Mode_IPU : GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    tTaskExitCritical(critical);
}

/**
 * 設置所有IO爲輸入態
 */
void ExtIOResetInput (void) {
    ExtIOSetDir(ExtIOPin0, 1);
    ExtIOSetDir(ExtIOPin1, 1);
    ExtIOSetDir(ExtIOPin2, 1);
    ExtIOSetDir(ExtIOPin3, 1);
}


void EXTI9_5_IRQHandler (void) {
    if (EXTI_GetITStatus(EXTI_Line5) != RESET) {
        if (extIntIOCb) {
            extIntIOCb(ExtIntIOPin4);
        }
        EXTI_ClearITPendingBit(EXTI_Line5);
    }

    if (EXTI_GetITStatus(EXTI_Line6) != RESET) {
        if (extIntIOCb) {
            extIntIOCb(ExtIntIOPin5);
        }
        EXTI_ClearITPendingBit(EXTI_Line6);
    }

    if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
        if (extIntIOCb) {
            extIntIOCb(ExtIntIOPin6);
        }
        EXTI_ClearITPendingBit(EXTI_Line7);
    }

    if (EXTI_GetITStatus(EXTI_Line8) != RESET) {
        if (extIntIOCb) {
            extIntIOCb(ExtIntIOPin7);
        }
        EXTI_ClearITPendingBit(EXTI_Line8);
    }
}

extevent.h:

/**
 * @brief 按鍵事件處理設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#ifndef EXTEVENT_H
#define EXTEVENT_H

#include "extio.h"
#include "button.h"

#define EXTEVENT_TASK_PRIO           0
#define EXTEVENT_TASK_ENV_SIZE       512

void ExtEventInit (void);
void ExtEventButtonPress (ButtonId id);
void ExtEventExtIOInt (ExtIntIOPin pin);

#endif //MONITOR_H

extevent.c:

/**
 * @brief 監控任務設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include <string.h>
#include "tinyOS.h"
#include "extevent.h"
#include "button.h"
#include "uart.h"
#include "extio.h"
#include "WaveGen.h"

typedef enum {
    ExtEventButton = 1 << 0,
    ExtEventExtIO4 = 1 << 1,
    ExtEventExtIO5 = 1 << 2,
    ExtEventExtIO6 = 1 << 3,
    ExtEventExtIO7 = 1 << 4,
    ExtEventFlagAll = 0x1F,
}ExtEventType;

// 任務相關
static tTaskStack exteventTaskEnv[EXTEVENT_TASK_ENV_SIZE];
static tTask exteventTask;

static tFlagGroup flagGroup;

/**
 * @param msg
 */
static void showMsg (const char * msg) {
    UartWrite(msg, strlen(msg));
}

/**
 * 通知按鍵按下
 */
void ExtEventButtonPress (ButtonId id) {
    tFlagGroupNotify(&flagGroup, 1, ExtEventButton);
}

/**
 * 通知引腳中斷
 */
void ExtEventExtIOInt (ExtIntIOPin pin) {
    switch (pin) {
        case ExtIntIOPin4:
            tFlagGroupNotify(&flagGroup, 1, ExtEventExtIO4);
            break;
        case ExtIntIOPin5:
            tFlagGroupNotify(&flagGroup, 1, ExtEventExtIO5);
            break;
        case ExtIntIOPin6:
            tFlagGroupNotify(&flagGroup, 1, ExtEventExtIO6);
            break;
        case ExtIntIOPin7:
            tFlagGroupNotify(&flagGroup, 1, ExtEventExtIO7);
            break;
    }
}

static void buttonEvent (ButtonId id) {
    switch (id) {
        case ButtonStartStop:
            WaveStopOutput();
            ExtIOResetInput();

            showMsg("Start/Stop Button Press\r\n");
            break;
        case Button1:
            showMsg("Button1 Press\r\n");
            break;
        case Button2:
            showMsg("Button2 Press\r\n");
            break;
        case Button3:
            showMsg("Button3 Press\r\n");
            break;
        default:
            break;
    }
}

static void extioEvent (uint32_t resultFlag) {
    if (resultFlag & ExtEventExtIO4) {
        showMsg("Pin4 Interrupt\r\n");
    }

    if (resultFlag & ExtEventExtIO5) {
        showMsg("Pin5 Interrupt\r\n");
    }

    if (resultFlag & ExtEventExtIO6) {
        showMsg("Pin6 Interrupt\r\n");
    }

    if (resultFlag & ExtEventExtIO7) {
        showMsg("Pin7 Interrupt\r\n");
    }
}

/**
 * 監控任務
 * @param param
 */
void exteventTaskEntry (void * param) {
    ButtonId buttonId;

    for (;;) {
        uint32_t resultFlag;

        tFlagGroupWait(&flagGroup, TFLAGGROUP_SET_ANY | TFLAGGROUP_CONSUME, ExtEventFlagAll, &resultFlag, 0);
        if (resultFlag & ExtEventButton) {
            ButtonNoWaitGetPressed(&buttonId);
            buttonEvent(buttonId);
        } else if (resultFlag & (ExtEventExtIO4 | ExtEventExtIO5 | ExtEventExtIO6 | ExtEventExtIO7)) {
            extioEvent(resultFlag);
        }
    }
}

/**
 * 監控初始化
 */
void ExtEventInit (void) {
    tFlagGroupInit(&flagGroup, 0);
    tTaskInit(&exteventTask, exteventTaskEntry, (void *) 0x0, EXTEVENT_TASK_PRIO, exteventTaskEnv, sizeof(exteventTaskEnv));
}

app.c:

/**
 * @brief tOS應用示例
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include "tinyOS.h"
#include "app.h"
#include "hal.h"

#include "button.h"
#include "uart.h"
#include "extio.h"
#include "WaveGen.h"
#include "cli.h"
#include "extevent.h"

#include "string.h"

static tTaskStack task1Env[TASK1_ENV_SIZE];     // 任務1的堆棧空間
static tTask task1;

void task1Entry (void * param) {
    for (;;) {
        tTaskDelay(1);
    }
}

/**
 * App的初始化
 */
void tInitApp (void) {
    halInit();

    ButtonInit(ExtEventButtonPress);
    UartInit();
    ExtIOInit(ExtEventExtIOInt);
    WaveGenInit();

    CLIInit();
    ExtEventInit();

    tTaskInit(&task1, task1Entry, (void *) 0x0, TASK1_PRIO, task1Env, sizeof(task1Env));
}



參考資料:

  1. 手把手教你學用嵌入式操作系統
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章