加载中…
个人资料
潇洒走一回
潇洒走一回
  • 博客等级:
  • 博客积分:0
  • 博客访问:930
  • 关注人气:2
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
博文
(2020-07-05 22:34)
分类: 硬件

调试modbus  tcp 整理百度文库文档如下

《modbus-tcp-报文举例分析》

《MODBUSTCP通讯报文》

https://img-blog.csdn.net/20161211155853707?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGluZ3l1YW4xaGFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

关于Modbus 报文

Client request:

19 B2 00 00 00 06 06 03 00 27 00 02

上面是modbus客户端发出的报文内容,为modbus tcp/ip协议格式,其前面的六个字节为头字节( header handle);

19 B2 00 00 00 06

19 B2 两个字节是Client发出的检验信息,Sever端只是需要将这两个字节的内容copy以后再放到response的报文的相应位子就可以了

00 00 两个字节是表示tcp/ip 的协议的modbus的协议;

00 06 两个字节表示的是header handle后面还有多长的字节,即表示的是该字节以后的字节长度(lengch),可以看到在00 06后面还有 “06 03 00 27 00 02”六个字节,所以这两个字节表示的就是6;

 

PDU:

“06 03 00 27 00 02”

06 一个字节表示slave address;

03 为Fuction code ;

00 27 表示Client request的寄存器地址;

00 02 表示request 寄存器的长度;(寄存器个数)

 

Server response:

19 B2 00 00 00 07 06 03 04 00 00 00 00

Header handle : “ 19 B2 00 00 00 07”

19 B2 为Server返回的检验码,copy from Client request;

其它的表示与客户端的表示相同

 

PDU:

“ 06 03 04 00 00 00 00”

06 是Slave address,从地址数;

03是Function code ;

04表示了回复给Client request的需要读的寄存器的值的个数,这里因为主地址向从地址读了2个寄存器即2个16位的寄存器(2个字)所以这里为4个字节,因为2个字为4个字节,而04这个字节表示的就是从地址要回复给主地址的寄存器字节长度,

00 00 00 00 表示了4个字节的值

 

ModBusTcp与串行链路Modbus的数据域是一致的,具体数据域可以参考串行modbus。这里给出几个ModbusTcp的链路解析说明,辅助新人分析报文。

1、数据请求

97 76 00 00 00 06 04 04 00 7D 00 7D

 

示例

长度

说明

备注

Map报文头

0x97

1

事务处理标识符Hi

客户机发起,服务器复制,用于事务处理配对

0x96

1

事务处理标识符Lo

0x0000

2

协议标识符号

客户机发起,服务器复制

Modbus协议 = 0.

0x0006

2

长度

从本字节下一个到最后

 

0x04

1

单元标识符

客户机发起,服务器复制

串口链路或其他总线上远程终端标识

功能码

0x04

1

功能码,读寄存器

参考标准modbus协议

数据

0x007D

2

起始地址

 

0x 007D

2

寄存器数量

 

校验

 

 

2、数据请求回复

97 76 00 00 00 FD 04 04 FA AB 9E 41 18 7A E1 3F 94 7A E1 3F 94 0A 3D 3F 97 51 EC 3F 98 CC CD C0 6C 33 33 C0 E3 CC CD C0 EC EB 85 41 F1 D7 0A 41 E9 47 AE 41 ED EB 85 41 F1 19 9A 43 D0 E6 66 43 C9 4C CD 43 CF EB 85 41 F3 66 66 42 0F CC CD 41 C2 E6 66 44 0A 1E B8 41 FB A3 D7 42 0C CC CD 41 BC C0 00 44 0A B8 52 41 F6 5C 29 42 0F 47 AE 41 D1 C6 66 44 0A 00 00 00 00 C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0A 00 0A 00 0A 00 04 00 04 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F

 

示例

长度

说明

备注

Map报文头

0x97

1

事务处理标识符Hi

客户机发起,服务器复制,用于事务处理配对

0x96

1

事务处理标识符Lo

0x0000

2

协议标识符号

客户机发起,服务器复制

Modbus协议 = 0.

