单片机工程经验 - 分层思想

单片机工程经验 - 分层思想

单片机工程经验是本人对单片机编程从业以来的一点经验总结,希望能为大家的职业生涯提高一点点效率,不要陷入整日重复造轮子的境地。人生不只有工作,还有生活。

分层思想

分层思想其实一点都不神秘,它就是简单的把一个项目分成多个层级,仅可上层调用下层接口,且不能跨层级调用。这里我简单的把单片机程序分为3层。

应用层
驱动层
硬件层

矩阵键盘为例子

这里我们以矩阵键盘为例子。
矩阵键盘

  1. 将ROW1 ~ ROW4设置为输出模式、将COL1 ~ COL4设置为输入。
  2. 将ROW1输出低电平,ROW2、ROW3、ROW4输出高电平。
  3. 读取COL1 ~ COL4电平,如果有一列电平为低,则相应的按键按下,比如K2按下时COL2就会收到低电平,而此时K5~K16无论是否按下,对这个电平都没有影响,因为即使按下,也只是增加一个3.3V的上拉。
  4. 将不同的ROW输出低电平,其他输出高重复读取COL上的电平。这样每次哪一行上的ROW置低就能读取到这一ROW上4个按键的状态。

分层

硬件层

ROW1 ~ ROW4,COL1 ~ COL4需要连接到单片机的8个普通IO口上,而不同单片机的IO口操作寄存器或者库函数都不同,如果这里不做分层,那么换一个单片机就得重写矩阵键盘的驱动,这显然是我们不想要的。

首先我们对IO口操作进行抽象化处理,我们发现IO口操作无非就是设置IO输入输出模式,输出高低电平,检测输入电平的高低。
然后我们就可以抽象出这么三个函数

drv_pin_mode(int id,int mode);
drv_pin_write(int id,int level);
drv_pin_read(int id);

因为不同的IO口都可以单独配置,所以我们要将IO传入函数,这里不同的id就能代表不同的IO口。
不同单片机这三个函数的实现都不同,但这没有关系,只要我们把这三个函数的实现单独写成一个源文件,那么换单片机是不是只要换这个源文件就可以了?这样我就实现了硬件层的分层。

驱动层

驱动层就需要考虑我们需要为上层应用层提供什么东西,比如矩阵键盘需要提供哪个按键被按下、led需要提供亮灯灭灯闪烁、温度传感器需要提供温度值。而对上层应用来说,我不需要知道你是怎么实现的,驱动只要提供应用层想要的东西就行了。
对于矩阵键盘来说,应用层只需要一个函数

char get_key_date();

应用层实时调用这个函数就可以知道哪个按键被按下了。
或者我们可以使用回调函数的方式。

void set_key_callback(void (*key_date)(char key));

上层应用先写好函数,这个函数可以处理不同的键值按下,然后调用上方函数设置好回调,当矩阵键盘驱动检测到按键被按下时就可以自动调用这个回调函数。

采用这两种方法就可以实现矩阵键盘驱动写好后,下次需要使用可以直接将驱动复制而无需更改任何代码。

矩阵键盘驱动实现

首先我们思考矩阵键盘和硬件有哪些关联。

  1. ROW和COL的数量是不确定的
  2. 每个ROW和COL对应的IO口是不确定的。

因为这两个不确定,所以我们需要把这两个不确定的条件传入驱动

void set_key_row_col(int *rowMap,int rowNum,int *colMap,int colNum);

这里rowMap是ROW列的引脚id数组,rowNum是数组长度,col也同理。
比如row的id是1357,col的id是2468,那么可以这样调用

int row[] = {1,3,5,7};
int col[] = {2,4,6,8};
set_key_row_col(row,4,col,4);

在驱动层,我们就可以将这几个参数保存下来,以便后续使用。

接着我们要思考矩阵键盘的具体实现了,我们知道一开始需要将row设置成输出,col设置成输入,所以可以定义一个初始化函数

void keyi_init()
{
	int i = 0;
	for(i = 0; i < rowNum;i++)
	{
		drv_pin_mode(rowMap[i],PIN_MODE_OUT);
		drv_pin_write(rowMap[i],1);
	}
	for(i = 0; i < colNum;i++)
	{
		drv_pin_mode(colMap[i],PIN_MODE_IN);
	}
}

然后我们就要考虑轮流去读取col的值。

