Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1521637
  • 博文数量: 226
  • 博客积分: 3997
  • 博客等级: 少校
  • 技术积分: 2369
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-19 17:26
个人简介

Never save something for a special occasion. Every day in your life is a special occasion.

文章分类

全部博文(226)

文章存档

2018年(5)

2017年(11)

2016年(1)

2015年(17)

2014年(14)

2013年(30)

2012年(5)

2011年(52)

2010年(107)

分类: 嵌入式

2011-01-06 21:09:52

HT-C语法

 简介......................................1
C 语言的程序结构..............................2
 语句..................................2
 注释..................................2
标识符....................................3
 保留字..................................3
数据类型..................................3
 数据类型与大小............................3
 宣告..................................4
常量......................................5
 整型常量................................5
 字符型常量..............................6
 字符串常量..............................6
 枚举常量................................6
运算符....................................7
 算术运算符..............................7
 关系运算符..............................7
 等式运算符..............................7
 逻辑运算符..............................8
 位运算符................................8
 复合赋值运算符............................8
 递增和递减运算符..........................9
 条件运算符..............................9
 逗号运算符..............................9
 运算符的优先权与结合性.......................10
 类型转换................................11
程序流程控制................................12
函数....................................16
 古典形式................................16
 现代形式................................16
指针与数组................................17
 指针..................................17
 数组..................................17
结构体与共用体(Structures and Unions) ..................18
前置处理伪指令..............................19                                                    
盛群C 语言的扩充功能与限制.....................24
 关键字................................24
 存储器区块(memory bank)......................25
 位数据类型..............................25
 内嵌式汇编语言............................26
 中断..................................26
 变量..................................27
 静态变量................................27
 常量..................................27
 函数..................................27
 数组..................................28
 常量..................................28
 指针..................................28
 初始值................................28
 乘数/除数/模..............................29
 内建函数................................29
 堆栈..................................30

简介
由于受限于盛群单片机的硬件结构,因此只能支持部分的 ANSI C。

本章节包含以下的主题:
•  C 语言的程序结构
•  标识符
•  数据类型
•  常量
•  运算符
•  程序流程控制
•  函数
•  指针与数组
•  结构体与共用体
•  前置处理程序伪指令
•  盛群C 语言的扩充功能与限制

C语言的程序结构
C 语言程序由语句、注释和前置处理程序伪指令组合而成。

语句
语句由变量、常量、运算符和函数共同组成,以分号作为结束符。

注释
注释经常用于在文件中解释源程序语句的意义与作用,除了在 C 关键字的中间、函数名称之间或变量名称之间外,注释可以放置在程序中的任何位置。

标识符
标识符的名称包含连续的字母、数字或下划线,不过需要遵守下列规则:
•  第一个字符不可为数字
•  最长只能有 31个字符
•  大写字母与小写字母是不同的
•  不可以使用保留字

保留字
 auto bit  break case char
 const continue default do else
 enum extern for  goto if
 int  long return short signed
 static struct switch typedef union
 unsigned void volatile while
HT-C 不提供 double、float和 register这三个保留字。

数据类型

数据类型与大小
HT-C四种基本数据类型:
 bit  单一的位
 char 占用一个字节的字符
 int  占用一个字节的整数
 void 数值的空集合,用于函数没有返回值的类型。

可使用的限定词:
限定词   适用的数据类型     作用
const    any    将数据放入 ROM地址区
long    int     生成16位的整数
short    int    生成 8 位的整数
signed   char, int    生成一个有符号的变量
unsigned char, int    生成一个无符号的变量

宣告
在定义变量的大小及数据类型之前必须先要宣告此变量的存在。 语法:
data_type variable_name [,variable_name...];

限定词 const可以使用在任何变量的宣告,主要是定义此变量的值为不可改变的, 也就是宣告时用const限定的这个变量会存放在ROM地址区。

HT-C特点:const 变量必须在宣告时设定初始值。

可用@号将变量定址:
data_type variable_name @ memory_location;

若MCU有多个RAM Bank,则可由memory_location的高字节指定存储器区块编号。
范例:
  int v1 @ 0x40;   // declare v1 in the RAM bank 0 offset 0x40
  int v2 @ 0x160;  // declare v2 in the RAM bank 1 offset 0x60                                   
  int port[8] @ 0x20;  // array port takes memory location. 0x20 through 0x27

所有被盛群 C编译器实现的变量, 除了被宣告为外部变量之外, 都为静态变量。

