PIC单片机 C编程技巧.docx

上传人:b****2 文档编号:2026503 上传时间:2023-05-02 格式:DOCX 页数:47 大小:31.06KB
下载 相关 举报
PIC单片机 C编程技巧.docx_第1页
第1页 / 共47页
PIC单片机 C编程技巧.docx_第2页
第2页 / 共47页
PIC单片机 C编程技巧.docx_第3页
第3页 / 共47页
PIC单片机 C编程技巧.docx_第4页
第4页 / 共47页
PIC单片机 C编程技巧.docx_第5页
第5页 / 共47页
PIC单片机 C编程技巧.docx_第6页
第6页 / 共47页
PIC单片机 C编程技巧.docx_第7页
第7页 / 共47页
PIC单片机 C编程技巧.docx_第8页
第8页 / 共47页
PIC单片机 C编程技巧.docx_第9页
第9页 / 共47页
PIC单片机 C编程技巧.docx_第10页
第10页 / 共47页
PIC单片机 C编程技巧.docx_第11页
第11页 / 共47页
PIC单片机 C编程技巧.docx_第12页
第12页 / 共47页
PIC单片机 C编程技巧.docx_第13页
第13页 / 共47页
PIC单片机 C编程技巧.docx_第14页
第14页 / 共47页
PIC单片机 C编程技巧.docx_第15页
第15页 / 共47页
PIC单片机 C编程技巧.docx_第16页
第16页 / 共47页
PIC单片机 C编程技巧.docx_第17页
第17页 / 共47页
PIC单片机 C编程技巧.docx_第18页
第18页 / 共47页
PIC单片机 C编程技巧.docx_第19页
第19页 / 共47页
PIC单片机 C编程技巧.docx_第20页
第20页 / 共47页
亲,该文档总共47页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

PIC单片机 C编程技巧.docx

《PIC单片机 C编程技巧.docx》由会员分享,可在线阅读,更多相关《PIC单片机 C编程技巧.docx(47页珍藏版)》请在冰点文库上搜索。

PIC单片机 C编程技巧.docx

PIC单片机C编程技巧

PIC单片机C编程技巧

科技2008-06-2711:

27:

15阅读235评论0  字号:

大中小 订阅

PIC单片机 C编程技巧

 

 

 

 

PIC单片机C编程技巧

[作者:

风之羽翼  ]

1、PICC和MPLAB集成

PICC和MPLAB集成:

PICC有自己的文本编辑器,不过是DOS风格的,看来PICC的工程师要专业冷到酷底了...

大家大可不必用它,如果你没什么癖好的话,你不会不用UltraEdit吧?

1:

建立你的工作目录:

建议在C盘根目录下建立一个以A开头的文件夹做为工作目录.因为你会发现它总是在你查找文件时候第

一个跳入你眼中.

2:

MPLAB调用PICC.(以MPLAB5.7版本为例子)

启动MPLAB.在Project-->InstallLanguageTool:

LanguageSuite----->hi-techpicc

ToolName---->PICCCompiler

Executable---->c:

hi-picinpicc.exe(假如你的PICC是默认安装的)

选Command-line

最后OK.

上面这步只需要设定一次,除非你重新安装了MPLAB.

3:

创建你的项目文件:

(假如你实现用EDIT编辑好了一个叫AA.C的C代码文件)

Project-->NewProject-->FileName--->myc(假如我们把项目文件取名字叫MYC.PJT)

右边窗口当然要选择中你的工作目录.然后OK.

4:

设定你的PICC工作参数:

Project-->EditProject

上面4个栏目就用默认的,空的也就让它空着,无所谓的.

需要修改的是:

DevelopmentMode---->选择你的PIC型号.当然要选择MplabSIMSimulator

让你可以用软件仿真.

LanguageToolSuite--->HI-TECHPICC

上面的步骤,你可能会遇见多个提示条,不要管它,一路确定.

下面是PICC编译器的选择项:

双击ProjectFiles窗口里面的MYC.HEX,出现一个选择拦目.命令很多,大家可以看PICC文本编

辑器里面的HELP,里面有详细说明.

下面就推荐几个常用也是建议用的:

