PIC单片机 C编程技巧

更新时间:2023-06-08 02:27:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

PIC 单片机 C 编程技巧

PIC 单片机 C 编程技巧 1、PICC和MPLAB集成 、PICC和MPLAB集成 、PICC PICC和MPLAB集成: PICC和MPLAB集成: 集成 PICC有自己的文本编辑器,不过是DOS风格的,看来P PICC有自己的文本编辑器,不过是DOS风格的,看来P 有自己的文本编辑器 DOS风格的 ICC的工程师要专业冷到酷底了... ICC的工程师要专业冷到酷底了... 的工程师要专业冷到酷底了 大家大可不必用它,如果你没什么癖好的话, 大家大可不必用它,如果你没什么癖好的话,你不会不用 UltraEdit 吧? 1:建立你的工作目录: 建立你的工作目录: 建议在C盘根目录下建立一个以A 建议在C盘根目录下建立一个以A开头的文件夹做为工作目 录.因为你会发现它总是在你查找文件时候第 一个跳入你眼中. 一个跳入你眼中. 2:MPLAB调用PICC.(以MPLAB5.7版本为 :MPLAB调用PICC.(以MPLAB5 调用PICC.( 例子) 例子) 启动MPLAB.在 启动MPLAB.在 Project-->Install Language Tool: MPLAB. Language Suite----->hi-tech picc Tool Name ---->PICC Compiler Executable ---->c:hi-picinpicc.exe (假如你的PICC是默 假如你的PICC 假如你的PICC是默

认安装的) 认安装的) 选 Command-line 最后OK. 最后OK. 上面这步只需要设定一次,除非你重新安装了MPLAB. 上面这步只需要设定一次,除非你重新安装了MPLAB. 3:创建你的项目文件:(假如你实现用EDIT编辑好了一 创建你的项目文件:(假如你实现用EDIT编辑好了一 :(假如你实现用EDIT 个叫AA.C的C代码文件) 个叫AA.C的 代码文件) AA.C Project-->New Project-->File Name--->myc (假如我们把项 目文件取名字叫MYC.PJT) 目文件取名字叫MYC.PJT) 右边窗口当然要选择中你的工作目录.然后OK. 右边窗口当然要选择中你的工作目录.然后OK. 4:设定你的PICC工作参数: 设定你的PICC工作参数: PICC工作参数 Project-->Edit Project 上面4个栏目就用默认的,空的也就让它空着,无所谓的. 上面4个栏目就用默认的,空的也就让它空着,无所谓的. 需要修改的是: 需要修改的是: Development Mode---->选择你的PIC型号.当然要选择 选择你的PIC型号. 选择你的PIC型号 Mplab SIM Simulator 让你可以用软件仿真. 让你可以用软件仿真. Language Tool Suite--->HI-TECH PICC 上面的步骤,你可能会遇见多个提示条,不要管它

,一路确定. 上面的步骤,你可能会遇见多个提示条,不要管它,一路确定. 下面是PICC编译器的选择项: 下面是PICC编译器的选择项: PICC编译器的选择项 双击 Project Files 窗口里面的MYC.HEX,出现一个选择 窗口里面的MYC.HEX,出现一个选择 MYC.HEX, 拦目.命令很多,大家可以看PICC文本编 拦目.命令很多,大家可以看PICC文本编 PICC

辑器里面的HELP,里面有详细说明. 辑器里面的HELP,里面有详细说明. HELP,里面有详细说明 下面就推荐几个常用也是建议用的: 下面就推荐几个常用也是建议用的: Generate debug info 以及下面的2项. 以及下面的2 Produce assembler list file 就在它们后面打勾即可,其它的不要管,除非你有特殊要求 就在它们后面打勾即可,其它的不要管,除非你有特殊要求. 5:添加你的C代码文件: 添加你的C代码文件: 当进行了前面几步后, 找到AA.C文件就OK AA.C文件就 当进行了前面几步后,按 Add Node 找到AA.C文件就OK 了. 6:编译C代码: 编译C代码: 最简单的一步:直接按下F10. 最简单的一步:直接按下F10. 编译完后,会出现各种调试信息.C代码对应的汇编代码就是 编译完后,会出现各种调试信息.C代码对应的汇编代码就是 .C 工作目录里面的AA.IST,用 工作目录里面的AA.IST,用EDIT AA.IST, 打开可以看见详细的对比. 打开可以看见详细的对比. 7:其它,要是一切都没问题,那么你就可以调试和烧片了, 其它,要是一切都没问题,那么你就可以调试和烧片了, 和以往操作无异. 和以往操作无异. 2、如何从汇编转向 PICC 、 语言的基础。 首先要求你要有 C 语言的基础。PICC 不支持 C++,这对于习 , 惯了 C++的朋友还得翻翻 C 语言的书。C 的朋友还得翻翻 语言的书。 代码的头文件一定要有# 代码的头文件一定要有#i nclude<pic.h>,它是很多头文件的 , 集合, 集合,C 编译器在 pic.h 中根据你的芯片自动栽

入相应的其它头文件。这点比汇编好用。 入相应的其它头文件。这点比汇编好用。载入的头文件中其实 是声明芯片的寄存器和一些函数。 是声明芯片的寄存器和一些函数。顺便摘抄 一个片段: 一个片段: static volatile unsigned char TMR0 @ 0x01; static volatile unsigned char PCL @ 0x02; static volatile unsigned char STATUS @ 0x03; 可以看出和汇编的头文件中定义寄存器是差不多的。如下: 可以看出和汇编的头文件中定义寄存器是差不多的。如下: TMR0 EQU 0X01; ; PCL EQU 0X02; ; STATUS E

QU 0X03; ; 都是把无聊的地址定义为大家公认的名字。 都是把无聊的地址定义为大家公认的名字。 一:怎么附值? 怎么附值? 附值,汇编中: 如对 TMR0 附值,汇编中: MOVLW 200; ; MOVWF TMR0; ; 当然得保证当前页面在 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; ; 在汇编中是: 在汇编中是: BCF PORTA,0; , ; BSF PORTA,2; , ; 者是大同小异的, 可以看出 2 者是大同小异的,只是 C 中不需要考虑页面的问 题。 三:内存分配问题: 内存分配问题: 在汇编中定义一个内存是一件很小心的问题, 在汇编中定义一个内存是一件很小心的问题,要考虑太多的问 题,稍微不注意就会出错。比如 16 位的 稍微不注意就会出错。 注意就会出错 运算等。 就不需要考虑太多。下面给个例子: 运算等。用 C 就不需要考虑太多。下面给个例子:

16 位的除法(C 代码): 位的除法( 代码): INT X=5000; ; INT Y=1000; ; INT Z=X/Y; ; 而在汇编中则需要花太多精力。 而在汇编中则需要花太多精力。 代码, 闪烁: 给一个小的 C 代码,用 RA0 控制一个 LED 闪烁: #i nclude<pic.h> void main() { int x; CMCON=0B111; //掉 A 口比较器,要是有比较器功能的话。 掉 口比较器,要是有比较器功能的话。 ADCON1=0B110; //掉 A/D 功能,要是有 A/D 功能的话。 掉 功能, 功能的话。 TRISA=0; //RA 口全为输出。 全为输出。 loop:RA0=!RA0; ! ; for(x=60000;--x;){;} //延时 延时 goto loop; } 的意思: 说说 RA0=!RA0 的意思:PIC 对 PORT 寄存器操作都是先 ! 读取----修改 写入 读取 修改----写入。上句的含义是程序先 修改 写入。 读 RA0,然后取反,最后把运算后的值重新写入 RA0,这就实 ,然后取反

, , 现了闪烁的功能。 现了闪烁的功能。

3、浅谈 PICC 的位操作 、 处理器对位操作是最高效的, 由于 PIC 处理器对位操作是最高效的,所以把一些 BOOL 变 量放在一个内存的位中, 量放在一个内存的位中,既可以达到运算 速度快,又可以达到最大限度节省空间的目的。 速度快,又可以达到最大限度节省空间的目的。在 C 中的位操 作有多种选择。 作有多种选择。 ********************************************* 如:char x;x=x|0B00001000; /*对 X 的 4 位置 1。*/ 对 。 char x;x=x&0B11011111; /*对 X 的 5 位清 0。*/ 对 。 把上面的变成公式则是: 把上面的变成公式则是: #define bitset(var,bitno)(var |=1<<bitno) #define bitclr(var,bitno)(var &=~(1<<bitno)) 则上面的操作就是: 则上面的操作就是:char x;bitset(x,4) char x;bitclr(x,5) ************************************************* 但上述的方法有缺点,就是对每一位的含义不直观, 但上述的方法有缺点,就是对每一位的含义不直观,最好是能 在代码中能直观看出每一位代表的意思, 在代码中能直观看出每一位代表的意思, 这样就能提高编程效率,避免出错。 这样就能提高编程效率,避免出错。如果我们想用 X 的 0-2 位 分别表示温度、电压、 分别表示温度、电压、电流的 BOOL 值可以 如下: 如下: unsigned char x @ 0x20; /*象汇编那样把 X 变量定义到一个 象汇编那样把 固定内存中。 固定内存中。*/ bit temperature@ (unsigned)&x*8+0; /*温度 温度*/ 温度

bit voltage@ (unsigned)&x*8+1; /*电压 电压*/ 电压 bit current@ (unsigned)&x*8+2; /*电流 */ 电流 的位就有一个形象化的名字, 2、 这样定义后 X 的位就有一个形象化的名字, 不再是枯燥的 1、 、 、 3、4 等数字了。可以对 X 全局修改, 、 等数字了。 全局修改, 也可以对每一位进行操作: 也可以对每一位进行操作: char=255; temperature=0; if(voltage)...... ***************************************************************** 还有一个方法是用 结构来定义: 还有一个方法是用 C 的 struct 结构来定义: 如: struct cypok{ temperature:1; /*温度 温度*/ 温度 voltage:1; /*电压 电压*/ 电压 current:1; /*电流 电流*/ 电流 none:4; }x @ 0x20; 这样就可以用 x.temperature=0; if(x.current).... 等操作了。 等操作了。 ********************************************************** 上面的方法在一些简单的设计中很有效, 上面的方法在一些简单的设计中很有效,但对于复杂的设计中 就比较吃力。如象在多路工业控制上。 就比较吃力。如象在多路工业控制上。 多路工业控制上

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

度、电压、电流。 路输出。如:有 2 路控制,每一路的前端信号有温度、电压、电流。 后端控制有电机、喇叭、继电器、 后端控制有电机、喇叭、继电器、LED。如果用汇编来实现的话, 。如果用汇编来实现的话, 是很头疼的事情, 来实现是很轻松的事情, 是很头疼的事情,用 C 来实现是很轻松的事情,这里也涉及到一点 C 的内存管理(其实 C 的最大优点就是内存管理)。 的内存管理( 的最大优点就是内存管理)。 采用如下结构: 采用如下结构: union cypok{ struct out{ motor:1; /*电机 电机*/ 电机 relay:1; /*继电器 继电器*/ 继电器 speaker:1; /*喇叭 喇叭*/ 喇叭 led1:1; /*指示灯 指示灯*/ 指示灯 led2:1; /*指示灯 指示灯*/ 指示灯 }out; struct in{ none:5; temperature:1; /*温度 温度*/ 温度 voltage:1; /*电压 电压*/ 电压 current:1; /*电流 电流*/ 电流 }in; char x; }; union cypok an1; union cypok an2;

上面的结构有什么好处呢? 上面的结构有什么好处呢? 细分了信号的路 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 路信号。 并且可 以极其方便的扩充。 以极其方便的扩充。 如添加更多路的信号,只需要添加: 如添加更多路的信号,只需要添加: union cypok an3; union cypok an4; 从上面就可以看出用 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){;} 汇编后: 汇编后: movlw 100 bcf 3,5 bcf 3,6 movwf _delay l2 decfsz _delay goto l2 return 从代码可以看出总的指令是是 303 个,其公式是 8+3*(X-1)。 ( )。 注意其中循环周期是 X-1 是 99 个。这 类型的循环体, 时候, 里总结的是 x 为 char 类型的循环体,当 x 为 int 时候,其中 值的影响较大。 受 X 值的影响较大

。建议设计一个 char 类型的 循环体,然后再用一个循环体来调用它, 循环体,然后再用一个循环体来调用它,可以实现精确的长时 间的延时。 间的延时。下面给出一个能精确控制延时的 函数,此函数的汇编代码是最简洁、最能精确控制指令时间的: 函数,此函数的汇编代码是最简洁、最能精确控制指令时间的: void delay(char x,char y){ char z; do{ z=y;

do{;}while(--z); }while(--x); } 其指令时间为: ( ( 其指令时间为:7+(3*(Y-1)+7)*(X-1)如果再加上函数调 ) )( ) 指令、页面设定、 用的 call 指令、页面设定、传递参数 个指令。则是: )。如果 花掉的 7 个指令。则是:14+(3*(Y-1)+7)*(X-1)。如果 ( ( ) )( )。 要求不是特别严格的延时,可以用这个函数: 要求不是特别严格的延时,可以用这个函数: void delay(){ unsigned int d=1000; while(--d){;} } 的延时, 此函数在 4M 晶体下产生 10003us 的延时,也就是 10MS。如 。 果把 D 改成 2000,则是 20003us,以此类 , 以此类 后减量, 推。有朋友不明白,为什么不用 while(x--)后减量,来控制设定 有朋友不明白, 后减量 X 值是多少就循环多少周期呢?现在看看编 值是多少就循环多少周期呢? 译它的汇编代码: 译它的汇编代码: bcf 3,5 bcf 3,6 movlw 10 movwf _delay l2 decf _delay incfsz _delay,w goto l2 return 可以看出循环体中多了一条指令,不简洁。 可以看出循环体中多了一条指令,不简洁。所以在 PICC 中最 好用前减量来控制循环体。 好用前减量来控制循环体。

再谈谈这样的语句: 再谈谈这样的语句: for(x=100;--x;){;}和 for(x=0;x<100;x++){;} 和 者意思一样,但可以通过汇编查看代码。 从字面上看 2 者意思一样,但可以通过汇编查看代码。后者代 码雍长, 码雍长,而前者就很好的汇编出了简洁的代 中最好用前者的形式来写循环体 的形式来写循环体, 码。所以在 PICC 中最好用前者的形式来写循环体,好的 C 编 译器会自动把增量循环化为减量循环。 译器会自动把增量循环化为减量循环。因为 这是由处理器硬件特性决定的。 这是由处理器硬件特性决定的。PICC 并不是一个很智能的 C 编译器,所以还是人脑才是第一的, 编译器,所以还是人脑才是第一的,掌握一些 经验对写出高效,简洁的代码是有好处的。 经验对写出高效,简洁的代码是有好处的。 5、深入探讨PICC之位操作 、深入探讨PICC之位操作 PICC 一:用位操作来做一些标志位,也就是BOOL变量.可以简 用位操作来做一些标志位,也就是BOOL变量. BOOL变量 单如下定义: 单如下定义: bit a,b,c; PICC会自动安排一个内存, PI

CC会自动安排一个内存,并在此内存中自动安排一位来 会自动安排一个内存 对应 a,b,c.由于我们只是用它们来简单的 由于我们只是用它们来简单的 表示一些0 表示一些0,1信息,所以我们不需要详细的知道它们的地址 信息, \位究竟是多少,只管拿来就用好了. 位究竟是多少,只管拿来就用好了 二:要是需要用一个地址固定的变量来位操作,可以参照PI 要是需要用一个地址固定的变量来位操作,可以参照PI C.H里面定义寄存器. C.H里面定义寄存器. 里面定义寄存器 如:用25H内存来定义8个位变量. 25H内存来定义8个位变量. static volatile unsigned char myvar @ 0x25;

