发布新日志

  • C语言练习

    2009-08-18 13:06:28

    练习1
     题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
    1.程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去
           掉不满足条件的排列。
    2.程序源代码:
    main()
    {
    int i,j,k;
    printf("\n");
    for(i=1;i<5;i++)    /*以下为三重循环*/
      for(j=1;j<5;j++) 
       for (k=1;k<5;k++)
        {
         if (i!=k&&i!=j&&j!=k)    /*确保i、j、k三位互不相同*/
         printf("%d,%d,%d\n",i,j,k);
        }
    }

     


    练习2
    题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高
        于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提
        成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于
        40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于
        100万元时,超过100万元的部分按1%提成,从键盘输入当月利润I,求应发放奖金总数?
    1.程序分析:请利用数轴来分界,定位。注意定义时需把奖金定义成长整型。      
    2.程序源代码:
    main()
    {
    long int i;
    int bonus1,bonus2,bonus4,bonus6,bonus10,bonus;
    scanf("%ld",&i);
    bonus1=100000*0.1;bonus2=bonus1+100000*0.75;
    bonus4=bonus2+200000*0.5;
    bonus6=bonus4+200000*0.3;
    bonus10=bonus6+400000*0.15;
      if(i<=100000)
       bonus=i*0.1;
      else if(i<=200000)
          bonus=bonus1+(i-100000)*0.075;
         else if(i<=400000)
             bonus=bonus2+(i-200000)*0.05;
            else if(i<=600000)
                bonus=bonus4+(i-400000)*0.03;
               else if(i<=1000000)
                   bonus=bonus6+(i-600000)*0.015;
                  else
                   bonus=bonus10+(i-1000000)*0.01;
    printf("bonus=%d",bonus);
    }


    练习3
    题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
    1.程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上268后再开方,如果开方后
           的结果满足如下条件,即是结果。请看具体分析:
    2.程序源代码:
    #include "math.h"
    main()
    {
    long int i,x,y,z;
    i=0;
    for (i=1;i<100000;i++)
      { x=sqrt(i+100);   /*x为加上100后开方后的结果*/
       y=sqrt(i+268);   /*y为再加上168后开方后的结果*/
        if(x*x==i+100&&y*y==i+268)/*如果一个数的平方根的平方等于该数,这说明此数是完全平方数*/
         printf("\n%ld\n",i);
      }
    }

    练习4
    题目:输入某年某月某日,判断这一天是这一年的第几天?
    1.程序分析:以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,特殊
           情况,闰年且输入月份大于3时需考虑多加一天。
    2.程序源代码:
    main()
    {
    int day,month,year,sum,leap;
    printf("\nplease input year,month,day\n");
    scanf("%d,%d,%d",&year,&month,&day);
    switch(month)/*先计算某月以前月份的总天数*/
    {
      case 1:sum=0;break;
      case 2:sum=31;break;
      case 3:sum=59;break;
      case 4:sum=90;break;
      case 5:sum=120;break;
      case 6:sum=151;break;
      case 7:sum=181;break;
      case 8:sum=212;break;
      case 9:sum=243;break;
      case 10:sum=273;break;
      case 11:sum=304;break;
      case 12:sum=334;break;
      default:printf("data error");break;
    }
    sum=sum+day;  /*再加上某天的天数*/
      if(year%400==0||(year%4==0&&year%100!=0))/*判断是不是闰年*/
       leap=1;
      else
       leap=0;
    if(leap==1&&month>2)/*如果是闰年且月份大于2,总天数应该加一天*/
    sum++;
    printf("It is the %dth day.",sum);}

    练习5
    题目:输入三个整数x,y,z,请把这三个数由小到大输出。
    1.程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x>y则将x与y的值进行交换,
           然后再用x与z进行比较,如果x>z则将x与z的值进行交换,这样能使x最小。
    2.程序源代码:
    main()
    {
    int x,y,z,t;
    scanf("%d%d%d",&x,&y,&z);
    if (x>y)
    {t=x;x=y;y=t;} /*交换x,y的值*/
    if(x>z)
    {t=z;z=x;x=t;}/*交换x,z的值*/
    if(y>z)
    {t=y;y=z;z=t;}/*交换z,y的值*/
    printf("small to big: %d %d %d\n",x,y,z);
    }

    练习6
    题目:输出9*9口诀。
    1.程序分析:分行与列考虑,共9行9列,i控制行,j控制列。
    2.程序源代码:
    main()
    {
        int i,j,result;
        printf("\n");
        for(i=1;i<10;i++)
        {
          for(j=1;j<=i;j++)
            {
                result=i*j;
                printf("%d*%d=%-3d",j,i,result);   /*-3d表示左对齐,占3位*/
            }
            printf("\n");   /*每一行后换行*/
        }
    或者
    main()
    {
        int i,j,result;
        printf("\n");
        for(i=1;i<10;i++)
        {
            for(j=1;j<10;j++)
            {
                result=i*j;
                printf("%d*%d=%-3d",j,i,result);
            }
            printf("\n");
        }

    练习7
    题目:要求输出国际象棋棋盘。
    1.程序分析:用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方格,还是白方格。
    2.程序源代码:
    #include "stdio.h"
    main()
    {
    int i,j;
    for(i=0;i<8;i++)
      {
       for(j=0;j<8;j++)
        if((i+j)%2==0)
         printf("%c%c",219,219);
        else
         printf(" ");
        printf("\n");
      }
    }

    练习8
    题目:打印楼梯,同时在楼梯上方打印两个笑脸。
    1.程序分析:用i控制行,j来控制列,j根据i的变化来控制输出黑方格的个数。
    2.程序源代码:
    #include "stdio.h"
    main()
    {
    int i,j;
    printf("\1\1\n");/*输出两个笑脸*/
    for(i=1;i<11;i++)
      {
      for(j=1;j<=i;j++)
       printf("%c%c",219,219);
      printf("\n");
      }
    }

    练习9
    题目:2的N次方。
    2.程序源代码:
    #include"stdio.h"
    main()
    {
        long int i,x,m;
        x=1;
        for(i=1;i<10;i++)
        {
            x=2*x;
            printf("%d\n",x);
        }
    }

    练习10
    题目:判断101-200之间有多少个素数,并输出所有素数。

        程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。

    程序源代码:
    #include "math.h"
    main()
    {
     int m,i,k,h=0,leap=1;
     printf("\n");
     for(m=101;m<=200;m++)
      { k=sqrt(m+1);
       for(i=2;i<=k;i++)
         if(m%i==0)
          {leap=0;break;}
       if(leap) {printf("%-4d",m);h++;
            if(h%10==0)
            printf("\n");
            }
       leap=1;
      }
     printf("\nThe total is %d",h);
    }

    练习11
    题目:输入三角形的三边长,求三角形面积。
    程序分析:三角形面积格式:area=s(s-a)(s-b)(s-c)的平方根。
    程序源代码
    #include"math.h"
    main()
    {
    float a,b,c,s,area;
    scanf("%f,%f,%f",&a,&b,&c);
    s=1.0/2*(a+b+c);
    area=sqrt(s*(s-a)*(s-b)*(s-c));
    printf("a=%7.2f,b=%7.2f,c=%7.2f,s=%7.2f\n",a,b,c,s);
           printf("area=%7.2f\n",area);
    }

    练习12
    题目:从键盘输入一个大写字母,要求改用小写字母输出。
    程序源代码
    #include"stdio.h"
    main()
    {
        char c1,c2;
        c1=getchar();
        printf("%c,%d\n",c1,c1);
        c2=c1+32;
        printf("%c,%d\n",c2,c2);
    }

    练习13
    题目:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个“水仙花数”,因为153=1的三次方+5的三次方+3的三次方。

        程序分析:利用for循环控制100-999个数,每个数分解出个位,十位,百位。

    程序源代码:
    #include"stdio.h"
    main()
    {
        int n,i,j,k,n2;
        scanf("%d",&n2);
        for(n=100;n<n2;n++)
        {
        i=n/100;
        j=n/10%10;
        k=n%10;
        if(i*100+j*10+k==i*i*i+j*j*j+k*k*k)
        {
        printf("%3d %3d %3d\n",i,j,k);
        }
    }
    }

    练习14
    题目:将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。

        程序分析:对n进行分解质因数,应先找到一个最小的质数k,然后按下述步骤完成:
     (1)如果这个质数恰等于n,则说明分解质因数的过程已经结束,打印出即可。
     (2)如果n<>k,但n能被k整除,则应打印出k的值,并用n除以k的商,作为新的正整数你n,重复执行第一步。
     (3)如果n不能被k整除,则用k+1作为k的值,重复执行第一步。

    程序源代码:
    #include"stdio.h"
    main()
    {
        int n,i;
        printf("\nplease input a number:\n");
        scanf("%d",&n);
        printf("%d=",n);
        for(i=2;i<n;i++)
        {
            while(n!=i)
            {
                if(n%i==0)
                {
                    printf("%d*",i);
                    n=n/i;
                }
                else
                    break;
            }
        }
        printf("%d",n);
    }

    练习15
    题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
    程序分析:这是个Fibonacci数列40个数,这个数列有如下特点:第1,2两个数为1,1.从第3个数开始,该数是其前面两个数之和。
    源代码:
    #include<stdio.h>
    main()
    {
        long int f1,f2;
        int i;
        f1=1;f2=1;
        for(i=1;i<=20;i++)
        {
            printf("%12ld%12ld",f1,f2);
            if(i%2==0) printf("\n");
            f1=f1+f2;
            f2=f2+f1;
        }
    }


    #include<stdio.h>
    main()
    {
        int i;
        int f[20]={1,1};
        for(i=2;i<20;i++)
            f[i]=f[i-2]+f[i-1];
        for(i=0;i<20;i++)
        {
            if(i%5==0) printf("\n");
            printf("%12ld",f[i]);
        }
        printf("\n");
    }

    练习16
    求n的阶乘(n!)
    分析:用函数递归方法解;
    源程序
    #include <stdio.h>

    float fac(int n)
    {
        float f;
        if(n<0) f=0;
        else if(n==0 || n==1) f=1;
        else f=fac(n-1)*n;
        return(f);
    }

    main()
    {
        int n;
        float y;
        printf("input a integer number:");
        scanf("%d",&n);
        y=fac(n);
        if (y==0) printf("n<0,dataerror!");
        else printf("%d! =%15.0f",n,y);
    }


  • C++创建文本文件

    2009-08-09 15:05:11

    #include <stdio.h>
    #include<direct.h>
    void main()
    {
        FILE * fp;
       int i;
       for(i = 1;i < 5; i++)
       {       char name[128];
           sprintf(name, "D:\\1\\wenjian%d.txt", i);
            
            fp    = fopen(name, "w");
            fclose(fp);
       }
        system("pause");
    }
  • 怎样在VS2005里开发C语言项目

    2009-05-30 21:14:49

    1,  打开vs2008,选择C++语言,创建一个空项目,见图1、2、3;

    2,  进去后,选中源文件文件夹——>工程——>新建项,或右击源文件文件夹——>添加——>新建项,见图4;


    3,  在弹出的“添加新项”界面的右边选择“C++文件”或者“文本文件”——>在下面的名字框里填上文件的名称例如“name.c”,注意,一定要有后缀名.C  。然后就可以编辑调试啦!见图5;

    4,   写一个Hello World,加一句system("pause");用来让它显示结果,不至于一闪而过。

    5,  按F7-->F5,效果,见图6;
  • 转VS2005调试技巧

    2009-05-30 21:12:39

    下面有从浅入深的6个问题,您可以尝试回答一下

    1. 一个如下的语句for (int i = 0; i < 10; i++){if (i == 5)j = 5;},什么都写在一行,你怎么在j=5前面插入断点
    2. 在一个1000次的循环体内部设置断点,你希望当循环进行到900次后中断,怎么才能做到呢?
    3. 你有一个表达式在上面循环的某一次发生了变化,你想知道是哪一次,在哪个地方,怎么才能做到?
    4. 你希望你的断点在被命中100次后,每命中三次中断一次,比如第103,第106,第109怎样做?
    5. 你有在调试一个服务程序,希望在其内部打上了断点,可是,由于这是一个公用的服务你不希望其他访问这个服务的程序被你的调试所干扰,你想怎么办?
    6. 怎样知道2个断点中断的时间间隔

    问题1,2

    这两个问题最简单,我在一个例子里说明

    例如如下循环

    for(int i=0;i<1000;i++){doSomeThing......}

    在循环的大括号上单击右键,插入断点,用这个方法,可以对付那些喜欢把语句写在一行上的家伙,其实,随着.Net3.5中Linq的出现,我们肯定也会经常在在一行上写复杂的表达式,这个时候用这种插入方法会比较管用

    ok,现在我们来编辑这个断点的条件,在断点上右键单击,选择如图菜单项

    在弹出的窗口中可以设置断点命中的条件i==900

    注意我是在调试C#代码,默认的条件语句语法是C#,如果你想切换,那就需要用Ctrl-B,来插入断点,并在弹出窗口中选择语言

    通过这样设置条件断点,我们就可以解决我们的问题1,2了

    问题3

    同样通过设置条件断点我们还可以解决我们的问题3,对表达式变化的跟踪

    string user="yizhu2000"

    for(int i=0;i<10000;i++){

    DoSomething1()

    .......

    DoSomethingN()

    }

    当循环执行完毕时我们发现user变成了"smart_boy",你不知道这个值是在第几次循环的时候变化的,那么你是不是会选择打上断点,一次一次中断,来查看呢?当然不用

    在循环体结束的位置我们设置一个断点,打开条件编辑窗口(打开方法同上),设置表达式为user,勾选下面的HasChanged,也就是说,你告诉断点,当user的值发生变化时才触发

    (注意:第一次执行到断点的时候,程序一定会中断,并计算这时表达式的值,所以,所谓发生变化,指的是以后执行到断点是表达式的值和第一次执行到断点时表达式的值的比较)

    问题4

    如何让断点在指定的命中次数或者大于某个次数时触发呢?方法是设定几个断点的HitCount,右键单击断点,在弹出菜单中选择Hit Count,会弹出如下窗口

    在"when the break point is hit"下拉列表里,我们可以看到四个选项

    break always:总是中断

    break when the hit count is equal to:等于某次数时中断

    beak when the hit count is a multpile of:当次数是某数的倍数时中断

    break when the hit count is greater than or equal to:当大于等于某数时中断

    问题5

    前面4个问题都已经解决了,第5个问题的解决方法是利用断点的Filter功能,比如我希望断点只有被机器名为yizhu的机器访问才能触发,我可以这样设置

    当其他机器访问程序的时候,断点将不会触发,这样做的优点是通过设置机器名,我们可以让其他机器访问的时候感觉不到断点的存在,除此之外我们可以设 置机器名,进程号,进程名,线程号,线程名作为filter,而且还可以把他们组合起来,比如我希望通过当机器yizhu的dllhost进程调用时才触 发,那么问题就可以设置为MachineName="yizhu"&ProcessName="dllhost"

    问题6

    现在我们来解决第6个问题:

    在程序性能调试的时候,我们经常需要知道某段代码的执行效率,一般来说,我们可以在程序中加入时间点,通过时间点相减来取得时间间隔,这种方法有个 显而易见的缺点就是需要修改程序,想要不修改程序,就需要借助一些工具,那么有没有什么方法可以声明式的插入时间点,并计算值呢?其实断点完全可以做到

    在给出方法前,我们来看看断点的另外一个设置项,When Hit,这个选项可以让我们在命中断点后做一些事情,包括输出一些内容,或者调用宏,比如输出一个程序中变量的值

    我们输出了变量user的值,下面Continue Execution表示程序不会中断,输出后继续执行,注意表达式需要用{}括起来,,其他的部分会被作为字符串输出。设定WhenHit后断点变成了方形(看厌了圆断点,我还挺喜欢这个方家伙的)

    在output中查看输出结果,如下:

    既然可以计算表达式,我们的第一个最简方案就出来了,也就是在程序执行到断点的时候,输出DateTime.Now,这样当然是可行的,但是我们需 要的是时间间隔,所以我们还需要自己来算个减法,还是挺麻烦的,怎么样才能让程序自己输出时间间隔呢?有一个想法是这样的,我们在上一个断点声明一个时间 变量,然后在下面的断点里用DataTime.Now减去这个变量,即

    断点一的条件:{DateTime _t=DateTime.Now;}

    断点二的条件:{DateTime.Now-t;}

    看起来不错,但是实际运行时就有问题了,让我们看看输出吧

    上面高亮的部分说,变量申明只能在immediate window中进行,所以断点一的变量没有申明成功,关于immediatewindow,我们以后会涉猎到,反正就是说想在表达式里申明变量,没门,死路一条.那么我们怎么才能不申明变量又时间点呢?

    这时我想起了Thread.SetData 方法,这个方法可以往当前线程专门提供的空间中插入一些数据,并且可以通过GetData得到数据,具体细节参考

    http://msdn2.microsoft.com/zh-cn/library/system.threading.thread.setdata(VS.80).aspx

    于是方案就有了,在第一个断点处把时间放入Thread的DataSlot,然后第二个断点取出来相减

    断点一的条件:{Thread.SetData(Thread.GetNamedDataSlot("ExecutionTime"),DateTime.Now);}

    断点二的条件:{DateTime.Now-(DateTime)System.Threading.Thread.GetData(System.Threading.Thread.GetNamedDataSlot("ExecutionTime"));}

    看看输出效果

    我们的目的已经达到了,output中成功的输出了时间间隔,当然,还不是很完善,首先,这个方法限于两个断点,你想多打几个断点,测试两两间的间隔还是比较麻烦.测量精度也可以提高,大家有兴趣可以自己研究这个方法的扩展

  • 转:C语言嵌入式系统编程修炼之软件架构篇

    2009-05-06 16:52:14

    模块划分

      模块划分的"划"是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的 部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到 了相对论),C语言模块化程序设计需理解如下概念:

      (1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;

      (2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;

      (3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

      (4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:

    /*module1.h*/
    int a = 5; /* 在模块1的.h文件中定义int a */

    /*module1 .c*/
    #include "module1.h" /* 在模块1中包含模块1的.h文件 */

    /*module2 .c*/
    #include "module1.h" /* 在模块2中包含模块1的.h文件 */

    /*module3 .c*/
    #include "module1.h" /* 在模块3中包含模块1的.h文件 */

      以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不需要这样的程序。正确的做法是:

    /*module1.h*/
    extern int a; /* 在模块1的.h文件中声明int a */

    /*module1 .c*/
    #include "module1.h" /* 在模块1中包含模块1的.h文件 */
    int a = 5; /* 在模块1的.c文件中定义int a */

    /*module2 .c*/
    #include "module1.h" /* 在模块2中包含模块1的.h文件 */

    /*module3 .c*/
    #include "module1.h" /* 在模块3中包含模块1的.h文件 */

      这样如果模块1、2、3操作a的话,对应的是同一片内存单元。

      一个嵌入式系统通常包括两类模块:

      (1)硬件驱动模块,一种特定硬件对应一个模块;

      (2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。

      多任务还是单任务

      所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。

       多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任 务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存 放任务的"上下文"(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个 寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。

      嵌入式多任务OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遥不可及的神坛之物,我们可以用不到1000行代码实现一个针对80186处理器的功能最简单的OS内核,作者正准备进行此项工作,希望能将心得贡献给大家。

      究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。
    单任务程序典型架构

      (1)从CPU复位时的指定地址开始执行;

      (2)跳转至汇编代码startup处执行;

      (3)跳转至用户主程序main执行,在main中完成:

      a.初试化各硬件设备;

      b.初始化各软件模块;

      c.进入死循环(无限循环),调用各模块的处理函数

      用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循环,其首选方案是:

    while(1)
    {
    }

      有的程序员这样写:

    for(;;)
    {
    }

      这个语法没有确切表达代码的含义,我们从for(;;)看不出什么,只有弄明白for(;;)在C语言中意味着无条件循环才明白其意。

      下面是几个"著名"的死循环:

      (1)操作系统是死循环;

      (2)WIN32程序是死循环;

      (3)嵌入式系统软件是死循环;

      (4)多线程程序的线程处理函数是死循环。

       你可能会辩驳,大声说:"凡事都不是绝对的,2、3、4都可以不是死循环"。Yes,you are right,但是你得不到鲜花和掌声。实际上,这是一个没有太大意义的牛角尖,因为这个世界从来不需要一个处理完几个消息就喊着要OS杀死它的WIN32 程序,不需要一个刚开始RUN就自行了断的嵌入式系统,不需要莫名其妙启动一个做一点事就干掉自己的线程。有时候,过于严谨制造的不是便利而是麻烦。君不 见,五层的TCP/IP协议栈超越严谨的ISO/OSI七层协议栈大行其道成为事实上的标准?

      经常有网友讨论:

    printf("%d,%d",++i,i++); /* 输出是什么?*/
    c = a+++b; /* c=? */

      等类似问题。面对这些问题,我们只能发出由衷的感慨:世界上还有很多有意义的事情等着我们去消化摄入的食物。

      实际上,嵌入式系统要运行到世界末日。

      中断服务程序

       中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序 (ISR),类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。

      中断服务程序需要满足如下要求:

      (1)不能返回值;

      (2)不能向ISR传递参数;

      (3) ISR应该尽可能的短小精悍;

      (4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。

      在某项目的开发中,我们设计了一个队列,在中断服务程序中,只是将中断类型添加入该队列中,在主程序的死循环中不断扫描中断队列是否有中断,有则取出队列中的第一个中断类型,进行相应处理。

    /* 存放中断的队列 */
    typedef struct tagIntQueue
    {
     int intType; /* 中断类型 */
     struct tagIntQueue *next;
    }IntQueue;

    IntQueue lpIntQueueHead;

    __interrupt ISRexample ()
    {
     int intType;
     intType = GetSystemType();
     QueueAddTail(lpIntQueueHead, intType);/* 在队列尾加入新的中断 */
    }

      在主程序循环中判断是否有中断:

    While(1)
    {
     If( !IsIntQueueEmpty() )
     {
      intType = GetFirstInt();
      switch(intType) /* 是不是很象WIN32程序的消息解析函数? */
      {
       /* 对,我们的中断类型解析很类似于消息驱动 */
       case xxx: /* 我们称其为"中断驱动"吧? */
        …
        break;
       case xxx:
        …
        break;
       …
      }
     }
    }

      按上述方法设计的中断服务程序很小,实际的工作都交由主程序执行了。
    硬件驱动模块

      一个硬件驱动模块通常应包括如下函数:

      (1)中断服务程序ISR

      (2)硬件初始化

      a.修改寄存器,设置硬件参数(如UART应设置其波特率,AD/DA设备应设置其采样速率等);

      b.将中断服务程序入口地址写入中断向量表:

    /* 设置中断向量表 */
    m_myPtr = make_far_pointer(0l); /* 返回void far型指针void far * */
    m_myPtr += ITYPE_UART; /* ITYPE_UART: uart中断服务程序 */
    /* 相对于中断向量表首地址的偏移 */
    *m_myPtr = &UART _Isr; /* UART _Isr:UART的中断服务程序 */

      (3)设置CPU针对该硬件的控制线

      a.如果控制线可作PIO(可编程I/O)和控制信号用,则设置CPU内部对应寄存器使其作为控制信号;

      b.设置CPU内部的针对该设备的中断屏蔽位,设置中断方式(电平触发还是边缘触发)。

      (4)提供一系列针对该设备的操作接口函数。例如,对于LCD,其驱动模块应提供绘制像素、画线、绘制矩阵、显示字符点阵等函数;而对于实时钟,其驱动模块则需提供获取时间、设置时间等函数。

      C的面向对象化

      在面向对象的语言里面,出现了类的概念。类是对特定数据的特定操作的集合体。类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合,我们可以利用函数指针将struct模拟为一个包含数据和操作的"类"。下面的C程序模拟了一个最简单的"类":

    #ifndef C_Class
    #define C_Class struct
    #endif
    C_Class A
    {
     C_Class A *A_this; /* this指针 */
     void (*Foo)(C_Class A *A_this); /* 行为:函数指针 */
     int a; /* 数据 */
     int b;
    };

       我们可以利用C语言模拟出面向对象的三个特性:封装、继承和多态,但是更多的时候,我们只是需要将数据与行为封装以解决软件结构混乱的问题。C模拟面向 对象思想的目的不在于模拟行为本身,而在于解决某些情况下使用C语言编程时程序整体框架结构分散、数据和函数脱节的问题。我们在后续章节会看到这样的例 子。

      总结

      本篇介绍了嵌入式系统编程软件架构方面的知识,主要包括模块划分、多任务还是单任务选取、单任务程序典型架构、中断服务程序、硬件驱动模块设计等,从宏观上给出了一个嵌入式系统软件所包含的主要元素。

      请记住:软件结构是软件的灵魂!结构混乱的程序面目可憎,调试、测试、维护、升级都极度困难。
Open Toolbar