1 监控任务设计
1.1 概述
监控任务用于监控CPU利用率、管脚状态,然后在串口中报告状态。示例较简单,也可以做得更为复杂些。系统结构图如下:
监控任务设计:
虽然监控任务基本上也是周期性去检查各个状态;但是系统中没有使用软定时器去周期检查。因为除周期性检查状态外,还需要做一些其它工作,在定时函数中完成并不方便。如果再考虑以后想在监控任务中做一些其它耗时操作,如果这些也放在定时器中去做,则会影响到系统中其它定时器。
监控任务的主要工作是等待邮箱中的消息。一旦等不到超时,则认为需要检查状态。不过,这种方式有个缺点:比如5s监控,在4.9秒后收到邮箱消息时,则会重新等待延时。
命令链表:
我们将所有的命令用一个复杂的联合体来保存,当然你也可以分开写成多个结构体。所有命令块的分配与释放通过存储块来管理。
具体监控任务通过命令链表来配置。该链表中的插入和删除操作完全由监控任务自己来负责,而不是允许命令解释器任务直接操作,这样就避免了两个任务同时读写监控列表问题的情况出现,这样就不必添加任务资源保护处理。此外,通过邮箱来发送请求,命令解释器任务可以在发送请求后立即做自己的工作。监控任务可以在有空闲时再对该命令进行处理。整体效率更高。
1.2 代码实现
cli.c:
/**
* @brief 命令解释器设计
* @details
* @author 01课堂 李述铜 http://01ketang.cc
* @date 2017-06-01
* @version 1.0
* @copyright 版权所有,禁止用于商业用途
*/
#include <string.h>
#include <stdlib.h>
#include "tinyOS.h"
#include "cli.h"
#include "uart.h"
#include "extio.h"
#include "WaveGen.h"
#include "monitor.h"
#define TERMINAL_BS 0x08
#define TERMINAL_SPACE ' '
#define TERMINAL_CR '\r'
#define TERMINAL_LF '\n'
#define TERMINAL_TAB '\t'
// 空白字符
static const char spaceCh[] = {TERMINAL_SPACE, TERMINAL_CR, TERMINAL_LF, TERMINAL_TAB, '\0'};
static const char * unknownPinMsg = "Unknown Pin\r\n";
static const char * unknownCmdMsg = "Unknown Command!\r\n";
static const char * noEnoughParamMsg = "No Enough Param!\r\n";
// 任务相关
static tTaskStack cliTaskEnv[CLI_TASK_ENV_SIZE]; // 任务1的堆栈空间
static tTask cliTask;
// 命令行提示缓冲区
static char promoteBuffer[CLI_CMD_PROMATE_SIZE];
/**
* 显示消息
*/
static void showMsg (const char * msg) {
UartWrite(msg, strlen(msg));
}
/**
* 输出显示字符
* @param ch
*/
static void showCh (const char ch) {
UartWrite(&ch, 1);
}
/**
* 显示提示符
*/
static void showPromote (void) {
UartWrite(promoteBuffer, strlen(promoteBuffer));
}
/**
* 更改提示符
*/
static void setPromote (const char * newPromote) {
strncpy(promoteBuffer, newPromote, sizeof(promoteBuffer));
promoteBuffer[sizeof(promoteBuffer) - 1] = '\0';
}
/**
* 提示符修改命令
*/
static void promoteCmd (void) {
char * promote = strtok(NULL, spaceCh);
if (promote == NULL) {
showMsg(noEnoughParamMsg);
return;
}
setPromote(promote);
}
/**
* 将引脚名转换为内部序号
* @param pinCh
* @return
*/
static ExtIOPin convertPinNum (char * pinCh) {
ExtIOPin pinNum;
if (pinCh == NULL) {
return ExtIOPinEnd;
}
pinNum = (ExtIOPin)(*pinCh - '0');
if (pinNum >= ExtIOPinEnd) {
return ExtIOPinEnd;
}
return pinNum;
}
/**
* 外部IO命令解析
*/
static void extioCmd () {
char * type = strtok(NULL, spaceCh);
if (type == NULL) {
showMsg(noEnoughParamMsg);
return;
}
if (strstr(type, "get")) { // 命令extio get pin
ExtIOPin pin;
ExtIOState state;
pin = convertPinNum(strtok(NULL, spaceCh));
if (pin == ExtIOPinEnd) {
showMsg(unknownPinMsg);
return;
}
state = ExtIOGetState(pin);
showMsg((state == ExtIOHigh) ? "1\r\n" : "0\r\n");
} else if (strstr(type, "set")) { // 命令extio set pin value
ExtIOPin pin;
char * value;
pin = convertPinNum(strtok(NULL, spaceCh));
if (pin == ExtIOPinEnd) {
showMsg(unknownPinMsg);
return;
}
value = strtok(NULL, spaceCh);
if (value == NULL) {
showMsg(noEnoughParamMsg);
return;
}
ExtIOSetState(pin, *value == '0' ? ExtIOLow : ExtIOHigh);
} else if (strstr(type, "dir")) { // 命令extio dir pin in/out
ExtIOPin pin;
char *outType;
pin = convertPinNum(strtok(NULL, spaceCh));
if (pin == ExtIOPinEnd) {
showMsg(unknownPinMsg);
return;
}
outType = strtok(NULL, spaceCh);
if (outType == NULL) {
showMsg(noEnoughParamMsg);
return;
}
ExtIOSetDir(pin, strstr(outType, "in") ? 1 : 0);
} else {
showMsg(noEnoughParamMsg);
}
}
/**
* 波形输出命令解析
*/
static void waveCmd () {
char *type = strtok(NULL, spaceCh);
if (type == NULL) {
showMsg(noEnoughParamMsg);
return;
}
if (strstr(type, "square")) { // 命令wave square
WaveSelectType(WaveSquare);
} else if (strstr(type, "start")) {
WaveStartOutput();
} else if (strstr(type, "stop")) {
WaveStopOutput();
} else {
showMsg(noEnoughParamMsg);
}
}
static void monitorPinCmd (void) {
MonitorCmd * cmd;
uint8_t pin;
uint8_t isOn = 0;
char * pinCh = strtok(NULL, spaceCh); // 解析引脚 pinnum
if (pinCh == NULL) {
showMsg(noEnoughParamMsg);
return;
} else {
char *on_off;
pin = atoi(pinCh);
on_off = strtok(NULL, spaceCh); // 解析开关on/off
if (on_off == NULL) {
showMsg(noEnoughParamMsg);
return;
} else if (strstr(on_off, "on")) {
isOn = 1;
}
cmd = MonitorAllocCmd();
cmd->isAdd = isOn;
cmd->target = MonitorExtIOPin;
cmd->options.pin.pinNum = pin;
MonitorSendCmd(cmd);
}
}
static void monitorCPUCmd (void) {
uint8_t isOn = 0;
char * on_off = strtok(NULL, spaceCh); // monitor cpu on/off
if (on_off == NULL) {
showMsg(noEnoughParamMsg);
return;
} else if (strstr(on_off, "on")) {
MonitorCmd * cmd = MonitorAllocCmd();
char * percent = strtok(NULL, spaceCh); // monitor cpu on percent
isOn = 1;
if (percent != NULL) {
float cpuPercent = (float)atof(percent);
cmd->options.cpu.warning = 1;
cmd->options.cpu.warnPercent = cpuPercent;
} else {
cmd->options.cpu.warning = 0;
}
cmd->isAdd = isOn;
cmd->target = MonitorCPUUsage;
MonitorSendCmd(cmd);
}
}
/**
* 波形输出命令解析
*/
static void monitorCmd (void) {
char *type = strtok(NULL, spaceCh);
if (type == NULL) {
showMsg(noEnoughParamMsg);
return;
}
if (strstr(type, "on")) { // monitor on
MonitorOn();
} else if (strstr(type, "off")) { // monitor off
MonitorOff();
} else if (strstr(type, "pin")) { // monitor pin num on/off
if (MonitorIsOn()) {
monitorPinCmd();
} else {
showMsg("Turn on Monitor first!\r\n");
}
} else if (strstr(type, "cpu")) { // monitor cpu on/off
if (MonitorIsOn()) {
monitorCPUCmd();
} else {
showMsg("Turn on Monitor first!\r\n");
}
}
}
/**
* 未知命令处理
*/
static void unknowCmd (void) {
showMsg(unknownCmdMsg);
}
/**
* 读取一行数据,如果命令超过则截断
*/
static void readLine (char * buffer, uint32_t maxLen) {
uint32_t index = 0;
while (index < maxLen) {
char ch;
UartRead(&ch, 1);
switch (ch) {
case TERMINAL_BS: // 退格键
if (index > 0) {
buffer[index--] = '\0';
showCh(TERMINAL_BS);
showCh(TERMINAL_SPACE);
showCh(TERMINAL_BS);
}
break;
case TERMINAL_CR:
showCh(TERMINAL_LF);
default:
showCh(ch);
buffer[index++] = ch;
if ((ch == '\n') || (ch == '\r') || (index >= maxLen)) {
buffer[index] = '\0';
return;
}
break;
}
}
}
/**
* 解析命令
*/
static void processCmd (char * cmdLine) {
char * cmdStart;
// 获取开头
cmdStart = strtok(cmdLine, spaceCh);
if (cmdStart == NULL) {
return;
}
// 识别命令
if (strstr(cmdStart, "extio")) {
extioCmd();
} else if (strstr(cmdStart, "wave")) {
waveCmd();
} else if (strstr(cmdStart, "promote")) {
promoteCmd();
} else if (strstr(cmdStart, "monitor")) {
monitorCmd();
} else {
unknowCmd();
}
}
/**
* 解释器任务
* @param param
*/
void cliTaskEntry (void * param) {
static char cmdBuffer[CLI_CMD_BUFFER_SIZE];
for (;;) {
showPromote();
readLine(cmdBuffer, sizeof(cmdBuffer));
processCmd(cmdBuffer);
}
}
/**
* 命令解释器设计
*/
void CLIInit (void) {
strcpy(promoteBuffer, ">>");
tTaskInit(&cliTask, cliTaskEntry, (void *) 0x0, CLI_TASK_PRIO, cliTaskEnv, sizeof(cliTaskEnv));
}
monitor.h:
/**
* @brief 监控任务设计
* @details
* @author 01课堂 李述铜 http://01ketang.cc
* @date 2017-06-01
* @version 1.0
* @copyright 版权所有,禁止用于商业用途
*/
#ifndef MONITOR_H
#define MONITOR_H
#include "tinyOS.h"
#define MONITOR_TASK_PRIO 0
#define MONITOR_TASK_ENV_SIZE 512
#define MONITOR_MAX_CMD 10
#define REPORT_BUFFER_SIZE 128
#define MONITOR_DEFAULT_TIMEOUT 1000
// 监控对像
typedef enum {
MonitorCPUUsage,
MonitorExtIOPin,
}MonitorTarget;
// 监控命令
typedef struct _MonitorCmd {
tNode linkNode;
uint8_t isAdd;
MonitorTarget target;
union MonitorOption{
struct {
uint8_t pinNum; // 管脚序号
}pin;
struct {
uint8_t warning;
float warnPercent;
}cpu;
}options;
}MonitorCmd;
void MonitorInit (void);
void MonitorOn (void);
void MonitorOff (void);
uint8_t MonitorIsOn (void);
MonitorCmd * MonitorAllocCmd (void);
void MonitorFreeCmd (MonitorCmd * cmd);
void MonitorSendCmd (MonitorCmd * cmd);
void MonitorSetPeriod (uint32_t ms);
#endif //PROJECT_MONITOR_H
monitor.c:
/**
* @brief 监控任务设计
* @details
* @author 01课堂 李述铜 http://01ketang.cc
* @date 2017-06-01
* @version 1.0
* @copyright 版权所有,禁止用于商业用途
*/
#include <string.h>
#include <stdio.h>
#include "monitor.h"
#include "uart.h"
#include "extio.h"
static uint8_t monitorIsOn;
__align(8) static tTaskStack monitorTaskEnv[MONITOR_TASK_ENV_SIZE];
static tTask monitorTask;
static tMbox cmdMbox;
static void * msgBuffer[MONITOR_MAX_CMD];
static tMemBlock cmdMemBlock;
static MonitorCmd cmdMem[MONITOR_MAX_CMD];
static tList monitorCmdList;
static uint32_t monitorPeriod; // 监控周期
static char reportBuffer[REPORT_BUFFER_SIZE];
/**
* 分配命令块
* @return
*/
MonitorCmd * MonitorAllocCmd (void) {
MonitorCmd * cmd = 0;
tMemBlockWait(&cmdMemBlock, (void **)&cmd, 0);
memset(cmd, 0, sizeof(MonitorCmd));
tNodeInit(&cmd->linkNode);
return cmd;
}
/**
* 释放命令块
* @param cmd
*/
void MonitorFreeCmd (MonitorCmd * cmd) {
tMemBlockNotify(&cmdMemBlock, cmd);
}
/**
* 添加监控命令
* @param cmd
*/
void MonitorSendCmd (MonitorCmd * cmd) {
tMboxNotify(&cmdMbox, cmd, tMBOXSendNormal);
}
/**
* 设置监控周期
* @param ms
*/
void MonitorSetPeriod (uint32_t ms) {
monitorPeriod = ms;
}
/**
* 找到配置相同的命令
* @param cmd
* @return
*/
static MonitorCmd * findCmd (MonitorCmd * cmd) {
tNode * currNode;
for (currNode = tListFirst(&monitorCmdList); currNode != (tNode *)0; currNode = tListNext(&monitorCmdList, currNode)) {
MonitorCmd * currentCmd = (MonitorCmd *)tNodeParent(currNode, MonitorCmd, linkNode);
if (currentCmd->target == cmd->target) {
switch (currentCmd->target) {
case MonitorCPUUsage:
return currentCmd;
case MonitorExtIOPin:
if (currentCmd->options.pin.pinNum == cmd->options.pin.pinNum) {
return currentCmd;
}
break;
default:
break;
}
}
}
return 0;
}
/**
* 添加命令
*/
static void procReceivedCmd (MonitorCmd * cmd) {
MonitorCmd * existCmd = findCmd(cmd);
if (cmd->isAdd) {
if (existCmd) {
tListRemove(&monitorCmdList, &existCmd->linkNode);
MonitorFreeCmd(existCmd);
}
tListAddLast(&monitorCmdList, &cmd->linkNode);
} else {
if (existCmd) {
tListRemove(&monitorCmdList, &existCmd->linkNode);
MonitorFreeCmd(existCmd);
}
MonitorFreeCmd(cmd);
}
}
static void showReportMsg (const char * msg) {
UartWrite(msg, strlen(msg));
}
static void reportCPUUsage (MonitorCmd * cmd) {
float cpuUsage = tCpuUsageGet();
sprintf(reportBuffer, "CPU usage:%lf\r\n", cpuUsage);
showReportMsg(reportBuffer);
if (cmd->options.cpu.warning) {
if (cmd->options.cpu.warnPercent <= cpuUsage) {
showReportMsg("Warning: CPU usage too high\r\n");
}
}
}
static void reportExtIOPin (MonitorCmd * cmd) {
ExtIOState state;
ExtIOPin pin = (ExtIOPin)cmd->options.pin.pinNum;
if (pin >= ExtIOPinEnd) {
return;
}
state = ExtIOGetState(pin);
sprintf(reportBuffer, "ExtIO Pin %d:%d\r\n", pin, (state == ExtIOHigh) ? 1 : 0);
showReportMsg(reportBuffer);
}
/**
* 报告错误
* @param cmd
*/
static void reportTarget (void) {
tNode * currNode;
for (currNode = tListFirst(&monitorCmdList); currNode != NULL; currNode = tListNext(&monitorCmdList, currNode)) {
MonitorCmd * currentCmd = (MonitorCmd *)tNodeParent(currNode, MonitorCmd, linkNode);
switch (currentCmd->target) {
case MonitorCPUUsage:
reportCPUUsage(currentCmd);
break;
case MonitorExtIOPin:
reportExtIOPin(currentCmd);
break;
}
}
}
/**
* 监控任务
* @param param
*/
void monitorTaskEntry (void * param) {
for (;;) {
uint32_t err;
MonitorCmd * cmd;
err = tMboxWait(&cmdMbox, (void **)&cmd, monitorPeriod / TINYOS_SYSTICK_MS);
if (err == tErrorNoError) {
procReceivedCmd(cmd);
} else {
reportTarget();
}
}
}
/**
* 监控初始化
*/
void MonitorInit (void) {
monitorIsOn = 0; // 未开启
}
void MonitorOn (void) {
if (monitorIsOn) {
return;
}
monitorIsOn = 1;
monitorPeriod = MONITOR_DEFAULT_TIMEOUT;
tListInit(&monitorCmdList);
tMboxInit(&cmdMbox, msgBuffer, MONITOR_MAX_CMD);
tMemBlockInit(&cmdMemBlock, cmdMem, sizeof(MonitorCmd), MONITOR_MAX_CMD);
tTaskInit(&monitorTask, monitorTaskEntry, (void *) 0x0, MONITOR_TASK_PRIO, monitorTaskEnv, sizeof(monitorTaskEnv));
}
void MonitorOff (void) {
if (monitorIsOn == 0) {
return;
}
tTaskForceDelete(&monitorTask);
monitorIsOn = 0;
}
uint8_t MonitorIsOn (void) {
return monitorIsOn;
}
参考资料: