3028|11

7815

帖子

57

TA的资源

裸片初长成(中级)

楼主
 

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

昨晚我已经完成了 通过LED闪烁,测试uSer中的任务是否已经成功加入了定时器中断。

然而,最后我们也发现了,main函数的初始化显示出了 这个虽然很简单但已经显示出混乱的迹象。

这是个危险的苗头。
所以我们在继续开始以前,必须先理清条理。

经过我们前面的实现,我们已经很清楚了 回调函数 的存在是为了实现 uSer 和 Apper 之间的通信。

我们这里考虑一下,两者之间需要什么通信。
为了更清晰这种思路,我画了个图。

此帖出自编程基础论坛
点赞 关注
 

回复
举报

7815

帖子

57

TA的资源

裸片初长成(中级)

沙发
 

久违的PDL

说实话,到了这个时候,我一时之间不知道该怎么往下走。
我不知道该做什么才能真正带来改善,尽管说,不一定非做什么改变不可,但慢慢想来,当前的现状确实不够使。

在毫无思路之下,我只好回到原点,再次思索,我最初的目标是什么?
要实现这些目标,我该做什么改变。

通过什么方式呢?
图示的方法似乎没什么用。
也许是一种下意识的习惯,我不自觉的打开npp,写起了 注释。

这是我在 《代码大全》 里学来的一种做法。
称为 PDL,也就是说 在写程序以前,通过注释的形式,思考函数该如何一步一步求精。
后来我发现,这样写确实没什么意义,因为 代码本身确实比 注释更有实际意义。

然而,作为一种习惯,在我毫无办法的时候,这个习惯居然帮助了我——我开始完全没意识到自己在写PDL,直到我思考完毕时,才意识到。

因为我写注释习惯用英文写。
所以我首先呈现原来的 PDL,再写中文版本。
  1. /*
  2. first
  3. we will have  uCTimer
  4.               uCGpio
  5. */
  6.                           
  7.    // First,we want to define a TIMER;
  8.    //     so we need to initialize it.but TimerInitial will be define in Apper;
  9.    // so,first we need to insert uCTimer_Initial into uSer;
  10.    
  11.    // Also,we should swap a TaskList out to Apper;
  12.    //     yes,I said Apper call functions from uSer is easy things,but,we want
  13.    //     it be automaticically;
  14.    // so ,we also need to send out a TaskList-Function pointer(?).
  15.    
  16.    /*
  17.                            uS_Init()
  18.       -- TaskList -->   ----------------  <-- TimerInitial --                           
  19.    */
复制代码
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

板凳
 

PDL的中文解释

我经常会下意识在写笔记时,中英文混合,但为了避免因为我英语不是很好,可能造成误解的可能,我特意写一个对应的中文版本。

首先,我们在Apper里,肯定会实现一些具体于寄存器的外设实现。

在当前的例子里,我们会有两个外设 定时器中断 和 GPIO。

我们首先要实现一个定时器。所以我们要初始化它,然而,定时器的初始化只能在Apper里实现,所以,首先,我们要把 定时器的初始化 插入到 uSer里,自然我们会采取 函数回调的方式。

接下来,我们还要把一个定义在uSer里的一些需要放在 定时器中断里运行的 任务,我们考虑,将在uSer里统一把它们通过uSTaskList()函数 封装。

我们已经知道,对于封装好的uSer库,要在Apper里调用它的 uSTaskList()是非常自然简单的事情,但是,我们希望Apper尽可能做更少的事情,因此我们希望这个过程是自动的。
因此,我们同样需要借助 函数指针的方式把这个函数传递出来。

然后自动植入TimerIsr里。

我们画个简单的示意图。
于是乎,我们居然不经意就得到了一个我们真正需要的 初始化函数接口。奇妙吧,哈哈!

   /*
                           uS_Init()
      -- TaskList -->   ----------------  <-- TimerInitial --                           
   */
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

4
 

对uS_Init()接口的进一步扩展

我们已经知道,定时器中断只是第一个我们要在uSer和Apper之间通信的外设,事实上,我们还将有许许多多的外设需要在两者之间通信。
所以我们仍然采取 我们前面提到过的 结构体封装一系列 函数指针,以简化接口。