static volatile bit b7 @ (unsigned)&myvar*8+7; static volatile bit b6 @ (unsigned)&myvar*8+6; static volatile bit b5 @ (unsigned)&myvar*8+5; static volatile bit b4 @ (unsigned)&myvar*8+4; static volatile bit b3 @ (unsigned)&myvar*8+3; static volatile bit b2 @ (unsigned)&myvar*8+2; static volatile bit b1 @ (unsigned)&myvar*8+1; static volatile bit b0 @ (unsigned)&myvar*8+0; 这样即可以对MYVAR操作,也可以对B --B7 这样即可以对MYVAR操作,也可以对B0--B7直接位 MYVAR操作 操作. 操作. 但不好的是,此招在低档片子,如C5X系列上可能会出问题. 不好的是,此招在低档片子, 系列上可能会出问题. 还有就是表达起来复杂,你不觉得输入代码受累么? 还有就是表达起来复杂,你不觉得输入代码受累么?呵呵 三:这也是一些常用手法: 这也是一些常用手法: #define testbit(var, bit) ((var) & (1 <<(bit))) //测试某一位,可以做BOOL运算 //测试某一位,可以做BOOL运算 测试某一位 BOOL #define setbit(var, bit) ((var) |= (1 << (bit))) //把某一位置 //把某一位置 1 #define clrbit(var, bit) ((var) &= ~(1 << (bit))) //把某一位 //把某一位 清0 付上一段代码 可以用MPLAB调试观察 付上一段代码,可以用MPLAB调试观察 MPLAB #i nclude<pic.h> #define testbit(var, bit) ((var) & (1 <<(bit))) #define setbit(var, bit) ((var) |= (1 << (bit))) #define clrbit(var, bit) ((var) &= ~(1 << (bit))) char a,b;

void main(){ char myvar; 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的共用体来表示: 用标准C的共用体来表示: #i nclude<pic.h> union var{ unsigned char byte; struct { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } bits; }; char a,b; void main(){ static union var myvar; myvar.byte=0B10101010; a=myvar.bits.b0; b=myvar.bits.b1; if(myvar.bits.b7) a=255; else a=100; while(1){;}

} 五:用指针转换来表示

: 用指针转换来表示: #i nclude<pic.h> typedef struct { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } bits; //先定义一个变量的位 先定义一个变量的位 #define mybit0 (((bits *)&myvar)->b0) //取 myvar 取 的地址( 的地址(&myvar)强制转换成 bits 类型的指针 ) #define mybit1 (((bits *)&myvar)->b1) #define mybit2 (((bits *)&myvar)->b2) #define mybit3 (((bits *)&myvar)->b3) #define mybit4 (((bits *)&myvar)->b4) #define mybit5 (((bits *)&myvar)->b5) #define mybit6 (((bits *)&myvar)->b6) #define mybit7 (((bits *)&myvar)->b7) char myvar; char a,b; void main(){ myvar=0B10101010; a=mybit0; b=mybit1; if(mybit7) a=255; else a=100; while(1){;} } [NextPage]

六:五的方法还是烦琐,可以用粘贴符号的形式来简化它. 五的方法还是烦琐,可以用粘贴符号的形式来简化它. #i nclude<pic.h> typedef struct { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } bits; #define _paste(a,b) a##b #define bitof(var,num) (((bits *)&(var))->_paste(b,num)) char myvar; char a,b; void main(){ 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语言的优势是能直接对低层硬件操作, 总结:C语言的优势是能直接对低层硬件操作,代码可以非常 :C语言的优势是能直接对低层硬件操作