void key_thread()
{
	static int rowIndex = 0;
	int i = 0;
	drv_pin_write(rowMap[rowIndex],0);// 将某一row输出低电平
	for(i = 0; i < colNum;i++)
	{
		if(drv_pin_read(colMap[i]) == 0)// 如果读取到低电平
		{
			if(key_date)// 如果用户设置了回调函数
			{
				key_date(i*rowNum + rowIndex);// 执行回调函数并输入键值
			}
		}
	}
	drv_pin_write(rowMap[rowIndex],1);// 将这一row输出高电平
	rowIndex++;// 换到下一轮输出低电平
	if(rowIndex >= rowNum)// 如果全部row都检查过了就回到第 0 row开始检查
	{
		rowIndex = 0;
	}
}

只要应用层不断的去调用key_thread()函数,那么就可以十分简单的获取到键值。当然这个驱动没有考虑按键消抖,辨别长按双击等,这里只介绍思想,不作深入研究,你可以根据这些思想去思考怎么完善这个程序。

应用层

经过上述介绍,应用层需要完成的事情非常简单

  1. 定义好row和col的id,然后调用set_key_row_col函数
  2. 编写好回调函数然后调用set_key_callback告知驱动
  3. 调用keyi_init函数对驱动进行初始化
  4. 不断调用key_thread函数完成驱动的操作

至此,我们就可以发现,无论是什么单片机,无论几个口的矩阵键盘,只要将硬件层和驱动层复制到项目中,都只需要上述4部即可完成对矩阵键盘的使用。

总结

分层思想的好处是,硬件层和驱动层都只需要编写一次,极大的减少工作量,工程师可以专心思考应用层的实现。而由于这两层完全不涉及逻辑,可以轻松编写单元测试代码,只要一次测试通过,以后这两层代码将安心使用而不用担心bug的出现,减少测试压力。
当然分层思想也不是没有缺点,首先就是资源占用的增加,分层势必增加许多冗余代码,而且每一层都有自己的资源分配,尽管不同层级的资源代表的东西是相同的,但是为了减少耦合不得不另外分配资源,同时部分编译器可能对调用堆栈有限制,分层的实现会加重函数调用的深度。而且部分的代码可能不支持,比如51单片机对于函数指针的使用可能会出现问题。
总结下就是,对于资源丰富的现代单片机建议使用分层思想来构筑程序,这样的程序不仅写的快而且质量高。如果单片机是落后的八九十年代的产品,那还是不要用了。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 学习单片机分层思想可以从以下几个方面入手: 1. 了解单片机分层的基本概念,包括硬件层、驱动层和应用层。 2. 阅读相关书籍和文章,学习分层设计在单片机编程中的重要性。 3. 实践编程,在实际项目中使用分层思想进行设计和实现。 4. 总结经验,不断总结和反思自己的编程实践,提高自己的编程能力。 5. 学习其他的编程思想,如面向对象编程思想,会对分层思想有很大帮助. ### 回答2: 要学好单片机分层思想,首先需要了解分层思想的概念和重要性。分层思想是指将复杂的系统划分为若干个层次,每个层次负责不同的功能,并通过接口进行交互,从而使系统更加可靠、可扩展和易于维护。 学好分层思想,可以从以下几个方面入手: 1. 理解分层思想的原理和目的。了解分层思想的核心思想是将系统分解为多个独立的模块,每个模块负责单一的功能,并通过接口进行交互。同时,要明确分层思想的目的是提高系统的可维护性、可拓展性和易用性。 2. 掌握常用的分层架构。学习并掌握常用的分层架构,如MVC(Model-View-Controller)模型、三层架构等。这些架构模式可以帮助我们更好地组织和设计单片机程序,并使其更具可阅读性和可复用性。 3. 小规模实践。通过实际项目来进行小规模的实践,将系统按照分层思想进行模块化设计和编码。通过实际操作,可以更好地理解和掌握分层思想,并培养良好的编码习惯。 4. 学习设计模式。设计模式是解决软件设计中常见问题的可复用方案。学习并应用一些常用的设计模式,如工厂模式、观察者模式、适配器模式等,可以帮助我们更好地分离和组织代码功能。 5. 多阅读相关资料和源码。多阅读相关的书籍、文章和开源项目的源码,深入理解别人优秀的设计和实现。通过学习和借鉴他人的经验,可以提高自己的分层思维和编码水平。 在学习单片机分层思想的过程中,需要不断实践和积累经验。只有不断地在实际项目中运用分层思想,不断地总结和反思,才能真正掌握和应用好分层思想,提高单片机程序的可靠性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝忧云枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值