Generatedebuginfo以及下面的2项.

Produceassemblerlistfile

就在它们后面打勾即可,其它的不要管,除非你有特殊要求.

5:

添加你的C代码文件:

当进行了前面几步后,按AddNode找到AA.C文件就OK了.

6:

编译C代码:

最简单的一步:

直接按下F10.

编译完后,会出现各种调试信息.C代码对应的汇编代码就是工作目录里面的AA.IST,用EDIT

打开可以看见详细的对比.

7:

其它,要是一切都没问题,那么你就可以调试和烧片了,和以往操作无异.

2、如何从汇编转向PICC

首先要求你要有C语言的基础。

PICC不支持C++,这对于习惯了C++的朋友还得翻翻C语言的书。

C

代码的头文件一定要有#include,它是很多头文件的集合,C编译器在pic.h中根据你的芯片自动栽

入相应的其它头文件。

这点比汇编好用。

载入的头文件中其实是声明芯片的寄存器和一些函数。

顺便摘抄

一个片段:

staticvolatileunsignedcharTMR0@0x01;

staticvolatileunsignedcharPCL@0x02;

staticvolatileunsignedcharSTATUS@0x03;

可以看出和汇编的头文件中定义寄存器是差不多的。

如下:

TMR0EQU0X01;

PCLEQU0X02;

STATUSEQU0X03;

都是把无聊的地址定义为大家公认的名字。

一:

怎么附值?

如对TMR0附值,汇编中:

MOVLW200;

MOVWFTMR0;

当然得保证当前页面在0,不然会出错。

C语言:

TMR0=200;//无论在任何页面都不会出错。

可以看出来C是很直接了当的。

并且最大好处是操作一个寄存器时候,不用考虑页面的问题。

一切由

C自动完成。

二:

怎么位操作?

汇编中的位操作是很容易的。

在C中更简单。

C的头文件中已经对所有可能需要位操作的寄存器的每

一位都有定义名称:

如:

PORTA的每一个I/O口定义为:

RA0、RA1、RA2。

RA7。

OPTION的每一位定义为:

PS0、

PS1、PS2、PSA、T0SE、T0CS、INTEDG、RBPU。

可以对其直接进行运算和附值。

如:

RA0=0;

RA2=1;

在汇编中是:

BCFPORTA,0;

BSFPORTA,2;

可以看出2者是大同小异的,只是C中不需要考虑页面的问题。

三:

内存分配问题:

在汇编中定义一个内存是一件很小心的问题,要考虑太多的问题,稍微不注意就会出错。

比如16位的

运算等。

用C就不需要考虑太多。

下面给个例子:

16位的除法(C代码):

INTX=5000;

INTY=1000;

INTZ=X/Y;

而在汇编中则需要花太多精力。

给一个小的C代码,用RA0控制一个LED闪烁:

#include

voidmain()

{

intx;

CMCON=0B111;//掉A口比较器,要是有比较器功能的话。

ADCON1=0B110;//掉A/D功能,要是有A/D功能的话。

TRISA=0;//RA口全为输出。

loop:

RA0=!

RA0;

for(x=60000;--x;){;}//延时

gotoloop;

}

说说RA0=!

RA0的意思:

PIC对PORT寄存器操作都是先读取----修改----写入。

上句的含义是程序先

读RA0,然后取反,最后把运算后的值重新写入RA0,这就实现了闪烁的功能。

3、浅谈PICC的位操作

由于PIC处理器对位操作是最高效的,所以把一些BOOL变量放在一个内存的位中,既可以达到运算

速度快,又可以达到最大限度节省空间的目的。

在C中的位操作有多种选择。

*********************************************

如:

charx;x=x|0B00001000;/*对X的4位置1。

*/

charx;x=x&0B11011111;/*对X的5位清0。

*/

把上面的变成公式则是:

#definebitset(var,bitno)(var|=1<

#definebitclr(var,bitno)(var&=~(1<

则上面的操作就是:

charx;bitset(x,4)

charx;bitclr(x,5)

*************************************************

但上述的方法有缺点,就是对每一位的含义不直观,最好是能在代码中能直观看出每一位代表的意思,