HT-C特点:无论是静态变量或是外部变量,盛群 C编译器都不会为其预设初始值。


常量
整型常量
后缀:l或 L、u 或 U、ul或 UL
前缀:
  二进制常量:0b或 0B
  八进制常量:0
  十进制常量:无
十六进制常量:0x或 0X

字符型常量

转义字符(escape sequence)
  转义字符     说明     十六进制数值
  \a    警报(铃声)字符 07
  \b    退格字符        08
  \f    换页字符        0C
  \n    换行字符        0A
  \r    回车字符        0D
  \t    横向跳格字符    09
  \v    竖向跳格字符    0B
  \\    反斜杠字符      5C
  \?    问号字符        3F
   \'     单引号字符   27
   \"     双引号字符   22

字符串常量
字符串常量是一个字符数组并且在字符的最后附加一个隐含的零值。

枚举常量
例如:
  enum {PORTA, PORTB, PORTC};
枚举常量是 int型(-128~127) ,而且也可以指定一个明确的整数值给各枚举常量,
例如:
  enum {BIG=10, SMALL=20};
如果没有对枚举常量指定明确的数值时,第一个枚举常量值为 0,之后的枚举常量将依序加 1。
枚举语句也可以被命名,例如:
  enum boolearn {NO, YES};
在枚举语句中第一个名称(NO)的值为 0,下一个的名称的值是 1。

运算符
下面讨论各种类型的运算符。

算术运算符
     +  加
     -  减
     *  乘
     /  除
     %  模运算符(余数为小于除数的正数或零)

关系运算符
     >   大于
     >=  大于或等于
     <   小于
     <=  小于或等于

等式运算符
     = =  等于
     !=  不等于

逻辑运算符
     &&  逻辑AND
     ||  逻辑OR
      !  逻辑NOT

