[读书笔记]《C语言嵌入式系统编程修炼》

时间:2022-12-14 17:47:41

  大学前两年一直搞的是单片机,写的是嵌入式C语言程序,走过了不少弯路,现在感觉仍然在走弯路。有幸偶尔看到了这篇文章,深感自己以前写程序的时候存在很多误区。现写篇博客做下总结。

第一篇 软件架构篇


1.1 模块划分

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

  • (1)模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块借口的声明;
  • (2)某模块提供给其它模块调用的外部函数以及数据需要在.h文件中冠以extern关键字声明;
  • (3)只在模块内调用的函数和全局变量,尽量使用static进行修饰,避免可能发生的命名冲突;
  • (4)永远不要在.h文件中定义变量!定义变量和声明变量的区别在于,定义会给变量分配内存,是汇编阶段的概念;而声明则只是告诉包含该声明的模块,在本模块或者其它模块定义了该函数或变量,需要在连接阶段从其它模块寻找外部函数和变量。例如:
//module1.h
int a = 5;    //在模块1的.h文件中定义了int a  (最好不要在.h文件中定义变量)

//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 = 5;    //在模块1的.h文件中声明整型变量a

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

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

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

  这样如果模块1、2、3操作a的话,对应的是同一片内存单元。
 
   一个嵌入式系统通常包括两类模块:

  • (1)硬件驱动模块,一种特定硬件对应一个模块;
  • (2)软件功能模块,其模块的划分应满足低耦合、高内聚的要求。

1.2 多任务还是单任务

  所谓“单任务系统”是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地“同时”执行多个任务。
 
  多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存放任务的"上下文"(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。
 
  究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。

1.3 单任务程序典型架构

  • (1)从CPU复位时的指定地址开始执行;
  • (2)跳转至汇编代码startup处执行;
  • (3)跳转至用户主程序main执行,在main中完成:
      a.初试化各硬件设备;
      b.初始化各软件模块;
      c.进入死循环(无限循环),调用各模块的处理函数



3

  以前的类似闪烁程序是这样写的:

int i;
void showDot()
{
    i++;
    if(i%2)
        showChar(":",xPos,yPos);
    else
        showChar(" ",xPos,yPos);        
}



  其实像这本书上写更好:

void showDot()
{
    static bool bShowDot = TRUE;   //static变量,避免值被重新初始化
    if(bShowDot)
        showChar(":",xPos,yPos);
    else
        showChar(" ",xPos,yPos); 

    bShowDot = !bShowDot ;   //使用!操作符,来改变 bShowDot 的值,从而达到闪烁效果     
}

第3章