单片机复习


编程题目:

1.AD/DA

a.锯齿波

		ORG		2000H
START:
		MOV		R0,#0FEH
		MOV		A,#00H
LOOP:
		MOVX	@R0,A			;数据递增
		INC		A
		SJMP	LOOP

b.三角波

		ORG		2000H
START:
		MOV		R0,#0FEH
		MOV		A,#00H
		MOVX	@R0,A
UP:		
		MOVX	@R0,A		;数据递增
		INC		A
		JNZ		UP
DOWN:
		DEC		A			;;数据递减
		MOVX	@R0,A
		JNZ		DOWN
		SJMP	UP

c.矩形波

		ORG		2000H
START:
		MOV		R0,#0FEH		;;FE为**P0**口的地址
LOOP:
		MOV		A,#data1	;;第一个电平
		MOVX	@R0,A
		LCALL	DELAY1
		MOV		A,#data2	;;第二个电平
		MOVX	@R0,A
		LCALL	DELAY2
		SJMP	LOOP

技术指标:

(1)分辨率;(2)建立时间;(3)精度

(1)分辨率指输入给AD转换器的单位数字量变化引起的模拟量输出变化。

(2)描述AD转换器转换快慢的一个参数

(3)位数越多精度越高

D/A的两种工作方式:(硬件链接方式)

a.单缓冲方式:指DAC0832内部的两个数据缓冲器有一个处于直通方式,另外一个处于受C51控制的锁存方式。在实际应用当中,如果只有一路模拟量输出,或者不要求多路同步输出时,可采用单缓冲方式。

单片机复习

b.双缓冲方式:对于DA转换,要求同步输出时,必须采用双缓冲同步方式,这种方式下,数据量的输入和AD转换是分两步进行的。单片机必须通过LE1来锁存待转换数字量,通过LE2来启动D/A转换

单片机复习

ADC0809硬件联接

技术指标:

(1)转换时间和转换效率(转换时间的倒数) ;(2)分辨率:1/2nx100%; n为量化时的位数

数码管

1.段码的定义:

单片机复习

代码位 D7 D6 D5 D4 D3 D2 D1 D0
显示段 dp g f e d c b a

a.静态显示

b.动态显示

​ 原理:在某一时刻,只让某一位选线处于选通状态,而其他各个位选线处于关闭状态,同时段码线上输出相应要显示的字符的段选信号,这样就可以使得多个数码管中的一个发光显示,而其他的数码管处于熄灭状态,在下一个时刻同样的来显示另外的数码管。虽然字符在不同的时间显示,但由于LED显示器的余晖和人眼的视觉暂留效应作用,只要每一位显示的时间间隔足够短,就会出现多个数码管同时显示的假象!!!

#include <reg52.h>

typedef unsigned int u16;	  
typedef unsigned char u8;

#define seg P0
#define sel P2

u8 smgduan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};// 段选
u8 d1=0,d2=0,d3=0,d4=0;			
bit	flag=0;		
u8	i=0;									
								
void delay5ms(){
   unsigned char a,b;
    for(b=19;b>0;b--)
        for(a=130;a>0;a--);  
}
void InitTimer0(void){
    TH0 = 0x0D8;
    TL0 = 0x0F0;
    EA = 1;
    ET0 = 1;
    TR0 = 1;
}
void InitTimer1(void){
    TH1 = 0x0EC;
    TL1 = 0x78;
    EA = 1;
    ET1 = 1;
    TR1 = 1;
}

void InitExT0(void){
	EA = 1;
	EX0 = 1;
	IT0 = 1;		//“0”时,为电平触发,为“1”时,为下降沿触发
}
void InitExT1(void){
	EA = 1;
	EX1 = 1;
	IT1 = 1;		//“0”时,为电平触发,为“1”时,为下降沿触发
}
void key_clear()interrupt 0 { //外中断0控制清楚(P3^2)
	d1=d2=d3=d4=0;
	flag=0;
}

void key_stop()interrupt 2 {	//外中断1控制暂停(P3^3)
		flag = ~flag;
}

void cnt()interrupt 1{				//定时器中断 10ms计数
	TH0 = 0x0D8;
  TL0 = 0x0F0;
	if(flag==0){
			if(d1==9){
				d1 = 0;
				if(d2 == 9){
					d2 = 0;
					if(d3==9){
						d3=0;
						if(d4==5)
							d4=0;
						else
							d4++;
					}	
					else 
						d3++;
				}
				else 
					d2++;
			}
			else 
				d1++;
		}
	else
		;
}

