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));
}
參考資料: