2016-03-27 17:12:06 u013151320 阅读数 13606
  • 蓝桥杯历届真题讲解

    应某大学邀请,为同学们做算法题目的讲解,蓝桥杯的题目很好,很适合学习编程的同学们,把这些算法掌握好,为将来的软件开发打下坚实的基础。

    999 人正在学习 去看看 付强

近期和我们实验室的小伙伴一起参加了第七届蓝桥杯单片机开发与设计组省赛,在这里将备赛期间的一些资料和经验总结一下,并分享给大家。

蓝桥杯单片机开发板是国信长天的CT107D单片机综合训练平台,个人感觉这块开发板的设计还是很不错的。之前一直用的普中的板子,自己也做过一些最小系统板,练习了蓝桥杯的板子后根本不想再用普中的板子了。普中的板子将单片机的学习划分成很多个模块,操作各个模块时需要手动接线,因此很适合初学者,而蓝桥杯的板子则不需要自己手动接线,开发板也没有模块划的划分,能够让我们对单片机编程有更深入的理解,适合进阶练习。

虽然CT107D中使用了很多的锁存器,电路看起来比较复杂,但是熟悉之后会发现自己对之前毫不在意的锁存器有了更深刻的认识,对于模块化编程与代码复用也有了更深入的认识。通过这次比赛我对单片机编程也有了不同的认识,学到一个很重要的单片机编程套路就是通过定时器来实现前后台的编程思想,虽然以前也这么用过中断来处理一些事务,但是没有这么深刻的认识。同时,学会看芯片文档对于你以后做开发会有很大的帮助。总之,比赛过后有很多想要分享的东西,我会慢慢的写好每一篇博客。

在这一系列的笔记中,我会将自己蓝桥杯备赛期间写的一些代码以及做过的一些模拟题与大家进行分享,欢迎大家与我交流心得。

2016-03-27 17:15:53 u013151320 阅读数 10528
  • 蓝桥杯历届真题讲解

    应某大学邀请,为同学们做算法题目的讲解,蓝桥杯的题目很好,很适合学习编程的同学们,把这些算法掌握好,为将来的软件开发打下坚实的基础。

    999 人正在学习 去看看 付强

一、流水灯

图1
图1 LED部分电路原理图分析
根据上图分析,要控制LED灯,首先我们需要了解74HC573锁存器,然后控制P0口的输出。
根据74HC573的真值表,当LE为高时,锁存器左边输入什么右边就输出什么,当LE为低时,锁存器输出的是上一次的值,即实现了锁存。如下图:
图2
图2 74HC573真值表
要实现流水灯则必须改变74HC573的输出,则必须将Y4C置为高,给P0赋值后再将Y4C置为低进行锁存。我们在原理图中找到Y4C(如图3),图中WR与GND用跳帽连接起来,即WR为低电平0。

图3
图3 74HC138与74HC02

如图所示,Y4C由Y4和WR共同控制,74HC02是一个4路2输入或非门功能,WR为低电平,要使Y4C为高,则Y4必须为低电平,而Y4又由74HC138译码器控制。74HC138译码器的真值表(如图4),要使得Y4为0,则三个输入端应该为100,即P2^7=1,P2^6=0,P2^5=0。又只需要操作P2口的这三位,不需要配置其他的五位,所以P2端口应该配置为:P2 = ((P2&0x1f)|0x80);

图4

编写代码:

while(1)
{
    for(i=0; i<8; i++)
    {
        P2 = ((P2&0x1f)|0x80);   //配置74HC573,使其不锁存
        P0 = ~(0x01<<i);     //左移i位,按位取反
        P2 &= 0x1f;    //再次配置74HC573,使其锁存
        delay();       //适当延时使LED亮得充分
    }
}

看到这里我们可能会感叹点个LED灯都这么麻烦,但是只要你把这个思路理清了,之后的蜂鸣器、继电器、数码管都是这个套路,因为他们共用的P0和P2端口,且都用到了上图3中的锁存器、138译码器和或非门。这就实现了一个8位的I/O口进行复用的分时来控制多个设备,这种设计是比较好的。

二、蜂鸣器和继电器
查看原理图找到继电器和蜂鸣器所在,然后可以看到(如图5)继电器和蜂鸣器的都是通过ULN2003来驱动的,ULN2003内部集成了7个达林顿管,可以有7路输入输出,这里不仅驱动了继电器和蜂鸣器,还有直流电机与步进电机。同样,我们只需要控制Y5C和P0口,而Y5C的控制则与Y4C的控制大同小异,这里就不详细讲解了。我们可以得到控制Y5C的正确配置P2端口的代码为:P2 = ((P2&0x1f)|0xA0);