这样就能提高编程效率,避免出错。

如果我们想用X的0-2位分别表示温度、电压、电流的BOOL值可以

如下:

unsignedcharx@0x20;/*象汇编那样把X变量定义到一个固定内存中。

*/

bittemperature@(unsigned)&x*8+0;/*温度*/

bitvoltage@(unsigned)&x*8+1;/*电压*/

bitcurrent@(unsigned)&x*8+2;/*电流*/

这样定义后X的位就有一个形象化的名字,不再是枯燥的1、2、3、4等数字了。

可以对X全局修改,

也可以对每一位进行操作:

char=255;

temperature=0;

if(voltage)......

*****************************************************************

还有一个方法是用C的struct结构来定义:

如:

structcypok{

temperature:

1;/*温度*/

voltage:

1;/*电压*/

current:

1;/*电流*/

none:

4;

}x@0x20;

这样就可以用

x.temperature=0;

if(x.current)....

等操作了。

**********************************************************

上面的方法在一些简单的设计中很有效,但对于复杂的设计中就比较吃力。

如象在多路工业控制上。

前端需要分别收集多路的多路信号,然后再设定控制多路的多路输出。

如:

有2路控制,每一路的前端信

号有温度、电压、电流。

后端控制有电机、喇叭、继电器、LED。

如果用汇编来实现的话,是很头疼的事

情,用C来实现是很轻松的事情,这里也涉及到一点C的内存管理(其实C的最大优点就是内存管理)。

采用如下结构:

unioncypok{

structout{

motor:

1;/*电机*/

relay:

1;/*继电器*/

speaker:

1;/*喇叭*/

led1:

1;/*指示灯*/

led2:

1;/*指示灯*/

}out;

structin{

none:

5;

temperature:

1;/*温度*/

voltage:

1;/*电压*/

current:

1;/*电流*/

}in;

charx;

};

unioncypokan1;

unioncypokan2;

上面的结构有什么好处呢?

细分了信号的路an1和an2;

细分了每一路的信号的类型(是前端信号in还是后端信号out):

an1.in;

an1.out;

an2.in;

an2.out;

然后又细分了每一路信号的具体含义,如:

an1.in.temperature;

an1.out.motor;

an2.in.voltage;

an2.out.led2;等

这样的结构很直观的在2个内存中就表示了2路信号。

并且可以极其方便的扩充。

如添加更多路的信号,只需要添加:

unioncypokan3;

unioncypokan4;

从上面就可以看出用C的巨大好处

4、PICC之延时函数和循环体优化。

很多朋友说C中不能精确控制延时时间,不能象汇编那样直观。

其实不然,对延时函数深入了解一下

就能设计出一个理想的框价出来。

一般的我们都用for(x=100;--x;){;}此句等同与x=100;while(--x){;};

或for(x=0;x<100;x++){;}。

来写一个延时函数。

在这里要特别注意:

X=100,并不表示只运行100个指令时间就跳出循环。

可以看看编译后的汇编:

x=100;while(--x){;}

汇编后:

movlw100

bcf3,5

bcf3,6

movwf_delay

l2decfsz_delay

gotol2

return

从代码可以看出总的指令是是303个,其公式是8+3*(X-1)。

注意其中循环周期是X-1是99个。

里总结的是x为char类型的循环体,当x为int时候,其中受X值的影响较大。

建议设计一个char类型的

循环体,然后再用一个循环体来调用它,可以实现精确的长时间的延时。

下面给出一个能精确控制延时的

函数,此函数的汇编代码是最简洁、最能精确控制指令时间的:

voiddelay(charx,chary){

charz;

do{

z=y;

do{;}while(--z);

}while(--x);

}

其指令时间为:

7+(3*(Y-1)+7)*(X-1)如果再加上函数调用的call指令、页面设定、传递参数

花掉的7个指令。

则是:

14+(3*(Y-1)+7)*(X-1)。

如果要求不是特别严格的延时,可以用这个函数:

voiddelay(){

unsignedintd=1000;

while(--d){;}

}

此函数在4M晶体下产生10003us的延时,也就是10MS。

如果把D改成2000,则是20003us,以此类

