PICNIC

编程Tips

■编程Tips

0. 关于PIC16F877A版本的不匹配 ←NEW

1. 编程方面的资料

2. PICNIC固件的概要

3. 编程Tips

 

→提问 讨论用的BBS

←返回目录

2006年4月5日 15:44 更新

 


●关于PIC16F877A版本的不匹配(2004/4/29追記)


关于IT技术实习的PIC编程的一连串的不匹配,其原因和回避方法已经解明。在这里向大家介绍。希望各位同学仔细确认自己的PIC单片机的种类后,再进行实习。

<写入不良的原因>
TriState有限公司出品的PICNIC组件中配备的PIC单片机,从某时期开始由PIC16F877-20/P变为了 PIC16F877A-1/P。而借出的PIC烧写器 ver.3 (烧写软件 ver.2.9.5)不对应这个877A。

<回避方法>
有两种方法
(1)不使用877A,而使用扩充组件中附属的PIC16F877-20/P进行实习。
(2)从秋月电子通商购买把烧写器的ROM升级到ver.4的组件。

<总结>
按以下的组合可以正常的写入。
烧写软件ver2.9.5+烧写器ver.3+PIC16F877-20/P
烧写软件ver2.9.21(最新)+烧写器ver.4版本升级ROM+PIC16F877/877A


●编程方面的资料

Microchip PIC系列周边LSI数据表书籍 其他开发事业链接 FAQ


6.1.1 Microchip PIC系列

→从这里进入所有的资料链接

Microchip公司的主页

美国Microchip公司
http://www.microchip.com/

Microchip公司 日本分公司
http://www.microchip.co.jp/


6.1.2 周边LSI资料

→从这里进入所有的资料链接

Ethernet-NIC (Realtek RTL-8019AS)
Serial-EEPROM (ATMEL 93C46)
摄氏温度传感器 (NS LM35(DZ))
LCD显示面板 (SUNLIKE SC1602BSLB 日立HD44780)


6.1.3 书籍 其他开发事业链接 FAQ

→从这里进入所有的资料链接

书籍

PIC单片机活用手册(CQ出版社)
PIC单片机自动控制(CQ出版社)
Microchip公司参考书籍

其它开发事业链接 FAQ

Eric's PIC Page
PIC is fun
Microchip公司FAQ集
PIC FAQ的主页

●PICNIC固件的概要

内藏协议下载各模块概要关于固件的动作初始化设定的自动控制


6.2.1 内藏协议

在本PICNIC的固件中内藏着以下协议。

Ethernet帧收送信处理
ARP收信处理
IP收信处理
ICMP收信处理
UDP收信处理
TCP收信处理
HTTP收信处理


6.2.2 下载

请从下面的地址下载PICNIC ver.2用的对应MPASM汇编编译器的固件(ver1.2)

汇编语言源代码(v12.asm)

已编译的HEX文件(v12.hex) 列表文件(v12.lst) Error文件(v12.err)

※参考 PIC16F877用的包含的头文件(Include header file) P16F877.inc


6.2.3 各模块的概要

固件全体有7380行代码。
主要分成一下几大模块。

21~37行:各种常量的定义
42~53行:工作存储器区域
61~466行:面向协议栈的各种环境变量的定义

470行:程序开始处(ORG 0)
482~621行:中断例程(收信处理,溢出)
628~815行:各种功能函数(TCP帧编号的管理,错误恢复)

820~889行:主循环

902~1018行:以太帧(frame)接收处理(RTL8019AS缓冲区的管理)
1025~1068行:ARP协议处理
1172~1341行:IP协议处理(收信缓冲区的管理)
1349~1434行:ICMP协议处理
1441~1565行:UDP协议处理(并行端口的输出管理)
1571~1682行:DHCP(BOOTP)协议处理(IP地址的获得)
1690~2323行:TCP协议处理(仅含服务器功能,含有包(packet)输出例程)

2328~2393行:A/D变换的输入处理
2398~2500行:输入输出状态的前处理例程
2504~2558行:短信息的解析处理
2563~2764行:套接字(socket)的制作,检验和(check sum)的计算

2771~3032行:有关RTL8019AS的初始化
3037~3214行:检验和的计算,包送信的处理(NIC水准)
3221~3344行:以太帧,IP帧的作成

3352~3578行:UDP→并行I/O的控制,A/D变换功能
3584~3560行:UDP校验和的计算
3655~3873行:串行接口的控制

3879~4024行:套接字的管理
4030~4290行:以太网广播(broad cast)的送信处理

4295~4369行:有关EEPROM的数据写入
4555~4577行:EEPROM的初始化
6063~6130行:有关EEPROM的程序库
7266~7306行:设定EEPROM的默认值

4379~4548行:URL指定的CGI的解析处理
4583~4643行:根据URL参数变更并行I/O的输出值

4651~4919行:启动例程(有关投入电源时的设定)
4625~5036行:有关LCD显示的程序库
5042~5292行:各种运算函数的程序库

5297~5404行:引导模式 初始化例程
5411~5944行:有关串联终端间的操作指令的处理
7015~7054行:引导模式 终端的显示
7311~7374行:引导模式 指令解析

5952~6056行:当前的套接字状态的通知处理(现在未使用)

6139~6266行:在LCD面板上显示获得的IP地址的处理
6431~6697行:从DHCP服务器获得IP地址的处理

6273~6424行:串行端口→UDP送信处理
6703~7009行:UDP收信→串行端口的输出,LCD面板的显示

7061~7258行:存储Web浏览器HTML格式的应答信息 格式的区域


6.2.4 关于固件的动作

关于这之中已经安装好的各种协议栈,用户几乎不需修改。有关在web浏览器显示的控制用网页的修改,从7061行开始,需要修改的是存储HTML格式的应答信息及格式的部分。

由于以太帧长度的限制,无法一次传送全部的HTML格式的文本,故把它分割成了4块(从ESTAB0到ESTAB9共4次)送出。这些传送的处理由从1690行开始的TCP包处理序列来实现。

为了在以太网线断线,互联网不通,或各种协议的处理中陷入冻结状态时能够恢复,在时钟中断例程中,安装了自动复位功能。例如,在冻结大约15秒后强制恢复到TCP的LISTEN状态。

关于HTTP协议仅安装了GET方法部分。
作为简易HTTP服务器,根据来自web浏览器的要求,首先解析送来的URL中的参数(?之后),并把控制信息保存到PIC内部的EEPROM中,然后对各种外部接口进行控制。最后,把当前的PICNIC信息(上述的HTML文本)传送给浏览器。

<参考>
ハイパーテキストトランスファープロトコール(HTTP)第1.0版仕様書
TCPの接続状態遷移図


6.2.5 固件的初期设定的顺序

固件的启动例程(投入电源时的设定,4651行~)的设定顺序如下:

(1)把初期设定时所需数据(IP地址设定等)从内藏EEPROM中复制到RAM

(2)LCD液晶显示面板的初始化

(3)EthernetNIC(RTL8019AS) 的初始化,及把CONFIG寄存器的内容装入NIC

(4)从外置串行EEPROM93C46取得MAC地址

(5)把在(4)中取得的MAC地址写入NIC的PAR寄存器

