PICC18中文手册
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PICC18使用说明
重要说明:仅就PICC18V9.50PL3安装目录下的使用手册的编程使用最紧密相关的部分做翻译。
本翻译纯粹为本人业余兴趣所致。
本人不承担因翻译错误、偏差带来的任何后果。
并且保留在不做通知的情况下升级本翻译文档的权利。
请查阅英文原始版本说明书,本文仅做入门参考。
另外,本文翻译的目的在于加深对PICC18的认识和理解,所以将大量采用意译而非逐字翻译。
故可能和英文原版有较大的篇幅差别。
版本说明:增加了对指针的翻译,中断处理部分的翻译。
3.1.1 与ANSI标准C的区别
受PIC18 MCU的硬件限制,PICC18不支持函数的递归调用。
3.1.2
同样的C代码可能在不同版本的编译器或者不同的编译器之间会编译成不同的汇编代码。
3.2.1
编辑安装目录下的pic-18.ini文件,可以增加用户自定义的新的PIC18系列的MCU。
3.2.2 CONFIG的操作
PICC18可以在源代码中配置CONFIG,由于PIC18 MCU的CONFIG有多个字节,所以采用如下语法:
__CONFIG(2, BW8 & PWRTDIS & WDTPS1 & WDTEN);
注意,前面是两个下划线,这是一个宏__CONFIG()。
该宏的定义在系统文件 htc.h中,根据PICC18编译器特性,如果再每个源文件中都使用了#include <pic18.h>,使用该宏则可不必再写#include <stc.h>。
这个宏,必须在函数外使用。
3.2.3 ID区定义
和CONFIG区操作类似,采用如下语法:
__IDLOC(15F01);
注意,前面是两个下划线,这是一个宏__CONFIG()。
该宏的定义在系统文件 htc.h中,根据PICC18编译器特性,如果再每个源文件中都使用了#include <pic18.h>,使用该宏则可不必再写#include <stc.h>。
这个宏,必须在函数外使用。
3.2.
4.1 EE区操作
很多时候,需要在MCU运行前,事先在EE区烧入一定数据,MCU上电后,则可从EE区读出相关数据,执行相应操作。
这个操作和上面的CONFIG操作类似,采用一个系统定义的宏,采用如下语法:
__EEPROM_DATA(0, 1, 2, 3, 4, 5, 6, 7);
注意,前面是两个下划线,这是一个宏__CONFIG()。
该宏的定义在系统文件 htc.h中,根据PICC18编译器特性,如果再每个源文件中都使用了#include <pic18.h>,使用该宏则可不必再写#include <stc.h>。
这个宏,必须在函数外使用。
使用这个宏,必须也只能一次性初使化8个字节。
而且第一次调用这个宏就是只能是在0地址开始初使化。
从0初使化到7,要想再初使化8个字节,就再调用一次。
比如如下:
__EEPROM_DATA(0, 1, 2, 3, 4, 5, 6, 7);
__EEPROM_DATA(8,9,10,11, 12,13,14,15);
即使不想初使化前8个字节,只初使化第10个字节,也要采用上述的写法从0写到15。
不过一般没有必要特意从某个地址开始初使化EE区。
建议我们不要去挑战编译器的组织方式,把有这样的系统调用宏全部放在主文件中,以便于修改。
如果是在MCU的运行过程中需要对EE区操作,则可以考虑在程序中使用自己编写读写EE 区函数,或者采用系统定义的一个宏(不是系统函数)。
写EE区address字节一个valus值,如下:
EEPROM_WRITE(address,value);
从EE区的address地址读回值,存入variable变量,如下:
variable=EEPROM_READ(address); variable 为自定义的unsigned char变量。
这些宏为了保证操作,在过程中关断了总中断GIE。
这可能会导致某些情况下一些实时控制系统崩溃——由于中断不能即时响应。
比如发电机控制。
PIC18 MCU写EE区一个字节一共需要8MS,插除4MS,写4MS。
3.2.
4.2 FLASH操作
用来在程序中写COPY FLASH区的一个块到另外一个块:
flash_write(source_pointer, length, dest_pointer);
读FLASH(程序)区的一个字节
variable=flash_read(address);
3.2.5外扩程序区操作
用PIC18的MCU,基本上无人使用它提供的这个功能。
本章节不翻译。
有兴趣请查阅英文原版。
如果一定要外扩,建议使用51或者增强型51MCU。
3.2.6位指令
PICC18会尽可能使用位指令来提高编译效率。
比如使用:
unsigned int foo;
foo |= 0x40;
会编译成如下指令:
bsf _foo,6
如果要清或者置某个整形变量的某个位,可以采用下面的系统定义的宏。
#define bitset(var,bitno) ((var) |= 1UL < < (bitno))
#define bitclr(var,bitno) ((var) &= ~(1UL < < (bitno)))
比如上面提到的操作,也可以用如下语法实现:
bitset(foo,6);
3.2.7 多字节变量的特殊功能寄存器
比如以16位定时器的读写来说,由于硬件特性,从PIC16到PIC18系列的MCU,都必须遵照写TIMEX,先写TMRXH,再写TMRXL,读则相反,则写读TMRXL,TMRXH。
以读TMR1为例子,采用如下语法实现:
unsigned char i;
i=TMR1L;
i+=TMR1H<<8;
3.3.5 运行中的启动代码
C程序在进入MAIN()函数执行前,会要求初使化一些东西,并使芯片从复位时候的状态转入一种确定的状态。
通常说来,启动代码是一段普通的预编译的子程序,将链接到用户程序中。
即使用户的程序不需要启动代码的各个方面,多余的启动代码照样链接,虽然这是无害的,但占用了程序空间,延迟了用户自编写的程序的执行。
PICC18采用了一种新颖的策略来识别什么样的启动代码是必要的,此处翻译省略。
启动代码在每次编译的时候都会自动链接,包含启动代码的单独的文件每次都会被删除,如果要想在编译后依然看到它,可以采用如下编译选项,--RUNTIME=default,+keep。
这个文件名为startup.as,至于到底这个文件在什么位置出现,由于本人没使用过,暂时不翻译,可以使用搜索功能找到它。
这些过程是不需要用户去干预的
3.3.5.4上电子程序
在某些情况下,我们需要在一上电或者一复位的时候,通常是前几个指令周期,就根据上电或者复位情况执行特定代码。
这个用户提供的汇编模块会在复位后被马上执行,通常这个模块是在C函数中使用嵌入汇编代码完成。
一个虚拟的(空的)上电子程序被包含在powerup.as 文件中。
这个文件可以被拷贝,修改,添加到你的项目中。
添加后不需要任何特殊的编译,链接设置,或者用代码指定跳到该文件。
编译器会自动检查你是否使用了上电子程序。
并在复位后自动跳转。
如果使用了上电代码,则需要在初使化后添加一个跳转到start。
power.as 文件在编译器安装目录的source文件夹下可以找到。
强调一下,启动代码(startup.as)和上电代码(powerup.as)是不同的东西,启动代码在所有的情况下都是需要的,主要是执行变量的初使化。
PICC18会在最开始的时候把所有的变量给个初值,如果你定义的时候给了初值,没有给的,它会把变量清0。
而上电代码是为了应付特殊的需求的,如果不添加文件,是不会自动产生的。
3.4支持的数据类型和变量
多字节变量的存储格式位,低字节低地址,高字节高地址。
基本数据类型如下表。
进制表示。
数据类型和进制表示上用的是标准的C语言风格,每种变量占用的内存字节数都比较符合ANSI C,比如整形变量就是2个字节。
某些单片机的C编译器,整形变量则可能是一个字
节,这需要大家小心。
另外增加了单片机需要的位变量类型。
为了适应单片机计算,浮点类型允许设置为24位或者32位(可以在编译选项中设置)。
任何的整形常量将由一个最小的存储长度来存储同时保证数据不会溢出。
如果加上了后缀“L”或者“l”则表明此常量为unsigned long 或者signed long 。
后缀“U”或者“u”将表明此常量为unsigned类型,如果为“UL”,则为unsigned long。
浮点常量将有两种类型,除非有明确的后缀“L”或者“l”表示其为double类型,“F”或者“f”表示其为FLOAT类型。
字符串常量或者字符串数据都用双引号来表示比如用“Hello world”。
用const char *来定义一个字符串常量,并把这些数据存储在程序区。
把一串字符串常量分派给一个非常数字符指针,编译器会产生警告,比如:
char * cp= "one"; // "one" in ROM, 产生警告
const char * ccp= "two"; // "two" in ROM, 正确
定义一个非常量的字符数组可以采用如下方式:
char ca[]= "two"; // "two" different to the above
则将在RAM中初使化two,two从程序中拷贝而来。
两段分离开的常数则由编译器自动链接。
比如中间只空一格的如下表达:
const char * cp = "hello " "world";
将把"hello world"分配给cp。
3.4.2位数据类型和变量
PICC18用关键字bit来声明一个位变量,只存储0或者1。
如果加上static,且在函数内声明,则只可在函数内部使用,例如:
static bit init_flag;
如果在函数外声明,例如,
bit init_flag;
则为全局函数。
位变量不能定义为一个局部变量,“auto”。
所以当在函数内部定义一个位变量,包括main 函数,一定要加上static声明其为局部静态变量,如上所示范。
位变量也不能做为一个函数的参数。
但是,一个函数可以定义为bit类型来返回一个位的值。
这个位变量值将放在STATUS 寄存器的C位返回。
位变量在很多时候表现得和无符号字符型变量很相似,但他储存0和1,所以这提供了一个方便高效的方法来存储布尔符号而不需要消耗大量的RAM空间。
尽管如此,不存在指向一个位变量的指针,也不能静态初使化位变量。
把一个整形变量整体赋值给一个位变量,则最后一位将赋值给该位变量,这和ANSI C对布尔类型的转换是不一样的。
比如:
int data = 0x54;
bit bitvar;
bitvar = data;
由于data的最低位为0,所以bitvar将为0。
如果你想用类似ANSI C对布尔变量类型转换时候的操作,即把整形数赋值给位变量,位变量为0还是1,取决于原始的整形数据是否为0,则可以采用如下语法:
bitvar = data != 0;
位变量在启动代码会被强制清0,如果你想对一个单独的位变量给非0的初值,请在自己的
用户代码中处理。
而不能指望在定义的时候给初值就可。
如果使用了——strict编译选项,则位变量不可用。
3.4.3 Using Bit-Addressable Registers
本章节介绍如何强制定位一个位变量到某个地址,一般这只对特殊寄存器有用,不建议对变量做这样的操作,具体形式可以参考MCU的头文件中对特殊功能寄存器各个位的定义,比如
static unsigned char RCON @ 0xFD0;
static near bit PD @ (unsigned)&RCON*8+2;
3.4.4 8位整形数据类型和变量
PICC18同时支持有符号和无符号字符型(signed char and unsigned char),如果关键字signed 和unsigned 没有标明,则char默认状态下为unsigned char。
但如果使用了--CHAR=signed,则char表示signed char。
后续的英文原始文档不过再次强调一个观点,所谓的字符型,实际上应该完全看作一个8位的整形,对字符型的操作和对8位整形的操作没有什么区别,就是符号型和整形之间是有天然的转换关系。
比如你可以写char XXX=’A’,也可以写char XXX=0X41。
这两者都是等效的。
XXX在内存中的表示形式都是0X41。
对这点如果有不太熟悉的,可以参考潭浩强的C语言教材。
3.4.5 16位整形数据类型和变量
翻译略。
编程无特殊注意事项。
3.4.6 32位整形数据类型和变量
翻译略。
编程无特殊注意事项。
3.4.7 Floating Point Types and Variables
数据的存储遵照如下格式。
浮点类型一般为有符号型,所以采用无符号声明一个浮点数是非法的。
通过编译选项--double=fast32可以有效提高浮点计算的速度,但以程序区的空间增大为代价。
在MPLAB IDE下,需要注意设置观察变量的属性,才能正确观察浮点数。
具体方法如下:以定义了一个double vat=10000;的浮点数(不是整形)的观察为例子。
首先,HI-TECH的编译器支持32位,但是为了节约空间提高运算效率,在不对编译器选项做设置的情况下,即使是double,不过是24位。
另外HI-TECH采取的是IEEE754规范。
由于各个编译器厂家采取的浮点规范略有不同,就导致了我们在观察的时候需要注意观察属性的设置。
现在见附图。
使用MPLAB IDE英文版用户,可参造找到适当的位置。
3.4.8 结构体和成员体
结构体和联合体,除了单独的位变量不能做为其成员,使用上和标准C没什么区别。
3.4.8.1 位结构体
在汇编语言的情况下,我们会把几个位变量放在一个字节内。
以方便管理。
PICC18提供了更方便的位结构体类型。
在位结构体中,你可以在任意位定义单独的变量,并定义多个位的位段。
这和标准C也是类似的。
例如:
struct {
unsigned lo : 1;
unsigned dummy : 6;
unsigned hi : 1;
} foo;
lo定义在bit0(最低位),hi定义在bit7(最高位)。
如果你只想在bit0和bit7定义,中间六位暂时不想使用,可简化定义为:
struct {
unsigned lo : 1;
unsigned : 6;
unsigned hi : 1;
} foo;
而初使化则可以这样:
struct {
unsigned lo : 1;
unsigned mid : 6;
unsigned hi : 1;
} foo = {1, 8, 0};
则foo在的单元实际上为实际上为 0 001000 1 ,即0x11。
3.4.8.2 结构体和联合体的限制
如果整个结构体为常量,则全部数据将仿在程序区,显然,将只能读出。
如果这样,所有的成员必须被初使化。
例如:
const struct {
int number;
int *ptr;
} record = { 0x55, &i};
如果某个结构体的内部成员为常量,但结构体不是常量,则整个结构体将放在RAM中,但每个成员都变成只读。
比如:
struct {
const int number;
int * const ptr;
} record = { 0x55, &i};
3.4.9 标准类型限制
PICC18支持ANSI C的限制以及为嵌入式专门设置的一些关键字。
3.4.9.1 Const and V olatile Type Qualifiers
const会告诉编译器,该变量为只读,无法修改。
常量被定义的时候就必须初使化。
volatile用来告诉编译器一个参量成功访问后也未必能获得值,这能避免启动代码去错误操作它。
一般用于特殊寄存器的定义。
3.4.10 特殊类型限定
PICC18提供了一些特殊的限定字来把变量分配到特定的RAM空间。
包括persisten, near ,far。
这些限定也可以用在指针上。
但是不能用在局部自动变量上。
如果一定要用在局部变量上,只能用在局部静态变量上。
如:
static persistent int intvar;
PICC18依然支持关键字bank1,bank2,bank3,这些关键字的存在是为了让代码能无缝地从PICC(支持PIC12/16/17系列的编译器)移植到PICC18。
PICC18允许这些限定字,但这些限定不会对变量的定位发生任何影响。
3.4.10.1 Persistent类型限定
该类型告诉编译器,在上电复位的时候这些变量不应该被自动清0。
比如特殊功能寄存器,都加了此限定字。
如果要实现某些功能,需要某些特殊变量在上电的时候不被清0,则可加此关键字进行限定。
3.4.10.2 Near 类型限定
near类型用来把局部静态变量指定到PIC18的ACCESS BANK。
如:
static near unsigned char fred;
3.4.10.3 Far类型限定
FAR是为了把变量定义在外扩的ROM。
不推荐使用。
只有支持外扩的MCU才支持FAR类型。
3.4.11 Bdata类型限定
该关键字只在程序采用small mode 编译时有意义。
在这样的模式下,所有的局部静态和全局变量都放在ACCESS BANK,bdata则能限制这些变量放在非ACCESS区。
然后这些变量就表现得和在large mode下一样。
这些能防止ACCESS BANK在small mode下溢出,以免更换到large mode模式编译。
3.4.12 指针类型
PICC18支持两种基本指针,数据指针,函数指针。
数据指针存储变量的地址,以使变量可以为程序读写修改。
函数指针存储可执行子函数的入口,以便通过指针间接调用。
3.4.12.1 RAM指针
RAM指针为16位,可以寻址PIC18全空间的RAM。
指向near的指针只有8位,只能访问near 变量(存储在ACCESS RAM的变量)。
3.4.12.2 Const and Far Pointers
常量和FAR指针,可为16位或者24位,这由编译选项--CP=24或者 --CP=16决定。
项目中的所有模块必须采用同样宽度的指针。
指针指向FAR变量和常量是基本一样,只有一点不同,指向FAR变量的,可能用来改变所指向的内容,指向常量的是绝对不行的。
16位的常量和FAR指针可以访问全部的RAM空间和大部分的程序空间(64K字节以内)。
在运行时,指针的内容将被检查。
地址范围超过RAM区上限的,将采用表读表写指令访问程序区,小于RAM区上限的则访问RAM区。
如果常量指针保存的地址在RAM区,RAM 的该地址的内容不应当变化。
默认情况下,链接选项会把常量放在RAM的地址上限,以保证访问正确的存储空间。
当目标MCU的程序区超过64K字节,只有低64K字节能用16位指针访问。
后续内容为24位指针,牵涉到CONFIG区操作,翻译暂缓。
3.4.12.3 函数指针
翻译暂略,内容与上面基本一致,讲述了16位指针与24位指针的不同,但没有给出具体的使用例程。
3.4.12.4 联合类型限定与指针
类型限定可用来限定指针本身的类型以及指针指向的变量的类型。
举例如下:
near int * nip ;
int * near inp ;
near int * near ninp ;
nip是指所指向的变量为near int,即存储在ACCESS RAM,则指针本身为8位,但是会存在全部RAM BANK的某些地方。
inp是指指向的变量为int,则可能在RAM的任何一个角落,则指针需要为16位,但加了near限定后,指针变量将放在ACCESS RAM。
ninp则指用存储在ACCESS RAM的8位指针,指向存储在ACCESS RAM的变量。
其他限定词带来的变量存储,指示,如上所示范。
为了兼容PICC,PICC18允许BANK1,BANK2,BANK3这些关键字的存在,但是这些关键字不影响PICC18的代码。
3.5 存储类型和参数分配
与标准C基本相同,无重要信息,暂时不翻译,请查看英文文档。
3.6 函数
与编程操作基本无关,无重要信息,暂时不翻译,请查看英文文档。
3.7
与编程操作基本无关,无重要信息,暂时不翻译,请查看英文文档。
3.8 操作数
3.8.1 整形提升
整形提升是值得认真学习的。
C不比汇编,编译器有时会自动扩充变量长度,以做中间计算使用,每款单片机的C编译器,都必须充分学习其整形提升的语法规则。
否则则请大量使用中间变量,或者手动控制整形提升。
笔者初次独立编写PID和一些数学处理函数,就死在了对整形提升没有认真体会上。
当数学算式中存在不只一个操作数时,典型情况下它们的类型应当严格一致,否则编译器会自动进行类型转换。
转换的原则是向“大”的方向转。
即使操作数都是同样的类型,但在某些情况下,也会发生整型提升。
如果你不注意的话,那么异常的结果就发生了。
整形提升会在枚举类型,有符号无符号字符型,有符号无符号整形的变量下发生。
例如:
unsigned char count, a=0, b=50;
if(a - b < 10)
count++;
a-b的结果是206(这个结果不是比10小),因为a,b在进行减法前都被转化成signed int 类型了。
然后再从-50转换成了unsigned int。
实际上,很多情况下,一个变量的范围都是受限制的,并不会达到其类型的上下限。
按笔者的经验,这段代码可以改造为如下。
unsigned int temp_b=b;
temp_b+=10;
if(a<temp_b)
count++;
因为a,b本身就为8位,所以转换一个就够了。
为了避免下溢(出现负号,出现负号则需要进行有符号计算,比较浪费代码),强烈建议采用这样的无符号提升,并做加法处理。
再看一个例子:
unsigned char count, c;
c = 0x55;
if( ~c == 0xAA)
count++;
似乎理论上对c取反,8位下,55取反应该就是0XAA,但是编译器做了整形提升,结果为0XFFAA,出错了。
那么其实可以用变通的写法。
unsigned char count, c;
c = 0x55;
c=~c;
if( c == 0xAA)
count++;
由于C取反又给了C,所以没有整形提升,则判断不出错。
我的观点是,在不是很了解编译器什么时候进行整形提升时,我们尽量避免在判断语句中进行计算后的比较判定。
而只做简单的判定。
另外,用对0XFF的异或,来取代求反。
或者用类似的宏汇编来完成求反。
3.8.2 移位过程中的提升
这里的关键是对有符号数进行移位的时候会发生什么。
无符号数则与ANSI C是一致的。
如果需要直接和汇编指令挂钩的操作,可以采用系统定义的宏或者自己用嵌入式汇编。
ANSI C,对于有符号数右移时,符号位可能被清0,或者保持。
(应该是取决于C编译器的版本)。
右移动有符号数,PICC18按上述的后者进行,即先整体移动,然后保持符号位。
右移动无符号数,最高位清0。
左移动无符号或者有符号数,最低为清0。
3.8.3 除以一个整数和对一个整数求模(取余数)
除0将导致结果为0。
3.9 PSECT用法
没研究清楚,暂时不翻译。
3.10 中断的处理
高中断的声明和PICC一致,用关键字interrupt。
低中断的声明用interrupt low_priority。
声明了低中断处理函数,并不会把什么中断自动变为低中断,每个中断的高低,还是由程序的代码中修改。
3.10.2 中断中的变量保护
PIC18 MCU在发生中断时,只将PC压入堆栈。
其他的变量保护需要由软件完成。
PICC18会自动检测需要保存的变量,在软件上做保存。
如果中断中调用了某个函数,而这个函数定义在中断函数之前,那么编译器将适当包保存必要的变量,否则,最坏的情况就是全部可能用到变量都会被保存。
PICC18不扫描内嵌汇编代码,中断中内嵌汇编代码用到的变量,编译器不会自动保存。
高中断,编译器会动用影子寄存器进行变量保护还原,但是指明了compile for icd2,则不会动用影子寄存器,因为ICD2会占用这些空间。
关于高低中断将变量保护到哪个段,与编程无关,在此不翻译。
3.10.2 中断中的变量还原
变量自动还原,无需我们关心。