0x00FD

2

长度

从本字节下一个到最后

 

0x04

1

单元标识符

客户机发起,服务器复制

串口链路或其他总线上远程终端标识

功能码

0x04

1

功能码,读寄存器

参考标准modbus协议

数据

0x FA

1

字节个数

 

0x----

 

数据

校验

 

 

 

3、写多个寄存器

97 79 00 00 00 09 04 10 00 00 00 01 02 00 01

 

示例

长度

说明

备注

Map报文头

0x97

1

事务处理标识符Hi

客户机发起,服务器复制,用于事务处理配对

0x79

1

事务处理标识符Lo

0x0000

2

协议标识符号

客户机发起,服务器复制

Modbus协议 = 0.

0x0009

2

长度

从本字节下一个到最后

 

0x04

1

单元标识符

客户机发起,服务器复制

串口链路或其他总线上远程终端标识

功能码

0x10

1

功能码,读寄存器

参考标准modbus协议

数据

0x0000

2

起始地址

 

0x 0001

2

写寄存器数量

 

0x 02

1

写字节的个数

 

00 01

2

目标值

 

校验

 

 

 

4、写多个寄存器响应

97 79 00 00 00 06 04 10 00 00 00 01

 

示例

长度

说明

备注

Map报文头

0x97

1

事务处理标识符Hi

客户机发起,服务器复制,用于事务处理配对

0x79

1

事务处理标识符Lo

0x0000

2

协议标识符号

客户机发起,服务器复制

Modbus协议 = 0.

0x0006

2

长度

从本字节下一个到最后

 

0x04

1

单元标识符

客户机发起,服务器复制

串口链路或其他总线上远程终端标识

功能码

0x10

1

功能码,读寄存器

参考标准modbus协议

数据

0x0000

2

起始地址

 

0x 0001

2

寄存器个数

 

校验

 

 

阅读    收藏 
标签:

杂谈

分类: 硬件

PIC8位在PICC中的数据类型  

2010-07-18 17:49:26|  分类: 程序设计 |  标签: |字号 订阅

PIC8位MCU的内存都是8位字节结构的,所以PICC中对于数据的划分都是以8位为基础的。

在汇编中,我们对一个地址为0X20的内存附值:

movlw 255;

movwf 0x20;

但一个内存是8位结构,能表示最大的数是255。要是超过了会怎么样呢?

movlw 256;

movwf 0x20;

通过DEBUG后,可以看出0X20中的值不是256,而是0了。

这里可以看出PIC处理器计算过程是当计算结果超过内存能容纳的最大数(也就是所谓溢出),则自动抛弃最高位。

如下:

255         - 256

11111111-100000000

256的2进制有9位,于是PIC处理器就自动抛弃最高位1,剩下的8个0放入内存,于是就成了0了。这过程是由硬件自动完成的。

在PICC中象这样的概验比比都是。

如:char x;

for(x=255;x--;){;}

是完成255个循环。

要是for(x=256;x--;){;}

则是0个循环。

因为PICC中定义CHAR类型数据是8位。

再如:int x;

for(x=65537;x--){;}

则是只循环一次。因为PICC中INT数据是16位,占2个存储单位。

如上所述,在规划一个程序之前要考虑程序中可能涉及到的最大计算量来定义数据类型,不然将会发生溢出,导致数据混乱。

但也不可为了简便,全用长结构的类型来申明数据类型,不然会导致计算时间慢,空间占用多。

如X可能大于255,但不会大于65535,那么申明:unsigned int x;

如果X可能会是负数就一定要定义int x;

同上要是X可能大于65535,那么就得定义long x;

浮点数是一个很特殊的类型,不需要了解它的构造,只需要了解它是一个24位或32位的存储结构,是一个能表示小数的数据类型,浮点表示的数字范围极 大,所以在用浮点类型时候,不需要考虑是否溢出的问题。


用PIC写高效的位移操作  
  
  在许多模拟串行通信中需要用位移操作。

以1-W总线的读字节为例,原厂的代码是:

unsigned char read_byte(void)
{
 unsigned char i;
 unsigned char value = 0;
 for (i = 0; i < 8; i++)
 {
  if(read_bit()) value| = 0 x 01<<i;
  // reads byte in, one byte at a time and then
  // shifts it left
  delay(10); // wait for rest of timeslot
 }
 return(value);
}

虽然可以用,但编译后执行效率并不高效,这也是很多朋友认为C一定不能和汇编相比的认识提供了说法。

其实完全可以深入了解C和汇编之间的关系,写出非常高效的C代码,既有C的便利,又有汇编的效率。

首先对 for (i = 0; i < 8; i++)做手术,改成递减的形式:

for(i=8;i!=0;i--),因为CPU判断一个数是否是0(只需要一个指令),比判断一个数是多大来的快(需要3个指令)。

再对value| = 0 x 01<<i;做手术。

value| = 0 x 01<<i;其实是一个低水平的代码,效率低,DALLAS的工程师都是NO1,奇怪为什么会如此疏忽。

仔细研究C语言的位移操作,可以发现C总是先把标志位清0,然后再把此位移入字节中,也就是说,当前移动进字节的位一定是0。

那么,既然已经是0了,我们就只剩下一个步骤:判断总线状态是否是高来决定是否改写此位,而不需要判断总线是低的情况。

于是改写如下代码:

for(i=8;i!=0;i--){
  value>>=1;                       //先右移一位,value最高位一定是0
  if(read_bit())   value|=0x80;                       //判断总线状态,如果是高,就把value的最高位置1
 }

这样一来,整个代码变得极其高效,编译后根本就是汇编级的代码。

 

再举一个例子:

在采集信号方面,经常是连续采集N次,最后求其平均值。

一般的,无论是用汇编或C,在采集次数上都推荐用8,16,32、64、128、256等次数,因为这些数都比较特殊,对于MCU计算有很大好处。

我们以128次采样为例:注:sampling()为外部采样函数。

unsigned int total;

unsigned char i,val;

for(i=0;i<128;i++){

total+=sampling();

}

val=total/128;

以上代码是很多场合都可以看见的,但是效率并不怎么样,狂浪费资源。

结合C和汇编的关系,再加上一些技巧,就可以写出天壤之别的汇编级的C代码出来

首先分析128这个数是0B10000000,发现其第7位是1,其他低位全是0,那么就可以判断第7位的状态来判断是否到了128次采样次数。

在分析除以128的运算,上面的代码用了除法运算,浪费了N多资源,完全可以用右移的方法来代替之,

val=total/128等同于val=(unsigned char)(total>>7);

再观察下去:total>>7还可以变通成(total<<1)>>8,先左移动一位,再右移动8位,不就成 了右移7位了么?

可知道位移1,4,8的操作只需要一个指令哦。

有上面的概验了,就可以写出如下的代码:

unsigned int total;

unsigned char i=0

unsigned char val;

while(!(i&0x80)){                 //判断i第7位,只需要一个指令。

total+=sampling();

i++;

}

val=(unsigned char)((total<<1)>>8);                    //几个指令就代替了几十个指令的除法运算

 

哈哈,发现什么?代码量竟然可以减少一大半,运算速度可以提高几倍。

再回头,就可以理解为什么采样次数要用推荐的一些特殊值了。


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

我测试过,PICC也支持常数指针,并且也会自动分页,实在是一大喜事。

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

#define DBYTE ((unsigned char volatile *) 0)

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

#define CWORD ((unsigned int volatile *) 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);
}


初浅研究PIC之延时函数和循环体优化  
  
  很多朋友说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编译器会自动把增量循环化为减量循环。因为这是由处理器硬件特性决定的。

PIC并不是一个很智能的C编译器,所以还是人脑才是第一的,掌握一些经验对写出高效,简洁的代码是有好处的。


PIC的指令系统

