2017/3/22 如何用C 来完成SN8系列芯片的程序设计 2017/3/22
Review 内嵌汇编 程序结构 SN8 C studio 的安装与使用 数据类型与运算 程序流程控制 函数 2017/3/22 Review SN8 C studio 的安装与使用 数据类型与运算 程序流程控制 函数 结构体、联合在SN8 C程序中的应用 中断 位操作 内嵌汇编 程序结构
2017/3/22 SN8 C studio 的安装与使用 SN8 C studio 的安装 SN8 C studio应用实例
数据类型与运算 数据类型 基本类型 字符型(char) 整型(int) 长整型(long) 浮点型(float) 构造类型 2017/3/22 数据类型与运算 数据类型 基本类型 字符型(char) 整型(int) 长整型(long) 浮点型(float) 构造类型 数组(array) 结构体(struct) 共用体(union) 枚举(enum) 指针类型 空类型
专有数据类型 数据类型 Size(Byte) 数据取值范围 Signed char(short、int) 1 -128~+127 2017/3/22 专有数据类型 数据类型 Size(Byte) 数据取值范围 Signed char(short、int) 1 -128~+127 Unsigned char(short、int) 0~255 Signed long 2 -32768~+32767 Unsigned long 0~65535 float、double 4 Pointer enum
常量的定义 先来看看汇编的常量定义: door_service_c equ #80 ;80ms去门抖动 2017/3/22 常量的定义 先来看看汇编的常量定义: door_service_c equ #80 ;80ms去门抖动 t0int_c equ #224 ;t0中断时间 segment_c equ #3 ;最多3段烹调 注:上面数值前的#号,是SN8ASM的符号,用于提示后面的是立即数。
再来看看用SN8 C是如何定义相同的常量的: 2017/3/22 常量的定义 再来看看用SN8 C是如何定义相同的常量的: #define door_service_c 80 //80ms去门抖动 #define t0int_c 224 //t0中断时间 #define segment_c 3 //最多3段烹调 NOTE: 对习惯于写汇编的人来说,千万注意C对大小写敏感!并从变量定义就要开始注意!
数值列表 汇编的表: disp_automenu: ;显示菜单用第二数字表格 dw 0000h dw 0ae1fh ;A-1 2017/3/22 数值列表 汇编的表: disp_automenu: ;显示菜单用第二数字表格 dw 0000h dw 0ae1fh ;A-1 dw 0ae2fh ; dw 0ae3fh ; dw 0ae4fh ; dw 0ae5fh ; dw 0ae6fh ; dw 0ae7fh ; 表的内容都是用DW关键字来定义
变量定义与限制 __RAM与__ROM关键字的使用: 将变量存放在RAM中 [默认] 2017/3/22 变量定义与限制 __RAM与__ROM关键字的使用: Unsigned int __RAM ramVeriable; __RAM unsigned int ramVeriable2; Unsigned int __ROM romVeriable; __ROM unsigned int romVeriable2; 将变量存放在RAM中 [默认] 将变量存放在ROM中
常量数值列表定义 C定义的数值列表: unsigned long __ROM disp_automenu[]= { 2017/3/22 常量数值列表定义 C定义的数值列表: unsigned long __ROM disp_automenu[]= { 0x0000,0x0ae1f,0x0ae2f,0x0ae3f, 0x0ae4f,0x0ae5f,0x0ae6f,0x0ae7f }; 定义一个数组来存储这些表的数值
变量的定义 汇编的定义变量的方法: .DATA org 0h temp1 ds 1 分别占用的Byte单位的RAM空间 temp2 ds 1 2017/3/22 变量的定义 汇编的定义变量的方法: .DATA org 0h temp1 ds 1 temp2 ds 1 led_dp ds 1 step ds 1 Job_mode ds 2 Pow_mode ds 4 分别占用的Byte单位的RAM空间 用DS关键字来定义变量空间
C支持不同长度的变量类型,这样就方便了程序员的使用 2017/3/22 变量的定义 用C定义变量: unsigned int temp1; unsigned int temp2; unsigned int led_dp; unsigned int step; unsigned long job_mode; unsigned long power_mode1; float powerValue; int temp1_1; long temp2_2; C支持不同长度的变量类型,这样就方便了程序员的使用
(un)signed int/short/char 2017/3/22 变量定义的对比 ASM.的定义 C的定义 DS 1 (un)signed int/short/char DS 2 (un)signed long DS 4 Float/double
能使用无符号数的都使用无符号数,以免处理出错,因为芯片内部是以无符号数处理的。 2017/3/22 变量类型的选择 在选择数据类型的时候,在能够顺利完成功能的情况下,请尽量选择占空间少的数据类型,这样不管是在RAM空间使用上还是在产生代码效率上都有很多的好处! 能使用无符号数的都使用无符号数,以免处理出错,因为芯片内部是以无符号数处理的。
数据的存储 SN8芯片的数据存储区 Bank0 0000H 通用存储区 128Byte 用户存储区 007FH 0080H 系统寄存器区 2017/3/22 数据的存储 SN8芯片的数据存储区 Bank0 0000H 通用存储区 128Byte 用户存储区 007FH 0080H 系统寄存器区 80H~FFH为系统寄存器区域 00FFH Bank1 00100 更多的用户存储区 01FFh
系统寄存器 系统寄存器表 2017/3/22 1 2 3 4 5 6 7 8 9 A B C D E F L H R Z Y X PFLAG 1 2 3 4 5 6 7 8 9 A B C D E F L H R Z Y X PFLAG RBANK - AMPM P4CON DAM ADM ADB ADR PEDGE P2M P4M P5M INTRQ INTEN OSCM WDTR TC0R PCL PCH P0 P2 P4 P5 T0M TC0M TC0C TC1M TC1C TC1R STKP P0UR P2UR P4UR P5UR @YZ STK7L STK7H STK6L STK6H STK5L STK5H STK4L STK4H STK3L STK3H STK2L STK2H STK1L STK1H STK0L STK0H
这些系统寄存器都是以大写字母进行定义的,在程序中使用时要注意这一点。 2017/3/22 .H档中对系统寄存器定义 #include <sn8p2708a.h> 每个程序都要包含相应芯片的.H档 #define L (*((__RAM unsigned int*)0x80)) #define H (*((__RAM unsigned int*)0x81)) #define R (*((__RAM unsigned int*)0x82)) #define Z (*((__RAM unsigned int*)0x83)) #define Y (*((__RAM unsigned int*)0x84)) #define X (*((__RAM unsigned int*)0x85)) #define PFLAG (*((__RAM unsigned int*)0x86)) …… 这些系统寄存器都是以大写字母进行定义的,在程序中使用时要注意这一点。
2017/3/22 程序流程控制 顺序结构 判断分支结构 串行分支结构 并行分支结构 循环结构
2017/3/22 顺序结构 顺序结构流程 A操作 B操作 框内A、B操作按顺序执行
顺序结构的实现对比 .ASM .C b0bset key_bibi_f mov a,#0f0h mov menu_disp_h,a 2017/3/22 顺序结构的实现对比 .ASM .C b0bset key_bibi_f mov a,#0f0h mov menu_disp_h,a mov a,#00h mov menu_disp_l,a mov a,#11111111b mov disp5,a key_bibi_f = 1; menu_disp_h = 0xf0U; menu_disp_l = 0; disp5 = 0xffU;
通过判断条件P是否成立来选择A或B进行操作 2017/3/22 判断分支结构 判断分支结构的流程 P为真? A操作 B操作 通过判断条件P是否成立来选择A或B进行操作
判断分支结构的实现对比 .ASM .C cmprs a,#0ah nop b0bts0 fc jmp $+3 b0mov a,y ret 2017/3/22 判断分支结构的实现对比 .ASM .C cmprs a,#0ah nop b0bts0 fc jmp $+3 b0mov a,y ret add a,#6h if(result_buf > 0x0a) { result_buf = input + 6; } else result_buf = input;
这就是多级判断分支组合,想想它们又都是怎么实现的呢? 2017/3/22 串行分支结构 串行分支结构流程 P1为真? P2为真? Pn为真? C1 C2 C3 Cn N Y 这就是多级判断分支组合,想想它们又都是怎么实现的呢?
C用if,else if嵌套来实现串行分支结构 2017/3/22 串行分支结构实现对比 .ASM .C Buzzer00: b0bts1 P1 jmp buzzer10 …… jmp buzzer40 buzzer10: b0bts1 P2 jmp buzzer20 buzzer20: if(P1){ } else if(P2){ else if(Pn){ …… } C用if,else if嵌套来实现串行分支结构
并行分支结构其实是一个条件判断有多种可能,这又如何实现呢? 2017/3/22 并行分支结构 并行分支结构流程 P=? A1 A2 A3 An P=1 P=2 P=3 P=n 并行分支结构其实是一个条件判断有多种可能,这又如何实现呢?
并行分支结构实现对比 .ASM .C C可以用switch…Case来实现 mov a,step b0bts1 fz Jmp ks82 2017/3/22 并行分支结构实现对比 .ASM .C mov a,step b0bts1 fz Jmp ks82 cmprs a,ONE_PRESS_C Jmp ks83 cmprs a,TWO_PRESS_C Jmp ks84 cmprs a,BESPOKE_ING_C Jmp ks85 cmprs a,SELECT_TIME_C Jmp ks86 switch(step) { case 0: ks81();break; case ONE_PRESS_C: ks82();break; case TWO_PRESS_C : ks83();break; case BESPOKE_ING_C : ks84();break; …… } C可以用switch…Case来实现
2017/3/22 循环结构1——While While循环流程 A P为真? Y N 先判断,后执行!
While循环实现对比 .ASM .C tempbuf = 0; while(tempbuf==15) { ++tempbuf; } 2017/3/22 While循环实现对比 .ASM .C Clr y Loop: B0mov a,y Cmprs a,#15 Jmp $+2 Jmp loop90 decms y jmp loop loop90: RET tempbuf = 0; while(tempbuf==15) { ++tempbuf; } 编译器的转换并非和我们的比较代码一样
2017/3/22 循环结构2——do…while循环 Do…while循环流程 A P为真? Y N 先执行,后判断!
Do…while循环在编译器的转换中具有最高的效率 2017/3/22 Do…while循环实现对比 .ASM .C ClrRAM: clr Y b0mov Z,#0x7f ClrRAM10: clr @YZ decms Z jmp ClrRAM10 clr @YZ mov a,#00H unsigned int * pyz = (unsigned int *)0x7f; do{ *pyz = 0x00; --i; }while(i); Do…while循环在编译器的转换中具有最高的效率
2017/3/22 函数 函数的定义 函数参数的传递 函数参数与全局变量
函数的定义 SN8 C 函数声明方式: 返回值类型 函数名(形参1数据类型,形参2数据类型,……); 在C当中,函数都应该先声明,后调用 ! 2017/3/22 函数的定义 SN8 C 函数声明方式: 返回值类型 函数名(形参1数据类型,形参2数据类型,……); 在C当中,函数都应该先声明,后调用 ! 如: unsigned int bcd(unsigned int);
函数的定义方式 函数定义方式: 返回值类型 函数名(参数列表) 参数类型表; { 函数体; } Note: 2017/3/22 函数的定义方式 函数定义方式: 返回值类型 函数名(参数列表) 参数类型表; { 函数体; } Note: 对于无返回值的函数,都要声明是void,以免系统为其预留空间! 仅在传统格式当中出现
函数参数传递与返回 函数调用是通过参数传递和返回来传递数值 clock_min = bcd(clock_min); 2017/3/22 函数参数传递与返回 clock_min = bcd(clock_min); 函数调用是通过参数传递和返回来传递数值 unsigned int bcd(unsigned int input) { … … … return(result_buf); }
函数传递的内部实现 _callee_arg ? ; ? 为参数个数。 2017/3/22 函数传递的内部实现 假设于caller函数内调用callee函式. callee 的参数名称为: _callee_arg ? ; ? 为参数个数。 例如: int foo(int a, int b, long c) 会产生 _foo_data SEGMENT DATA INBANK ;OVERLAYABLE _foo_arg0 DS 1 ; int a _foo_arg1 DS 1 ; int b _foo_arg2 DS 2 ; long c 这些都由系统来完成!
实际参数clock_min被直接赋值给函数的形参_bcd_arg0,然后在调用函数中参加运算。最后返回值是在A中得到的 。 2017/3/22 函数参数传递实现对比 .ASM .C MOV A, (_clock_min) __SelectBANK _bcd_arg0 MOV _bcd_arg0, A ;End push arg.... call _bcd __SelectBANK (_clock_min) MOV _clock_min, A clock_min = bcd(clock_min); 实际参数clock_min被直接赋值给函数的形参_bcd_arg0,然后在调用函数中参加运算。最后返回值是在A中得到的 。
返回值的存放 返回值类型 寄存器 unsigned/signed char A unsigned/signed short 2017/3/22 返回值的存放 返回值类型 寄存器 unsigned/signed char A unsigned/signed short unsigned/signed int unsigned/signed long A,R float A,R,Y,Z 返回值是数据结构的,系统则会在调用函数的时候增加一个隐含参数(地址)传递给被调函数,被调函数在完成功能运算后将结果放到指定的位置上,程序从函数里返回后就可以从该地址读取返回值。
2017/3/22 尽管从原理上看起来,函数的参数传递与返回值的C与汇编的转换的形式是一样的,但是编译器产生代码时依然可能产生一些冗余代码。所以,在面对单片机这样的资源的硬件编程,过多的参数传递及过于复杂的返回值往往造成代码转换的低效率,这是用户需要注意的。
对于面对硬件的单片机编程,我们还是建议多用全局变量来传递数值,而不是用参数 。 2017/3/22 全局变量与函数参数 全局变量 函数参数 贴近汇编的形式,产生代码的效率高。 变量关联性高,模块化,可维护性,封装性差。 模块化,可维护性,封装性好。 存在数据传递的环节 ,更增加了转换的代码量。 对于面对硬件的单片机编程,我们还是建议多用全局变量来传递数值,而不是用参数 。
2017/3/22 构造数据类型 结构体 联合体
结构体的定义 结构体定义的形式: Struct 结构体类型名{ 成员1数据类型 成员1名称; 成员2数据类型 成员2名称; …… }; 2017/3/22 结构体的定义 结构体定义的形式: Struct 结构体类型名{ 成员1数据类型 成员1名称; 成员2数据类型 成员2名称; …… }; 例如: Struct structType{ Unsigned int memb1; Unsigned long memb2; Float memb3; };
结构体在存储形式上是按定义形式的先后,将成员逐个存储摆放形成一个数据块。 2017/3/22 结构体存储形式 结构体存储 memb1 memb2 memb3 结构体在存储形式上是按定义形式的先后,将成员逐个存储摆放形成一个数据块。
用结构体定义位域 位域的定义形式: Struct 结构体名称{ Unsigned bit0:1; Unsigned bit1:2; 2017/3/22 用结构体定义位域 位域的定义形式: Struct 结构体名称{ Unsigned bit0:1; Unsigned bit1:2; Unsigned bit2:1; Unsigned bit3:1; Unsigned num:4; }; 成员数据类型都是Unsigned。 冒号后面的数据是指占用位的数量,如成员bit1会占用2个bit 。
2017/3/22 结构体定义的限制 对于struct,我们只能对其实例安排存放的空间,加__RAM和__ROM限制字加于限制。而对于struct的成员我们就无法对其进行限制,这个原因是非常明显的,若是都允许对存放空间进行限制的话,就会造成冲突。如下定义是系统不允许的: struct StuType { int __ROM data; // 错误 };
联合体的定义 定义一个联合类型的一般形式为: union 联合名 { 成员表 }; 2017/3/22 联合体的定义 定义一个联合类型的一般形式为: union 联合名 { 成员表 }; 例如: union perdata { int memb1; long memb2; float memb3; };
在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。事实上是同一个空间通过不同的名称调用 。 2017/3/22 联合体的存储形式 联合体的存储 memb1 memb2 memb3 在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。事实上是同一个空间通过不同的名称调用 。
联合体在程序中的应用 将一个Long型数据与两个int型数据的结构体组成Union,如: 2017/3/22 联合体在程序中的应用 通过定义longType 的实例,我们就可以既对一个long型数据进行操作,又可以对数据的高低字节进行操作,既兼顾了C的方便特性,又能有汇编的灵活性。 将一个Long型数据与两个int型数据的结构体组成Union,如: union longtype { unsigned long longV; struct inttype unsigned int int_l; unsigned int int_h; }intV; };
2017/3/22 与struct一样,我们只能对UNION实例安排存放的空间,加__RAM和__ROM限制字加于限制。而对于union的成员我们就无法对其进行限制。
2017/3/22 中断 中断函数的定义 中断过程的分析 中断函数的结构
__interrupt MyHandler () { .... } 2017/3/22 中断函数的定义 中断函数声明方式如下: __interrupt MyHandler () { .... } 这是一个SN8 C专有的函数,用关键字__interrupt 来声明。__interrupt关键字(以两个底线开头)指示函数是要作中断向量的处理函数。 中断向量函数是一个无参数, 也无返回值的特殊函数。 中断向量进入点会备份所有寄存器,中断向量结束前, 前述备份的项目均会被还原。这些都是由编译器内部完成的。
2017/3/22 中断过程分析 程序在主循环中运行到①的时候,中断条件成立,系统产生中断,此时,系统将会去执行中断程序。而为了能从中断中正确返回,在进入中断程序之前,系统会对当前的状态(ACC,PC等等)进行保存。然后,在②处程序运行进入中断程序,③处中断程序结束,系统又需要将原来的运行状态还原,然后在④处继续运行主循环程序。一次中断完成。 ① ② ③ ④
中断函数的结构 建议对中断程序作这样的安排 Y N 中断1处理程序 中断2处理程序 中断3处理程序 中断n处理程序 RETI 2017/3/22 中断函数的结构 是中断1? 是中断2? 是中断3? 是中断n? Y N 中断1处理程序 中断2处理程序 中断3处理程序 中断n处理程序 RETI 建议对中断程序作这样的安排
2017/3/22 中断程序与主循环的关联 __interrupt intserv(void) { if(INTRQ&0x10) { _bCLR(&INTRQ,4); T0INT(); } else if(INTRQ&0x20) { _bCLR(&INTRQ,5); TC0INT(); … void TC0INT(void) TC0C=0x64; ftc0int = 1; void INTround(void) { unsigned int bitValue = 0; if(ftc0int) ftc0int = 0; fkeyTimer = 1; fflashTimer = 1; ThandDelay = 1; } … ... Void main(void) { …. INTround(); 为了使中断资源不被长时间占用,我们的中断程序内不能运行任何长时间占用系统时间的程序!其实占用中断资源的任务,我们完全可以安排到中断外去完成!我们只需要告诉主控程序发生了中断以及是哪个中断就可以了!
2017/3/22 位操作 位的定义 位的运算 位比较在程序流程控制中的应用
用户自定义位 Step1 Step2 Step3 位域的定义 : 定义一些具体的结构体实例: 2017/3/22 用户自定义位 Step1 Step2 位域的定义 : Struct bitDefine{ Unsigned bit0:1; Unsigned bit1:1; Unsigned bit2:1; Unsigned bit3:1; Unsigned bit4:1; Unsigned bit5:1; Unsigned bit6:1; Unsigned bit7:1; }; 定义一些具体的结构体实例: Struct bitDefine flag1,flag2,flag3; Step3 用宏定义的方法将我们需要的位名称赋予相对应的位。如: #define fkeypress (flag1.bit1) #define fchatfinish (flag1.bit2) #define fkeyProcessing (flag1.bit3) #define FhandDelay (flag1.bit4)
2017/3/22 位运算 对位的操作主要包含以下几种:置位,清除,位与(&),位或(|),位非(~),位异或(^),左移(<<),右移(>>)。 例如: Fkeypress = 1; //置位 Fkeypress = 0; //清除 keyinbuf <<=2; tempbuf = P0&0x03; keyinbuf |= tempbuf; keyinbuf = ~keyinbuf;
系统寄存器位操作函数 位操作函数原型 : 传入参数 address 与 bitOffset 必须为常数不可为变量。 2017/3/22 系统寄存器位操作函数 位操作函数原型 : void _bSET(unsigned long address, unsigned int bitOffset); void _bCLR(unsigned long address, unsigned int bitOffset); int _bTest0(unsigned long address, unsigned int bitOffset); int _bTest1(unsigned long address, unsigned int bitOffset); 传入参数 address 与 bitOffset 必须为常数不可为变量。 BitOffset 有效值为 0 ~7。 address 的高位为 bank number。
系统寄存器位操作 对系统寄存器进行位的置位和清除: _bSET(&TC0M,7); _bCLR(&TC0M,7); 系统转换的对应代码: 2017/3/22 系统寄存器位操作 对系统寄存器进行位的置位和清除: _bSET(&TC0M,7); _bCLR(&TC0M,7); 系统转换的对应代码: PreB0SET 218 7 0 PreB0CLR 218 7 0
三种方式产生的代码基本相同,第二种使用更方便。 2017/3/22 位比较在程序流程控制中的应用 系统寄存器的位判断可用以下3种方法: 1 用专用函数: if(_bTest1(&INTRQ,4)) { _bCLR(&INTRQ,4); T0INT(); } 用位运算的方法: if(INTRQ&0x10) { _bCLR(&INTRQ,4); T0INT(); } 3 三种方式产生的代码基本相同,第二种使用更方便。 2 用系统定义的位名称: if(FT0IRQ) { _bCLR(&INTRQ,4); T0INT(); }
2017/3/22 内嵌汇编 如何内嵌汇编 内嵌汇编时变量的传递
内嵌的汇编会被原样转换嵌入生成的汇编码当中 ! SN8 C 提供专门的关键字__asm(两个下划线)用于在C的源代码内嵌入汇编。 __asm关键词有以下两种用法: __asm(“code\n”) __asm { asm_text } 内嵌的汇编会被原样转换嵌入生成的汇编码当中 !
程序用“#pragma ref x”通知sn8cc, 变量x是有用到的! 内嵌汇编时变量的传递 “#pragma ref id”预处理指令 例如: void func(void) { int x; #pragma ref x __asm { … ;; 存取局部变量 x. } return; 程序用“#pragma ref x”通知sn8cc, 变量x是有用到的!
全局变量与内嵌汇编的传递 .C 生成代码档中的定义 原来它们将每一全局变量的前面都增加了一个“_”来标识它们! union flagWord2 { unsigned int flagByte; struct bitdefine2 unsigned bit0:1; unsigned bit1:1; unsigned bit2:6; }flagBit; }led_dp; unsigned int door_cnt; unsigned int door_cnt1; unsigned int door_cnt2; .stabs "led_dp:G43",32,0,0,_led_dp .stabs "door_cnt2:G14",32,0,0,_door_cnt2 .stabs "door_cnt1:G14",32,0,0,_door_cnt1 .stabs "door_cnt:G14",32,0,0,_door_cnt 原来它们将每一全局变量的前面都增加了一个“_”来标识它们! 在嵌入的汇编中,如果要读写全局变量,就在它们的前面加上一个“_”
SN8 C程序的结构组织 主函数与子函数 构建可复用文件 构建具有实用性的程序
任务的分时处理 对于整个程序而言,安排好main()函数对其他子函数的调用起着至关重要的作用。我们知道,大多任务我们没法在1毫秒内完成,有的甚至需要很长时间的延时,而在很短的时间内环境有可能发生改变,外部可能产生很多个请求。怎么样让程序既能完成任务(响应)又能及时接受外部请求及时处理? 我们把所有的任务都分别用一个标志位来标示他们是不是完成,对于完成的任务会产生什么样新的需求也用一个标志来标识它。而对于正在进行的任务我们每次只去完成应该完成的一部分,将这些正在完成的任务轮换进行处理直到完成。改换这一思想是实现我们的编程方案的关键。
我们可以将一个任务分为60或70次来完成,实现了分时实现。而很多占用很长时间的任务也都可以这么做。 传统按键扫描与分时按键扫描的对比 传统按键扫描 我们可以将一个任务分为60或70次来完成,实现了分时实现。而很多占用很长时间的任务也都可以这么做。 1 ① ② 分时按键扫描 2 ① ② …
程序层次结构图 任务队列 主控程序 功能1 功能2 功能3 功能n ……… 功能实现层 请求 响应、使能
程序结构可以分为任务排列层、主控层和功能实现层。 主控层依据功能实现层的请求标志设置来形成队列形成任务排列层,并对获取的请求安排响应和使能的优先顺序。然后读取FIFO里面的任务并发出使能和响应标志,主控层为系统的主循环,控制整个程序的运行,并且将通过设置响应和使能标志来调用功能实现层的子程序来实现功能。 任务排列层建立一个FIFO,FIFO里面存储系统标志位,按事件发生的顺序和主控程序的安排逐个地排列。 功能实现层为系统的功能程序(子程序),它由可复用的各种功能模块组成。在每个模块的开头进行程序判断,看是否需要执行程序或使能部分功能,并且在执行过程中,根据程序执行的结果对标志位进行置位,并返回给主控层,由主控程序安排响应!
实现层的子函数 程序主体 退出 执行程序的条件是不是成立? Yes No 在程序开头进行判断是否需要运行,若是条件没有满足或者没有接收到主控程序的使能信息,就直接跳转到程序结束,返回主控程序。而主控程序则每个循环(<1ms)都调用一次子程序进行查询!
对于全局变量我们可以通过在子程序文件中通过定义public型数据来实现程序的模块化。 构建可复用文件 #include <custom.h> Extern keyScan(); Extern keyInbuf; Extern keyChkbuf; Extern keyCvtbuf; Extern keyCode; … Void main(void) { keyScan(); } … Public keyScan(); Public unsigned int keyInbuf; Public unsigned int keyChkbuf; Public unsigned int keyCvtbuf; Public unsigned int keyCode; Void keyScan(void) { } 对于全局变量我们可以通过在子程序文件中通过定义public型数据来实现程序的模块化。
Thanks! 联系我: Phone:86-755-267196666-247 E-mail:dragon@sonix.com.cn