2468|7

7815

帖子

57

TA的资源

裸片初长成(中级)

楼主
 

只为uC而生,uS成长历程 2 [复制链接]

现在,我们要正儿八经地来做 定时器中断 的测试函数了。

首先要说明的事情是
我们测试的 定时中断 是 uSer里的那个植入 定时器中断服务 的函数真的在中断里跑起来了。

而不是我们在Apper里写的中断。
因为从头到尾我们都在开发uSer和测试uSer。

前面说过,测试方法是 通过翻转LED,让我们观察到LED闪烁。

现在有一个问题:
我的gpio是在Apper里实现的,而我却要把它扔到uSer里,而我们一再强调:
绝对禁止他们俩之间有任何奸情,只允许函数接口调用。
也就是说,我们还得想办法给他们暗度陈仓,我承认这略有点操蛋........


我越想越觉得这个问题非常严重,以至于接下来的时间,我居然需要一大堆口水来解释。
然而最后,你会发现,最后我还是回到了原点。

其实你大概已经猜到这个问题我在等饭吃饭的时候已经想明白了,既然回到原点,我为什么还要啰啰嗦嗦地解释呢?
这里,是一个很重要的原则和思想:
解决一件事情,往往有N种方法,但这N种方法中没有任何一种具有绝对优势——否则另外那N-1种早挂了几千年。
所以,科学的方法是我们需要列举这N种方法,对比优势,然后针对具体问题,来扬长避短。
恩,这种事和选MCU差不多。
此帖出自编程基础论坛

最新回复

“解决一件事情,往往有N种方法,但这N种方法中没有任何一种具有绝对优势——否则另外那N-1种早挂了几千年。 所以,科学的方法是我们需要列举这N种方法,对比优势,然后针对具体问题,来扬长避短”:rose:  详情 回复 发表于 2013-7-31 20:34
点赞 关注
 

回复
举报

9185

帖子

5

TA的资源

管理员

沙发
 
“解决一件事情,往往有N种方法,但这N种方法中没有任何一种具有绝对优势——否则另外那N-1种早挂了几千年。
所以,科学的方法是我们需要列举这N种方法,对比优势,然后针对具体问题,来扬长避短”:rose:
此帖出自编程基础论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

板凳
 

首先,我们先来实现

这里要搞清楚一个事情。
gpio的初始化和 亮./灭 或者其他(比如翻转)的一系列是由 Apper实现的。
但是,uSer至少知道自己需要这些东西。

也就是说,uSer里应该有一个地方给Apper注册。

由于uSCore这个东西,本身是不涉及任何实际外设的。
所以这个东西必然不多,而且是专为某些特定理由才存在的,比如测试。

所以我们在uSCore里增加一个测试模块 uSCoreTest.c

----------------

以上内容存档,我发现我不知道该怎么走下去了。
这不算什么稀奇的事情。
当自上往下走不通,我们就尝试一下逆向行驶。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

4
 

在只有Timer和gpio两个外设情况下考虑问题

  1. uSer
  2.        
  3. TimerIsr();
  4. Led_Dark();       

  5. ---------------------------------------------------

  6. Apper                        

  7. void Led_Initial(void)
  8. void Led_Dark(void)

  9. void Timer_Initial(void)
  10. void TimerIsr(void)
复制代码
在这个简单的示意里
void TimerIsr(void) 这表示是函数的实现(函数定义)

TimerIsr(); 表示需要调用这个函数;

而后,uSer是提前给封装成一个库的。
也就是说,但凡它调用的而需要在Apper里实现的代码。
这说明,只能通过 函数指针 回调 或者说 我在2里写的东西,也就是一个同名函数。

我想了很久,不管怎么,这都是没办法回避的。

一个在实现以前就需要调用的函数,你除了 回调 或者 同名函数 以外,反正我是想不到第二个办法了。