PIC 8位单片机共有三个级别,有相对应的指令集。基本级PIC系列芯片共有指令33条,每条指令是12位字长;中级PIC系列芯片共有指令35 条,每条指令是14位字长;高级PIC系列芯片共有指令58条,每条指令是16位字长。其指令向下兼容。

  一、PIC汇编语言指令格式
   PIC系列微控制器汇编语言指令与MCS-51系列单片机汇编语言一样,每条汇编语言指令由4个部分组成,其书写格式如下:
  标号 操作码助 记符 操作数1,操作数2;注释
  指令格式说明如下:指令的4个部分之间由空格作隔离符,空格可以是1格或多格,以保证交叉汇编时,PC机能识 别指令。
  1&#57360;标号 与MCS-51系列单片机功能相同,标号代表指令的符号地址。在程序汇编时,已赋以指令存储器地址的具体数值。汇编语言中 采用符号地址(即标号)是便于查看、修改,尤其是便于指令转移地址的表示。标号是指令格式中的可选项,只有在被其它语句引用时才需派上标号。在无标号的情 况下,指令助记符前面必须保留一个或一个以上的空格再写指令助记符。指令助记符不能占用标号的位置,否则该助记符会被汇编程序作标号误处理。
   书写标号时,规定第一字符必须是字母或半角下划线“—”,它后面可以跟英文和数字字符、冒号(:)制符表等,并可任意组合。再有标号不能用操作码助记符和 寄存器的代号表示。标号也可以单独占一行。
  2&#57360;操作码助记符 该字段是指令的必选项。该项可以是指令助记符,也可以由伪指令及宏命令组成,其 作用是在交叉汇编时,“指令操作码助记符”与“操作码表”进行逐一比较,找出其相应的机器码一一代之。
  3&#57360;操作数 由操作数的数据值或以符号 表示的数据或地址值组成。若操作数有两个,则两个操作数之间用逗号(,)分开。当操作数是常数时,常数可以是二进制、八进制、十进制或十六进制数。还可以 是被定义过的标号、字符串和ASC&#8545;码等。具体表示时,规定在二进制数前冠以字母“B”,例如B10011100;八进制数前冠以字母“O”,例如 O257;十进制数前冠以字母“D”,例如D122;十六进制数前冠以“H”,例如H2F。在这里PIC 8位单片机默认进制是十六进制,在十六进制数之 前加上Ox,如H2F可以写成Ox2F。
  指令的操作数项也是可选项。
  PIC系列与MCS-51系列8位单片机一样,存在寻址方 法,即操作数的来源或去向问题。因PIC系列微控制器采用了精简指令集(RISC)结构体系,其寻址方式和指令都既少而又简单。其寻址方式根据操作数来源 的不同,可分为立即数寻址、直接寻址、寄存器间接寻址和位寻址四种。所以PIC系列单片机指令中的操作数常常出现有关寄存器符号。有关的寻址实例,均可在 本文的后面找到。
  4&#57360;注释 用来对程序作些说明,便于人们阅读程序。注释开始之前用分号(;)与其它部分相隔。当汇编程序检测到分号时,其后 面的字符不再处理。值得注意:在用到子程序时应说明程序的入口条件、出口条件以及该程序应完成的功能和作用。


  二、清零指令 (共4条)
  1&#57360;寄存器清零指令
  实例:CLRW;寄存器W被清零
  说明:该条指令很简单,其中W为PIC单片机的工作寄 存器,相当于MCS-51系列单片机中的累加器A,CLR是英语Clear的缩写字母。
  2&#57360;看门狗定时器清零指令。
  实 例:CLRWDT;看门狗定时器清零(若已赋值,同时清预分频器)
  说明:WDT是英语Watchdog Timer的缩写字母。CLR见上述 说明。注意该两条指令无操作数。
  3&#57360;寄存器f清零指令。指令格式:CLRF f
  实例:CLRF TMRO;对TMRO清零
   说明:在PIC系列8位单片机中,常用符号F(或f)代表片内的各种寄程器和F的序号地址。F取值按PIC系列不同型号而不同,一般为 Ox00~Ox1F/7F/FF。TMRO代表定时器/计数器TMRO,所以CLRF对寄程器清零,采用了直接寻址方式直接给出要访问的寄存器TMRO。
   4&#57360;位清零指令。指令格式 BCF f,b
  实例:BCF REG1,2;把寄存器REG1的D2位清零
  说明:BCF是英语 Bit Clear F的缩写。指令格式中的F,同上说明;符号b是表示PIC片内某个8位数据寄存器F的位号(或位地址),所以b的取值为0~7或 D0~D7。实例中REG是Register的缩写。实例中的2代表指令格式中的b=2即寄存器REG1的D2位。
  通过上述四条清零指令格式 和实例,可以说明,学习PIC系列8位单片机的指令时应首先了解指令的助记符意义(功能),再有就是它的表达方式。初学者没有必要死记指令,重要是理解和 实践。

  三、面向字节、常数与控制操作的指令

  1&#57360;传送立即数至工作寄存器W指令
  指令格 式:MOVLW k;k表示常数、立即数和标号
  说明:MOVLW是Move Literal to w的缩写
  实 例:MOVL 0x1E;常数30送W

  2&#57360;I/O口控制寄存器TRIS设置指令
  指令格式;TRIS f
  说 明;TRIS f是Load TRIS Register的缩写。其功能是把工作寄存器W的内容送入I/O口控制寄存器f。当W=0时,置对应I/O口为 输出;W=1,置I/O口为输入。


  实例:MOVLW 0x00 ;把00H送入W
     TRIS RA ;置 PIC RA口为输出
     MOVLW 0xFF ;把FFH送入W
     TRIS RB ;置PIC RB口为输入


   说明:这是PIC汇编语言中常用的几条指令,即设置某个I/O口(这里是RA口和RB口)为输入或输出的语句。可见,识读指令时,一应充分理解语句格式 的功能,二应前后联系阅读。


  3&#57360;W寄存器内容送寄存器f(W内容保持不变)指令
  指令格式:MOVWF f
   说明:MOVWF是Move W to f的缩写
  实例:MOVLW 0x0B;送0BH送W
     MOVWF 6 ;送W内容 到RB口
  说明:第一条指令0x0B(常数11)送工作寄存器W,第二条指令,把W内容常数11送到寄存器F6中,查表F6即为RB口,所以 PORT_B(B口)=0BH=D11


  4&#57360;寄存器f传送指令
  指令格式:MOVF f,d
  说 明:MOVF是Move f的缩写。F代表PIC中的某个寄存器。指令中的d规定:d=0时,f内容送W;d=1时,f内容送寄存器。
  实 例:MOVF 6,0 ;RB口内容送W
     MOVWF 8  ;RB口内容送f8
  说明:第一条指令中的6代表寄存器f=6, 查寄存器表f=6为RB口;0代表d=0,代表选择的目标为寄存器W。第二条指令中的8代表寄存器f=8。所以两条指令结果是把RB口的内容送f8。至于 f8内容是多少?还应在汇编语言开始时附加指令,这里从略。


  5&#57360;空操作指令
  指令格式:NOP
  说 明:NOP是英语No Operation的缩写。NOP无操作数,所以称为空操作。执行NOP指令只使程序计数器PC加1,所以占用一个机器周期。
   实例:MOVLW 0xOF   ;送OFH到W
     MOVWF PORT_B ;W内容写入B口
     NOP       ;空操作
     MOVF PORT_B,W ;读操作
  说明:该三条指令是一种对I/O口的B口连续操作的实例,其目的达到写入 B口的内容要读出时,应保证写、读之间有个稳定时间,因此加入了空操作指令NOP。


  6&#57360;无条件跳转指令
  指令格 式:GOTO k
  说明:执行该条指令时,将指令转移到指定的地址(跳转)。指令中的k,常与程序中的标号联系起来。
  实例:见第9 条指令中


  7&#57360;寄存器内容减1,结果为零的间跳指令
  指令格式:DECFSZ f,d
  说 明:DECFSZ是英语Decrement f,Skip of not 0的缩写。符号f,d代表的意义,前述已作说明。该条指令是指寄存器的内容减1 存入W(d=0)或f(d=1)中。若指令执行结果减1不为零,指令顺序执行;为零时,就间跳下一条指令后再执行(等效顺序执行一条空指令NOP),实际 指令中,当d=1时,该项常被略去。


  8&#57360;寄存器内容加1,结果为零间跳指令
  指令格式:INCFSZ f,d
   说明:INCFSZ是英语Increment f,Skip of 0的缩写。该条指令与上一条(7)指令差别仅在于“1”上,即执行这条指令时,寄存 器f内容加1,若结果不为零,则指令顺序执行;为零则指令间跳执行。执行这条指令的其它逻辑关系与上条相同。


  9&#57360;子程序返回 指令
  指令格式:RETLW k
  说明:RETLW是Return Literal to W的缩写。该指令代表子程序返回,返回前 先把8位立即数送W。


  实例:PIC某个汇编语言的延时子程序(摘要):
      (1)BELY MOVLW 0xC5 ;送延时常数0C5H入W
     (2)   MOVWF COUNT2;0C5H送入计数器2
      (3)   CLRF COUNT1;对计数器1清零
     (4)LOOP INCFSZ COUNT1;计数器1加1计数器1加
             1结果不为零,跳转循环
     (5)   GOTO LOOP ;
     (6)    DECTSZ CPUNT2 ;计数2减1计数器2减1
            结果不为零,跳转循环重
            复执 行第4条指令
     (7)   GOTO LOOP ;
     (8)   RETLW 0   ;子程序执行结束返回


   说明:程序中的注释已分别对每条指令的功能作了说明,补充说明1&#57360;当执行第(4)条加1指令结果为零时,就间跳转到执行第(6)条指令。2&#57360;当执行第 (6)条减1指令结果为零时,就间跳转到第(8)条子程序返回,整个延时指令才算完成。3&#57360;计数器1或2代表PIC中某个寄存器,该寄存器由程序开始的伪 指令赋值决定(关于伪指令今后将作专门介绍)。 


  
 本文关于指令的注释将与前述指令中的略有不同。前述指令注释时是 对指令具体完成的功能给以说明,这种注释方法对初学者确实易于接受和理解,但是实际应用中的PIC产品汇编语言的注释通常是以程序要做什么(或指令的作 用)而不是说指令的直接功能。鉴于上述原因,下述的指令注释将改变过去的注释方法,用程序应起的作用作注释。


  10&#57360;寄程器半 字节交换指令
  指令格式:SWAPF f,d
  说明:SWAPF是Swap f的合写。符号f、d的意义与前述的相同。该条指令的功 能是寄存器f的高4位与低4位交换,即指令执行前,若寄存器f的8位状态为D7、D6、D5、D4、D3、D2、D1、D0,执行后的8位状态变为D3、 D2、D1、D0、D7、D6、D5、D4,其结果存入W(d=0)或f(d=1)中。
  实例:中断现场保护是中断技术中重要部分。由于 PIC16C××指令系统中没有进栈PUSH和出栈POP指令,所以只能用其它指令来实现。因为在主程序中常常用到工作寄存器W和状态寄存器 STATUS,所以中断现场保护常要保护寄存器W和STATUS。


  下面是对PIC16C7×系列芯片中断现场保护的实例程 序。
