Transcript 结构体

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