模块化编程初学者最好资料

更新时间:2023-09-29 04:46:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

C语言的模块化编程 注释:自己手工打造,

编写目的:我不是计算机专业毕业的,以前没编过程序,什么KEIL、CW、IAR集成开发工具更是闻而未闻,只简单的了解VC6.0,看过谭浩强的C教程,这是我的基础。但是发现在嵌入式开发中根本不知道是怎么回事,研究了几个星期,发现这里面的渊源是这样的: 一、模块的概念: 1.1什么模块?

一个.c文件和一个.h文件组合起来就是一个模块,如:LCD.C + LCD.h 一般来说除了main函数,一个.c文件就会有一个.文件 1.2 为什么用模块?

A: 当一个项目很复杂的时间,比如长见的手机,你开发这个嵌入式系统:里面的功能很多:键盘的驱动、显示屏的驱动、串口的通信、定时器管理、、、、、等等,一个人根本不可能做完,所以把功能分给项目小组中不同的成员去完成。

B:第一代手机嵌入式花费了3年完成,投资人力10人,资金8千万,5个月后研发第二代手机,芯片字宽改变。那么想用第一代开发的程序的话,就需要更改一些字的声明等,通过模块化的.h文件很容易的实现。 举一个简单的例子:

一工程的头文件data.h的内容: #ifndef _TYPE_H #define _TYPE_H

#define IN #define OUT #define IN_OUT

typedef unsigned int u16 typedef unsigned short u16; typedef unsigned char u8; typedef int i32; typedef char i8;

typedef i16 RET_STATUS;

typedef struct STRUCT_UTC_TIME {

u32 sec; u32 nanosec_q;

} UTC_TIME;

如在8位单片机的平台下,有如下一个变量定义: u16 g_nTimeCounter = 0 ;

如果移植32单片机的平台下,想要其的范围依旧为16位。

上面2行的意思是:在8位机上定义的无符号整形变量是16位的,如果把定义的语句直接移植到32位机上,那么长度就是32位(64位?)。也就是说int a,在8位机上占用16为,在32位机上占用32位(64位?)

如果项目的头文件data.h,里面是定义整个工程用到的数据类型(枚举、结构、链表、共用体、类)。

想要其的范围依旧为16位(可以理解为程序本身需要这样),那么只需要在data.h文件里做如下改变:typedef unsigned short int u16。

具体细节我也不能说清楚,我们只要知道模块化编程的好处是:可以划分工作,并

且使开发出来的程序容易移植。 二、h文件和C文件的关系?

一句话:.h文件是.c文件的描述,是c文件对外的接口(不准确,但是对出学者可以这样理解,只有理解了才有深入下去的可能)。

2.1 接口的描述

晕!!!什么是接口?接口的作用之一是提供应用程序给开发人员以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。白话就是,我们通过接口可以访问文件里面的函数、全局变量等,更白话的是说,一个自动面包级,你只要知道他是面包级就可以,知道哪里是面粉的入口,出来的是面包就可以。

为了给篇幅注水,拷贝一些资料: 在大规模程序开发中,一个程序由很多个模块组成,很可能,这些模块的编写任务被分配到不同的人。而你在编写这个模块的时候很可能就需要利用到别人写好的模块的借口,这个时候我们关心的是,它的模块实现了什么样的接口,我该如何去调用,至于模块内部是如何组织的,对于我而言,无需过多关注。而追求接口的单

一性,把不需要的细节尽可能对外部屏蔽起来,正是我们所需要注意的地方。

个人觉得语言描述绝对没有用例子讲解的清楚,下面是一个h文件和c例子: LCD.C文件文件里面有一个函数:

LcdPutChar(char cNewValue) ; //在当前位置输出一个字符 LCD.C的头文件LCD.h 其内容如下: #ifndef _LCD_H_ #define _LCD_H_

Extern LcdPutChar(char cNewValue) ; #endif

这个函数可能会为很多模块调用,现假如db_ point.C文件要调用此函数,那么只需要在db_ point.C里面包含过#include “Lcd.h”就可以了。同样,假如db_ point.C调用这个函数后经过变化,给网口发送一个信息,那么db_ point.C的功能可以在db_ point.h文件里描述:以便别的文件调用。 2.2预处理

对于菜鸟,对程序的预编译也是比较头疼的,现把预编译讲解一下:知道的童鞋可以跳过。

预编译的种类:宏定义、文件包含和条件编译。

宏定义:#define;文件包含:#include;条件编译:#if--#else--#endif, 还有一个错误提示 #error。

A:宏定义:#define OUT printf(“Hello,World”)同时,可以用#undef规定宏的作用域: #define OUT printf(“Hello,World:”) Void main ( )

