8086微机实验代码示例解释
程序员文章站
2024-02-02 15:29:28
...
1、8255的A口输入,B口输出,将输入的数据从B口输出,观察灯的显示是否与输入一致。
#include "conio.h"
#define M8255_A 0x640 //端口定义:将端口地址取一个别名
#define M8255_B 0x642
#define M8255_C 0x644
#define M8255_CON 0x646
#define Read8255(x) inportb(x) //这两个函数应该是模拟器自带的函数
#define Write8255(port,x) outportb(port, x) //用于输入和输出
void main(void)
{
unsigned char temp;
Write8255(M8255_CON, 0x90); //8255初始化
while(1)
{
temp = Read8255(M8255_A); //输入:捕获按键输入,赋值给temp
Write8255(M8255_B, temp); //输出:将从键盘中捕获的输入,给B端口输出到数码管上面
}
}
从第一份代码我们可以学到我们的按键是如何被代码捕获:
temp = Read8255(输入端口)
以及输出到显示器(至于为啥是显示器而不是别的,其实有点奇怪)
Write8255(输出端口,temp)
2、8255的A口,B口均为输出,使D0~D7从左向右循环亮一灯,使D8~D15从右向左循环亮一灯。
代码如下:
#include "conio.h"
#define M8255_A 0x640 //端口定义
#define M8255_B 0x642
#define M8255_C 0x644
#define M8255_CON 0x646
#define Read8255(x) inportb(x)
#define Write8255(port,x) outportb(port, x)
void delay(unsigned int i) //延迟函数,通过空循环来实现
{
unsigned int j;
for(j=0; j<i; j++);
}
void main(void)
{
unsigned char temp1, temp2;
Write8255(M8255_CON, 0x80); //8255初始化
temp1 = 0x80; //1000 0000
temp2 = 0x01; //0000 0001
while(1)
{
Write8255(M8255_A, temp1); //向A端口输出temp1
temp1 = (temp1>>1)|(temp1<<7);
Write8255(M8255_B, temp2); //输出
temp2 = (temp2<<1)|(temp2>>7);
delay(0xc000);
delay(0xc000);
}
}
经过测试,发现temp1变化如下:
10000000
01000000 01000000
00100000 00100000 00100000
00010000 00010000 00010000 00010000
10000000 10000000 10000000 10000000 1000
01000000 01000000 01000000 01000000 0100 00000100
根据规律如果只看末八位:
10000000
01000000
00100000
00010000
00001000
00000100
...
即从左向右动
再来看看temp2:
1 // 0000 0001
2 // 0000 0010
4 // 0000 0100
8 // 0000 1000
16 // 0001 0000
32 // 0010 0000
64 // 0100 0000
128 // 1000 0000
257 // 0000 0001 0000 0001
514 // 0000 0010 0000 0010
如果只看后八位,那么结果是:
0000 0001
0000 0010
0000 0100
0000 1000
0001 0000
0010 0000
0100 0000
1000 0000
0000 0001
0000 0010
...
即从右向左动
从这个代码中我们学到以下知识点:
2.1、灯从右向左动:
unsign char x;
x = 0x80;
while(1){
Write8255(输出端口,x);
x = (x<<1)|(x>>7);//左到右是(x>>1)|(x<<7)
delly(0xc000);//延迟
}
3、本实验使8255端口A工作在方式0并作为输出口,端口B工作在方式1并作为输入口
代码如下:
#include "conio.h"
#define M8255_A 0x600
#define M8255_B 0x602
#define M8255_C 0x604
#define M8255_CON 0x606
#define Rdata(port) inportb(port)
#define Wdata(port, x) outportb(port, x)
void interrupt mir7(void);
void main(void)
{
_asm{
PUSH DS
MOV AX, 0000H
MOV DS, AX //将0000H放到栈中(即将地址放入到栈中)
MOV AX, OFFSET mir7 //取中断入口地址
MOV SI, 003CH //中断矢量地址
MOV [SI], AX //填IRQ7的偏移矢量
MOV AX, CS //段地址
MOV SI, 003EH
MOV [SI], AX //填IRQ7的段地址矢量
CLI
POP DS
}
//初始化主片8259
//用于控制中断的
Wdata(0x20, 0x11); //1011 //ICW1:写入8259偶地址中(A0=0,在AT机中为20H/A0H)
Wdata(0x21, 0x08); //1000 //ICW2:写入8259奇地址中(A0=1,在AT机中为21H/A1H)
Wdata(0x21, 0x04); //ICW3:表示主8259上的IR3引脚上联有从8259
Wdata(0x21, 0x01); //ICW4:表示设置为中断自动结束方式
Wdata(0x21, 0x6f); //OCW1
/*
*ICW1是用来设定中断请求触发的方式的命令字。它的各位有如下意义:
D4=1:作为ICW1的标志。
D0(IC4)=1:指出初始化程序中将设置ICW4。
D1(SNGL):D1=0,系统中只有一片8259;D1=1,系统中有多片8259。
D2(ADI):在8086/8088系统中不起作用,设定为0。
D3(LTIM):D3=0,边沿触发;D3=1,电平触发。
*/
/*
*ICW2=08H,则IR0~IR7请求对应的中断类型码分别为:08H、09H、0AH、0BH、0CH、0DH、0EH、0FH。
*/
Wdata(M8255_CON, 0x86); //初始化8255
Wdata(M8255_CON, 0x05);
_asm sti
while(1);
}
//中断处理程序
void interrupt mir7(void) //中断程序用于输入
{ unsigned char temp;
temp = Rdata(M8255_B) ; //输入
Wdata(M8255_A, temp); //输出
outportb(0x20, 0x20); //清中断
}
从这一个代码中我们学到了如下知识点:
3.1、初始化8259
Wdata(0x20, 0x11); //1011 //ICW1:写入8259偶地址中(A0=0,在AT机中为20H/A0H)
Wdata(0x21, 0x08); //1000 //ICW2:写入8259奇地址中(A0=1,在AT机中为21H/A1H)
Wdata(0x21, 0x04); //ICW3:表示主8259上的IR3引脚上联有从8259
Wdata(0x21, 0x01); //ICW4:表示设置为中断自动结束方式
Wdata(0x21, 0x6f);
3.2、初始化8255
Wdata(M8255_CON, 0x86); //初始化8255 0101 0010
Wdata(M8255_CON, 0x05); // 0000 1001
4、AD采样,将采样值进行显示
代码:
#include "conio.h"
typedef unsigned int uint;
typedef unsigned char uchar;
#define Start (outportb(0x640, 0x00)) // 启动AD
#define Read (inportb(0x640)) // 读AD采样结果
void delay(uint count)
{
uint i;
for(i=0; i<count; i++);
}
// 显示多个字符
void dispm(uchar *src, uchar len)
{
uchar i;
for(i=0; i<len; i++)
{
_AL=*src;
_asm{
mov AH, 01h
int 10h
}
src++;
}
}
// 显示单个字符
void disps(uchar val)
{
_AL=val;
_asm{
mov ah, 01h
int 10h
}
}
// Hex转换为ASC码,转换低4位
uchar Hex2AscL(uchar val)
{
//uchar temp;
val &= 0x0f;
if(val>9)
val+=0x37;
else
val+=0x30;
return val;
}
// Hex转换为ASC码,转换高4位
uchar Hex2AscH(uchar val)
{
//uchar temp;
val=(val&0xf0)>>4;
if(val>9)
val+=0x37;
else
val+=0x30;
return val;
}
static uchar flag[]="DAC0809 IN0: ";
static uchar kg[]=" ";
void main()
{
while(1)
{
uchar ADval;
Start; // 启动AD采样
delay(0x100);
ADval=Read; // 读采样结果
dispm(flag, 13); // 显示: DAC0809 IN0:
disps(Hex2AscH(ADval)); // 显示高位
disps(Hex2AscL(ADval)); // 显示低位
dispm(kg, 8);
delay(0xf000);
delay(0xf000);
}
}
学到了:
4.1、十六进制数与ASC码的转换
uchar Hex2AscL(uchar val)
{
//uchar temp;
val &= 0x0f;
if(val>9)
val+=0x37;
else
val+=0x30;
return val;
}
// Hex转换为ASC码,转换高4位
uchar Hex2AscH(uchar val)
{
//uchar temp;
val=(val&0xf0)>>4;
if(val>9)
val+=0x37;
else
val+=0x30;
return val;
}
5、屏幕打印程序,通过功能调用将A–Z在显示屏上 显示。
代码:
void delay(unsigned int x)
{
unsigned int i;
for(i=0; i<x; i++);
}
void main(void)
{
unsigned char count;
for(count=0x41; count<0x5B; count++) //57~91即大写字母
{
_DL = count;
_asm{
mov AH, 02H //显示输出
int 21H //提示操作系统显示
}
delay(0xf000);
}
while(1);
}
学到了
5.1、如何调用系统程序(显示)
mov AH, 02H
int 21H
实际上,调用系统来做某一件事情的时候,可以调用int 21H,然后前面将指令放入到AH中即可。
6、功能描述: 使用8255完成键盘及数码管显示实验
- 8255的B口为数码管的段驱动,A口为列扫描及数码
- 管位驱动,C口为行扫描。
- 按下按键,该键对应值按顺序显示在数码管上。
代码:
#include <stdlib.h>
#include <conio.h>
void dis(void);
void clear(void);
void ccscan(void);
void putbuf(void);
void getkey(void);
void delay(int time);
int MY8255_A = 0x0600;
int MY8255_B = 0x0602;
int MY8255_C = 0x0604;
int MY8255_MODE = 0x0606;
// 数码管显示对应值代码
char a[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
char b[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int cc;
int b_n;//指示光标的位置
int n;
void main()
{
outp(MY8255_MODE, 0x81); //写8255控制字
b_n = 5;//光标默认在最高位,即第6位
while(1)
{
dis(); //显示子程序
clear(); //清屏子程序
ccscan(); //键盘扫描子程序
if (cc)
{
dis();
delay(0x5);
clear();
ccscan();
if (cc)//如果cc!=0即c口不为15
{
getkey();
}
}
}
}
// 确定按下按键具体值
void getkey(void)
{
int i;
int j = 0xfe; // 1111 1110
for(i=0;i<=3;i++)//枚举行
{
outp(MY8255_A, j);
if ( !( (inp(MY8255_C)) & 0x01) )//第1行
{
n = i + 0;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x02) )//第2行
{
n = i + 4;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x04) )//第3行
{
n = i + 8;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x08) )//第4行
{
n = i + 12;
putbuf();
return;
}
j <<= 1;
}
}
// 按键扫描
void ccscan(void)
{
outp(MY8255_A, 0x00);
cc = inp(MY8255_C);//获取输入的行
cc = (~cc) & 0x0F; //0000 1111
//1 14/2 13/3 12/4 11
//相当于是获取15-(x%16)
}
// 显示
void dis(void)
{
int i;
int j = 0xdf;//1101 1111
for(i=0;i<=5;i++)
{
outp(MY8255_A, 0xff);//
outp(MY8255_B, a[b[i]]);//枚举每一位的段驱动,显示数码管
outp(MY8255_A, j);
delay(0x5);
j = (j>>1)|(j<<7);
}
/*j:
1101 1111
1110 1111
1111 0111
1111 1011
1111 1101
1111 1110
*
*/
}
// 清屏
void clear(void)
{
outp(MY8255_B, 0x00);
}
void putbuf(void)
{
b[b_n] = n;//将当前光标处设置为按下的值
b_n--;//向右移动光标
if (b_n == -1)//如果当前的光标溢出了,调整光标的位置为最高位
{
b_n = 5;
}
dis();//展示刚才的东西
clear();//清空段驱动中的值
ccscan();
while (cc)
{
dis();
clear();
ccscan();
}
}
void delay(int time)
{
int i;
int j;
for(i=0;i<=time;i++)
{
for(j=0;j<=0x100;j++)
{ }
}
return;
}
从上面的代码中我们学到了:
1、获取输入
cc = inp(MY8255_C);//获取输入的行
cc = (~cc) & 0x0F; //0000 1111
2、获取值:
void getkey(void)
{
int i;
int j = 0xfe; // 1111 1110
for(i=0;i<=3;i++)//枚举行
{
outp(MY8255_A, j);
if ( !( (inp(MY8255_C)) & 0x01) )//第1行
{
n = i + 0;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x02) )//第2行
{
n = i + 4;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x04) )//第3行
{
n = i + 8;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x08) )//第4行
{
n = i + 12;
putbuf();
return;
}
j <<= 1;
}
}
7、输出文字
代码:
#include "conio.h"
#include "Chzdot.h" // 汉字字库
#define Row1(x) outportb(0x600, x) // 定义端口
#define Row2(x) outportb(0x640, x)
#define Col1(x) outportb(0x680, x)
#define Col2(x) outportb(0x6c0, x)
void Delay(void) // 延时子程序
{
unsigned int x;
for(x=0; x<50; x++);
}
void clear(void) // 清除子程序
{
Row1(0x00);
Row2(0x00);
Col1(0xff);
Col2(0xff);
}
void main(void)
{
unsigned char Scan=0x01, i, j;
unsigned int count=0;
clear();
while(1)
{
for(i=0; i<200; i++)
{
for(j=0; j<8; j++)
{
Row1(0x00);
Col1(~hzdot[count]); // 列0--7
Col2(~hzdot[count+1]); // 列8--15
count+=2;
Row1(Scan); // 行0--7
Scan = (Scan<<1)|(Scan>>7); // 行扫描
Delay();
}
Row1(0x00);
for(j=0; j<8; j++)
{
Row2(0x00);
Col1(~hzdot[count]);
Col2(~hzdot[count+1]);
count+=2;
Row2(Scan); // 行8--15
Scan = (Scan<<1)|(Scan>>7); // 行扫描
Delay();
}
Row2(0x00);
count-=32;
}
count+=2;
if(count==(352-32)) count=0;
}
}
8、使用8254进行乐曲演奏
代码:
#include "conio.h"
#define M8254_A 0x6c0
#define M8254_B 0x6c2
#define M8254_C 0x6c4
#define M8254_CON 0x6c6
#define Wdata(port, x) outportb(port, x)
typedef unsigned char uchar;
typedef unsigned int uint;
long Clk=0x0f4240; //输入频率为1MHz=F4240
//======频率表======
static uint freq_tab[]={371,495,495,495,624,556,495,556,624,
495,495,624,742,833,833,833,742,624,
624,495,556,495,556,624,495,416,416,371,
495,833,742,624,624,495,556,495,556,833,
742,624,624,742,833,990,742,624,624,495,
556,495,556,624,495,416,416,371,495,0};
//======时间表======
static uchar time_tab[]={4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 1, 3, 6, 2,
4, 4, 6, 2, 4, 4, 6, 2, 4, 4,
12, 4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 4, 6, 2, 4, 4,
6, 2, 4, 4, 6, 2, 4, 4, 12};
//======基本延时程序======
void delay(uint x)
{
uint i, j;
for(i=0; i<x; i++)
{
for(j=0; j<0x5000; j++);
}
}
uint Val_cnt;
int aaaa;
void main(void)
{
uchar count=0;
uchar tmpL, tmpH;
while(1)
{
Val_cnt = Clk/freq_tab[count];
tmpL = (uchar)(Val_cnt&0xff);
tmpH = (uchar)((Val_cnt>>8)&0xff);
Wdata(M8254_CON, 0x36); //定时器0、方式3
Wdata(M8254_A, tmpL);
Wdata(M8254_A, tmpH); //写计数值
time_tab[count]=time_tab[count]*3;
delay(time_tab[count]);
count++;
if(freq_tab[count]==0x00) count=0x00; //判断乐曲结束
}
}
仿造上面的程序,我写了一个组合程序。
#include <stdlib.h>
#include <conio.h>
void dis(void);
void clear(void);
void ccscan(void);
void putbuf(void);
void getkey(void);
void delay(int time);
int MY8255_A = 0x0600;
int MY8255_B = 0x0602;
int MY8255_C = 0x0604;
int MY8255_MODE = 0x0606;
#define M8254_A 0x6c0
#define M8254_B 0x6c2
#define M8254_C 0x6c4
#define M8254_CON 0x6c6
#define Row1(x) outportb(0x600, x) // 定义端口
#define Row2(x) outportb(0x640, x)
#define Col1(x) outportb(0x680, x)
#define Col2(x) outportb(0x6c0, x)
// 数码管显示对应值代码
char a[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
char b[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int cc;
int b_n;
int n;
//////////////////////////////////////////////////////////////////////////音乐
//======频率表======
#define Wdata(port, x) outportb(port, x)
typedef unsigned char uchar;
typedef unsigned int uint;
static uint freq_tab[]={371,495,495,495,624,556,495,556,624,
495,495,624,742,833,833,833,742,624,
624,495,556,495,556,624,495,416,416,371,
495,833,742,624,624,495,556,495,556,833,
742,624,624,742,833,990,742,624,624,495,
556,495,556,624,495,416,416,371,495,0};
//======时间表======
static uchar time_tab[]={4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 1, 3, 6, 2,
4, 4, 6, 2, 4, 4, 6, 2, 4, 4,
12, 4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 4, 6, 2, 4, 4,
6, 2, 4, 4, 6, 2, 4, 4, 12};
static uint freq_tabb[]={432,654,432,123,765,546,432,432,543,
341,543,435,654,535,876,423,987,542,
654,534,654,353,534,432,765,231,654,344,
423,964,654,862,643,734,654,142,543,432,
867,563,634,765,432,543,765,321,765,321,
556,495,556,624,495,416,416,371,495,0};
//======时间表======
static uchar time_tabb[]={4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 1, 3, 6, 2,
4, 4, 6, 2, 4, 4, 6, 2, 4, 4,
12, 4, 6, 2, 4, 4, 6, 2, 4, 4,
6, 2, 4, 4, 12, 4, 6, 2, 4, 4,
6, 2, 4, 4, 6, 2, 4, 4, 12};
long Clk=0x0f4240; //输入频率为1MHz=F4240
void delay1(uint x)
{
uint i, j;
for(i=0; i<x; i++)
{
for(j=0; j<0x5000; j++);
}
}
uint Val_cnt;
int aaaa;
///////////////////////A~Z
void delay2(unsigned int x)
{
unsigned int i;
for(i=0; i<x; i++);
}
void Delay3(void) // 延时子程序
{
unsigned int x;
for(x=0; x<50; x++);
}
void clear3(void) // 清除子程序
{
Row1(0x00);
Row2(0x00);
Col1(0xff);
Col2(0xff);
}
void printAZ(int k){
///打印出A~Z
uchar count=0;
count=0;
if(k==0){
for(count=0x41; count<0x44; count++)
{
_DL = count;
_asm{
mov AH, 02H
int 21H
}
delay2(0xf000);
}
}else if(k==1){
for(count=0x43; count>=0x41; count--)
{
_DL = count;
_asm{
mov AH, 02H
int 21H
}
delay2(0xf000);
}
}
}
void soundmusic(int k){
uchar count=0;
uchar tmpL, tmpH;
//Val_cnt=0;
if(k==0){
while(1)
{
Val_cnt = Clk/freq_tab[count];
tmpL = (uchar)(Val_cnt&0xff);
tmpH = (uchar)((Val_cnt>>8)&0xff);
Wdata(M8254_CON, 0x36); //定时器0、方式3
Wdata(M8254_A, tmpL);
Wdata(M8254_A, tmpH); //写计数值
//time_tab[count]=time_tab[count]*3;
delay1(time_tab[count]*3);
count++;
if(freq_tab[count]==0x00) break; //判断乐曲结束
}
}else{
while(1)
{
Val_cnt = Clk/freq_tabb[count];
tmpL = (uchar)(Val_cnt&0xff);
tmpH = (uchar)((Val_cnt>>8)&0xff);
Wdata(M8254_CON, 0x36); //定时器0、方式3
Wdata(M8254_A, tmpL);
Wdata(M8254_A, tmpH); //写计数值
//time_tabb[count]=time_tabb[count]*3;
delay1(time_tabb[count]*3);
count++;
if(freq_tabb[count]==0x00) break; //判断乐曲结束
}
}
}
void main()
{
//音乐
///////////////////////////按键
outp(MY8255_MODE, 0x81); //写8255控制字
b_n = 5;
while(1)
{
dis(); //显示子程序
clear(); //清屏子程序
ccscan(); //键盘扫描子程序
if (cc)
{
dis();
delay(0x5);
clear();
ccscan();
if (cc)
{
getkey();
}
}
}
}
// 确定按下按键具体值
void getkey(void)
{
int i;
int j = 0xfe;
for(i=0;i<=3;i++)
{
outp(MY8255_A, j);
if ( !( (inp(MY8255_C)) & 0x01) )
{
n = i + 0;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x02) )
{
n = i + 4;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x04) )
{
n = i + 8;
putbuf();
return;
}
if ( !( (inp(MY8255_C)) & 0x08) )
{
n = i + 12;
putbuf();
return;
}
j <<= 1;
}
}
// 按键扫描
void ccscan(void)
{
outp(MY8255_A, 0x00);
cc = inp(MY8255_C);
cc = (~cc) & 0x0F;
}
// 显示
void dis(void)
{
int i;
int j = 0xdf;
for(i=0;i<=5;i++)
{
outp(MY8255_A, 0xff);
outp(MY8255_B, a[b[i]]);
outp(MY8255_A, j);
delay(0x5);
j = (j>>1)|(j<<7);
}
}
// 清屏
void clear(void)
{
outp(MY8255_B, 0x00);
}
void putbuf(void)
{
if(n==0){
soundmusic(0);
printAZ(0);
}else if(n==1){
soundmusic(1);
printAZ(1);
}
b[b_n] = n;
b_n--;
if (b_n == -1)
{
b_n = 5;
}
dis();
clear();
ccscan();
while (cc)
{
dis();
clear();
ccscan();
}
}
void delay(int time)
{
int i;
int j;
for(i=0;i<=time;i++)
{
for(j=0;j<=0x100;j++)
{ }
}
return;
}
功能:
根据键盘输入音乐切换并打印出音乐的相关介绍
上一篇: 8254分频
下一篇: 微店如此垃圾 小马哥他造吗?