往年考点分布

alt text


必考知识点

  • LED指示灯控制
  • 数码管显示单元
  • 蜂鸣器或继电器
  • 独立按键(几乎必考)

alt text


需要学的内容

alt text


LED指示灯

alt text

  • 重点在138译码器以及573锁存器
  1. 138译码器

alt text

  • 其中:A2为P27(C),A1为P26(B),A0为P25(A)
  • CBA对应765对应210,且低电平有效
  • 38译码器控制输出为低电平,从而与WR的或非门输出高电平使能LE,这里是选择Y4C~Y7C,使能LE驱动锁存器进行P0口的输入控制
  1. 573锁存器
  • 使能端LE为高,输出端等于输入端(LE连至Y4C)
  • 使能端LE为低,输出端不变 选择锁存器:
void Set_HC573(unsigned char channel, unsigned char dat)
{
  P0 = dat;                      //待设置数据
  switch(channel)                //选通锁存器
  {
    case 4:
      P2 = (P2 & 0x1f) | 0x80;  //Y4输出0,LED控制
    break;
    case 5:
      P2 = (P2 & 0x1f) | 0xa0;  //Y5输出0,蜂鸣器和继电器控制
    break;
    case 6:
      P2 = (P2 & 0x1f) | 0xc0;  //Y6输出0,数码管位选
    break;
    case 7:
      P2 = (P2 & 0x1f) | 0xe0;  //Y7输出0,数码管段码
    break;
    case 0:
      P2 = (P2 & 0x1f) | 0x00;  //所有锁存器不选择
    break;
  }
  P2 = (P2 & 0x1f) | 0x00;      //设置完成,关闭全部锁存器
}
//===================系统初始化===================
void Init_sys()
{
  Set_HC573(0, 0x00);        //关闭所有锁存器
  Set_HC573(5, 0x00);        //关闭蜂鸣器和继电器
  Set_HC573(4, 0xff);        //关闭全部LED灯
  DisplaySMG_All(0xff);      //关闭全部数码管
  Init_DS1302_Param();      //初始化DS1302
}

alt text

  1. LED点亮原理
  • LE使能连至Y4C,Y4C为或非门的输出,WR接地,只需Y4为为0,(100为Y4低电平,因为低电平有效)即可输出高,Y4是由P25~27的138译码器控制.之后便可通过锁存器使输出等于输入
  • LED给0点亮,因为其接至电源(P0=0x00即为全部点亮)

蜂鸣器与继电器

alt text alt text

  • N_BUZZ低电平使蜂鸣器鸣叫
  • ULN2003为达林顿驱动管,内部为非门连接,即左侧573锁存器的输入P0口需要为高电平才能使N_BUZZ低电平
  • LE使能端为Y5C,即101才能使Y5输出低电平,(LED使能对应Y4C,蜂鸣器与继电器对应Y5C)才能经过和接地的WR组成的或非门输出高电平,从而驱动573锁存器
  • 继电器对应的使P04,P04也要为高电平才能使继电器吸合
    • 开继电器P04,0x10为0001 0000,对应的为P04
  • 蜂鸣器鸣叫:
    • 1.Y5输出低电平
    • 2.P06输出高电平
    • 3.综述:开启蜂鸣器P06,0x40为0100 0000,从左往右分别为P07 P06 P05 P04 | P03 P02 P01 P00
//选择YNC与P0输入
void outputP0(unsigned char channel,unsigned char date)
{
	HC138_init(channel);
	P0= date;
}
//开启继电器与蜂鸣器
outputP0(5,0x10);//开继电器P04,0x10为0001 0000,对应的为P04
outputP0(5,0x40);//开启蜂鸣器P06,0x40为0100 0000,从左往右分别为P07 P06 P05 P04 | P03 P02 P01 P00


共阳数码管的静态显示

alt text alt text

  • Y7C控制段码,Y6C控制公共端
  • Y6C驱动后,P0为选择8个COM端口,即选择8个数码管中的哪一位
  • Y7C驱动后,P0为选择输入数据,即数码管显示什么

alt text

  • 共阳极为均接在同一电源下,给低电平即可点亮
  • 共阴极为均接在同一地上,给高电平即可点亮
  • abcdefgh对应P00~P07
  • P0赋值的16进制数对应的为:h g f e d c b a,从左往右转为16进制

共阳数码管的动态显示

alt text alt text 单个数码管动态显示

void DisplaySMG_Bit(unsigned char pos, unsigned char value)
{
  Set_HC573(6, 0x01 << pos);    //数码管的段位
  Set_HC573(7, value);          //数码管显示内容
  DelaySMG(TSMG);                //数码管点亮时长
  Set_HC573(6, 0x01 << pos);        
  Set_HC573(7, 0xff);            //消隐
}

全部数码管静态显示(常用于关闭所有)

void DisplaySMG_All(unsigned char value)
{
  Set_HC573(6, 0xff);            //使能所有数码管
  Set_HC573(7, value);          //数码管显示内容  
}
动态显示段码
void dynamic_display()
{
	dispalySMG_bit(SMG_duanma[2],0);
	delay(500);
	dispalySMG_bit(SMG_duanma[0],1);
	delay(500);
	dispalySMG_bit(SMG_duanma[1],2);
	delay(500);
	dispalySMG_bit(SMG_duanma[8],3);
	delay(500);
	
	dispalySMG_bit(SMG_duanma[16],4);
	delay(500);
	dispalySMG_bit(SMG_duanma[16],5);
	delay(500);
	
	dispalySMG_bit(SMG_duanma[month/10],6);
	delay(500);
	dispalySMG_bit(SMG_duanma[month%10],7);
	delay(500);
}
//数码管延时时,需在延时中加dynamic_display();否则延时时数码管不显示
void delay_show(unsigned char t)
{
	while(t--)
	{
		dynamic_display();
	}
}

独立按键

alt text

  • 可直接看代码,注意引脚和寄存器的对应,以及YNC的选择对应
  • 所有按键按下时为低电平
  • S7对应P30,S6对应P31,S5对应P32,S4对应P33
  • 可用状态机执行条件指令
void scan_key()
	{
		if(S7 == 0)
	{	
		delay(100);
		if(S7 == 0)
		{
			if(sta==0)
			{
			L1=0;
			sta=1;
			}
		else if(sta==1)
		{
			L1=1;
			sta=0;//释放状态
		}
		}
	}	
	
		if(S6 == 0)
{
		delay(100);
		if(S6 == 0)
			if(sta==0)
			{
				L2=0;
				sta=2;
			}
		else if(sta==2)
		{
		L2=1;
		sta=0;
		}
}//等等
	}

alt text


矩阵键盘

alt text

  • 由于键盘按下时为低电平,所以逐行赋低电平,逐列检测列是否也为低电平
  • 有四行四列,且分别sbit定义后,检测时同上
  • 第一行到第四行对应从P30到P33,P37到P34对应第一列到第四列,P36为P42,P37为P44
//定义初始化
sfr P4 = 0xC0
sbit HC138_C=P2^7;
sbit HC138_B=P2^6;
sbit HC138_A=P2^5;
	
sbit row1=P3^0;
sbit row2=P3^1;
sbit row3=P3^2;
sbit row4=P3^3;