(6)确认动作模式(通常or 引导)的跳线开关的状态。然后进入被选择模式的主例程

(7)如果有必要(IP=0.0.0.0的时候)从DHCP服务器获得IP地址,用广播发送DHCP协议的DISCOVER信息。

※在完成上面处理后,就进入帧信息的收信待机状态。

 

●编程Tips

1. 关于程序存储器的空间
2. 固件上的程序模块与占用程序存储器的对应关系
3. 关于EEPROM数据区域中当前的容纳值
4. 关于把默认値写回EEPROM的处理

5. 关于web控制页面信息的作成
6. "In", "Out", "High", "Low"等短信息的取得
7. 关于向DA(data ascii)中添加转义符(escape sequence)与字对齐(word alignment)
8. 填充指令要从字对齐后的字的高位开始

9. 关于室温传感器的显示(RA5)
10. 把现在设定的摄氏(℃),改为显示华氏(゜F)温度

11. 关于内部定时器&中断
12. 关于实时时钟 (RTC)

13. 关于CGI ?参数指令路径与向EEPROM中的写入
14. 关于从DHCP取得IP地址的处理(注意点)
15. 显示「送信包数」的结构

16. 关于v12.asm的内部定时器 中断处理
17. 关于ad_in模块的ADCON0控制寄存器的错误装配


6.3.1 关于程序存储器的空间

程序存储器的全体容量8192bit。
地址空间是0x0000--0x1FFF(13bit)。

