本帖最后由 会笑的星星 于 2020-1-9 11:17 编辑
之前在“单片机程序---二层架构”里谈过设计二层架构的方法,也强调了二层架构的优点在于分离硬件层,使得硬件层更具有通用性。但缺点在于应用层的程序过于耦合,导致应用层的程序比较复杂并且复用性不够。为了解决二层架构的缺点,还需要把应用层一分为二,也就是中间层以及应用层,这两层与硬件层一起构成了所谓三层架构,如下图示。
这里多出来的中间层就是对应用层功能的抽象。抽象的好处在于一个是简化了应用层的设计,另一个好处就是就是中间层的代码复用性提高。这里我将以之前在“串口模块”这篇**中谈到的串口功能为例,看看如何设计单片机程序的三层架构。关于如何抽象,建议看看我之前写的“程序的抽象”那篇**。这里我以之前讲过的串口模块为例,来说说设计三层架构的方法。我先描述一下问题。
(1)应用层利用串口发出调试信息,这些调试信息包括 : 字符串、十六进制的字符形式。 (2)应用层处理串口接收到的信息以便完成一些事情。
在解决上述两个问题之前,我再次强调一下程序分层的一个重要原则 --- 尽量只有上层调用下层,而不是相反。如果要从下层调用上层,原则上采用回调函数的方式来实现,但在实际中需要根据情况来决定。
先看问题(1),解决串口发出调试信息的问题。我们按照三层架构的方法来设计这个功能。首先我们看应用层。
按要求,需要向应用层需要提供两个抽象接口。一个用于发送任意字符串。另一个用于发送任意长度的十六进制对应的字符数据。如下的例子所示。 - //应用层
- #include "mid_serial.h"
- test_dat[6] = {0x01,0x02,0x03,0x04,0x05,0x06}
- //函数功能: 发送字符串
- serial_u0_send_str("tx:");
- //函数功能: 将十六进制转换为对应的字符并发送出去
- serial_u0_send_hex_char(test_dat,5);
- 结果:
- tx:01 02 03 04 05
serial_u0_send_str()以及serial_u0_send_hex_char()函数在“串口模块“那篇**讲过,这里不再详细说明。
这两个抽象接口的具体实现单独的封装在一个名为mid_serial.c的文件中,接口声明在mid_serial.h中,这两个文件构成我们所谓的中间层。为了能让中间层发送相关的功能工作,这个mid_serial.c文件内还需要做两件事。一个是定义中间层需要的变量,比如串口发送缓存区等。另一个是管理串口发送缓冲区的函数,这个函数属于中间层自己的,但是需要应用层调用才能让中间层工作。我把这几个函数的声明写在下面,具体源码见“串口模块”那篇**。 - //中间层 --- mid_serial.h
- #define UART_TX_BUF_LENGTH_32 31
- #define UART_TX_BUF_LENGTH_64 63
- #define UART_TX_BUF_LENGTH_128 127
- #define UART0_TX_BUF_COUNT UART_TX_BUF_LENGTH_64
- //设置发送缓冲区长度,这里的长度是64个字节
- #define UART0_TX_FIFO_LENGTH (UART_TX_BUF_LENGTH_64+1)
- //初始化串口模块相关参数
- extern void serial_parameters_init(void );
- //串口发送缓冲区的管理函数,用于执行具体的数据发送。这个函数需要在应用层定时调用,
- //定时间隔由波特率决定
- extern void serial_u0_send_manage();
- //发送字符串
- extern void serial_u0_send_str(unsigned char *ptxs);
- //发送十六进制对应的字符格式数据
- extern void serial_u0_send_hex_char(unsigned char *ptxd,unsigned char len);
这样,中间层关于发送部分的代码就完成了。你可以发现,这样做一个好处是可以让这个模块更为通用,能方便的应用在其他项目上。另一个好处是让整个程序的结构更为清晰。
到目前为止,我们完成了问题(1)的应用层以及中间层的设计。最后我们还需要设计的是中间层需要的硬件层功能。
硬件层一个是需要提供串口的初始化函数确保硬件串口能工作。另一个是需要为中间层提供数据写入接口,以便把数据写到硬件寄存器,如下图所示。 - //硬件层 --- hal.h
- extern void hal_uart_init(void );
- //这个函数被中间层serial_u0_send_manage()直接调用
- extern void hal_uart_set_tx_data(unsigned char tx_data);
上述两个函数在讲“单片机程序设计---二层架构”中提到过,这里也不再说明。
至此,问题(1)的三层架构就设计完成了。为了更好的理解,我把他们之间的调用关系写在下面。
|