sbit col1=P4^4;
sbit col2=P4^2;
sbit col3=P3^5;
sbit col4=P3^4;
//按键检测
void key_scan()
{
	row1=0;//第一行0~3
	row2=row3=ro4=1;
	col1=col2=col3=col4=1;
	if(col1==0)
	{
		delay(500);
		if(col1==0)
		{
			while(col1==0);
			keynum=0;
			display_keynum(SMG_duanma[keynum]);
		}
	}
	else if(col2==0)
	{
		delay(500);
		if(col2==0)
		{
			while(col2==0);
			keynum=1;
			display_keynum(SMG_duanma[keynum]);
		}
	}
	else if(col3==0)
	{
		delay(500);
		if(col3==0)
		{
			while(col3==0);
			keynum=2;
			display_keynum(SMG_duanma[keynum]);
		}
	}
	else if(col4==0)
	{
		delay(500);
		if(col4==0)
		{
			while(col4==0);
			keynum=3;
			display_keynum(SMG_duanma[keynum]);
		}
	}
	
	row2=0;//第二行4~7
	row1=row3=ro4=1;
	col1=col2=col3=col4=1;
//。。。。同上

中断系统

  • 定时器优先级

alt text

  • 中断号与中断源对应的顺序

alt text

  • 中断相关的4个寄存器都可以位寻址:IE IP TCON SCON
  • 中断服务函数的编写:

alt text

  • 以下为两个中断请求标志

alt text

alt text

alt text

  • 尽量在中断内少做事情
  • 这里的S5连接的P32为外部中断0引脚
  • S4连接的P33为外部中断1引脚(功能复用)
void Init_INT0()
{
	IT0=1;//下降沿触发
	EX0=1;//外部中断0使能
	EA =1;//总开关使能
}

unsigned char num=0;

void Interrupt() interrupt 0
{
	num=1;//即按下S5即可给下降沿触发中断,中断执行标志即为num=1,由此进行if语句执行
}

定时器

alt text

  • 最大计数65536个脉冲,即65.5ms。若想延时10ms,只需计数10000个脉冲

alt text

  • 即(65535-10000)/256为TH0定时器0的高八位(计数初值寄存器)
  • (65535-10000)%256为TH0定时器0的低八位
  • 有关定时器的寄存器:

alt text alt text

  • TMOD分为两个部分,高四位控制定时器1,第四位控制定时器0
  • 编程思路:

alt text

  • 初始化定时器时:
////////////////////定时器初始化函数//////////////
void Init_Timer0()
{
	TMOD=0x01;//0x01为第四位的定时器0,TR0软启动加16为定时计数器
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;//定时50ms
	ET0=1;//使能
	EA=1;//使能
	TR0=1;//开启
}
//////////////////////////////////////////////
/////////////////定时器服务函数////////////////
unsigned char n=0;
void Timer0Serve() interrupt 1//定时器0中断号为1
{
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;//定时50ms(无自动重装,只得重新设置定时器初值)
	
	n++;
	if(n%10==0)
	{
		L1=~L1;
	}
	if(n==100)
	{
		L8=~L8;
		n=0;
	}
}
/////////////////////////////////////////////

定时器进阶

  • 定时器实现秒表功能

alt text

////////////定时器相关/////////////
void Init_Timer0()
{
	TMOD=0x01;//16位Timer0
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;
	
	ET0=1;
	EA=1;
	TR0=1;
}

void ServiceTimer0() interrupt 1
{
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;
	
	t_005s++;
	if(t_005s==20)
	{
		t_s++;
		t_005s=0;
		if(t_s++==60)
		{
			t_min++;
			t_s=0;
		}
		if(t_min==99)
		{
			t_min=0;
		}
	}
}
///////////////////////////////////

PWM脉宽调制

alt text

  • 1份脉宽可分成100份,通过控制份数来控制占空比
  • 比如:频率为100HZ的PWM脉宽信号,其周期为0.01s,即10ms,需要65535减去10 000从而得到10ms,但是10000分成100份,每份为100。即只需65535-100后运算,此时需要在中断函数中设置两个值,一个值用来++,可看作是时间的流逝;另一个值为占空比,通过比较这两个值来判断LED是否亮灭。而占空比的值可以直接设置也可通过按键状态机等方式来赋值。
  • 以下为定时器相关函数
unsigned char tim=0;
unsigned char duty=0;
void Init_Timer0()
{
	TMOD=0x01;
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;
	
	ET0=1;
	EA=1;
}
void ServiceTimer0() interrupt 1
{
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;

	tim++;
	if(tim==duty)
	{
		L1=1;
	}
	else if(tim==100)
	{
		L1=0;
		tim=0;
	}
}
  • 以下为按键相关函数
unsigned char n=0;//状态机参数
void delay(unsigned int t)
{
		while(t--);
}
void key_scan()
{
	if(S7==0)
	{
		delay(100);
		if(S7==0)
		{
			switch(n)//检测按键按下后直接进入状态机
			{
				case 0:L1=0;duty=10;TR0=1;n=1;
				case 1:duty=50;n=2;
				case 2:duty=90;n=3;
				case 3:L1=1;TR0=0;n=0;
			}	
			while(S7==0);//按下之后的松手检测
		}
	}
}

串口通信

  • 原理
  • 有4个串口模式
    • 常用定时器1的模式2产生波特率(8位自动重装)
  • RS485总线为半双工通信模式
  • 有关波特率的概念与计算

alt text

  • UART发送与接收

alt text alt text

  • 常用01模式,TB8与RB8为奇偶校验所用

alt text

  • 串口初始化
sfr AUXR=0x8e;
void Init_UART()
{
	TMOD=0x20;//定时器1自动重装8位产生波特率
	TH1=0xFD;
	TH1=0xFD;//重装时一样
	TR0=1;
	
	SCON=0x50;//8位UART,允许接收
	AUXR=0x00;//特殊寄存器置0才可发送与接收
	ES=1;//使能串口
	EA=1;
}
  • 发送函数以及用于接收的中断服务函数
void sent_date(unsigned char dat)
{
	SBUF=dat;
	while(TI==0);//0为一直发送
	TI=0;
}
unsigned char uart_dat;
void Service_UART() interrupt 4//4号为串口中断
{
	if(RI==1)
	{
		RI=0;
		SBUF=uart_dat;
		sent_date(uart_dat);
	}
}

综合实训

alt text alt text alt text


DS18B20

alt text alt text 需包含源文件onewire.c和头文件onewire.h alt text

//=================温度值显示函数=================
void DisplaySMG_Temp()
{
  switch(mode)
  {
    //2位小数显示模式
    case 1:
      smg_ds18b20 = temp_ds18b20 * 100;
      DisplaySMG_Bit(7,SMG_NoDot[smg_ds18b20  % 10]);
      DisplaySMG_Bit(6,SMG_NoDot[(smg_ds18b20 / 10) % 10]);    
      DisplaySMG_Bit(5,SMG_Dot[(smg_ds18b20 / 100) % 10]);
      if(smg_ds18b20 / 1000 != 0)
      {
        DisplaySMG_Bit(4,SMG_NoDot[smg_ds18b20 / 1000]);
      }
    break;
    //1位小数显示模式
    case 2:
      smg_ds18b20 = temp_ds18b20 * 10;
      DisplaySMG_Bit(7,SMG_NoDot[smg_ds18b20  % 10]);
      DisplaySMG_Bit(6,SMG_Dot[(smg_ds18b20 / 10) % 10]);    
      if(smg_ds18b20 / 100 != 0)
      {
        DisplaySMG_Bit(5,SMG_NoDot[smg_ds18b20 / 100]);
      }
    break;
    //整数显示模式
    case 3:
      smg_ds18b20 = temp_ds18b20;
      DisplaySMG_Bit(7,SMG_NoDot[smg_ds18b20  % 10]);
      if(smg_ds18b20 / 10 != 0)
      {
        DisplaySMG_Bit(6,SMG_NoDot[smg_ds18b20 / 10]);
      }
    break;
  }
}
//==============DS18B20温度读出与显示==============
void Read_Dsiplay_DS18B20()
{
  unsigned char LSB,MSB;      
  unsigned int temp = 0;      
  
  init_ds18b20();              //初始化DS18B20
  DisplaySMG_Temp();          //动态刷新数码管    
  Write_DS18B20(0xcc);        //忽略ROM操作
  Write_DS18B20(0x44);        //启动温度转换
  DisplaySMG_Temp();          //动态刷新数码管
  init_ds18b20();              //初始化DS18B20
  DisplaySMG_Temp();          //动态刷新数码管
  Write_DS18B20(0xcc);        //忽略ROM操作
  Write_DS18B20(0xbe);        //读出内部存储器
  LSB = Read_DS18B20();        //第0字节:温度低8位
  MSB = Read_DS18B20();        //第1字节:温度高8位
  DisplaySMG_Temp();          //动态刷新数码管
  //上述程序中插入多处数码管刷新,可使显示亮度充足
  temp = MSB;                  //合成16位温度原始数据
  temp = (temp << 8) | LSB;
  if((temp & 0xf800) == 0x0000)    //处理正温度
  {
    temp_ds18b20 = temp * 0.0625;  //计算实际温度值
  }
  DisplaySMG_Temp();          //动态刷新数码管
}

DS1302

alt text alt text alt text

  • 注意允许写入以及禁止写入的设置,且为BCD码,需要写入的时间直接0x即可
//=================初始化DS1302的参数=================
void Init_DS1302_Param()
{
  unsigned char i;
  Write_Ds1302_Byte(0x8E,0x00);  //解除写保护
  for(i = 0; i < 7; i++)        //依次写入7个参数
  {
    Write_Ds1302_Byte(WRITE_RTC_ADDR[i],TIME[i]);
  }
  Write_Ds1302_Byte(0x8E,0x80);  //使能写保护
}
//===============设置DS1302中的时分秒=================
void Set_DS1302_Time()
{
  Write_Ds1302_Byte(0x8E,0x00);    //解除写保护
  Write_Ds1302_Byte(0x80,sec);    //设置:秒
  Write_Ds1302_Byte(0x82,min);    //设置:分
  Write_Ds1302_Byte(0x84,hour);    //设置:时
  Write_Ds1302_Byte(0x8E,0x80);    //使能写保护
}
//===============读取DS1302中的时分秒=================
void Read_DS1302_Time()
{
  if(F_pause == 0)              //在非暂停模式中
  {
    sec = Read_Ds1302_Byte(0x81);
    min = Read_Ds1302_Byte(0x83);
    hour = Read_Ds1302_Byte(0x85);
  }  
  DisplaySMG_RTC();              //显示最新的时分秒
}  

本文章使用limfx的vscode插件快速发布