v12.asm(ver1.2.0.0下,以下为汇编后机器语言使用存储器的情况。

PAGE#0 : 0x0000-0x07FF : 0x0000--0x07EA 使用中
PAGE#1 : 0x0800-0x0FFF : 0x0820--0x0FED 使用中
PAGE#2 : 0x1000-0x17FF : 0x1000--0x1240,0x1300--0x151C,0x1700--0x17FF 使用中
PAGE#3 : 0x1800-0x1FFF : 0x1800--0x190A,0x1A00--0x1B9D,0x1D00--0x1D9F
0x1F00--0x1FC1 使用中

可以看出已经没有什么空闲的空间。

所以要想追加别的功能,只有通过把以下已有的功能删除,以空闲出这部分存储器的空间,再把新功能的代码加入。

1441~1565行:UDP协议处理(并口输出管理)
1571~1682行:DHCP(BOOTP)协议处理(有关IP地址的取得)
3352~3578行:UDP→并行I/O控制,A/D变换功能
3584~3560行:UDP校验和的計算
3655~3873行:串口的控制

5297~5404行:引导模式 初始化例程
5411~5944行:串联终端的操作指令
7015~7054行:引导模式 终端显示
7311~7374行:引导模式 指令解析用数据

6431~6697行:从DHCP服务器取得IP地址的处理

6273~6424行:串口→UDP送信处理
6703~7009行:UDP收信→串口输出,LCD面板的显示


6.3.2 固件上的程序模块与占用程序存储器的对应关系(v1.2.0.0)

<程序存储器>

PAGE 1
程序开始 0x0000--0x07EA

PAGE 2
并行I/F的控制~ 0x0820--0x0FED
(0x0800到0x081F的32位未使用)

PAGE 3
串口收信数据送信 0x1000--0x1240
引导模式 0x1300--0x13FB

Web浏览器用信息#1 0x1400--0x151C
Web浏览器用信息#2 0x1700--0x190A 跨越 PAGE3-->4
Web浏览器用信息#3 0x1A00--0x1B9D
Web浏览器用信息#4 0x1D00--0x1D9F

※0x0300毎に表示領域を相対指定。db 0,0 (0000h)がブロック終端識別データ列。

default_values_begin:
EEPROM数据初始值 0x1F00--0x1FC1 IP地址等初始值

<EEPROM存储器>

EEPROM区域 0x2100--0x21FF (IP地址等初始值)

IP地址 0x2100 (192.168.0.200)
子网掩码 0x2104 (255.255.255.0)
默认网关 0x2108 (0.0.0.0)
固件版本 0x210C (1.2.0.1)
HTTP端口号 0x2110 (80d)
LCD端口号 0x2112 (10000d)
PARALLEL端口号 0x2114 (10001d)
SERIAL端口号 0x2116 (10002d)

以后为未使用区域 0x2118--0x21FF

 


6.3.3 关于EEPROM数据区域中的当前值

IP:192.168.0.200, PORT:80的时候EEPROM数据的例:(256字节 0x0000--0x00FF)

0000:C0 A8 00 C8 FF FF FF 00 00 00 00 00 01 02 00 01 ・・・・・・・・・・・・・・・・
0010:00 50 00 00 27 11 27 12 FF FF FF FF FF FF FF FF ・P・・'・'・・・・・・・・・

以后到0x00FF为止的值是0xFF。
现在,0x0000--0x0017使用中。用户从0x0020可以使用。

EE数据存储器的绝对地址为0x2100—0x21FF(绝对地址0x2100指向EE数据存储器的0x0000)。
例:程序中的ORG 0x2100语句后,用DE伪指令把上示的这些数据作为初始值写入ROM(MPASM负责对应)。


6.3.4 关于把默认値写回EEPROM的处理

把EEPROM返回到初始值时需要的值被放在0x1F00--0x1F17(18h字节)(§6.3.3参照)。

在本程序中,利用default_values_end - default_values_begin的减算取得这个区域的大小,然后把这个区域的内容用模块initial_values写回EEPROM区域。

实际写入EEPROM的作业是利用模块write_eeprom,一边操作EEADR, EEDATA等寄存器一边进行的。

<注意点>

从上面可以看出,(到目前为止)程序中的

区域 0x2100--0x2117(18h字节)和,default_values_begin:
EEPROM数据初始值 0x1F00--0x1F17(18h字节)

的地方的定数值需要一致。到目前为止,以下4项很重要。

IP地址 0x2100 (192.168.0.200)
子网掩码 0x2104 (255.255.255.0)
默认网关 0x2108 (0.0.0.0)
固件版本 0x210C (1.2.0.1)

如果不一致的话,用web控制页面中的按钮「default」来读取默认值值的时候,所读取得值会与ROM中的写入值不同。

(逆手にとって、複数コンテクストによるreconfigurableとするのも一興か?)

另外,关于HTTP_PORT, LCD_PORT, PARALLEL_PORT, SERIAL_PORT的各定数,因被定义在语句org 0x2100之前,故更改时只需更改那一个地方。


6.3.5 关于web控制页面的信息的做成

关于PICNIC web控制页面的做成,各寄存器体(寄存器体)的内容。

参考URL:
ASCII码表:
http://dbkun.cs.shinshu-u.ac.jp/cai/c2/text/e_ascii.html

MPASM Users Guide with MPLINK and MPLIB
page 59--60
microchip/33014g.pdf#page=59

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(1) I/O Ports的分配

RA0--RA3 模拟输入(RA0和RA1是端子台CN4)
RA5 室温传感器IC输入(换算成Celsius度并显示)
RB0--RB1 数字输入(RB0,RB1同时连接端子台CN4的接点输入)
RB2--RB5 LCD显示器专用(用户使用不可)
RB6--RB7 数字输出(RB6,RB7同时连接端子台CN3的接点输出)

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(2) 应答信息区域

从#1到#4分为4部分。

PAGE 3
Web浏览器用信息#1 0x1400--0x151C
Web浏览器用信息#2 0x1700--0x190A
Web浏览器用信息#3 0x1A00--0x1B9D
Web浏览器用信息#4 0x1D00--0x1D9F

每隔0x0300相对指定显示区。

以下为各寄存器体的内容。

#1:Content-type: text/html -- 到I/O RA5 In xx Celsius 的</TD></TR>
#2:RB0 In -- 到显示I/O Ports 的[Reload] 按钮的</FORM>
#3:网络设定 -- 到显示[Save][Default]按钮的</FORM>
#4:Status的显示 -- 到最后的</HTML>

DB 0,0 (0000h)是用来识别块尾的数据串。


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(3)关于各项内容的送信处理

job_ESTAB→以下的从00到99的各模式依次顺序过渡。

  goto ESTAB_00
  goto ESTAB_01
  goto ESTAB_02
  goto ESTAB_03
  goto ESTAB_99

ESTAB_00:
  TCP处理→http端口Establish的时候,
  URL中如果含有?字符,
  转到解析URL中的?参数的指令的模块。(转到→parse_cgi_tag)

  如果不含,
estab1:
  寄存器体地址设定为 LW=14h ,把BANK#1的信息用send_mes模块发送。

  MOVLW 014H ;寄存器体地址=14h fan
  MOVWF wk
  goto send_mes ; 发送信息

ESTAB_01:
  寄存器体地址设定为 LW=17h fan,把BANK#2的信息用send_mes模块发送。

ESTAB_02:
  寄存器体地址 LW=1Ah fan,发送BANK#3的信息

ESTAB_03:
  寄存器体地址 LW=1Dh fan,发送BANK#4的信息

ESTAB_99:
  送信完毕的处理:call send_fin+call inc_seq_no


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(4) 发送信息的send_mes模块

从2017行附近开始。

send_mes_head: 做成TCP协议头
send_mes0: 从寄存器体区域读取数据。
  Test
  如果读取的数据是0,0 即显示寄存器体读取完毕的指令,则结束(→send_mes9)

  或者如果数据是分支处理指示符时,则完成以下处理。

  Meta字符 && 0x7F : 以下为分支处理指示符:
    "$"(24h) : ctrl_code2
    "@"(40h) : ctrl_code3
    "%"(25h) : ctrl_code4
    " ̄"(3Fh) : ctrl_code5

  如果是上記以外的一般字符的时候,把低位的7位传送到RTL8019缓冲区。然后回到send_mes0循环。

send_mes9: 退出对数据的送信
send_mes_foot: 做成TCP尾:计算校验和
最后调用transmit模块送信!


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(5) 关于各分支处理指示符及与其对应的处理

ctrl_code5: SOCKET STATUS 显示套接字的状态
   ̄",从0ah到0eh,显示套接字#1到#5(现在被停止)
  →  call put_socket_stat

ctrl_code4: 显示TRIS的输入 输出方向
  "%",00h到04h,显示RA0到RA5的输入输出方向。
  "%",10h到17h,显示RB0到RB7的输入输出方向。

ctrl_code3: PARALLEL 当前的输入输出值 High/Low的显示
  '@",00h到04h,显示RA0到RA5的模拟输入值。
  '@",10h到17h,显示RB0到RB7的数字输入输出值。

ctrl_code2: META CHARACTER 控制字符的输出例程
  → call ctrl_code1

ctrl_code1: 显示网络的设定值。对嵌入信息中的$?进行替换
  从"$","0"到"B"显示以下的设定内容。

goto put_mac_address ; 0:MAC地址
goto put_ip_address ; 1:IP地址
goto put_netmask ; 2:子网掩码
goto put_gateway_address ; 3:网关
goto put_http_port ; 4:http端口
goto put_lcd_port ; 5:lcd端口
goto put_io_port ; 6:parallel端口
goto put_232_port ; 7:232c端口
goto put_version ; 8:版本

goto put_send_packet ; 9:发送包数
goto put_this_ip ; A:自IP地址的显示
goto put_ptop ; B:PtoP IP地址

ctrl_code: 到ctrl_code1的标签?


6.3.6 "In", "Out", "High", "Low"等短信息的取得

关于"In", "Out", "High", "Low"等短信息的取得,
是从信息寄存器体的0x1F00开始的

org 1F00h
getadtable:
mes_low DT " Low ",0
mes_high DT " High ",0
mes_in DT "In ",0
mes_out DT "Out",0

显示信息的模块
get_short_mes:

ctrl_out, ctrl_in, ctrl_high, ctrl_low 分别处理。
例」:mes_out & .255 ; 'OUT' (低位8bit的掩码)

实际上的写出是用put_short_mes10进行的。

<提示>因此,如果在信息寄存器体中追加字符串定义

mes_open DT " Open ",0
mes_close DT " Close ",0

,然后调用时用它们取代_low, _high 就可以显示出

输入:
RB0 : 门    开着的状态:Open 关着的状态:Close
RB1 : 外出中模式 在家:Open 外出:Close

输出:
RB6 : 门锁 没有被锁上:Open 被锁上的状态:Close

。(_low <--> _Open, _high <--> _Close间对应)


6.3.7 关于向DA(data ascii)中添加转义符(escape sequence)与字对齐(word alignment)

参考URL:

ASCII码表:
http://dbkun.cs.shinshu-u.ac.jp/cai/c2/text/e_ascii.html

MPASM Users Guide with MPLINK and MPLIB
page 59--60
microchip/33014g.pdf#page=59

Escape sequence code
page105--
microchip/33014g.pdf#page=105

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

※关于DB,DA的7位句读(Packed-8bit value)

PIC16F877中的1个字(word,一条指令)=14位。
字的范围是,0x0000 -- 0x3FFF。
(2进制为 00.0000.0000.0000b -- 11.1111.1111.1111b)

在这里,把1个字考虑成两个连着的7位宽的「字节」(Packed-8bit value)的话,后面要讲的DB,DA的存储结果就比较容易理解。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(1)关于DB(Declare Data of One Byte)

用DB(Declare Data of One Byte)记述定数时,PIC16F877的时候,把它的14位自高位起按照6位+8位的顺序排列存储。
也就是说,DB指示的第奇数个(6位),第偶数个(8位)可以存储的值的范围分别是,0x00--0x3F(111111)与0x00--0xFF(11111111)。

例1-1:如果强行指定为DB 0xFF, 0xFF时,会出现

Message[303]: Program word too large. Truncated to core size. (FFFF)

的警告。且生成的值实际为0x3FFF。


DB对应的值的个数为偶数个。如果指定的是奇数个,则低位的8位用0填充。

例1-2:如果指定的是DB 0x3F ,则生成0x3F00。


可以把生成的串(14bit)看成7位+7位的ASCII码。这和后述的DA有很大的关系。

例1-3: 如果指定的是DB 0x06,0x8A,则生成0x068A。
把它按7位+7位的位宽分割时,自0x06=0000.0110b, 0x8A=1000.1010b,可得出

0x068A = 00.0110.1000.1010b → [000.1101]:[000.1010]b → [0x0D]:[0x0A]

也就是说,生成出CR+LF这个文本文件的「改行」的代码。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(2)关于DA(Store Strings in Program Memory)

比如,DA "HTTP" 这个指示。"H","T","P"等非元字符(meta character)的ASCII码的范围为低位的7位。
由DA伪指令生成的串(14bit)可以看成是被按7位+7位的ASCII码存储下来的。

一般来讲,DA指令后的字符串的第奇数个,第偶数个的值分别为0xXX, 0xYY的时候,即

 0xXX = 0xxx.xxxxb, 0xYY = 0yyy.yyyyb

时,上面的[x6...x0]与[y6...y0]构成的串xx.xxxx.xyyy.yyyyb 被作为一个字的[值]存储下来。

例2-1:DA "HTTP" 的时候,因

 "H" = 0x48 → 0100.1000b
 "T" = 0x54 → 0101.0100b
 "P" = 0x50 → 0101.0000b

,故"HT", "TP"被分别作为一个字,按

 [100.1000]:[101.0100] → 10.0100.0101.0100b = 0x2454
 [101.0100]:[101.0000] → 10.1010.0101.0000b = 0x2A50

生成出来。


DA对应的值的个数为偶数个。如果指定的是奇数个,则低位的7位用0填充。

例2-2:如果被指定为DA "K"这样奇数个的时候,因

 "K" = 0x4B → 0100.1011b

,故按"K"+7位的0生成

 [100.1011]:[000.0000] → 10.0101.1000.0000b = 0x2580


例2-3:DA 的值串与DB同样,被按6+8位存储。比如说,下面的两个的存储结果是一样的。

 DB 0x3F, 0xFF
 DA 0x3FFF

两者都生成0x3FFF。


例2-4:DA 的字符串中可以填充ASCII码的转义符(escape sequence),以及¥x??形式的16进制数。

下面的两个的存储结果是一样的。

 DA "test¥r¥n"
 DA "test¥x0D¥x0A"

两者都生成0x39F4 0x068A

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(3)关于向DA中填充转义符及字对齐(word alignment)

关于这次的web应答信息的记述,假如要满足以下的3条件。

(a) 在发送的信息最后,填充上CR+LF或者LF这个「改行」码。
(b) 寄存器体的终结用DB 0,0 记述,且利用字对齐排除7位的清零(zero filling)副作用,正确的让它动作。
(c) 利用Packed-8bit的字符串,有效的利用存储器空间。


如果不考虑条件(c)存储器的使用效率问题,且关于(a)(b)无视字对齐,只考虑向DA中填充转义符的话,方法就比较简单。


例3-1:向DA中填充转义符

关于HTTP 的应答及头:

----------------------------------------------------------------------
HTTP/1.0 200 OK[CR+LF]
Connection: close[CR+LF]
Content-type: text/html[CR+LF]
[CR+LF]            ←与信息正文之间需要空行
<HTML><HEAD><TITLE>PIC Network Interface Card</TITLE></HEAD><BODY>[LF]
----------------------------------------------------------------------

在发送以上的「字符串」时,如果可以无视条件(c)的话,
只需利用转义符按[CR]->¥r, [LF]->¥n,把他们用DA填充进去就可以了。

----------------------------------------------------------------------
DA "HTTP/1.0 200 OK¥r¥n"
DA "Connection: close¥r¥n"
DA "Content-type: text/html¥r¥n"
DA "¥r¥n"
DA "<HTML><HEAD><TITLE>PIC Network Interface Card</TITLE></HEAD><BODY>¥n"
----------------------------------------------------------------------

但是这里存在一个问题。有些web浏览器(如Netscape7.1等)会把¥r¥n后面的垃圾(0x00)作为正式的码加以解释,这么一来信息的类型就被解释为非text/html型而引起错误,因此不能使用以上的方法。


例3-2:考虑字对齐情况下的向DA填充转义符的方法

再度考虑发送HTTP应答及头,适当的分割字符串以使DA后的字符串的个数都成为偶数个。例如下面。

----------------------------------------------------------------------
HTTP/1.0 200 OK[CR+LF]C  18字符
onnection: close[CR+LF]  18字符
Content-type: text/htm   22字符
l[CR+LF][CR+LF]< 6个字符
HTML><HEAD><TITLE>PIC Network Interface Card</TITLE></HEAD><BODY>[LF] 66个字符
----------------------------------------------------------------------

以下把它们分配给DA伪指令。

----------------------------------------------------------------------
DA "HTTP/1.0 200 OK¥r¥nC"
DA "onnection: close¥r¥n"
DA "Content-type: text/htm"
DA "l¥r¥n¥r¥n<"
DA "HTML><HEAD><TITLE>PIC Network Interface Card</TITLE></HEAD><BODY>¥n"
----------------------------------------------------------------------

在这种情况下会生成以下的字(word)串。
生成了从地址0x1441到地址0x1482-1的0x40个字。
与不考虑字对齐相比,节省了两个字!


1441 2454 2A50 17B1 DA "HTTP/1.0 200 OK¥r¥nC"
1730 1032 1830
104F 258D 0543
144A 37EE 3765 31F4 DA "onnection: close¥r¥n"
34EF 373A 1063
366F 39E5 068A
1453 21EF 3774 32EE DA "Content-type: text/htm"
3A2D 3A79 3865
1D20 3A65 3C74
17E8 3A6D
145E 360D 050D 053C DA "l¥r¥n¥r¥n<"
1461 2454 26CC 1F3C DA "HTML><HEAD><TITLE>PIC Network Interface Card</TITLE>....."
2445 20C4 1F3C
2A49 2A4C 22BE
2849 21A0 2765
3A77 37F2 35A0
24EE 3A65 3966
30E3 32A0 21E1
3964 1E2F 2A49
2A4C 22BE 1E2F
2445 20C4 1F3C
214F 2259 1F0A
1482 ....


例3-3:如果可以不考虑可读性的时候

上面的例子中,所有的字符串都是偶数个,所以如果不考虑可读性,可以用DA把所有的用""排成一串。

----------------------------------------------------------------------
DA "HTTP/1.0 200 OK¥r¥nConnection: close¥r¥nContent-type: text/html¥r¥n¥r¥n<HTML><HEAD><TITLE>PIC Network Interface Card</TITLE></HEAD><BODY>¥n"
----------------------------------------------------------------------

这个的结果和例3-2一样。

<问题>
一行中有没有最大字符数的制限?
在* .lst文件中,过长的代码的显示在途中会被省略。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(4)DB与DA的结合单单麻烦吗?

如下原版的汇编源代码文件(v12.asm)中的DB与DA结合,只是导致失去了可读性,并没什么可取之处。

----------------------------------------------------------------
DA "HTTP/1.0 200 O"
DB 25,8D ;'K',13
DB 05,43 ;10,C
DA "onnection: close"
DB 06,8A ;, .13, .10,
DA "Content-type: text/htm"
DB 36,0D,05,0D,05,3C ;'l', .13, .10, .13, .10, '<'
----------------------------------------------------------------


6.3.8 填充指令要从字对齐后的字的高位开始

填充指示(如$8 等),必须要从字对齐后的所在字的高位开始。

如果$*从对齐的字的低位开始的时候,会被解释为字符的"$"和"8",显示成"$8"。

例:(误)的情况,因为$的位置为 <空格>$8 这一对字对齐的低位,所以被显示为"$8"。一个个计算字符串很麻烦,所以可以按照(正)的样子分为两个DA指令就比较简便。

(正)
  DA "<H1>PIC Network Interface Card Version "
  DA "$8</H1>"

(误)
  DA "<H1>PIC Network Interface Card Version $8</H1>"


6.3.9 关于室温传感器的显示(RA5)

温度传感器IC(NS社製 LM35DZ)与RA5连接,使用PIC内部的A/D变换器用10位的2进制值来显示。

LM35的数据表:
http://www.national.com/JPN/ds/LM/LM35.pdf

数据表上也有记载,这个IC的输出为校正了的直线摄氏℃(温度系数 +10.0mV/℃)(非常方便)。

(1)把RA5的二进制值转换为「℃度」后的值,向web控制页面的信息中的填充

RA5的值的填充在从org 1400h 开始的寄存器体1的最后,按

;
;++++++++++++++ 室温传感器的输入 RA5(℃换算) ++++++++++++++++++
;
DA "<TR><TD>RA5 "
DB 12,84 ;%",0000_0100b,(输入方向)
DA " </TD>"
DA "<TD ALIGN=¥"right¥">"
DB 20,04 ;@",00000100b,(显示℃换算后的值)
DA " Celsius</TD></TR>"

来指定的。也就是说,用控制字符 "@"+0x04来进行填充。

用send_mes0模块进行数据输出时,"@"(40h):分支到ctrl_code3模块,然后模拟输入端口的时候,由ad_in:模块开始A/D变换的处理作业。

PIC16F877内藏的是0~+5V输入,10位分解能的 A/D变换器。这个温度传感IC的输出的变换结果被放入到变量[val1:val]中。

RA5の場合、上記の温度係数、R14(10KΩ)によるVs=GNDへの接続、A/D変換範囲(0~5V)ならびに分解能(10進数で 0 から 1023(≒1000))を勘案し、入力値として0~約1000が得られたら、リニアに摂氏0℃~50℃と表示できるように計算(5倍)を行っている。

详细内容请参照,v12.asm汇编源代码文件的从2325行附近开始的ad_in:模块。

(2)"DB 12,84"的意义

刚才6.3.7中已经说明,为了把HTML信息中的<输入方向>填充为"In" or "Out"字符串的指示符。

在字对齐后的字的高位中发现"%"的时候,信息输出的处理会被分支,根据其后的低位中指定的参数,执行对应的内容。在现程序中生成的是"%"+0x04这个信息。

<解说>

这次用MPASM汇编语言的DB是Declare Data of One Byte,所以

 DB 0x12, 0x84

这个指令,在1个字14位的PIC16F877之下,会生成

 0x1284

这个机器语言源代码。

把它用7位+7位分割开就成为0x12=0001.0010b, 0x84=1000.0100b,所以

 0x1284 = 00.010.0101:000.0100b → [010.0101]:[000.0100]b → [0x25]:[0x04]

也就是说,生成"%"+0x04这个信息。

详细,请参考§6.3.7

(3) "%"元字符的处理

※信息送信模块send_mes从2017行附近开始。

++++++++++++++++++++++++++++++++++++++++++++++++++++
send_mes_head: TCP头的做成
send_mes0: 从寄存器体区域中读取数据。
  如果读取的数据是 0,0 这个寄存器体终结指示,结束(→send_mes9へ)

  或者如果是元字符的时候,按以下对处理进行分支:

  元字符 && 0x7F : 分支指示符如下:
  "$"(24h) : ctrl_code2
  "@"(40h) : ctrl_code3
  "%"(25h) : ctrl_code4
  " ̄"(3Fh) : ctrl_code5
++++++++++++++++++++++++++++++++++++++++++++++++++++

这次,因为是"%",故转到ctrl_code4:模块,

++++++++++++++++++++++++++++++++++++++++++++++++++++
ctrl_code4: TRIS 显示TRIS的输入 输出方向
  "%"+00h到04h,显示RA0到RA5的输入输出方向。
  "%"+10h到17h,显示RB0到RB7的输入输出方向。
++++++++++++++++++++++++++++++++++++++++++++++++++++

在这里因为是"%"+0x04这个指令,故要进行RA5的输入输出方向的「显示」,也就是说
要跳到输出"In" or "Out"字符串的模块。(请参照§6.3.6

刚才也已经说明过,RA5是从温度传感器的「输入」,因此检查PIC的寄存器的输入输出方向时,其结果应为"In "。

<提示>

例:以下的语句是同效的。 DB 12,84 ⇔ DA "%¥x04" ,详细请参照§6.3.7


6.3.10 把现在设定的摄氏(℃),改为显示华氏(゜F)温度

有两种方法。

(1)取代現在的摄氏(℃)传感器,换上华氏(゜F)温度传感器

取代现在的摄氏度传感器NS LM35(DZ),换上华氏度传感器LM34(DZ),就可以了。

LM34的资料:
http://www.national.com/JPN/ds/LM/LM34.pdf

因为温度系数,LM35是+10.0mV/℃,LM34是+10.0mV/F ,所以从A/D变换的结果计算温度的部分也是一样的。

为了防止混淆,IC交换后,web显示的 "Celsius" 变为 "Fahrenheit" 比较好。

(2)追加摄氏(℃)→华氏(゜F)的变换公式

继续使用摄氏(℃)传感器,显示华氏温度的时候,把以下的摄氏(℃)→华氏(゜F)的变换公式追加到刚才讲的ad_in:模块中。

++++++++++++++++++++++++++++++++++++++++
※摄氏→华氏的换算公式
  (华氏温度) = 1.8 × (摄氏温度) + 32
++++++++++++++++++++++++++++++++++++++++

因为摄氏温度不是负值,所以计算出的华氏温度也不是负值,所以不用把负数考虑到里面去。

<提示>
因为PIC16F877只能进行整数运算,所以要计算 X 1.8必须通过几位的向左移位。关于这个固件中有面向16位二进制值的除运算的模块(divide16模块)。

※いい問題なので、受講生の皆様への練習のために残しておきますね :-)


6.3.11 关于内部定时器&中断

<参考URL>

PIC16F877资料(日本语)
microchip/30292a-j.pdf

PICmicro PIC16F87x MID-RANGE Complete Reference Manual:
microchip/33023a.pdf

Microchip公司有关资料:
http://www.microchip.co.jp/1999seminar058-094.pdf?page=25
http://www.microchip.co.jp/1999seminar150-198.pdf?page=8

(1)关于内部定时器

PIC16F877中从TMR0到TMR2搭载了3个内部定时器。

PIC16F877资料 DS30292A-J page47--
microchip/30292a-j.pdf#page=47

8bit定时器/计数器 搭载前分频器(pre-scaler)
TMR0 --> 溢出的时候,设置T0IF中断标志

16bit定时器/计数器
TMR1 --> [TMR1H]:[TMR1L]溢出的时候,设置T1IF中断标志

8bit定时器/计数器
TMR2 --> 溢出的时候,设置T2IF中断标志

※因为PICNIC现在的Fosc=20MHz(50nsec),所以Fosc/4 = 5MHz(200nsec)

++++++++++++++++++++++++++++++++++++++++++++++++++++++

内部定时器0 TMR0(8位)

Block图
PIC16F877资料 DS30292A-J page47 图5-1
microchip/30292a-j.pdf#page=47

关联寄存器
PIC16F877资料 DS30292A-J page48 表5-1
microchip/30292a-j.pdf#page=48

TMR0寄存器:BANK#1, 101h

PSout := !TOCS && PSA && (Fosc/4)

PSA位清零 --> 前分频器分配给定时器0
前分频值:PSA位==L --> 1:2 ~ 1:256

TMR0寄存器:0xFF --> 0x00过渡时(溢出),
发生TMR0中断。

中断标志是T0IF(INTCON:2)(事先需要把位设置为0)
中断屏蔽是T0IE(INTCON:5)

++++++++++++++++++++++++++++++++++++++++++++++++++++++

内部定时器1 TMR1(16bit)

Block图
PIC16F877资料 DS30292A-J page50 图6-3
microchip/30292a-j.pdf#page=50

T1CON 控制寄存器
PIC16F877资料 DS30292A-J page49 图6-1
microchip/30292a-j.pdf#page=49

关联寄存器
PIC16F877资料 DS30292A-J page51 表6-2
microchip/30292a-j.pdf#page=51


TMR1寄存器 [TMR1H]:[TMR1L]:BANK#0, TMR1L:0Eh, TMR1H0Fh

PSout := !TMR1CS && (Fosc/4) 或者是RC振荡器

前分频值:1:1 ~ 1:8

TMR1寄存器:0xFFFF --> 0x0000过渡时(溢出),
发生TMR1中断。

中断标志是TMR1IF(PIR1:0)(事先需要把位设置为0)
中断屏蔽是TMR1IE(PIE1:0)

++++++++++++++++++++++++++++++++++++++++++++++++++++++

内部定时器2 TMR2(8bit)

Block图
PIC16F877资料 DS30292A-J page54 图7-2
microchip/30292a-j.pdf#page=54

T2CON 控制寄存器
PIC16F877资料 DS30292A-J page53 图7-1
microchip/30292a-j.pdf#page=53

关联寄存器
PIC16F877资料 DS30292A-J page54 表7-1
microchip/30292a-j.pdf#page=54

TMR2寄存器:BANK#0, 11h

PSout := TMR2ON && (Fosc/4)

前分频值:1:1 1:4 1:16
后分频值(4位):1:1~1:16

PR2寄存器:内装与TMR2寄存器的比较器。复位时 FFh

TMR2寄存器:0x00 --> 与PR2一致的话 --> 后分频 -->
发生TMR2中断。(TMR2寄存器返回 0x00)

中断标志是TMR2IF(PIR1:1)(事先需要把位设置为0)
中断屏蔽是TMR2IE(PIE1:1)

++++++++++++++++++++++++++++++++++++++++++++++++++++++

(2)关于中断处理

PICmicro PIC16F87x MID-RANGE Complete Reference Manual page123--
microchip/33023a.pdf#page=123

PIC16F877资料 DS30292A-J page143--
microchip/30292a-j.pdf#page=143

中断因素逻辑
PIC16F877资料 DS30292A-J page144 图12-11
microchip/30292a-j.pdf#page=144

中断因素有14个。


全局中断屏蔽位:GIE
中断屏蔽:INTCON
周边设备中断屏蔽位:PEIE

周边设备中断标志:PIR1 PIR2
周边设备中断屏蔽:PIE1 PIE2

中断处理发生后,GIE位变为0,以后的中断处理变为禁止,返回地址被推送到堆栈中,PC被加载0004h(中断处理的例程) 。

中断处理通过检查中断标志位,能够了解到中断处理的原因。

中断处理后,在中断处理被再次允许之前,需要把中断标志位用软件清零。(如不清零会造成中断的死循环)

必须保存中断处理中的Context(STATUS,W,PCLATH寄存器)。
PIC16F877资料 DS30292A-J page145--
microchip/30292a-j.pdf#page=145

※关于具体的装配,请参考上记的资料。

 


6.3.12 关于实时时钟

(1)在单片机PIC16F877中没有内藏实时时钟(RTC)。需要连接外部实时时钟IC(串口连接等)才能取得时间数据。

例:RICOH超小型串口输入输出实时时钟组件
RICOH Rx5C348A/B
http://www.ricoh.co.jp/LSI/spec/rtc/5c348/5c348ab-j.pdf

(2)毫秒单位,以及微秒单位的wait模块已经装配到了固件中。

(3)不是本实习用的PICNIC固件,在别的工程有从经由网络取得NTP服务器的时间数据的程序。
http://park11.wakwak.com/ ̄domo/time.html

(4)如果用什么方法能够设定现在的时间,并且计算处理时程序的存储器有剩余空间,而且利用内部定时器的中断处理,能实现以秒为单位的计数时,这个向PIC中装配的「現在時刻」的软件从理论上可能可以实现。

※上记的(3)实际上是以100msec的内部定时器中断功能来计算的。

但是,现在的v12.asm固件要是实现現在的机能,程序存储器空间不够,只有减去一些不用的机能。

(5)关于(4)如果把电源关掉现在的时间就会丢失,需要另外想办法从外部设定时间。这样会很麻烦,最后结论证明还是(1)中的方法最为实用。


6.3.13 关于CGI ?参数指令路径与向EEPROM中的写入

当用web浏览器更改如网关等地址时,根据由URL中的?参量传递过来的值,将对EEPROM中的网关地址等值进行更改。

例:http://192.168.1.200:8080/submit.cgi?00b=192.168.1.200&04b=255.255.255.0&08b=0.0.0.0&10w=8080

中的08b=0.0.0.0 就为参数。(以字节单位写为0,0,0,0)

(1)参数指令及向EEPROM中的写入的概要

++++++++++++++++++++++++++++++++++++++++
<処理>
在estab0:中,是否发现 '?'字符?(=GET指令的处理)
MOVLW '?'
SUBWF data0,0
BTFSC 3,2
GOTO parse_cgi_tag
++++++++++++++++++++++++++++++++++++++++

URL中如果含有"?",其之后被认为是参数跳到解析模块parse_cgi_tag 中。这是寄存器体间移动用的标签,实际的处理是parse_cgi 模块。

以下为从4374行附近开始的parse_cgi的处理概要。

++++++++++++++++++++++++++++++++++++++++
parse_cgi
 读取下一个字符。
 如果字符串读取完毕则退出移动至→parse9_tag
 确认是否是I/O关联
 确认是否是字(word)单位
 确认是否是字节(byte)单位
 如果出现"&"则显示开始下一个参量,所以返回到模块的最初

parse_cgi0
 出现"&"时显示开始下一个参量,返回到模块的最初
 从头开始一一读取被用"."隔开的数值
 到读完为止,反复执行parse_cgi0来读取値

parse_cgi2
parse_cgi3
 把从"."分开的数值算出的数值写入EEPROM,
 然后为了检查下一个参量返回到模块的最初

parse9_tag
 寄存器体间移动用的标签。
 用goto parse9 进行TCP送信处理(与estab1程序地址相同)
++++++++++++++++++++++++++++++++++++++++

由上可知,如果收到了改变网关地址的指令,马上就会改写EEPROM中的对应值。

(2)IP地址与http端口号使用存储器上的变量的值

在当前的固件中,IP地址与http端口号是在复位后的启动时,从EEPROM读取并复制到数据存储器上的this_ip,http_port变量。

生成包头(Packet head)的时候,不是从EEPROM,而是使用这个数据存储器上保存的值。

由此可见,即使从web页面改变IP地址与http端口号,也只是改变了EEPROM上的值,需要重启后,作为this_ip,http_port读取为止才能反映到设定的内容。

除此以外的 子网掩码值,网关值,因为是从EEPROM上直接读取值,所以马上就会反映到设定内容上。

<提示>

关于子网掩码值,网关值,如从同一路由器内侧连接的局域网PC来访问时,即使把这些值改变,通常的HTTP应答时也不受影响(因属同一网段(segment))可以「继续」访问web控制页面。


6.3.14 关于使用DHCP取得IP地址的处理(注意点)

在使用说明书中,有「如把IP地址=0.0.0.0,在复位的时候就可以用DHCP取得IP地址」这个记述,但实际运用时有要注意的地方。

以下为现在知道的不合适的地方。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
从DHCP服务器得到的应答值中,只使用了IP地址值,没有使用子网掩码,
及dhcp_router_ip的取得值。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(1)用DHCP取得地址

<DHCP协议packet>

;               DHCP协议
;
dhcp_header SET udp_data
dhcp_ope SET udp_data+0
dhcp_type SET udp_data+1
dhcp_phylen SET udp_data+2
dhcp_hop SET udp_data+3
dhcp_trans SET udp_data+4
dhcp_sec SET udp_data+8
dhcp_dummy SET udp_data + .10
dhcp_client_ip SET udp_data + .12
dhcp_user_ip SET udp_data + .16
dhcp_user_ip1 SET udp_data + .17
dhcp_user_ip2 SET udp_data + .18
dhcp_user_ip3 SET udp_data + .19
dhcp_server_ip SET udp_data + .20
dhcp_server_ip1 SET udp_data + .21
dhcp_server_ip2 SET udp_data + .22
dhcp_server_ip3 SET udp_data + .23
dhcp_router_ip SET udp_data + .24

<用DHCP取得IP地址的处理概要>
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main:
 ; 自己的IP地址是0.0.0.0?
do_dhcp
 dhcp_done的标志
 call dhcp

dhcp:
bootp_tx:
 dhcp地址的取得要求UDP包的送信
bootp_res:
 检查面对自己的MAC地址的bootp应答
 如果应答返回来了,把取得的IP地址dhcp_user_ip存入this_ip

→在定时器中断例程中检查dhcp_done标志,并进行清零。

※在main0循环中,检查dhcp_done标志。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

综上所述,从DHCP服务器得到的应答值中,只使用了dhcpuseIP地址值,没有使用子网掩码,及dhcp_router_ip的取得值。

所以,子网掩码和网关一直是默认的
netmask=255.255.255.0
gateway=0.0.0.0
。这点需要注意。

(2)需要与路由器外部访问的时候

把需要用的子网掩码与网关值

  事先作为初始值写入EEPROM
  或者从路由器内部连接的PC,使用URL?参数来作设定。

(3)网关地址等是否也可以改为自动取得?

从理论上说可以通过修改固件,利用子网掩码,dhcp_router_ip的取得值,和Windows计算机一样自动的设定网关等地址。

像this_ip, http_port那样,把它们作为数据存储器上的变量存放。不管是采用固定IP,还是从DHCP服务器中取得,根据不同的情况改变netmask, gateway的值就可以了。

关于值的设定,利用dhcp服务器的应答值,把新机能追加到设定this_ip等的地方就可以了。

现在对固件改造困难的是,参照netmask, gateway值的所有地方,都是读取EEPROM的值,要把它们全部改变为读取数据存储器上的值才行。

※難しいですが、うまく安定動作したら、ver1.2.1.0として配布して喜ばれるかもしれませんね :-)