现在我们来定义这个 形参 和 返回值。

我们已经提到,TimerInitial这类函数相当于Apper向uSer的注册函数。
因此这个结构体命名为

uSRegistStruct;

对于返回值。它是要在Apper中自动嵌入相应调用的函数组。

uSPosterStruct;  (uS邮递员)

好了,是时候实现它了。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

5
 

新的uSCore实现

  1. // in uSCore.h

  2. typedef struct
  3. {
  4.     void (*TimerInitial)(void);
  5. }uSCore_RegistStruct;

  6. void uSCore_Regist_Init(uSCore_RegistStruct *Str);
  7. void uSCore_RegistStruct_Copy(uSCore_RegistStruct *Dest,uSCore_RegistStruct *Src);


  8. typedef struct
  9. {
  10.     void (*TimerTask)(void);
  11. }uSCore_PosterStruct;

  12. void uSCore_Poster_Init(uSCore_PosterStruct *Str);
  13. void uSCore_PosterStruct_Copy(uSCore_PosterStruct *Dest,uSCore_PosterStruct *Src);

  14. uSCore_PosterStruct *uSInit(uSCore_RegistStruct *Str);
复制代码
  1. // in uSCore.c

  2. void uSCore_Regist_Init(uSCore_RegistStruct *Str)
  3. {
  4.     Str->TimerInitial = NULL;
  5. }

  6. // 因为此处不涉及函数指针的调用,因此不作指针非NULL检查
  7. void uSCore_RegistStruct_Copy(uSCore_RegistStruct *Dest,uSCore_RegistStruct *Src)
  8. {
  9.     Dest->TimerInitial = Src->TimerInitial;
  10. }

  11. void uSCore_Poster_Init(uSCore_PosterStruct *Str)
  12. {
  13.     Str->TimerTask = NULL;
  14. }

  15. // 因为此处不涉及函数指针的调用,因此不作指针非NULL检查
  16. void uSCore_PosterStruct_Copy(uSCore_PosterStruct *Dest,uSCore_PosterStruct *Src)
  17. {
  18.     Dest->TimerTask = Src->TimerTask;
  19. }

  20. uSCore_RegistStruct uSerStr;
  21. uSCore_PosterStruct uSerPost;

  22. uSCore_PosterStruct *uSInit(uSCore_RegistStruct *Str)
  23. {
  24.     uSCore_Regist_Init(&uSerStr);
  25.     uSCore_Poster_Init(&uSerPost);
  26.        
  27.     uSCore_RegistStruct_Copy(&uSerStr,Str);

  28.     // 此处调用 uS 本身的初始化,由此才可得到 uSPosterStruct;
  29.     return (&uSerPost);     
  30. }
复制代码
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

6
 

现在我们来实现Apper的初始化函数

当我写完以后,我发现初始化函数变得非常简单

而且,我给它取了一个很不错的名字 Initializer。
  1. uSCore_RegistStruct uS_Register;
  2. uSCore_PosterStruct *uS_Poster;

  3. void uS_Initializer(void)
  4. {
  5.      uSCore_Regist_Init(&uS_Register);
  6.      uSCore_Poster_Init(uS_Poster);
  7.      
  8.      uS_Register.TimerInitial = Timer_Initial;
  9.      
  10.      uS_Poster = uSInit(&uS_Register);
  11. }
复制代码
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

7
 

2013.8.3 说明

1.上面的函数里漏了一句

void uS_Initializer(void)
{
     uSCore_Regist_Init(&uS_Register);
     uSCore_Poster_Init(uS_Poster);
     
     uS_Register.TimerInitial = Timer_Initial;
     uS_Register.Led_4_Test = uCGpio_Toggle;
     
     uS_Poster = uSInit(&uS_Register);
}

另外,这个LED没有闪烁起来。
但今晚实在有点晚了。
明晚继续。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

8
 

奇怪的解决方法

本想停下来,洗完澡,始终无法停下来。
写着写着,纯粹突然想试试

我发现问题出在 每次中断来临时,uCGpio_Toggle被调用了两次。