MOVWF W_TEMP   ;将W内容存入到临时寄存器
             W_TEMP中
SWAPF STATUS,W   ;交换STATUS与W内容
MOVWF STATUS_TEMP ;将STATUS的内容存入到临
…           时寄存器 STATUS_TEMP中
中断服务程序

SWAPF STATUS_TEMP,W;交换STATUS_TEMP与W
             的内容
MOVWF STATUS    ;STATUS复原成原来的状态
SWAPF W_TEMP,F   ;交换 内容
SWAPF W_TEMP,W  ;W复原成原来的状态
  说明:上述程序中各条指令的注释基本上都是以程序应达到的目的而注释的, 对每条指令的功能几乎未涉及。这是初学者应特别注意的。


  11&#57360;子程序调用指令(Subroutine Call)
   指令格式:CALL k;k为立即地址
  说明:子程序调用,不同型号芯片的实现方法不尽相同,其共同点是首先将返回地址((PC)+1)压栈 保护,再转入所调用的子程序入口地址执行(与MCS-51指令功能相似)。
指令格式模式:HERE CALL DELAY;调用延时子程序
         …
       DELAY MOVLW 0x80 ;延时子程序
          RETLW 0
  说明: 调用指令执行前,PC=地址HERE
  调用指令执行后,PC=地址DELAY(标号),堆栈指针TOS=HERE+1(返回地址)。
   实例:见下条指令的实例


  12&#57360;寄存器内容取反指令
  指令格式:COMF f,d
  说明:COMF是 Complement f的缩写。其中d=1时,操作(f)→f;d=0时,操作(f)→w。
  功能:寄存器f内容取反后送入W(d=0)或f 自身(d=1)。
  实例:  ORG   0x1FF
       GOTO MAIN
       ORG   0
    DELAY …
   MAIN MOVLW 0 ;主程序开始
       TRTS 5 ;设置RA口为输出
     BCF 5,0  ;置RA口0位为0
   LOOP   CALL DELAY;闪动延时
       COMF 5&#57360; ;RA口求 反(亮—灭—
               亮……控制)
       GOTO LOOP ;循环
       …
   说明:上述指令是一种PIC16C54 LED发光控制实验部分程序。其中延时子程序DELY未列出,但不影响本条指令的识读。程序中的主程序开始的三 条指令,均已介绍过,紧跟着的CALL指令是调用执行子程序,其入口地址为标号DELAY。子程序执行结束后,又执行COMF 5的LED发光亮—灭…亮 —灭……控制指令。后面一条GOTO LOOP指令是达到LED循环点亮目的。


  13&#57360;面向位的操作指令(共4条,PIC高级 产品多增一条)
  该类指令除一条位清零外,另有一条寄存器f位b的置1指令和另外两条位跳步指令(PIC高级产品多增一条f的b位触发转换指 令)。
  (1)位置1指令。指令格式 BSF f,b
  说明:BSF是Bit Set f的缩写。F和b的意义与前述相同,该条指令 的功能是将寄存器f的b位置1。
  (2)位测试、为零间跳指令。指令格式 BTFSC f,b
  说明:BTFSC是 Bit Test,Skip if Clear的缩写。指令功能是测试寄存器f位“b”,如为0,跳过下一条指令;为1顺序执行,即当f(b)=0时,就 不执行当前指令而执行下一条指令(间跳),即用一条空指令NOP代替它,所以该条指令占用2个指令周期。
  (3)位测试、为1间跳指令。指令格 式 BTFSS f,b
  说明:BTFSS是Bit Test,Skip if Set的缩写。其指令的逻辑功能与上条相反,位测试 f(b)=1就间跳执行,f(b)=0顺序执行。
  上面介绍的PIC 8位单片机汇编语言指令仅是部分指令,此外还有循环左、右移指令;W和寄 存器f相“加”、相“与”指令和进入睡眠方式等指令。鉴于报纸版面的限制,不在这里一一介绍,今后将在程序的应用试验中再作补充说明。

阅读    收藏 
标签:

杂谈

分类: 硬件

我们需要些东西提供已知的电压。首先想到的是Vbe。中等电流密度下的Vbe约0.65V,但它随温度而变化,在室温环境中每升高1&#8451;电压会下降2mV。因此我们需要相对于温度变化不大的参数(更低的温度系数)。必须好好想一想。

还记得“delta Vbe”(ΔVbe)吗?双极结点有一个奇怪的特性:电压随温度升高而下降,但具有不同电流的两个结点间的电压(ΔVbe)却会上升。事实上,这个差异直接正比于绝对温度。

在室温(300K)时,温度变化1度,ΔVbe变化1/300。这表明ΔVbe相对较小,因此必须对它们进行放大。如果将ΔVbe提高到600mV,那么上述1/300变化将转换为2mV/&#8451;!(Vbe的温度效应是幅度相等,方向相反)。

两者相加,可以得到650mV+600mV(室温下),即1.25V。由于两个变化都与温度变化方向相反,因此总和维持不变。这样,1.25V刚好可以成为带隙基准的‘魔术电压’。

阅读    收藏 
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有