(4)关于DHCP协议的规格(BOOTP)

关于DHCP协议规格的详细,请全部从RFC中取得。
http://www.faqs.org/rfcs/

DHCP的草案标准是RFC2131。
RFC2131标准:
http://www.faqs.org/rfcs/rfc2131.html


历史上是先有的BOOTP,DHCP是它的扩张形式。

RFC 951(BOOTP)
RFC 1084(BOOTP Extensions)
RFC 1542(Clarifications and extensions for BOOTP)

RFC 2131 Dynamic Host Configuration Protocol, Draft Standard Protocol
RFC 2132 DHCP Options and BOOTP Vendor Extensions

<参考URL>

http://www.net.intap.or.jp/oiia/cont1/p0302.html%7B0recid=10342.html
http://www.net.intap.or.jp/oiia/cont1/p0302.html%7B0recid=10024.html


6.3.15 显示「发送包数」的结构

关于发送包数的显示,作为Sent Packets $9 进行填充处理。以下为具体的处理方法。

(1) send_mes模块的分支处理

send_mes0:模块内的,
; 高位7位如果是元字符进行分歧处理
MOVLW '$'
SUBWF data0,0
BTFSC 3,2
GOTO ctrl_code2
如对照正确,跳到ctrl_code2,然后再跳到ctrl_code1后对应 $?的值
(? -"0"(0x30))指定相应的模块
ctrl_code1
ADDWF 2,1
goto put_mac_address ; 0:MAC地址
goto put_ip_address ; 1:IP地址
goto put_netmask ; 2:子网掩码
goto put_gateway_address ; 3:网关
goto put_http_port ; 4:http端口
goto put_lcd_port ; 5:lcd端口
goto put_io_port ; 6:parallel端口
goto put_232_port ; 7:232c端口
goto put_version ; 8:版本的显示