void delay()interrupt 3{			//定时器中断 5ms数码管扫描速率
	TH1 = 0x0EC;
  TL1 = 0x78;
	i++;
	seg = 0x00;
	if(i==4)
		i=0;
}
void main(){
	TMOD=0x11;		
//0001_0001(<GATE><C/T><M1><M0>)//定义方式1,不启动多控制中断方式,计数模式(用内部时钟信号)
	IP=1;
//IP默认值为00H,此时优先级顺序为:外部中断0 > 定时/计数器0 > 外部中断1 > 定时/计数器1 > 串行中断 
//<--><--><PT2><PS:串口><PT1:T1中断><PX1:ET1中断><PT0:T0中断><PX0:ET0中断>
	InitExT0();
	InitExT1();
	InitTimer0();
	InitTimer1();
	
while(1){
	sel =i;
	switch(i){
				case 0:	seg = smgduan[d4];break;
				case 1: seg = smgduan[d3]+128;break;
				case 2: seg = smgduan[d2];break;
				case 3: seg = smgduan[d1];break;
				default: seg = 0;
			}
}	
}

矩阵键盘

2.按键消抖原理

常用软件方法去抖,即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。>

一般来说,软件消抖的方法是不断检测按键值,直到按键值稳定。实现方法:假设未按键时输入1,按键后输入为0,抖动时不定。可以做以下检测:检测到按键输入为0之后,延时5ms~10ms,再次检测,如果按键还为0,那么就认为有按键输入。延时的5ms~10ms恰好避开了抖动期。

3.矩阵键盘:

#include <reg51.h>

#define uchar unsigned char
#define uint 	unsigned int
	
void matrixkeyscan()
{
uchar temp,key;
    P3=0xfe;
    temp=P3;
    temp=temp&0xf0;
    if(temp!=0xf0)
    {
      delayms(10);
			temp=P3;
			temp=temp&0xf0;
      if(temp!=0xf0)
      {
        temp=P3;
        switch(temp)
        {
          case 0xee:
               key=0;
               break;
          case 0xde:
               key=1;
               break;
          case 0xbe:
               key=2;
               break;
          case 0x7e:
               key=3;
               break;
         }
         while(temp!=0xf0)
         {
           temp=P3;
           temp=temp&0xf0;
         }
        display(key);
      }
    }
    P3=0xfd;
    temp=P3;
    temp=temp&0xf0;
    if(temp!=0xf0)
    {
      delayms(10);
      temp=P3;
      temp=temp&0xf0;
      if(temp!=0xf0)
      {
        temp=P3;
        switch(temp)
        {
          case 0xed:
               key=4;
               break;
          case 0xdd:
               key=5;
               break;
          case 0xbd:
               key=6;
               break;
          case 0x7d:
               key=7;
               break;
         }
         while(temp!=0xf0)
         {
           temp=P3;
           temp=temp&0xf0;
         }
         display(key);
      }
      }
    P3=0xfb;
    temp=P3;
    temp=temp&0xf0;
    if(temp!=0xf0)
    {
      delayms(10);
			temp=P3;
      temp=temp&0xf0;
      if(temp!=0xf0)
      {
        temp=P3;
        switch(temp)
        {
          case 0xeb:
               key=8;
               break;
          case 0xdb:
               key=9;
               break;
          case 0xbb:
               key=10;
               break;
          case 0x7b:
               key=11;
               break;
         }
         while(temp!=0xf0)
         {
           temp=P3;
           temp=temp&0xf0;
         }
        display(key);
      }
      }
    P3=0xf7;
    temp=P3;
    temp=temp&0xf0;
    if(temp!=0xf0)
    {
      delayms(10);
      temp=P3;
      temp=temp&0xf0;
      if(temp!=0xf0)
      {
        temp=P3;
        switch(temp)
        {
          case 0xe7:
               key=12;
               break;
          case 0xd7:
               key=13;
               break;
          case 0xb7:
               key=14;
               break;
          case 0x77:
               key=15;
               break;
         }
         while(temp!=0xf0)
         {
           temp=P3;
           temp=temp&0xf0;
         }
        display(key);
      }
    }
}

串口

1.相关寄存器:

a.中断允许寄存器(IE

EA ES ET1 EX1 ET0 Ex0
AFH ACH ABH AAH A9H A8H

c.串行口中断标志寄存器(SCON

SM0 SM1 SM2 REN TB8 RB8 TI RI
控制模式 控制模式 控制多机通讯 使能接收 发送的第8位数据 j接收的第8位数据 发送中断标志位 接收中断标志位

d.定时器/计数器控制寄存器(TMOD

GATE C/T M1 M0 GATE C/T M1 M0
T1 控制段 1:计数/0:计时 模式控制(H) 模式控制(L) T0控制段 1:计数/0:计时 模式控制(H) 模式控制(L)

e. 串口波特率控制寄存器(PCON

SMOD
波特率控制

2.初始化程序设计:(波特率只和定时器/计数器1有关

void init_serialcomm(void)
{
    SCON  = 0x50;       
//SCON: mode 1, 8-bit【无多机通讯】 REN  <M0><M1><M2><REN><TB8><RB8><TI><RI>
    TMOD |= 0x20;       
 //TMOD: timer 1, mode 2,8-bit  <GATE><C/T><M1><M0><GATE><C/T><M1><M0>==[计数器1][计数器0]
    PCON |= 0x80;       
//SMOD=1;加倍波特率 		<SMOD><--><--><--><--><--><-->
    TH1 = 0xF4;       
//Baud:4800  fosc=11.0592MHz 	 <计数器方式2,为8位计数器,自动装载,由TH1--TL1>
    IE |= 0x90;       
//Enable Serial Interrupt 	 <EA> <X> <X> <ES> <ET1> <EX1> <ET0> <EX0>
    TR1 = 1;          
// timer 1 run 		 驱动计数器
}

3.多机通讯过程:

a.原理:

​ 在串行口以方式2或者方式3接收时,若SM2=1,表示设置为多及通信,这时:

​ 1.接收到的第9为数据为1时,数据才装入SBUF并且置中断标志RI=1,向CPU发出中断请求

​ 2.接收到的第9为数据为0时,则不会产生中断标志,信息被抛弃。

b.多机通讯的工作过程

​ 主机的RXD与所有从机的TXD相连接。

​ (1)从机初始化程序串行口中断,将串行口编程为方式2或者方式3接收,即9为数据异步通信方式,且SM2和REN位置1,使得从机只处于多及通讯且接收地址帧的状态。

​ (2)在主机和某一个从机通信之前,先将对应的设备地址通过串口发送出去,地址信息第9位为“1”,【和传送数据的区别在于数据的第9位为“0”】。当主机向各从机发送地址时,各从机的串口接收到的第9为数据为RB8=1;且由于SM2=1,则中断标志位RI置“1”,从机响应串口中断,执行中断服务程序,在中断服务程序中,判断主机发送的地址是否是本机的地址,若是本机地址,则使该从机SM2=0(即关闭多机通讯方式),准备接收主机发送的数据;若不相符合,则仍然保持SM2=1;【因为数据的第9位为0,SM2=0时对这一位不敏感;但是当SM2=1时,只对这一位的高电平敏感,从而实现定向传数据【多机通讯】】

单片机复习

4.实验程序(AD采样串口传输)

#include <reg51.h>

#define uchar unsigned char
#define uint 	unsigned int
	
#define sel P1
#define seg P0
uchar i;

uchar code smgduan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};// 段选

sbit 	cs=			P2^4;
sbit	dclk = 	P2^7;
sbit	din = 	P2^6;
sbit	dout = 	P2^5;

void delay(uint i){

while(i--);
}

void XPT2046_writebyte(uchar wx)  //串行输入1个字节的控制字
{  	uchar i;
		dclk = 0;
	for(i=0;i<8;i++)
	{	din=wx>>7;
		wx<<=1;
		dclk=0;
		dclk=1;		  //上跳沿输入
	}
}

uint XPT2046_read()	 //读出12位的转换数据
{	uint rx=0;
	uchar i;
	for(i=0;i<12;i++)
	{	rx<<=1;
		dclk=1;
		dclk=0;
		rx|=dout;		//下跳沿输出
	}
	return(rx);
}

uint getdata(uchar cmd)
{
	uchar i;
	uint value;
	dclk = 0;
	cs = 0;
	XPT2046_writebyte(cmd);
	for(i=6; i>0; i--); 	//延时等待转换结果
	dclk = 1;	
	dclk = 0;
	value=XPT2046_read();
	cs = 1;
	return value;	
}

void initcomm(void)
{
    SCON=0x50;  //设置为工作方式1 0101 0000
    TMOD=0x20;  //设置计数器工作方式2
    PCON=0x80;  //波特率加倍
    TH1=0xF3;   //计数器初始值,波特率为4800
    TL1=0xF3;
    EA=1;       //打开总中断
    ES=1;       //打开串口中断
    TR1=1;      //打开计数器  	
}

void uart_rx(uchar ch)
{
    SBUF=ch;
    while(TI==0);
    TI=0;
}

void main(){
	uint	send_data = 0;
	uchar d1,d2,d3,d4;
	uchar i,j;
	initcomm();

		while(1){
			send_data=getdata(0xa4);
			d4=send_data/1000;
			send_data = send_data%1000;
			d3=send_data/100;
			send_data = send_data%100;
			d2=send_data/10;
			send_data = send_data%10;
			d1=send_data/1;
			for(j=0;j<10;j++){
				for(i=0;i<4;i++){
					delay(300);
					sel =i;
				switch(i){
						case 0:	seg = smgduan[d4];break;
						case 1: seg = smgduan[d3];break;
						case 2: seg = smgduan[d2];break;
						case 3: seg = smgduan[d1];break;
						default: seg = 0;
					}
				}
				}
				uart_rx(d4+48);
				uart_rx(d3+48);
				uart_rx(d2+48);
				uart_rx(d1+48);
				uart_rx(0x0A);
			}
}

四种工作方式(方式1、3的波特率与T1计数器有关

方式0:波特率固定 fosc/12[^串行方式0]

方式1:波特率 = 2SMOD/32*定时器T1溢出率[^串行方式1]

方式2:波特率 = 2SMOD/64*fosc [^串行方式2]

方式3:波特率 = 2SMOD/32*定时器T1的溢出率 [^串行方式3]

定时器溢出率 = 256X=fosc/12256X{\frac{计数速率}{256-X}=\frac{f_{osc}/12}{256-X}}

=2SMOD32fosc12(256X){波特率 = \frac{2^{SMOD}}{32}*\frac{f_{osc}}{12*(256-X)}}

若晶振11.0592MHz,选用T1方式2定时器作为波特率发生器,波特率为2400bps

因为只有方式1、3得波特率是可调的,所以方式选择这两个。

设T1方式2定时,选择SMOD=0;

=2SMOD32fosc12(256X){波特率 = \frac{2^{SMOD}}{32}*\frac{f_{osc}}{12*(256-X)}} = 2400

得:X=244=F4H

解:先求溢出率:2400 = 溢出率/32; 得:溢出率=76800

求初值:76800 = 计数速率/(256-X)=fosc/12256X{\frac{f_{osc}/12}{256-X}} 得:计数速率 = 244 【晶振用11.0592MHz来做】

定时器/计数器

1.相关得寄存器

a.中断允许寄存器(IE

EA ES ET1 EX1 ET0 Ex0
AFH ACH ABH AAH A9H A8H

b.中断请求标志寄存器(TCON

TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
T1溢出中断 T0溢出中断 外部中断1 定时器1中断 外部中断0 定时器中断0

d.定时器/计数器控制寄存器(TMOD

GATE C/T M1 M0 GATE C/T M1 M0
T1 控制段 1:计数/0:计时 模式控制(H) 模式控制(L) T0控制段 1:计数/0:计时 模式控制(H) 模式控制(L)

2.定时器相关

在C51中我们可以利用的定时器/计数器只有两个:T0、T1

存在四种工作方式(12MHz):

方式0:计数器/定时器 为13位的1

方式1:计数器/定时器 为16位的 2

方式2:计数器/定时器为8位的【由硬件自动装载】3

方式3:计数器/定时器为两个8位的(只有T0能够工作在方式3,且此时T1停止计数)[^方式3]

装载值的计算:对于12MHz的晶振

例如:我们要计时100us,采用T0\T1的方式0、1、2都可以

方式0: (213 - X)*1us =100us; 可得:X = 8091 = 1F9B

方式1: (216 - X)*1us = 100us 可得:X = 65435 = FF9B

方式2: (28 - X)*1us = 100us; 可得:X = 155 = 9B

;;;;;;;;;;;;;;;;;;;;;  中断流水灯【T0方式1;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		ORG		0000H
		AJMP	START
		ORG		000BH;
		LJMP	TIMER0;
		ORG		0100H;
START:	
		MOV		TMOD,#01H;
		MOV		TH0,#3CH;
		MOV		TL0,#B0H;
		MOV		A,#FEH;
		MOV		R0,#20H;
		MOV		IE,#10000010B;
		MOV		IP,#10B;
		SETB	TR0;
		MOV		P1,A;
		SJMP	$;
TIMER0:	
		DJNZ	R0,LOOP;
		MOV		R0,#20;
		MOV		P1,A;
		RL		A;
LOOP:
		MOV		TH0,#3CH;
		MOV		TL0,#B0H;
		RETI
	END
*********************************   1s定时   *********************************
		ORG	0000H
RESET:	
		LJMP	MAIN
		ORG		000BH
		LJMP	IT0P
		ORG		1000H
MAIN:
		MOV		SP.#60
		MOV		B,#0AH
		MOV		TMOD,#01H
		MOV		TL0,#0B0H
		MOV		TH0,#3CH
		SETB	TR0
		SETB	ET0
		SETB	EA
HERE:
		SJMP	HERE
IT0P:
		MOV		TL0,#0B0H
		MOV		TH0,#3CH
		DJNZ	B,LOOP
		CLR		TR0
LOOP:
		RETI

  1. 最大可定时213x1us=8191us ~= 8.191ms ↩︎

  2. 最大可定时时间 216x1us=65535us ~= 65.535ms ↩︎

  3. 自动装载TH–>TL中重载,每次在定时器中断触发的时候,最大计时:28x1us=255us ↩︎