PIC单片机如何从汇编转向PICC

一、如何从汇编转向picc
首先要求你要有c语言的基础。c代码的头文件一定要有#include,它是很多头文件的集合,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 equ 0x03;
都是把无聊的地址定义为大家公认的名字。
1、如何赋值?
如对tmr0 附值,汇编中:
movlw 200;
movwf tmr0;
当然得保证当前页面在0,不然会出错。
c语言:
tmr0=200;//无论在任何页面都不会出错。
可以看出来c是很直接了当的。并且最大好处是操作一个寄存器时候,不用考虑页面的问题。一切由
c自动完成。
2、如何位操作?
汇编中的位操作是很容易的。在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 中不需要考虑页面的问题。
3、内存分配问题
在汇编中定义一个内存是一件很小心的问题,要考虑太多的问题,稍微不注意就会出错。比如16 位的
运算等。用c 就不需要考虑太多。下面给个例子:
16 位的除法(c 代码):
int x=5000;
int y=1000;
int z=x/y;
而在汇编中则需要花太多精力。
给一个小的c 代码,用ra0 控制一个led 闪烁:
#include
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,这就实现了闪烁的功能。
二、浅谈picc 的位操作
由于pic 处理器对位操作是最高效的,所以把一些bool 变量放在一个内存的位中,既可以达到运算
速度快,又可以达到最大限度节省空间的目的。在c 中的位操作有多种选择。
*********************************************
如:char x;x=x|0b00001000;
char x;x=x & 0b11011111;
把上面的变成公式则是:
#define bitset(var,bitno)(var |=1《#define bitclr(var,bitno)(var &=~(1《则上面的操作就是:
char x;bitset(x,4);
char x;bitclr (x,5);
*************************************************
但上述的方法有缺点,就是对每一位的含义不直观,最好是能在代码中能直观看出每一位代表的意思,
这样就能提高编程效率,避免出错。如果我们想用x 的0-2 位分别表示温度、电压、电流的bool 值可以如下:
unsigned char x @ 0x20;
bit temperature@ (unsigned)&x*8+0;
bit voltage@ (unsigned)&x*8+1;
bit current@ (unsigned)&x*8+2;
这样定义后x的位就有一个形象化的名字,不再是枯燥的1、2、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 的巨大好处。
三、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 编译器,所以还是人脑才是第一的,掌握一些
经验对写出高效,简洁的代码是有好处的。
四、深入探讨picc之位操作
1、用位操作来做一些标志位,也就是bool变量.可以简单如下定义:
bit a,b,c;
picc会自动安排一个内存,并在此内存中自动安排一位来对应a,b,c.由于我们只是用它们来简单的
表示一些0,1信息,所以我们不需要详细的知道它们的地址\位究竟是多少,只管拿来就用好了。
2、要是需要用一个地址固定的变量来位操作,可以参照pic.h里面定义寄存器。
如:用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操作,也可以对b0--b7直接位操作.
但不好的是,此招在低档片子,如c5x系列上可能会出问题.
还有就是表达起来复杂,你不觉得输入代码累么?呵呵
3、这也是一些常用手法
#definetestbit(var, bit)((var) & (1 《《(bit)))
//测试某一位,可以做bool运算
#definesetbit(var, bit)((var) |= (1 《《 (bit)))//把某一位置1
#defineclrbit(var, bit)((var) &= ~(1 《《 (bit)))//把某一位清0
附上一段代码,可以用mplab调试观察
#i nclude “pic.h”
#definetestbit(var, bit)((var) & (1 《《(bit)))
#definesetbit(var, bit)((var) |= (1 《《 (bit)))
#defineclrbit(var, bit)((var) &= ~(1 《《 (bit)))
char a,b;
void main()

我国IPv6发展的情况用户使用体验较差,IPv6业务发展普及任重道远
电脑的汉字显示原理
国内传感器技术的主要发展方向
CREAL将融资用于开发光场技术 希望加入到VR头盔跟AR眼镜中去
百度CTO王海峰:知识增强大语言模型具备人工智能的四项基础能力
PIC单片机如何从汇编转向PICC
工业连接器企业柯耐特的发展战略
大数据管理确保教学质量
太阳能灯带 无频闪PWM调光升压恒流LED芯片FP7208:为照明系统注入新能量
全球IGBT市场成长迅速,年复合增长率为7%左右
一种凸型多模光波导及多模色散调控方法
防御式编程之断言assert的使用
基于优化的多传感器全局位姿估计框架
松下电器制造行业系统解决方案为业务现场流程带来革新
Fastrax发布最新全球卫星导航定位模块IT600
基于FPGA的可调FIR滤波器在实际通信系统中的实现方法设计
传奇芯片555的起源与应用
关于倒置双斜率ADC提高动态范围的相关设计
数字计算机的硬件组成详解
基于MCU的抽油机节能控制仪表与控制技术