goto put_send_packet ; 9:发送包数
goto put_this_ip ; A:自己IP的显示
goto put_ptop ; B:PtoP IP地址

※$9之后是'$'+0x3a(":")。(参照ASCII码表)

$9的情况下,调用put_send_packet:模块。这个模块的处理是,参照IP包序列号:ident,按

val1 <-- 序列号:ident的高位
val <-- 序列号:ident的低位
val2,val3 <-- 0

代入各变量后,32位->10进制变换:调用put_decimal32, 显示发送包数。

(2)2进制32位的10进制变换处理 : put_decimal32模块

2进制32位的10进制变换put_decimal32()模块,从要显示的2进制值所在的区域:(val3:val2:val1:val),把指定的4个字(word)(字节有効)的内容作为「参数」读取,32位2进制值变换为10进制数(val_m)显示出来。

以下为有关的变量所在存储器区域。

※环境设定用变量
ident EQU 0AEH ; IPパケットシーケンス番号
ident1 EQU 0AFH ;
※广域变量
val EQU 32H ; 第1字节
val1 EQU 33H ; 第2字节
val2 EQU 34H ; 第3字节
val3 EQU 35H ; 第4字节
val_m EQU 36H ; for DECIMAL
val_cn EQU 37H ; 除算的余?
※10进制变换作业区域
decimal_top EQU 120H ; 2进制→10进制变换用

 