位运算符
     &  位 AND
      | 位 OR
     ^  位 XOR
     ~  取补(生成整数的 1阶补码(one's complement))
     >>  右移(一般为 逻辑右移)
     <<  左移

复合赋值运算符
        +=
        -=
        *=
        /=
        %=
        &=
        ︱=
        ^=
        >>=
        <<=

递增和递减运算符
     ++
     ++
     --
     --

条件运算符
? :

逗号运算符
一组用逗号分隔的表达式,由左计算到右,而左边表达式的值会被舍弃。左边
表达式的结果会先行计算出并会影响右边表达式执行的结果。整个表达式执行
结果的数值和数据类型将是最右边表达式的结果数值及数据类型。
范例:
  f (a, (t=3, t+2), c);
上式有三个参数,而第二个参数值为 5。

运算符的优先权与结合性
 运算符      说明     结合性
 [  ]      数组元素     由左到右
 ( )      小括号
 ->     结构体指针
 .      结构体成员
 sizeof 数据类型的长度
 ++      加 1      由右到左
 --      减 1
 ~      取 1 阶补码
 !      逻辑非
 -      负号
 +      正号
 &      变量地址
 *      存取指针所指地址的内容
 *      乘法运算     由左到右
 /      除法运算
 %      模运算
 +      加法运算     由左到右
 -      减法运算
 <<      左移运算     由左到右
 >>      右移运算
 <      小于      由左到右
 <=      小于或等于
 >      大于
 >=      大于或等于
 ==      等于      由左到右
 !=      不等于
 &      按位AND
 ^      按位XOR
 ︱     按位OR
 &&      逻辑AND
 ||     逻辑OR
 ?:     条件运算
 =      赋值      由右到左
 ,      逗号      由左到右

类型转换
对于数据类型转换的规则而言,大都是将较小的操作数转换为较大的操作数而不致遗漏数据,例如将整数类型转换为长整数类型。从 char 转到 long 则会做正负符号的延伸。 使用 cast运算符可以将任何表达式的结果做明确的数据类型
转换。例如:
  (type-name) expression


程序流程控制

if-else语句
•  语法
 if (expression)
  statement1;
 [else
  statement2;
 ]

for语句
•  语法
  for (initial-expression; condition-expression;
update-expression) statement;

while语句
•  语法
 while (condition-expression)
   statement;

do-while语句
•  语法
  do
  statement;
 while (condition-expression);

break和continue语句
•  语法
  break;
  continue;
•  说明
break语句用来强迫程序立即由 while、 for、 do-while循环和 switch中跳出。
break 语句会跳过正常的结束流程,如果它发生在嵌套循环的内部,则会返回上一层的嵌套。
Continue 语句会指示程序跳跃至循环的结束而重新开始下一轮循环。在while 和 do-while 循环中,Continue 语句会强迫立即执行condition-expression ,而在 for 循环 中,则 会 回 去 执行update-expression。

goto语句和语句标号
•  语法
  goto label;
•  说明
语句标号与变量名称的形式一样,但是其后要接冒号,其范围在整个函数中有效。

switch语句
•  语法
  switch (variable)
 {
  case constant1:
    statement1;
    break;
   case constant2:
    statement2;
    goto Label1;
  case constant3:
    statement3;
    break;
   default:
    statement;
  Label1:  statement4;
    break;
 }

switch 语句的限制为 switch 变量的数据类型必须为整数,而且只能与常量值做比较。

函数
针对具有多个程序存储器区块(bank)的单片机撰写程序时,函数就与变量有所不同,使用者不需要而且也没有办法将函数指定在存储器的固定区块(bank)。连接器(Linker)会将函数安排在程序存储器 ROM的适当区块。

→  函数的宣告
// classic form
  return-type function-name (arg1, arg2, ...);
// modern form
  return-type function-name (var-type arg1, var-type arg2, ...);

→  函数的定义
// classic form
return-type function-name (arg1, arg2, ...)
var-type arg1;
var-type arg2;
{
 statements;
}
// modern form
return-type function-name (var-type arg1, var-type arg2, ...)
{
 statements;
}

→  函数参数的传递
有两种函数参数传递的方法:
•  传值
•  传地址 // 实质和传值一样,都是拷贝。


指针与数组
指针
指针是存有另一变量地址的变量,宣告指针变量的语法如下:
  data-type *var_name;

数组
数组是具有相同数据类型而且可用同样名称使用的变量列表。
C编译器不对数组做边界检查。不支持将一个数组赋值给另一个数组的运算,必须一个一个元素来。

结构体与共用体(Structures and Unions)
→  结构体
•  语法
  struct struct-name
 {
  data-type member1;
  data-type member2;
  ...
  data-type membern;
  } [variable-list];

结构体变量宣告:
  struct struct-name variable-list;

访问成员:
  variable.member
  pointer->member

→  共用体
语法同结构体。
与结构体的区别是:共用体是将不同类型的变量聚集为一群并使用相同的存储器空间。

前置处理伪指令

→  宏替换:#define
•  语法
 #define name  replaced-text
 #define name [(parameter-list)] replaced-text
说明:
记住一点:宏其实就是文本替换。其主要目的是增加源程序的可读性与维护性。
如果一行写不下 replaced-text 可使用续行符(反斜线 \)。

•  范例
 #define TOTAL_COUNT 40
 #define USERNAME  Henry 
 #define MAX(a,b)  (((a)>(b))?(a):(b))
 #define SWAP(a,b)   {int tmp;\
        tmp=a;\
        b=a;\
        a=tmp;}

→  #error
•  语法
  #error “message-string”
•  说明
#error 伪指令会生成一个使用者所定义的诊断信息, message-string。 
•  范例
  #if    TOTAL_COUNT > 100
  #error  “Too many count.”
 #endif

→  条件编译:#if  #else  #endif
•  语法
 #if  expression
   source codes1
 [#else
   source codes2]
 #endif

说明:根据条件判断编译指定代码段。

•  范例
  #define MODE 2
  #if MODE > 0
   #define DISP_MODE MODE
 #else
   #define DISP_MODE 7
 #endif

→  条件编译:#ifdef
•  范例
 #ifdef DEBUG_MODE
  #define TOTAL_COUNT 100
 #endif

→  条件编译:#ifndef
•  范例
 #ifndef DEBUG_MODE
  #define TOTAL_COUNT 50
 #endif

→  条件编译:#elif
•  范例
 #if MODE==1
  #define DISP_MODE 1
 #elif MODE==2
  #define DISP_MODE 7
 #endif

→  条件编译:defined
•  范例
  #if defined DEBUG_MODE
  #define TOTAL_COUNT 50
 #endif

→  条件编译:#undef
•  范例
 #define TOTAL_COUNT 100
 ...
 #undef TOTAL_COUNT
 #define TOTAL_COUNT 50

→  文件包含:#include
•  语法
 #include
or
 #include “file-name”
•  说明
#include 会将指定文件的内容插入到源程序文件中。
当使用的格式时,编译器会从环境变量 INCLUDE所指定的路径中寻找file-name 文件,如果没有定义 INCLUDE,C 编译器会在指定的路径中搜寻文件。如果使用“file-name”的格式,则C 编译器会以指定的方式搜寻 file-name 文件,如果没有指定路径,则会从当前所在的路径中找寻文件。
•  范例
  #include
 #include “my.h”


盛群C语言的扩充功能与限制

HT-C是针对ht-MCU的C语言,对ANSIC有一些扩展。另一方面,由于MCU资源有限,HT-C只支持部分ANSI C。
使用时,要注意它们的区别。下面讲述HT-C的特点。

关键字

扩展的关键字:
 @  bit  norambank  rambank0  vector
没有的关键字:
 double  float  Register

存储器区块(memory bank)

将变量指定在BANK0将提高访问效率。另外,ht-c要求位变量定义在bank0。下面语法指定将变量定义在bank0:
•  语法
 #pragma rambank0
 //data declarations
 #pragma norambank
•  范例
  #pragma rambank0
  unsigned int i, j;  // 变量 i, j 放在储存区块 0
 long len;     // 变量 len 放在储存区块0
 #pragma norambank
  unsigned int iflag  // 变量 iflag 的储存区块不确定
 #pragma rambank0
 int tmp;     // 变量 tmp 放在储存区块0

位数据类型
此类型可用于变量的宣告、参数列表以及函数的返回值。位变量的宣告与其它 C 数据类型的变量宣告一样。对于具有多RAM/ROM 储存区块的单片机,应该将位变量宣告在 RAM 储存区块 0(#pragma rambank0)。
•  限制
− 为了利用位数据类型的优点,不建议将变量宣告为位数组的数据类型。
− 指针不可设定为位类型。

内嵌式汇编语言
•  语法
  #asm
 [label:] opcode [operands]
 ...
 #endasm

•  范例
  //convert low nibble value in the accumulator to ASCII
 #asm
  ; this is an inline assembly comment
  and a, 0fh
  sub a, 09h
  sz c add a, 40h-30h-9
  add a, 30h+9
 #endasm

中断
伪指令#pragma  vector 用来宣告 ISR的名称与存储器地址,之后若有函数的名称与#pragma vector 定义的符号名称相同时,此函数就是这个中断向量的中断服务程序。 在中断服务程序中的return语句将会编译成RETI指令。
•  语法
  #pragma vector symbol @ address
•  说明
symbol 是中断服务程序的名称,address 是中断地址,复位向量(地址0)固定由主函数 main( )使用,任何中断服务程序不可使用此中断地址。
•  限制
撰写 ISR程序时要注意有四种限制:
−  ISR 没有输入参数且返回类型是 void。
−  ISR 不能够重复进入,且而在 ISR中不可让任何中断再发生。
− 在程序中不要直接调用 ISR 程序,应该由中断信号输入时自行调用它。
− 在 ISR 中不要调用用 C写的函数。但是可以调用系统函数或C  编译器内建的函数(built-in function)  。如果必须在 ISR 中调用函数,可以使用汇编语言撰写这个函数。
•  范例
  #pragma vector timer0 @ 0x8
  extern void ASM_FUNCTION();
 void setbusy(){
 ...
 }
 void timer0(){
  ...
    ASM_FUNCTION();  //The ASM_FUNCTION should be an assembly function
  _delay(3)      //Ok; built-in function
  setbusy();    //Wrong! Do not call function
 }

变量
运算符“@”用来指定数据存储器中变量的地址。
•  语法
  data_type variable_name @ memory_location
说明:
若有多个ram bank,可用 memory_location 的高字节指定存储器区块的编号。
•  范例
  int v1 @ 0x5B;   // 宣告变量 v1 放置于 RAM bank 0, offset 0x5B
  int v2 @ 0x2F0;  // 宣告变量 v2 放置于 RAM bank 2, offset 0xF0

静态变量
提供有效范围在文件内的静态变量。
ht-c特点:不支持局部的静态变量。

•  范例
 static i;    // 宣告静态变量, 以文件为有效范围
  void  f1 () {
  i=1;        // OK 可以使用此变量
 }
  void f2 () {
  static int j; // local  static  variable  is  not  supported
  ...
 }

常量
ht-c特点:支持二进制常量表示。
•  范例
 0b101=5
 
函数
ht-c特点:不支持函数重入。

数组
任何数组应该配置在一个连续的存储器区内。

常量

ht-c要求:
常量必须宣告为全局型且在宣告时就要设定初始值。常量不可宣告为外部使用。数组常量需要指定数组的大小,否则会产生错误。
  const char carray [ ]={1,2,3};  // 错误,没有指定数组的大小
  const char carray [3]={1,2,3};  // 正确
 
ht-c要求:
字符串常量必须在包含 main( )  主函数的C 语言文件中使用。
 //test.c
  void f1 (char *s);
  void f2 () {
  f1 (“abcd”) //“abcd” 是字符串常量 
      // 如果在文件test.c 中没有定义main( )主函数则 Holtek C 编译器会发出错误信息
  ...
 }
 void main(){  ...  }

指针
指针不能用于常量与位变量。

初始值
ht-c要求:
全局变量宣告时不可以同时设定初始值,局部变量则无此项限制,但是常量在宣告时则一定要设定初始值。

•  范例
  unsigned int i1=0;      // 错误; 全局变量, 不可设定初始值 
  unsigned int i2;        // 正确
  const unsigned int i3;  // 错误; 常量, 必须设定初始值 
   const unsigned int i4=5; // 正确
  const char a1[5];         // 错误; 数组常量, 必须设定初始值

  const char a2[5]={0x1,0x2,0x3,0x4,0x5};  // 正确
  const char a2[4]=“abc”;  //={‘a’, ‘b’, ‘c’, 0}
  const char a2[3]=“abc”;  //={‘a’, ‘b’, ‘c’}
  const char a2[2]=“abc”;  // 数组大小不一致

乘法/除法/模
乘法、除法和模( “*”,“/”,“%” )运算符由系统调用执行。


内建函数

•  WDT & halt & nop
C 系统调用   汇编语言码
void _clrwdt ( )    CLR WDT
void _clrwdt1 ( )   CLR WDT1
void _clrwdt2 ( )   CLR WDT2
void _halt ( )    HALT
void _nop ( )    NOP

•  左移/右移
  void _rr (int*);    //rotate 8 bits data right
  void _rrc (int*);   //rotate 8 bits data right through carry
 void _lrr (long*);    //rotate 16 bits data right
  void _lrrc (long*);  //rotate 16 bits data right through carry
  void _rl (int*);    //rotate 8 bits data left
  void _rlc (int*);   //rotate 8 bits data left through carry
 void _lrl (long*);    //rotate 16 bits data left
  void _lrlc (long*);  //rotate 16 bits data left through carry

•  高/低半字节的交换
  void _swap (int*);    //swap nibbles of 8 bits data

•  以指令周期为单位的延迟函数
  void _delay(unsigned long);  //delay n instruction cycle

_delay 函数强迫单片机去执行所指定的周期数。周期数为零则会执行无穷的循环。
_delay函数的参数只能为常量值并不接受变量。

•  范例 1
 // 假设 watch dog timer 看门狗定时器已经启动
 // 看门狗定时器的清除指令选择为使用一条清除指令
  void error (){
  delay (0);  // 无穷的循环, 类似 while(1);
 }
 void dotest(){
  unsigned int ui;
  ui =0x1;
  rr(&ui);   //rotate right
    if (ui != (unsigned int)0x80) error();
  ui =0xab;
  swap(&ui);
    if (ui != (unsigned int)0xba) error();
 }

void main(){
  unsigned int i;
  for(i=0; i<100; i++){ 
    _clrwdt();
    _delay(10); // 延迟 10 个指令周期
    dotest();
  }
 }

•  范例 2
 // 假设 watch dog timer 看门狗定时器已经启动
 // 看门狗定时器的清除指令选择为使用两条清除指令
  void do test(){
 ...
 }
 void main(){
  unsigned int i;
  for(i=0; i<100; i++){ 
    _clrwdt1();
    _clrwdt2();
    dotest();
  }
 }

堆栈
因为盛群单片机堆栈的层数是有限的, 所以要考虑函数调用时的层数以避免堆栈溢出。
乘法、除法、取模和常量是使用“call”指令实现其功能的,都只占用一层的堆栈。

运算符/系统函数       所需要的堆栈层数
 main()        0
_clrwdt()      0
_clrwdt1()     0
_clrwdt2()     0
_halt()        0
_nop()         0
_rr(int*);     0
_rrc(int*);    0
_lrr(long*);   0
_lrrc(long*);  0
_rl(int*);     0
_rlc(int*);    0
_lrl(long*);   0
_lrlc(long*);  0
_delay(unsigned long)  1
*        1
/        1
%        1
constant array      1

 

 

 

阅读(2818) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~