居然无意解决了,却想不通为什么。
  1. static void (*TimerIsr)(void) = NULL;

  2. void get_uSTimerIsr(void)
  3. {
  4.      if(get_uS_Poster()->TimerTask != NULL)
  5.         TimerIsr = get_uS_Poster()->TimerTask;   
  6. }

  7. #pragma vector=0xF
  8. __interrupt void TIM2_UPD_OVF_BRK_IRQHandler(void)
  9. {  
  10.    TIM2_ClearITPendingBit(TIM2_IT_UPDATE);
  11.    
  12.    // I don't understand why it didn't work as follow?
  13.    /*
  14.           if(get_uS_Poster()->TimerTask != NULL)
  15.         TimerIsr = get_uS_Poster()->TimerTask;  
  16.    */
  17.    if(TimerIsr != NULL)
  18.       (*TimerIsr)();
  19. }
复制代码
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

9
 

关于昨晚最后的问题的解释

  1. // I don't understand why it didn't work as follow?
  2.    /*
  3.           if(get_uS_Poster()->TimerTask != NULL)
  4.         TimerIsr = get_uS_Poster()->TimerTask;  
  5.    */
复制代码
这段代码。
我们知道,我是从 get_uS_Poster()中取到一个 结构体地址。
这个地址是指向 我所要的 uS_Poster。
然后我的想法是通过它取出其中的 TimerTask这个函数的地址。

然而,我观察到一个很奇怪的现象。
似乎每次都被调用了两次?

却始终想不出怎么回事。

我设置断点,发现一个更要命的问题。
第一次可以进到中断,而且也判断到了 这个函数指针非NULL,然而,再运行一下,却发现程序死掉了。

因为它再没停住。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

10
 

怀疑没有分配好内存

在一点思绪都没有的情况下,我习惯性的想到,会不会是没有分配好空间呢?

于是我在gcc下写了一个简单程序测试。
  1. typedef struct
  2. {
  3.     void (*Test)();
  4. }pTest;

  5. #include
  6. #include

  7. void Helloworld(void)
  8. {
  9.      printf("Helloworld.\n");
  10. }

  11. void main(void)
  12. {
  13.      void (*pT)(void) = NULL;
  14.          pTest *Str;
  15.          
  16.          Str = (malloc)(sizeof(pTest)*1);
  17.          
  18.          Str->Test = Helloworld;
  19.          
  20.      while(1)
  21.          {
  22.              if(Str->Test != NULL)
  23.                     pT = Str->Test;
  24.                  if(pT != NULL)
  25.                    (*pT)();
  26.          }
  27. }
复制代码
于是我发现,如果我首先做一个分配,就会出现和昨晚uS下一样的情况。

但是我想不明白有件事情。

它既然能通过非NULL检测,为什么又会挂掉呢?

在gcc下结果更加明显,因为它直接跳出该程序了。
不管是gcc下的情形和单片机下的情形有所不同,然而

在单片机调试模式下,我没有看到打出错误信息 target stop
这应该是 带操作系统和不带操作系统有所不同。

所以我想不通
非NULL的一个函数指针,怎么就死了呢?

这里的一个盲点也许是  如果给一个(结构体)指针赋值(地址),是否就不再为空指针呢?
这话一说完就知道是废话。
但是,如果不是如此,为什么?
哦,我懂了。

看代码方便说明
  1. uSCore_PosterStruct *uS_Poster;

  2. void uS_Initializer(void)
  3. {
  4.      uSCore_Regist_Init(&uS_Register);
  5.      uSCore_Poster_Init(uS_Poster);
  6.      
  7.      uS_Register.TimerInitial = Timer_Initial;
  8.      uS_Register.Led_4_Test = uCGpio_Toggle;
  9.      
  10.      uS_Poster = uSInit(&uS_Register);
  11.      
  12.      // insert uSer to Apper
  13.      get_uSTimerIsr();
  14. }

  15. uSCore_PosterStruct *get_uS_Poster(void)
  16. {
  17.      return uS_Poster;
  18. }
复制代码
我们注意到这个很重要的uS_Poster从头到尾只是一个指针。
这也不打紧,然而,它的地址所指向的却是uS中的一个结构体地址。