非常接近汇编, 非常接近汇编,上面几个例子的位操作代码 是100%的达到汇编的程度的.另一个优势是可读性高,代 100%的达到汇编的程度的.另一个优势是可读性高, 码灵活.上面的几个位操作方法任由你选, 码灵活.上面的几个位操作方法任由你选, 你不必担心会产生多余的代码量出来. 你不必担心会产生多余的代码量出来. 6、在 PICC 中使用常数指针。 、 中使用常数指针。 常数指针使用非常灵活,可以给编程带来很多便利。我测试过, 常数指针使用非常灵活,可以给编程带来很多便利。我测试过, PICC 也支持常数指针,并且也会自动 也支持常数指针, 分页,实在是一大喜事。 分页,实在是一大喜事。 数据的常数指针( 定义一个指向 8 位 RAM 数据的常数指针(起始为 0x00): #define DBYTE ((unsigned char volatile *) 0) 数据的常数指针( 定义一个指向 16 位 RAM 数据的常数指针(起始为 0x00): #define CWORD ((unsigned int volati

le *) 0) ((unsigned char volatile *) 0)中的 0 表示指向 RAM 区域的起 中的 始地址,可以灵活修改它。 始地址,可以灵活修改它。 DBYTE[x]中的 x 表示偏移量。 中的 表示偏移量。 下面是一段代码 1: : char a1,a2,a3,a4; #define DBYTE ((unsigned char volatile *) 0) void main(void){ long cc=0x89abcdef; a1=DBYTE[0x24]; a2=DBYTE[0x25]; a3=DBYTE[0x26]; a4=DBYTE[0x27];

while(1); } 2: : char a1,a2,a3,a4; #define DBYTE ((unsigned char volatile *) 0) void pp(char y){ a1=DBYTE[y++]; a2=DBYTE[y++]; a3=DBYTE[y++]; a4=DBYTE[y]; } void main(void){ long cc=0x89abcdef; char x; x=&cc; pp(x); while(1); } 3: : char a1,a2,a3,a4; #define DBYTE ((unsigned char volatile *) 0) void pp(char y){ a1=DBYTE[y++]; a2=DBYTE[y++]; a3=DBYTE[y++]; a4=DBYTE[y]; } void main(void){ bank1 static long cc=0x89abcdef; char x; x=&cc; pp(x); while(1);

} 7、PICC 关于 unsigned 和 signed 的几个关键问题! 、 的几个关键问题! unsigned 是表示一个变量 或常数) (或常数) 是无符号类型。 signed 表 是无符号类型。 示有符号。它们表示数值范围不一样。 示有符号。它们表示数值范围不一样。 PICC 默认所有变量都是 unsigned 类型的,哪怕你用了 类型的, signed 变量。因为有符号运算比无符号运算耗资源, 变量。因为有符号运算比无符号运算耗资源, 运算一般不涉及有符号运算。 而且 MCU 运算一般不涉及有符号运算。在 PICC 后面加上 -SIGNED_CHAR 后缀可以告诉 PICC 把 signed 变量当作有符号处理。 变量当作有符号处理。 默认的无符号运算下看这样的语句 符号运算下看这样的语句: 在 PICC 默认的无符号运算下看这样的语句: char i; for(i=7;i>=0;i--){ ; //中间语句 中间语句 } 代码看上去是没有丁点错误的,但编译后, 这样的 C 代码看上去是没有丁点错误的,但编译后,问题出现 了: movlw 7 movwf i loop //中间语句 中间语句 decf i //只是递减,没有判断语句!!! 只是递减, 只是递减 没有判断语句!!! goto loop 时候,条件还成立,还得循环一次, 原因是当 i 是 0 时候,条件还成立,还得循环一次,直到 i 成

条件才不成立。 负 1 条件才不成立。而 PICC 在默认参数下是 不能判断负数的,所以编译过程出现问题。 不能判断负数的,所以编译过程出现问题。那么采用这样的语 句来验证: 句来验证: char i; i=7; while(1){ i--; //中间语句 中间语句 if(i==0)break; //告诉 PICC 以判断 i 是否是 0 来作为条件 告诉 } 编译后代码正确: 编译后代码正确: movlw 7 movwf i loop //中间语句 中间语句 decfsz i //判断是否是 0 判断是否是 goto loop 再编译这样的语句:(同样循环 8 次) 再编译这样的语句:(同样循环 :( for(i=8;i>0;i--){ ; } movlw 8 movwf i loop decfsz i //同上编译的代码。 同上

编译的代码。 同上编译的代码 goto loop

本文来源:https://www.bwwdw.com/article/fzd1.html

Top