推。

有朋友不明白,为什么不用while(x--)后减量,来控制设定X值是多少就循环多少周期呢?

现在看看编

译它的汇编代码:

bcf3,5

bcf3,6

movlw10

movwf_delay

l2

decf_delay

incfsz_delay,w

gotol2

return

可以看出循环体中多了一条指令,不简洁。

所以在PICC中最好用前减量来控制循环体。

再谈谈这样的语句:

for(x=100;--x;){;}和for(x=0;x<100;x++){;}

从字面上看2者意思一样,但可以通过汇编查看代码。

后者代码雍长,而前者就很好的汇编出了简洁的代

码。

所以在PICC中最好用前者的形式来写循环体,好的C编译器会自动把增量循环化为减量循环。

因为

这是由处理器硬件特性决定的。

PICC并不是一个很智能的C编译器,所以还是人脑才是第一的,掌握一些

经验对写出高效,简洁的代码是有好处的。

5、深入探讨PICC之位操作

一:

用位操作来做一些标志位,也就是BOOL变量.可以简单如下定义:

bita,b,c;

PICC会自动安排一个内存,并在此内存中自动安排一位来对应a,b,c.由于我们只是用它们来简单的

表示一些0,1信息,所以我们不需要详细的知道它们的地址\位究竟是多少,只管拿来就用好了.

二:

要是需要用一个地址固定的变量来位操作,可以参照PIC.H里面定义寄存器.

如:

用25H内存来定义8个位变量.

staticvolatileunsignedcharmyvar@0x25;

staticvolatilebitb7@(unsigned)&myvar*8+7;

staticvolatilebitb6@(unsigned)&myvar*8+6;

staticvolatilebitb5@(unsigned)&myvar*8+5;

staticvolatilebitb4@(unsigned)&myvar*8+4;

staticvolatilebitb3@(unsigned)&myvar*8+3;

staticvolatilebitb2@(unsigned)&myvar*8+2;

staticvolatilebitb1@(unsigned)&myvar*8+1;

staticvolatilebitb0@(unsigned)&myvar*8+0;

这样即可以对MYVAR操作,也可以对B0--B7直接位操作.

但不好的是,此招在低档片子,如C5X系列上可能会出问题.

还有就是表达起来复杂,你不觉得输入代码受累么?

呵呵

三:

这也是一些常用手法:

#definetestbit(var,bit)((var)&(1<<(bit)))

//测试某一位,可以做BOOL运算

#definesetbit(var,bit)((var)|=(1<<(bit)))//把某一位置1

#defineclrbit(var,bit)((var)&=~(1<<(bit)))//把某一位清0

付上一段代码,可以用MPLAB调试观察

#include

#definetestbit(var,bit)((var)&(1<<(bit)))

#definesetbit(var,bit)((var)|=(1<<(bit)))

#defineclrbit(var,bit)((var)&=~(1<<(bit)))

chara,b;

voidmain(){

charmyvar;

myvar=0B10101010;

a=testbit(myvar,0);

setbit(myvar,0);

a=testbit(myvar,0);

clrbit(myvar,5);

b=testbit(myvar,5);

if(!

testbit(myvar,3))

a=255;

else

a=100;

while

(1){;}

}

四:

用标准C的共用体来表示:

#include

unionvar{

unsignedcharbyte;

struct{

unsignedb0:

1,b1:

1,b2:

1,b3:

1,b4:

1,b5:

1,b6:

1,b7:

1;

}bits;

};

chara,b;

voidmain(){

staticunionvarmyvar;

myvar.byte=0B10101010;

a=myvar.bits.b0;

b=myvar.bits.b1;

if(myvar.bits.b7)

a=255;

else

a=100;

while

(1){;}

}

五:

用指针转换来表示:

#include

typedefstruct{

unsignedb0:

1,b1:

1,b2:

1,b3:

1,b4:

1,b5:

1,b6:

1,b7:

1;

}bits;//先定义一个变量的位

#definemybit0(((bits*)&myvar)->b0)//取myvar

的地址(&myvar)强制转换成bits类型的指针

#definemybit1(((bits*)&myvar)->b1)