{、、、、、、 OUT; 、、、、、、 #undef OUT

#define OUT printf(“I am 习总”); OUT;

} 此段函数执行输出将是:Hello,World:I am 习总

B:文件包含:我们知道一般的主函数前面都有#include这句预编译,

假如我们在d:\\程序练习这个文件下创建一个haha.h文件,这个文件只一句话:#include

那么我的主函数 #include可以用#include d:\\程序练习\\haha.h这句话替代。 现在明白包含的意思了吧。 C:条件编译1:

#ifdef OUT 程序段1 #else

程序段2

#endif 这几句话的意思是:假如前面定义了OUT,则编译程序段1,否则编译程序段2。 例子: #include

#define TIME void main() {

#ifdef TIME

cout<<\ #else

cout<<\ou can have a rest\ #endif }

条件编译2: LCD.h文件的内容:

#ifndef _LCD_H_ #define _LCD_H_

#endif

这个条件编译是为了防止重复包含:假如a.c和b.c文件都通过过

#include “Lcd.h”这个预处理包含了过Lcd.h,也就是使用了LCD.C的功能。a.c编译时,没有定义_LCD_H_,则执行#define _LCD_H_ ,当b.c编译时,已经有了Lcd.h的编译,那么就会对

1.预编译又称为预处理,是做些代码文本的替换工作,处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段。(以#开头的都是预编译语句)

嵌入式的外设:

Cpu与外设通信是通过外设的寄存器实现的,外设寄存器也称为I/O端口,通常包括状态寄存器、控制寄存器和数据寄存器。

外设寄存器为了能被CPU准确的访问,必须把自己的地址映射给CPU,这就是一直

让我头疼的地址映射:

所谓映射其实不是什么影子之类的,就是给外设寄存器划分一个地址而已,有的CPU(如Power PC、m68K等)把外设的寄存器地址映射给它单一的地址空间(RAM),简言之就是外设寄存器和内存(RAM)统一编址,以后cpu访问寄存器和访问内存的指令相同,这就是常说的内存映射。

而对于X86等的CPU,它把外设寄存在一个单独的地址空间编址(不同于内存的地址),这就是端口映射,cpu访问这样的外设需要单独的指令:IN OUT,IO地址空间一般较小,如x86为64KB(0——0xffff)。

这个表就是:address是实际的物理地址,把实际的物理地址分配给各个外设。 软件如何读写外设:

#define rGPGCON (*(volatile unsigned *)0x56000060) //Port G control

把地址(volatile unsigned *)0x56000060强制转换为无符号型整形指针,最外面的*号表示这个指针的内容。

向寄存器赋值:rGPBCON = 0x1000;//把0x1000赋值个地址为0x56000060的寄存器。

从寄存器取值:unsigned int val = rGPBCON;//把地址为0x56000060的寄存器的值赋值给变量val。 寄存器的位操作:

把0位置1:rGPGCON | = 0x1;// rGPGCON= rGPGCON|0x1 位或运算 把3位置1:rGPGCON | = 0x8; 把3位置1: rGPGCON |= (0x1 <

把第10,15位分别置1:rGPGON | = ( 0x01 << 10 ) | (0x01 << 15) ;//位或的等级高

具体例子:

通过一个对G口操作,控制LED1和LED2轮流闪烁。

首先对寄存器进行定义:

#define rGPGCON (*(volatile unsigned*)0x56000060) //Port G控制寄存器 #define rGPGDAT (*(volatile unsigned*)0x56000064) //Port G数据寄存器 #define rGPGUP (*(volatile unsigned*)0x56000068) //Port G上拉电阻禁止寄存器

要想对G口进行控制,只需对地址0x56000060的相应位进行赋值即可。 整个程序的代码: Void main( ) {

int flag, I;

Targe_initt( )//对硬件进行初始化,包括对输出口的定义:即类似上面三行的宏定义 while (1)//即无限循环,常说的死循环 { If(flag==0) {

for(i=0;i<100000;i++);//延时

rGPGCON=rGPGCON&0xfff0ffff|0x00050000; //配置第8、第9位为输出引脚,晕了吧

rGPGDAT=rGPGDAT&0xeff|0x200; //第8位输出为低电平,第9位输出为高电平, rGPGDAT&0xeff是清零第8位,然后再与0x200进行或,置位第9位

for(i=0;i<100000;i++);//延时 flag=1;

}

else{

for(i=0;i<1000000;i++); //延时

rGPGCON=rGPGCON&0xfff0ffff|0x00050000; //配置第8、第9位为输出引脚 rGPGDAT=rGPGDAT&0xdff|0x100; //第8位输出为高电平

//第9位输出为低电平 for(i=0;i<10000000;i++); //延时 flag=0; } }

} 未完待续

本文来源:https://www.bwwdw.com/article/bhid.html

Top