单片机工程经验 - 数据逻辑分离
举例
假设我们有一段程序,程序实现的功能是输入错误代码打印错误字符串。一般实现如下
enum{
ERR_NONE,
ERR_INVAL,
ERR_NOEN,
ERR_BUSY,
...
};
char* print_err(char errcode)
{
if(errcode == ERR_NONE)
{
return "No error occurred"
}
else if(errcode == ERR_INVAL)
{
return "Invalid argument"
}
else if(errcode == ERR_NOEN)
{
return "No such file or directory"
}
else if(errcode == ERR_BUSY)
{
return "Device or resource busy"
}
...
}
可以看到,虽然上述代码完成了功能,但是代码繁杂,如果需要更改需要寻找大量代码,这时如果我们使用下面程序进行数据和逻辑的分离会有什么效果呢。
解耦数据于逻辑
enum{
ERR_NONE,
ERR_INVAL,
ERR_NOEN,
ERR_BUSY,
...
};
const char errStr[]={
"No error occurred",
"Invalid argument",
"No such file or directory",
"Device or resource busy",
...
};
char* print_err(char errcode)
{
char i = 0;
if( errcode < sizeof(errStr)/sizeof(errStr[0]))
{
return errStr[errcode];
}
return "Unknow error code";
}
这样我们就完成了数据与逻辑的完全分离,无论怎么增减故障代码,都无需对print_err 函数进行更改。核心代码就几行,程序就会变得十分简洁,并且修改故障代码只需在errStr 数组中增减即可。
进阶玩法
或许你觉得上述代码很容易就能想到了,那我给大家来个骚操作。 矩阵键盘电路相信大家都很熟悉,不懂得可以去看我的 单片机工程经验 - 分层思想这篇文章。 我们假设这里的COL1 ~ COL4连接单片机的PA1 ~ PA4,ROW1 ~ ROW4连接单片机的PB1~PB4。定义一个unsigned long keyValue 用16位表示这16个按键是否有按下,按下对应位置1,没按下置0。既(COL数-1)+(ROW数-1)*4位代表(COL,ROW)是否按下,比如(COL3,ROW2)就是keyValue 第6位来表示。 那么接下来代码该怎么编写呢,首先我们的逻辑就是轮流扫描ROW,然后依次去读COL的值,对代码而言,COL和ROW的引脚号就是数据,而逻辑是相同的。接下来我们设计代码
const unsigned char col[] = {0xa1,0xa2,0xa3,0xa4};
const unsigned char row[] = {0xb1,0xb2,0xb3,0xb4};
static unsigned long keyValue = 0;
void func_loop()
{
static unsigned char index = 0;
unsigned char i = 0,j = 0;
for(i = 0; i < sizeof(row)/sizeof(row[0]);i++)
{
pin_write(row[i],1);
}
pin_write(row[index ],0);
for(i = 0; i < sizeof(col)/sizeof(col[0]);i++)
{
if(pin_read(col[i]) == 0)
{
keyValue |= (1<<(index*sizeof(col)/sizeof(col[0]) + i));
}
else
{
keyValue &= ~(1<<(index*sizeof(col)/sizeof(col[0]) + i));
}
}
index++;
if(index >= sizeof(row)/sizeof(row[0]))
{
index = 0;
}
}
注意:pin_read 和pin_write 都是 单片机工程经验 - 分层思想中进行了分层处理的函数。
上述代码非常简单,但他的好处也非常明显,func_loop 函数完全不需要更改就可以完全适应32个按键以下的基本上所有的矩阵键盘,而对于不同的矩阵键盘电路,我们只需要更改col 和row 数组中的值即可,无论是几行几列,都可以使用这套代码,那么程序移植是不是就很方便呢?
这段代码只实现了检测按键是否按下,并没有消抖,双击,长按等判断。为什么呢?因为如果我们将这些逻辑判断再单独写一个button 文件,button 中有一个get_keyvalue() 函数,这个函数只关心按键是否有被按下,然后消抖,双击,长按等逻辑判断全在button 中实现,那么无论底层代码是否是矩阵键盘还是独立按键是不是就都可以复用button 这个文件了呢?
如果一个项目里面你有80%的代码都可以复用,那摸鱼的时间还不是大把大把!!!
|