6.3.16 关于v12.asm内部的定时器 中断处理

<概要>

  使用了内部定时器TMR1来结束DHCP的要求处理,。
  对套接字的超时的检查是用dec_tm进行的。它也用内部定时器TMR1。


(1)在例程startup:模块内,对定时器 中断处理进行初始设定

(a) option寄存器的设定
(4662行附近)


                bsf     STATUS,RP0
MOVLW B'10000110'
DB 00,62 ; option寄存器
......
bcf STATUS,RP0

BANK#1/3的转换
Wreg←B'10000110'(bit7,2,1をイネーブル)
机器语言代码 0x0062 => option寄存器(BANK#1, 81h)←Wreg

(参考)
option寄存器Wreg读取的机器语言代码(00.0000.0110.0010)
PIC16F87x Complete Reference Manual page554
microchip/33023a.pdf#page=554

※なぜOPTION_REGに86hをロードしているのか、謎。
(PIC16Cとの互換性云々とデータシートには書いてある。)


(b) USART的设定&中断处理允许
(4781行附近)


                bsf     STATUS,RP0
MOVLW B'10100000' ; 把rc的设定返回
MOVWF TRISC
MOVLW B'00100110'
MOVWF TXSTA ; 把ASYNC模块的送信侧初始化
MOVLW BAUD_RATE
MOVWF SPBRG ; ボーレート設定
BSF PIE1,5 ; rcie; 允许收信中断
bcf STATUS,RP0
MOVLW B'10000000'
MOVWF RCSTA ; 把ASYNC模块的收信侧初始化
bcf STATUS,RP0
bcf INTCON,6 ;peie ; 允许周边设备的中断

为了串行接口USART的收信处理的中断处理的设定。
中断控制寄存器的设定是,

PEIE(INTCON[bit6])
PIE[bit5] (RCIE: USART Receive Interrupt Enable bit)

(参考)
中断控制寄存器 INTCON, PIE
PIC16F87x Complete Reference Manual page127-- §8.2.1
microchip/33023a.pdf#page=127


(c) 把DHCP要求中标志清零,定时器TMR1的初始化
(4803行附近)


                bsf     STATUS,RP0
CLRF dhcp_done
bcf STATUS,RP0
CLRF PORTB ; 初始状态 RB=00h
CLRF TMR1L
CLRF TMR1H
MOVLW B'000101'
MOVWF T1CON

把DHCP要求中标志(dhcp_done)清零(BANK#1)
PORB 的所有位Low(BANK#0)

定时器TMR1 16位([TMR1H]:[TMR1L])初始化为0
TICON ← B'xx000101'
; T1CKPS1:T1CKPS0 := 0:0, !T1SYNC:=1, TMR1ON :=1

TMR1CS := 0,即使用内部时钟。

!TISYNC : Timer1 External Clock Input Synchronization Select bit
-- 1 = "Do not synchronize external clock input"

TMR1ON: Timer1 On bit
-- 1 = "Enables Timer1"

T1CKPS1:T1CKPS0: Timer1 Input Clock Prescale Select bits
-- 00 = 1:1 Prescale value(Fosc/4 count up)


从上面可以看出,因为PICNIC的現在的 Fosc=20MHz(50nsec),
Fosc/4 = 5MHz(200nsec)。故TMR1的0x0000-->0xFFFF的时间,即
到中断处理所需要的时间是,

200nsec x 65535 = 13,107,000nsec (約13.1msec)

(参考)
TMR1控制寄存器 T1CON
PIC16F87x Complete Reference Manual page183
microchip/33023a.pdf#page=183


(d) 允许定时器TMR1中断,允许周边设备中断,允许全局中断的处理
(4811行附近)


                BSF     STATUS,RP0
BSF PIE1,TMR1IE
BCF STATUS,RP0
BSF INTCON,PEIE
BSF INTCON,GIE
BSF INTCON,GIE ;gie; 允许全局中断
goto main

PIE1[TMR1IE] 设定:允许定时器TMR1中断
INTCON[PEIE] 设定:允许周边设备中断
INTCON[GIE] 设定:允许全局中断

※なぜGIEイネーブルを2回やっているのかは不明。

 

(2)关于main, main0

Start之后,跳到main,确认DHCP的条件分歧后,进入main0。在main0内按照

  检查从USART I/F的收信(receive232c)
  检查DHCP要求的结束※使用定时器TMR1进行超时管理
  检查Ethernet-NIC收信缓冲区的溢出over flow 。如发生把溢出进行空读
  检查从Ethernet的套接字的收信(get_packet)

的顺序处理。

各々へgotoした後は、このmain0のラベルへ再びgotoして戻ってくるように工夫されている。

 

(3)关于中断处理的流程

检查PIR1,TMR1IF,如发生TMR1中断时,跳到int_tmr1。

DHCP done 的处理:int_tmr1

TMR1的中断间隔 约13.1msec * 80h
= 1676.8msec(約1.68秒)的间隔发出DHCP取得要求
管理定时器用的存储器:dhcp_done(BANK#1, 0xB2)

超时套接字的检查:int_tmr2
管理定时器用的存储器:使用了timer, timer_cn(BANK#1, 0xB3, 0xB4)
管理超时的模块:dec_tm

→返回dec_tm9

 


6.3.17 关于ad_in模块的ADCON0控制寄存器的错误设定

  在ad_in模块中,为了控制A/D变换器应改变ADCON0寄存器的时候,在固件(v12.asm)中,看起来却在改变ADCON1寄存器的值。
  但是,由于没有把STATUS寄存器的RP0改为High,作为结果没导致错误还是对BANK#0的ADCON0寄存器进行了操作。但是这个部分应该把代码改为对ADCON0的操作,请留意。

ad_in模块
(2320行附近开始)


;		AD変換
;
ad_in
RLF getmes_wk1,1
RLF getmes_wk1,1
RLF getmes_wk1,1
MOVLW B'00111000'
ANDWF getmes_wk1,1
MOVLW B'10000001'
IORWF getmes_wk1,1
MOVF getmes_wk1,0 ;设定ADCON1的值
MOVWF ADCON1
MOVLW 1
MOVWF wait_cn
MOVLW HIGH (wait_ms)
MOVWF PCLATH
;
call wait_ms ; 采样时间20us待机機
MOVLW HIGH (ad_in)
MOVWF PCLATH
;
bsf ADCON1,2 ;AD変換开始
btfsc ADCON1,2 ; AD変換等待
goto $-1

上范围中的 ADCON1要全部改写成ADCON0。

信州大学インターネット大学院

wasaki@cs.shinshu-u.ac.jp
Copyright(c) 2005 Katsumi Wasaki. All rights reserved.