我们再看继电器和蜂鸣器的电路,当ULN2003输出低电平时继电器和蜂鸣器才打开,而ULN2003每一路输入输出都加有一个非门,所以ULN2003的输入为高电平时蜂鸣器继电器才打开,为低电平时关闭。继电器对应的位为P0^4,蜂鸣器对应的位为P0^6,我们将P0口的这两位赋为1时,即P0=0x50时,两个设备均打开。

图
图5 蜂鸣器与继电器电路原理图

代码部分:

P2 = ((P2&0x1f)|0xA0);    //关闭锁存
P0 = 0x10;     //蜂鸣器关、继电器开
P2 &= 0x1f;   //打开锁存
2019-03-21 09:32:34 qq_43745917 阅读数 33
  • 蓝桥杯历届真题讲解

    应某大学邀请,为同学们做算法题目的讲解,蓝桥杯的题目很好,很适合学习编程的同学们,把这些算法掌握好,为将来的软件开发打下坚实的基础。

    999 人正在学习 去看看 付强

/*

//向E2prom写入

void AT24c02wirte(unsigned char addr,unsigned char dat)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}

//向E2prom读出

unsigned char AT24c02read(unsigned char addr)
{
	unsigned char num;
	IIC_Start();
	IIC_SendByte(0xa0);			//发送器件地址
	IIC_WaitAck();
	IIC_SendByte(addr);			//发送存储地址
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0xa1);			//发送读操作
	IIC_WaitAck();
	num=IIC_RecByte();			//读一字节
	IIC_Ack(0);
	IIC_Stop();
	
	return num;
}

//电位器使用驱动

unsigned char Read_AIN()
{	
	unsigned char temp;

	IIC_Start();
	IIC_SendByte(0x90);       //地址+写
	IIC_WaitAck();
	IIC_SendByte(0x03);      //选择电位器RB2
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0x91);      //地址+读
	IIC_WaitAck();
	temp = IIC_RecByte();
	IIC_Ack(1);                     //高电平回复
	IIC_Stop();
	
	return temp;
}

//光敏电阻

unsigned char read_AIN()
{
	unsigned char temp;
	

	IIC_Start();
	IIC_SendByte(0x90);            //地址+写
	IIC_WaitAck();
	IIC_SendByte(0x01);        //选择光敏电阻
	IIC_WaitAck();
	IIC_Stop();
	IIC_Start();
	IIC_SendByte(0x91);         //地址+读
	IIC_WaitAck();
	temp = IIC_RecByte();
	IIC_Ack(0);                      //低电平回复
	IIC_Stop();
	
	return temp;
}

*/

//蜂鸣器buzz是 P06控制
//继电器relay是 P04控制
//I/O代码
/

/P2=((P2&0x1f)|0xA0)
//P06=1;                 //打开蜂鸣器
//P2 &= 0x1f;

//P2=((P2&0x1f)|0xA0)
//P04=1;                 //打开继电器
//P2 &= 0x1f;

//DS18B20温度采集程序:整数
unsigned char rd_temperature(void)

{
    unsigned char low,high;
  	char temp;
  
  	init_ds18b20();
  	Write_DS18B20(0xCC);
  	Write_DS18B20(0x44); //启动温度转换
  	Delay_OneWire(200);

  	init_ds18b20();
  	Write_DS18B20(0xCC);
  	Write_DS18B20(0xBE); //读取寄存器

  	low = Read_DS18B20(); //低字节
  	high = Read_DS18B20(); //高字节
  
  	temp = high<<4;
  	temp |= (low>>4);
  
  	return temp;
}

//ds1302初始化

void init_time()
{
	unsigned char i;
	Write_Ds1302(0x8E,1);               //关闭写保护
	for(i=0;i<7;i++)
	{
		Write_Ds1302(write_add[i],time[i]);    //地址, 初始化时间
	}
	Write_Ds1302(0x8E,0);                //打开写保护
	
	Hour=Read_Ds1302(0x85);
	Minute=Read_Ds1302(0x83);
	Second=Read_Ds1302(0x81);
}

//BCD码->十进制

unsigned char BCD_Ten(uchar dat)
{
	return  ((dat>>4)*10 + (dat&0x0f));
}