前面我基本都采用 回调的方法实现。
因为前面分析,以后我们经常要做这种事情,所以,我们不得不分析 这两种思路到底各有什么优劣。

因为我们使用回调函数以前看出了,它看起来相当麻烦。
下面我们分析一下。

从 时间 空间 以及使用的繁易程度 作为三个主要比较项目。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

5
 

第一点 使用的难易程度

首先我们大致概括一下这两种方式的操作过程。
  1. #include "uSCore.h"
  2. #include

  3. void (*pTimerIsr)(void) = NULL;  

  4. void uSCore_Initial(void (*TimerIsrContainer)(void))
  5. {
  6.      if( (pTimerIsr != NULL) && (TimerIsrContainer != NULL) )
  7.         TimerIsrContainer = pTimerIsr;
  8. }
复制代码
  1. #include "uSCore.h"
  2. #include "uCGpio.h"
  3. #include "uCTimer.h"
  4. #include

  5. //==============================================================================

  6. void (*TimerIsrContainer)(void) = NULL;

  7. //==============================================================================

  8. void SystemInitial(void);

  9. void SystemInitial(void)
  10. {
  11.      uCGpio_Initial();
  12.      Timer_Initial(TimerIsrContainer);
  13. }

  14. //==============================================================================

  15. void main(void)
  16. {
  17.     uSCore_Initial(TimerIsrContainer);
  18.     SystemInitial();
  19.    
  20.     while(1);
  21. }


  22. // end of file -----------------------------------------------------------------
复制代码
以前面实现把一些动作 通过 回调 从uSer植入到Apper的TimerIsr,我们可以看到这个过程大致如下:
1.在uSer里,我们需要定义一个 函数指针,然后在uSer里把要放进去的内容放进去,然后再通过调用uSCore_Initial时,把它传递出去。
2.而在Apper里的情形则相反。
  我们仍然要定义一个函数指针,然后作为形参传入uSCore_Initial(),这样以后,我们就得到那个函数,然后就在TimerIsr里调用。

总结一下,我们要做三个步骤,而且我的天,是双方都要做:
1.各自定义一个函数指针;
2.通过一个函数传递地址来传递;
3.调用或者赋值;
这真的非常复杂,我勒个去!

而同名函数的方式呢?
太简单了,连我都差点爱上他了。
调用一个还没实现的函数,实在没什么太大不了。
只要在调用之前给他声明一下就可以了。
同名而已,这似乎没什么太大不了吧?
如我前面所说,反正我可以在这个名字下暗度陈仓包装很多很多乱七八糟的函数。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

6
 

第二点 空间和时间

相对而言,我们更加关心时间,所以我们先来比较时间。

首先我们知道,第一种方法看起来要麻烦许多,这也会带来时间的开销。
但是我们要非常注意一个点:

这些传递大多数是在初始化时进行的,而非程序不断循环调用的时候。
我们可以认为让程序一开始花多一点时间,这本身没有太了不起的。
我们必须公平公正,不要因为它麻烦就对它产生偏见,歧视。


当然,在调用时仍然有少许麻烦。
因为你并不是直接调用这个函数名啊,你是通过函数指针来调用的。

然而这两者之间到底会差别多大呢?
不好意思,我还没有测试过——实在是因为部署这种时钟级的测试比较麻烦
我这边的家伙是需要一个 逻辑分析仪。

而你的手段我就不知道了。

但是我们可以首先做一个比较,尽管我没有测试过函数指针
但我测试过一般的数据类型指针。
我当时的结论是,这两者之间几乎没差别。
我对此的理解是,函数名或者变量名本身都是一个地址,这和函数指针,数据指针本身也是一个地址一样,因此,都是调用一个地址,那能有什么区别呢?

但是理论归理论,我还是必须测试。否则,是不科学的。
但至少这种类比,我们可以暂时免除对于时间开销的担心。