在uS的代码是这样的
  1. uSCore_PosterStruct uSerPost;

  2. uSCore_PosterStruct *uSInit(uSCore_RegistStruct *Str)
  3. {
  4.     uSCore_Regist_Init(&uSerStr);
  5.     uSCore_Poster_Init(&uSerPost);
  6.        
  7.     uSCore_RegistStruct_Copy(&uSerStr,Str);
  8.    
  9.     // Initial Timer;
  10.     (*(Str->TimerInitial))();
  11.     // insert TimerTask;
  12.     uS_TaskList();
  13.     uSerPost.TimerTask = uS_TaskList;
  14.    
  15.     return (&uSerPost);     
  16. }
复制代码
那么,这个地址是外部的.
外部又会如何呢?

仍然解释不通
为了简单起见,我仍然回到gcc下测试,没观察到相似的结果
  1. typedef struct
  2. {
  3.     void (*Test)();
  4. }pTest;

  5. pTest *get_TestStruct(void);
  6. void Helloworld(void);
复制代码
  1. #include "struct.h"
  2. #include

  3. static pTest TestStruct;

  4. pTest *get_TestStruct(void)
  5. {
  6.     TestStruct.Test = Helloworld;
  7.     return (&TestStruct);
  8. }

  9. void Helloworld(void)
  10. {
  11.      printf("Helloworld.\n");
  12. }
复制代码
  1. #include
  2. #include
  3. #include "struct.h"

  4. void main(void)
  5. {
  6.      void (*pT)(void) = NULL;
  7.          pTest *Str;
  8.          
  9.          Str = get_TestStruct();
  10.          
  11.          //Str = (malloc)(sizeof(pTest)*1);
  12.          
  13.      while(1)
  14.          {
  15.              if(Str->Test != NULL)
  16.                     pT = Str->Test;
  17.                  if(pT != NULL)
  18.                    (*pT)();
  19.          }
  20. }
复制代码

[ 本帖最后由 辛昕 于 2013-8-4 14:43 编辑 ]
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

11
 

结尾

实际上,定时中断是应避免过于繁重的。

所以即使不需要malloc一次结构体指针,反复从结构体中提出函数指针再调用也是无益的。

因此,上面这个问题,我只是想尽可能了解原因,扫清我对C的语法盲点。

所以,最后这个问题我的处理方式如下:
  1. static void (*TimerIsr)(void) = NULL;

  2. void get_uSTimerIsr(void)
  3. {
  4.      if(get_uS_Poster()->TimerTask != NULL)
  5.         TimerIsr = get_uS_Poster()->TimerTask;   
  6. }

  7. #pragma vector=0xF
  8. __interrupt void TIM2_UPD_OVF_BRK_IRQHandler(void)
  9. {  
  10.    TIM2_ClearITPendingBit(TIM2_IT_UPDATE);

  11.    if(TimerIsr != NULL)
  12.       (*TimerIsr)();
  13. }
复制代码
这个TimerIsr同样要找一个地方来初始化
我选择了 uS_Initializer()里

  1. void uS_Initializer(void)
  2. {
  3.      uSCore_Regist_Init(&uS_Register);
  4.      uSCore_Poster_Init(uS_Poster);
  5.      
  6.      uS_Register.TimerInitial = Timer_Initial;
  7.      uS_Register.Led_4_Test = uCGpio_Toggle;
  8.      
  9.      uS_Poster = uSInit(&uS_Register);
  10.      
  11.      // insert uSer to Apper
  12.      get_uSTimerIsr();
  13. }
复制代码
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

12
 

对于为什么通过非NULL检查却会出现野指针的解释

在我整理当前代码的时候,我慢慢的想明白了早上的原因了。

的确是因为没有给 结构体分配空间 所致。
至于为什么能通过非NULL检查。

也许原因是,本来这就是一个空的(结构体指针)地址——空的地址上到底是什么内容呢?这是没人可以保证或者提前知道的。不确定的行为不讨论,否则会疯掉。

因此,它能通过非NULL检查也是完全有可能的。
NULL往往是0,但是,不能保证一个没有指定内容的指针会指向一片有着什么残留值的空间。

我想,要指向一片残留值为0的空间比起 非0 总是要更难吧。

因此,它通过了非NULL检查,却是一个非法引用空指针。
此帖出自编程基础论坛
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

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

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