Sky's blog

闲着无聊随便写的微机代码

Word count: 4,205 / Reading time: 18 min
2017/06/22 Share

0809的多通道采集

题目描述:

1
利用0809设计一个多通道的数据采集卡,要求采样率为500HZ,用8253通道0查询的方式实现采样率的控制(8253A的通道0工作于方式2,当OUT输出变低时启动一次A/D转换,8253的CLK0输出为1MHZ。),采集1000个数据存入内存BUFFER开始的缓冲区。

首先进行计算:
500HZ的采样率,即2ms一次
又因为8253的CLK0的输入为1MHZ
因此
1MHZ/500HZ = 2000
所以通道0的初值为2000
因此程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
STACK SEGMENT
DB 50 DUP(?)
STACK ENDS

CODE SEGMENT
ASSUME CS:CODE,SS:STACK
START:
MOV DX,96H ;指向8253的控制口
MOV AL,00110101B ;通道0,先送低字节再送高字节,方式2,BCD码
OUT DX,AL ;初始化8253的通道0完成
MOV DX,90H ;给8253的通道0送初值2000
MOV AL,00H
OUT DX,AL
MOV AX,20H
OUT DX,AL ;8253的通道0初值赋值完毕
LEA DI,BUFFER ;DI指针指向BUFFER
MOV BX,03E8H ;给BX里存放1000

AGAIN PROC NEAR
MOV CX,0008H ;0809通道计数
MOV DX,80H ;DX指向0809的通道0
NEXT:
OUT DX,AL ;启动转换
PUSH DX ;保存当前0809通道地址
MOV DX,8AH ;指向EOC状态口
POLL:
IN AL,DX ;读EOC状态
TEST AL,01H ;测试EOC是否为0(启动开始没)
JNZ POLL ;EOC为1(不为0),继续检验
NO_END:
IN AL,DX ;EOC为0,启动后再次读EOC
TEST AL,01H ;检测EOC是否为0
JZ NO_END ;为0,表示转换未结束,继续等待
POP DX ;为1,转换结束,当前0809通道地址出栈
IN AL,DX ;读转换后数据
MOV [DI],AL ;将读取后的数据存放于DI所指向的地址单元
INC DX ;0809通道指向下一个
INC DI ;指针指向下一个地址单元
DEC BX ;BX减一,数据传输完毕1个
JZ OVER ;如果BX=0,则表示数据已经传输完毕,跳转到结束
LOOP NEXT ;如果BX不等于0,继续循环
JMP AHAIN ;BX不等于0,且一次8通道采集结束,则跳转到一下一个周期
HLT
OVER:
HLT
AGAIN ENDP
CODE ENDS
END START

0809的单通道采集

这题是上题的变种(我怎么感觉都差不多,就删了几句代码)
题目要求:(和上题一样,只是从多通道变成了单通道)

1
利用0809设计一个单通道的数据采集卡,要求采样率为500HZ,用8253通道0查询的方式实现采样率的控制(8253A的通道0工作于方式2,当OUT输出变低时启动一次A/D转换,8253的CLK0输出为1MHZ。),采集1000个数据存入内存BUFFER开始的缓冲区。

计算我就不算了,还是上题一样的2000初值
我就默认只用INO这一个通道了
程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CODE SEGMENT
ASSUME CS:CODE
START:
MOV DX,96H ;指向8253的控制口
MOV AL,00110101B ;通道0,先送低字节再送高字节,方式2,BCD码
OUT DX,AL ;初始化8253的通道0完成
MOV DX,90H ;给8253的通道0送初值2000
MOV AL,00H
OUT DX,AL
MOV AX,20H
OUT DX,AL ;8253的通道0初值赋值完毕
LEA DI,BUFFER ;DI指针指向BUFFER
MOV BX,03E8H ;给BX里存放1000

AGAIN PROC NEAR
MOV DX,80H ;DX指向0809的通道0
NEXT:
OUT DX,AL ;启动转换
MOV DX,8AH ;指向EOC状态口
POLL:
IN AL,DX ;读EOC状态
TEST AL,01H ;测试EOC是否为0(启动开始没)
JNZ POLL ;EOC为1(不为0),继续检验
NO_END:
IN AL,DX ;EOC为0,启动后再次读EOC
TEST AL,01H ;检测EOC是否为0
JZ NO_END ;为0,表示转换未结束,继续等待
MOV DX,80H ;指向0809的通道0
IN AL,DX ;读转换后数据
MOV [DI],AL ;将读取后的数据存放于DI所指向的地址单元
INC DI ;指针指向下一个地址单元
DEC BX ;BX减一,数据传输完毕1个
JZ OVER ;如果BX=0,则表示数据已经传输完毕,跳转到结束
JMP AHAIN ;BX不等于0,则跳转到下一次采集
HLT
OVER:
HLT
AGAIN ENDP
CODE ENDS
END START

0809的多通道采集(只循环采集一次)

当然鄙人认为,如果考到多通道,应该不会像第一个例子那样,我估计比较垃圾,不会用1000个数据的要求,只会让你跑一遍8通道即可
所以,又变种了一个题目
题目要求:(和第一题一样,只是从1000个数据变成了跑1遍8通道)

1
利用0809设计一个多通道的数据采集卡,要求采样率为500HZ,用8253通道0查询的方式实现采样率的控制(8253A的通道0工作于方式2,当OUT输出变低时启动一次A/D转换,8253的CLK0输出为1MHZ。),8个通道轮流采集一次,并把数据存入内存BUFFER开始的缓冲区。

题目没改,计算不变,还是2000初值
程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#make_COM#

; COM file is loaded at CS:0100h
ORG 100h

STACK SEGMENT
DB 50 DUP(?)
STACK ENDS

CODE SEGMENT
ASSUME CS:CODE,SS:STACK
START:
MOV DX,96H ;指向8253的控制口
MOV AL,00110101B ;通道0,先送低字节再送高字节,方式2,BCD码
OUT DX,AL ;初始化8253的通道0完成
MOV DX,90H ;给8253的通道0送初值2000
MOV AL,00H
OUT DX,AL
MOV AX,20H
OUT DX,AL ;8253的通道0初值赋值完毕
LEA DI,BUFFER ;DI指针指向BUFFER

AGAIN PROC NEAR
MOV CX,0008H ;0809通道计数
MOV DX,80H ;DX指向0809的通道0
NEXT:
OUT DX,AL ;启动转换
PUSH DX ;保存当前0809通道地址
MOV DX,8AH ;指向EOC状态口
POLL:
IN AL,DX ;读EOC状态
TEST AL,01H ;测试EOC是否为0(启动开始没)
JNZ POLL ;EOC为1(不为0),继续检验
NO_END:
IN AL,DX ;EOC为0,启动后再次读EOC
TEST AL,01H ;检测EOC是否为0
JZ NO_END ;为0,表示转换未结束,继续等待
POP DX ;为1,转换结束,当前0809通道地址出栈
IN AL,DX ;读转换后数据
MOV [DI],AL ;将读取后的数据存放于DI所指向的地址单元
INC DX ;0809通道指向下一个
INC DI ;指针指向下一个地址单元
LOOP NEXT ;如果BX不等于0,继续循环
HLT
AGAIN ENDP
CODE ENDS
END START

0832延时产生梯形波(双缓冲)

0832地址F0H,F1H
延时20ms的梯形波
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
CODE SEGMENT
ASSUME CS:CODE
START:
MOV DX,F0H ;指向0832的基地址
MOV AL,00H ;下限值
MOV BX,0FFH ;AL上升的时候是从0FF~下一次0FF这么多次,所以保持平衡的时候也要这么多次
MOV CX,16EAH ;为了延时20ms计的数
PING_LOW:
OUT DX,AL ;启动转换
INC DX ;双缓冲,另一个也启动转换
OUT DX,AL
MOV DX,F0H ;指回第一个转换点
INC BX ;增加一次BX
DELAYTIME: ;延时20ms
LOOP DELAYTIME
CMP BX,0FFH
JNZ PING_LOW ;BX没到0FF,继续保持平衡点
UP:
OUT DX,AL ;BX到OFF后,进行上升
INC DX ;双缓冲
OUT DX,AL
MOV DX,F0H
INC AL
MOV CX,16EAH
DELAYTIME:
LOOP DELAYTIME
CMP AL,0FFH ;AL是否达到最大值
JNZ UP
DEC AL ;最大值减1
MOV CX,16EAH
PING_HIGH:
OUT DX,AL
INC DX ;双缓冲
OUT DX,AL
MOV DX,F0H
INC BX
DELAYTIME:
LOOP DELAYTIME
CMP BX,0FFH
JNZ PING_HIGH
DOWN:
OUT DX,AL
INC DX
OUT DX,AL
MOV DX,F0H
DEC AL
MOV CX,16EAH
DELAYTIME:
LOOP DELAYTIME
CMP AL,00H ;AL是否达到最小值
JNZ DOWN
JMP START ;达到就进行新的周期
CODE ENDS
END START

0832延时产生矩形波(单缓冲)

写双缓冲有点麻烦,我就写单缓冲了
还有延时20ms我也不写了,直接CALL DELAYT好了……
程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CODE SEGMENT
ASSUME CS:CODE
START:
MOV AL,0FFH
MOV BX,0FFH
MOV DX,F0H
HIGH:
OUT DX,AL
INC BX
CALL DELAY
CMP BX,0FFH
JNZ HIGH
MOV AL,00H
LOW:
OUT DX,AL
INC BX
CALL DELAY
CMP BX,0FFH
JNZ LOW
MOV AL,0FFH
JMP HIGH
CODE ENDS
END START

8251的发送数据

题目描述:

1
某系统用8251A串行发送1000个位于数据段中BUFFER开始的内存单元中的数据,采用异步方式,1个起始位,8个数据位,奇校验,1个停止位,波特率因子为16,发送时钟频率为19200,该8251A的地址为3F0H,3F2H,已知延时宏DELAY_TIME为控制口的写恢复时间,问

1.8251的方式字和命令字各是什么?
2.发送1000个数据大约需要多长时间?
3.编写完整的发送程序实现所要求的任务
先是第一问,易得:
方式字:01011110B;命令字:00010001B
然后是第二问:
发送一个数据:1+8+1+1=11位
收发波特率=19200/16=1200位/秒
所以时间=11*1000/1200=9.167秒
最后是第三问编程
程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CODE SEGMENT
ASSUME CS:CODE
START:
MOV DX,3F2H ;8251的控制口
MOV AL,00H ;送第一次00
OUT DX,AL
CALL DELAY_TIME
OUT DX,AL ;送第二次00
CALL DELAY_TIME
OUT DX,AL ;送第三次00
CALL DELAY_TIME
MOV AL,40H ;送内部复位命令字,使8251回到方式选择格式
OUT DX,AL
CALL DELAY_TIME
MOV AL,01011110B ;送方式字,一个停止位,奇校验,字符长度为8,异步方式x16
OUT DX,AL
CALL DELAY_TIME
MOV AL,00010001B ;送命令字,错误标志位复位,并且允许发送
OUT DX,AL
CALL DELAY_TIME

LEA DI,BUFFER ;DI指针指向BUFFER首地址
MOV CX,1000 ;计数器,1000个数据
NEXT_T:
IN AL,DX ;读状态字
TEST AL,01H ;测试TxRDY是否为1,即发送器是否准备好
JZ NEXT_T ;没准备好,继续循环
MOV DX,3F0H ;准备好,指向8251的数据口
MOV AL,[DI] ;从BUFFER里取出数据
OUT DX,AL ;发送数据
INC DI ;指针向下移
MOV DX,3F2H ;再指向8251控制口
LOOP NEXT_T ;循环1000次
HLT ;发送结束
CODE ENDS
END START

8251的接受数据

题目描述:

1
某系统用8251A串行接收1000个位于数据段中BUFFER开始的内存单元中的数据,采用异步方式,1个起始位,7个数据位,奇校验位,1个停止位,波特率因子为16,波特率为2400,该8251A的地址为80H,81H,已知延时宏DELAY_TIME为控制口的写恢复时间,请编写完整的接收程序实现所要求的任务

和上一题差不多,不再赘述,直接上程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CODE SEGMENT
ASSUME CS:CODE
START:
MOV DX,81H ;8251的控制口
MOV AL,00H ;送第一次00
OUT DX,AL
CALL DELAY_TIME
OUT DX,AL ;送第二次00
CALL DELAY_TIME
OUT DX,AL ;送第三次00
CALL DELAY_TIME
MOV AL,40H ;送内部复位命令字,使8251回到方式选择格式
OUT DX,AL
CALL DELAY_TIME
MOV AL,01011010B ;送方式字,一个停止位,奇校验,字符长度为8,异步方式x16
OUT DX,AL
CALL DELAY_TIME
MOV AL,00010100B ;送命令字,错误标志位复位,并且允许接收
OUT DX,AL
CALL DELAY_TIME

LEA DI,BUFFER ;DI指针指向BUFFER首地址
MOV CX,1000 ;计数器,1000个数据
NEXT_T:
IN AL,DX ;读状态字
TEST AL,02H ;测试RxRDY是否为1,即接收器是否准备好
JZ NEXT_T ;没准备好,继续循环
TEST AL,38H ;准备好,检查是否有错
JNZ ERROR ;有错,转出错处理程序
MOV DX,80H ;无错,指向8251的数据口
IN AL,DX ;接受数据
MOV [DI],AL ;从BUFFER里取出数据
INC DI ;指针向下移
MOV DX,81H ;再指向8251控制口
LOOP NEXT_T ;循环1000次
HLT ;接受结束
ERROR:
HLT
CODE ENDS
END START

8253延时跑马灯的反思

其实写一个延时跑马灯没有什么难度(软延时)
但是连上了8253后好像就变得困难一些了(大神不要喷我)
我今天反思了一下8253的方式0,2,3,分别思考了一下他们的延时
(为什么不思考别的
额,因为本渣觉得他不考~~2333333333)

方式0