//十进制->BCD码

unsigned char Ten_BCD(uchar dat)
{
    return  (((dat/10)<<4) + (dat%10));
}
2016-04-03 15:57:25 u013151320 阅读数 9498
  • 蓝桥杯历届真题讲解

    应某大学邀请,为同学们做算法题目的讲解,蓝桥杯的题目很好,很适合学习编程的同学们,把这些算法掌握好,为将来的软件开发打下坚实的基础。

    999 人正在学习 去看看 付强

数码管显示几乎是每次蓝桥杯单片机设计与开发组竞赛的必考部分,相对于使用LCD1602作为显示设备,使用数码管来显示能够更好的考察参赛选手的单片机基本功,因为CT107D开发平台的设计使得数码管的显示不仅牵扯到数码管显示的 基本内容,还涉及到74HC573锁存器,74HC02异或门,74HC138译码器,以及中断的知识,所以这是值得我们重视的一个部分。

一、原理分析
根据CT107D的原理图我们可以看出,数码管显示同样涉及到74HC573、74HC138和74HC02,这与上一节中的LED灯、继电器和蜂鸣器的控制大同小异。如下图,蓝色标注为位选控制端口,由锁存器U6来控制,需配置红色标注的Y6C来控制锁存;黄色标注为段选控制端口,由锁存器U7来控制,需配置红色标注的Y7C来控制锁存。

这里写图片描述

而,控制Y6C与Y7C则需要通过74HC18和74HC02来进行控制,如下图,Y6C由Y6和WR共同控制,74HC02是一个4路2输入或非门功能,WR为低电平,要使Y6C为高,则Y6必须为低电平,即74HC138译码器的三个输入端应该为100,即P2^7=1,P2^6=0,P2^5=0。又只需要操作P2口的这三位,不需要配置其他的五位,所以P2端口应该配置为:P2 = ((P2&0x1f)|0xC0)。同理,控制Y7C时P2端口应该置为:P2 = ((P2&0x1f)|0xE0)。
这里写图片描述

二、示例程序
这里采用前后台的编程思想,即使用定时器中断来处理显示部分,作为前台,而逻辑处理与数据采集部分等则在主程序流程中来完成,作为后台。在下面这段程序中,定时器每隔2ms中断一次执行一次显示程序,然后继续执行主程序的任务,我们只要修改需要显示的数组即可实现显示不同的数值,使显示部分模块化,程序显示部分只提供外部接口(数组),不修改显示函数,这样不容易出错,而且能够使显示更方便。

#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int

uchar code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};   //共阳极数码管段选编码数组
uchar dspbuf[] = {10,10,10,10,10,10,10,10};   //显示数据的数组
uchar dspcom = 0;   //位选控制变量


void display()  //显示函数
{   
    P2 = ((P2&0x1f)|0xE0);    //段选消隐
    P0 = 0xff;
    P2 &= 0x1f;

    P2 = ((P2&0x1f)|0xC0);   //位选
    P0 = 1<<dspcom;    //共阳极数码管位选
    P2 &= 0x1f;

    P2 = ((P2&0x1f)|0xE0);   //段选
    P0 = tab[dspbuf[dspcom]];
    P2 &= 0x1f;

    if(++dspcom == 8){      //位选编码,循环显示8位
        dspcom = 0;
    }    
}

void main()
{
   TMOD = 0x01;   //定时器0工作方式1
   TH0 = (65536-2000)/256;   //初始化
   TL0 = (65536-2000)%256;
   ET0 = 1;     //开定时器0中断允许位
   EA = 1;      //开总中断
   TR0 = 1;     //开定时器0

   while(1)
   {
      dspbuf[0] = 1;   //要显示不同的数据只需要修改此数组的值即可
      dspbuf[1] = 2;
      dspbuf[2] = 3;
      dspbuf[3] = 4;
      dspbuf[4] = 5;
      dspbuf[5] = 6;
      dspbuf[6] = 7;
      dspbuf[7] = 8;
   }   
}

void timer0() interrupt 1   //定时器0中断服务函数
{
   TH0 = (65536-2000)/256;
   TL0 = (65536-2000)%256;
   display();   //2ms显示一次
}
2016-04-04 19:27:00 u013151320 阅读数 11078
  • 蓝桥杯历届真题讲解

    应某大学邀请,为同学们做算法题目的讲解,蓝桥杯的题目很好,很适合学习编程的同学们,把这些算法掌握好,为将来的软件开发打下坚实的基础。

    999 人正在学习 去看看 付强