#definemybit2(((bits*)&myvar)->b2)

#definemybit3(((bits*)&myvar)->b3)

#definemybit4(((bits*)&myvar)->b4)

#definemybit5(((bits*)&myvar)->b5)

#definemybit6(((bits*)&myvar)->b6)

#definemybit7(((bits*)&myvar)->b7)

charmyvar;

chara,b;

voidmain(){

myvar=0B10101010;

a=mybit0;

b=mybit1;

if(mybit7)

a=255;

else

a=100;

while

(1){;}

}

 

[NextPage]

六:

五的方法还是烦琐,可以用粘贴符号的形式来简化它.

#include

typedefstruct{

unsignedb0:

1,b1:

1,b2:

1,b3:

1,b4:

1,b5:

1,b6:

1,b7:

1;

}bits;

#define_paste(a,b)a##b

#definebitof(var,num)(((bits*)&(var))->_paste(b,num))

charmyvar;

chara,b;

voidmain(){

a=bitof(myvar,0);

b=bitof(myvar,1);

if(bitof(myvar,7))

a=255;

else

a=100;

while

(1){;}

}

有必要说说#define_paste(a,b)a##b的意思:

此语句是粘贴符号的意思,表示把b符号粘贴到a符号之后.

例子中是

a=bitof(myvar,0);--->(((bits

*)&(myvar))->_paste(b,0))--->(((bits*)&(var))->b0)

可以看出来,_paste(b,0)的作用是把0粘贴到了b后面,成了b0符号.

总结:

C语言的优势是能直接对低层硬件操作,代码可以非常非常接近汇编,上面几个例子的位操作代码

是100%的达到汇编的程度的.另一个优势是可读性高,代码灵活.上面的几个位操作方法任由你选,

你不必担心会产生多余的代码量出来.

6、在PICC中使用常数指针。

常数指针使用非常灵活,可以给编程带来很多便利。

我测试过,PICC也支持常数指针,并且也会自动

分页,实在是一大喜事。

定义一个指向8位RAM数据的常数指针(起始为0x00):

#defineDBYTE((unsignedcharvolatile*)0)

定义一个指向16位RAM数据的常数指针(起始为0x00):

#defineCWORD((unsignedintvolatile*)0)

((unsignedcharvolatile*)0)中的0表示指向RAM区域的起始地址,可以灵活修改它。

DBYTE[x]中的x表示偏移量。

下面是一段代码1:

chara1,a2,a3,a4;

#defineDBYTE((unsignedcharvolatile*)0)

voidmain(void){

longcc=0x89abcdef;

a1=DBYTE[0x24];

a2=DBYTE[0x25];

a3=DBYTE[0x26];

a4=DBYTE[0x27];

while

(1);

}

2:

chara1,a2,a3,a4;

#defineDBYTE((unsignedcharvolatile*)0)

voidpp(chary){

a1=DBYTE[y++];

a2=DBYTE[y++];

a3=DBYTE[y++];

a4=DBYTE[y];

}

voidmain(void){

longcc=0x89abcdef;

charx;

x=&cc;

pp(x);

while

(1);

}

3:

chara1,a2,a3,a4;

#defineDBYTE((unsignedcharvolatile*)0)

voidpp(chary){

a1=DBYTE[y++];

a2=DBYTE[y++];

a3=DBYTE[y++];

a4=DBYTE[y];

}

voidmain(void){

bank1staticlongcc=0x89abcdef;

charx;

x=&cc;

pp(x);

while

(1);

}

7、PICC关于unsigned和signed的几个关键问题!

unsigned是表示一个变量(或常数)是无符号类型。

signed表示有符号。

它们表示数值范围不一样。

PICC默认所有变量都是unsigned类型的,哪怕你用了signed变量。

因为有符号运算比无符号运算耗资源,

而且MCU运算一般不涉及有符号运算。

在PICC后面加上-SIGNED_CHAR后缀可以告诉PICC把signed

变量当作有符号处理。

在PICC默认的无符号运算下看这样的语句:

chari;

for(i=7;i>=0;i--){

;//中间语句

}

这样的C代码看上去是没有丁

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2