方式0的延时比较麻烦,可以求出相应的初值,然后每次都需要重新初始化一次,检测电平,结束后进行跑马灯,然后再重新赋初值

方式2

方式2是可以用于跑马灯或者其他场合延时的,只需要检验OUT的低电平即可,每一次低电平,就是一次延时结束,可以对跑马灯进行左移或者右移。
下面是我随便写的一个基于方式2的延时跑马灯(8255+8253)
我就不写级联了,就写一个初值5000的……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
CODE SEGMENT
ASSUMT CS:CODE
START:
MOV DX,8255_PORT_CTL
MOV AL,10010000B
OUT DX,AL
MOV AL,00000001B
MOV BL,AL

MOV AL,00100101B
MOV DX,8253_PORT_CTL
OUT DX,AL
MOV DX,8253_PORT_0
MOV AL,50H
OUT DX,AL
RUN:
MOV DX,8255_PORT_A
IN AL,DX
TEST AL,00000001B ;检查是不是低电平,即开始了没有
JNZ RUN ;不是低电平就等待
MOV AL,BL ;是低电平,则移位,让下一个灯亮
MOV DX,8255_PORT_B
OUT DX,AL
ROL AL,1
MOV BL,AL
OVER:
MOV DX,8255_PORT_A
IN AL,DX
TEST AL,00000001B ;检查是不是高电平,即延时结束了没有
JZ OVER ;不是高电平,则继续等待
JMP RUN ;延时结束,进行下一轮程序
CODE ENDS
END START

方式3

和上一个差不多,但是跑马灯程序位置不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
CODE SEGMENT
ASSUMT CS:CODE
START:
MOV DX,8255_PORT_CTL
MOV AL,10010000B
OUT DX,AL
MOV AL,00000001B
MOV BL,AL

MOV AL,00100101B
MOV DX,8253_PORT_CTL
OUT DX,AL
MOV DX,8253_PORT_0
MOV AL,50H
OUT DX,AL
RUN:
MOV DX,8255_PORT_A
IN AL,DX
TEST AL,00000001B ;检查是不是低电平,即开始了没有
JNZ RUN ;不是低电平就等待
OVER:
IN AL,DX
TEST AL,00000001B ;检查是不是高电平,即延时结束了没有
JZ OVER ;不是高电平,则继续等待
MOV AL,BL
MOV DX,8255_PORT_B
OUT DX,AL
ROL AL,1
MOV BL,AL
JMP RUN ;延时结束,进行下一轮程序
CODE ENDS
END START

原因分析

方式2大体上和方式3相差不大,但是,方式3产生的是方波,而方式2不是,所以不能用每次检测一个低电平,一个高电平后操作的方式,只能用检测低电平,然后控制8255的方法……大概就是这样,8253的方式问题,还是得看波形~(OUT的波形)

最后猜一个8253能考的题

单独拿来考的话:
1.要么是我之前说的跑马灯……不会的看上面
2.那就是LED灯一灭一亮的周期题了
这种题实在是简单无比……直接级联貌似就解决问题,如果周期小的话,就初始化一个就OK……书上有,不再赘述

8255键盘

8255可以说很常见了,可以单独考,可以和别的一起考
如果8255要单独命题,那么一定是键盘了
现在这个键盘考的也是骚,什么花式都有:
2X2,3X5,4X4……
真的是皮,不过万变不离其中:
总是B口高位是行,低位是列
A口管的就是行
检测有没有键按下,就往A口送0(因为B口共阳极,所以默认都是1)
所以一旦检测到B口的低位有0,就说明有按下的……
然后就是编码什么的了
我感觉懂了后键盘还是比较简单的
总的来说就这么几步:
1.8255初始化
2.检测有没有键按下
3.没有键按下,就开始等待键按下
4.有键按下后就消抖20ms
5.消抖后再检测有没有键按下
6.没有就跳回步骤3,有就进行编码比对
7.逐行给A口送0,检测B口低位,知道有0出现
8.和键盘table比对,比到之后即可。

后记

大概这篇文章算是写完了,陆陆续续,写了很久,今天6月25号了……离微机考试也不过1个星期了,祝我好运吧~~~233333

CATALOG
  1. 1. 0809的多通道采集
  2. 2. 0809的单通道采集
  3. 3. 0809的多通道采集(只循环采集一次)
  4. 4. 0832延时产生梯形波(双缓冲)
  5. 5. 0832延时产生矩形波(单缓冲)
  6. 6. 8251的发送数据
  7. 7. 8251的接受数据
  8. 8. 8253延时跑马灯的反思
    1. 8.1. 方式0
    2. 8.2. 方式2
    3. 8.3. 方式3
    4. 8.4. 原因分析
    5. 8.5. 最后猜一个8253能考的题
  9. 9. 8255键盘
  10. 10. 后记