按键几乎也是每次蓝桥杯单片机设计与开发组竞赛的必考部分,因为这是CT107D开发平台上仅有的输入设备之一,而且按键也是单片机基础开发中常见的器件。

一、独立按键
1、原理分析

用跳帽把J5的引脚2和引脚3接起来,即可使用4个独立按键(如下图):S7,S6,S5,S4。按键的一端接到GND,另一端接到P3口的低四位,所以我们只要检测连接按键的P3口的某一位是否为低电平即可确定某一按键是否被按下。但是,因为存在抖动,所以需要进行消抖处理。总之,独立按键的按键检测基本流程为:检测是否有键按下,延时消抖,再次检测是否有键按下,执行按键按下后的相应操作,松手检测。

这里写图片描述

2、示例程序

sbit S7=P3^0;
sbit S6=P3^1;
sbit S5=P3^2;
sbit S4=P3^3;
//这里以S4为例
if(S4==0)   //检测S4是否被按下
{
    delay_ms(2);   //延时消抖
    if(S4==0)   //再次检测S4是否被按下
    {
       //这里执行按键后需要执行的操作  
    }
    while(S4==0);   //松手检测
}

二、矩阵键盘
1、原理分析

用跳帽把J5的引脚1和引脚2接起来,即可使用矩阵键盘(如下图)。矩阵键盘的检测常常采用行列扫描法。先进行行扫描,给P3口赋值为0x0f,当有键按下时P3口低四位的某一位的高电平会拉低,通过检测P3口高低电平的变化即可确定是哪一行的按键被按下,然后再给P3口赋值为0xf0,同理,当有键按下时P3口的高四位的某一位的高电平会被拉低,通过检测P3口的电平变化即可确定是哪一列的按键被按下,由行列交叉可以确定唯一的一个按键。

这里写图片描述

2、示例程序

先确定行来得到键值的临时值,待确定列后将键值的临时值加上列值得到最终的键值。当然,这里的按键编号可以进行合理的自定义,不需要和以下程序完全一样。

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define temp P3
uchar key_value = 0;
void keyscan()   //按键扫描函数
{
   temp = 0x0f;
   if(temp!=0x0f)    //扫描行
   {
      delay_ms(10);    //延时消抖
      switch(temp)
      {
         case 0x0e:   key_value = 0;   break;     
         case 0x0d:   key_value = 4;   break;
         case 0x0b:   key_value = 8;   break;
         case 0x07:   key_value = 12;  break;
      }

      temp = 0xf0;    //扫描列
      if(temp == 0x70)   key_value + = 0;     while(temp == 0x70);     //松手检测
      if(temp == 0xb0)   key_value + = 1;     while(temp == 0xb0);     //松手检测
      if(temp == 0xd0)   key_value + = 2;     while(temp == 0x0d);     //松手检测
      if(temp == 0xe0)   key_value + = 3;     while(temp == 0x0e);     //松手检测
   }
}

因为CT107D单片机开发平台采用IAP15F2K61S2单片机,使用的是转接板,由转接板原理图知矩阵键盘接的是P3口的低6位与P4.2、P4.4口,即用P4.2代替了P3.6,P4.4代替了P3.7,所以需要修改部分代码。

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define temp P3
sfr P4 = 0xc0;
sbit P44 = P4^4;
sbit P42 = P4^2;
uchar key_value = 0;
void keyscan()    //按键扫描函数
{
   temp = 0x0f; P42 = 0; P44 = 0;
   if(temp!=0x0f)    //扫描行
   {
      delay_ms(10);    //延时消抖
      switch(temp)
      {
         case 0x0e:   key_value = 0;   break;
         case 0x0d:   key_value = 4;   break;
         case 0x0b:   key_value = 8;   break;
         case 0x07:   key_value = 12;  break;
      }

      temp = 0xf0; P42 = 1; P44 = 1;    //扫描列
      if(P44 == 0)       key_value + = 0;     while(P44 == 0);       //松手检测
      if(P42 == 0)       key_value + = 1;     while(P42 == 0);       //松手检测
      if(temp == 0xd0)   key_value + = 2;     while(temp == 0x0d);   //松手检测
      if(temp == 0xe0)   key_value + = 3;     while(temp == 0x0e);   //松手检测
   }
}

[蓝桥杯]Readme

阅读数 22

没有更多推荐了,返回首页