至于空间,这是很明显的。
因为首先你一来一回就多了两个函数指针。
当然你会说,如果我要封装的内容比较复杂,涉及好多个函数指针怎么办?
这确实是个问题,但是我们已经讨论过很多次
这种来自Apper的函数实现,因为是涉及硬件,这真的是不多的——如果你做的程序越做越大,你就会越相信我这个结论。
因为,一个项目一旦复杂起来,主要是在业务逻辑本身的增长而非硬件外设,因为总共就那么多。

于是我们暂时得到一个大致的结论

在时间开销方面,两者的差别应该是几乎没区别。

而空间则稍微有多一些区别。
但是,在这个存储器越来越便宜,动辄就有几K几十K Flash的单片机年代(这些指针基本都是静态变量,所以他们都存储在程序存储区,也就是ROM ,FLASH,而非RAM)

有一种观念,不要为了将来会被逐渐取代的缺点而做过多的古怪方案。
因为写程序,最重要的仍然是 低复杂度,容易理解,好维护。

因此我们可以认为,在时间和空间开销方面比较,两者没有明显优劣。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

7
 
三个主要项目比较完成。
可以说,回调方案处于劣势,而且非常劣势。

现在我们考虑另一个原因,也正是这个原因,让我最终仍然倾向函数回调 的方案。
当然,显然我们要做出更多的改进,来减少这些劣势。

并且考虑某些场合,混合使用 同名方案,毕竟这确实是很不错的方法。

我们一开始考虑的都是很简单的情形
什么乱七八糟大白菜的gpio
在这种情况下,gpio的寄存器设置,有就有,没有就没有,最多我看不到LED闪烁,确实不算什么大不得的事情。

然而,如果我们现在在做一个稍微复杂一点的东西呢

比如说一个模拟i2c,我们知道不管如何,我们至少需要定义两个io,一个是SDA 一个是SCK。
而且要写高写低,还要改变IO方向.......

在这种情况,假设你在进入读数据的状态下,你的改变IO为读的方向居然没有定义,忘了或者传递失败....或者诸如此类....以至于你实际上根本没有改变IO方向,结果你就在那里等待接收。
假如你做了超时机制,那么还好,你没有死在里头。
假如你没有,完了,你直接死在这个地方,就像stm8s的硬件i2c一样,本身读写错误,但是没有超时退出机制,于是你就完蛋了,当系统复杂起来的时候,你连怎么死都不知道。

但是如果它是一个函数指针呢?
我们可以加一句判断是否为NULL,如果是NULL,我们可以直接知道返回错误,因为这个函数必然不可能正常运行。

当然,这就带来了另一个时间开销。
我的天,那么多函数指针,你都要查一次NULL?
要命。
而大多数情况下,你的函数其实都是非NULL,也就说你的程序大多数时间在做没必要的判断,这将是一个不小的问题。

这个时候我忽然想到,但是不对啊,因为,同名函数的情形下,uSer或者Apper必须有一个实现者啊,不然会编译不通过
的确如此,但是如果我们在写一个函数时,我们习惯性用空函数先代表还没实现的功能,从而可以使我们从上往下设计接口呢?

应该说,这个危险仍然有,而且不大不小。
你会想到靠人去查——但这种工作,靠人工去查绝对没有本来就有自动检查机制来的可靠。

这个原因,算是一个相当重要的原因。
所以,我们必须重新思考 两者的选择。

为了避免让过多的考虑影响我们前进的步伐,我暂时仍然采用 回调机制 来继续走下去。

我所要做的只是,想办法,简化回调机制带来的麻烦操作。

我在 定时中断 的基础上 加入gpio,然后重新考虑如何实现。
因为单纯考虑牵着时,问题很少,而集成的东西越来越多以后,问题会越来越复杂,所以,需要考虑新方法。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

8
 
快10点半了,准备洗澡,然后接着看看 超声波电路怎么做......
我一定会坚持下去的,就算做出来的东西实在对不起观众,恶心死人不偿命。
此帖出自编程基础论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表