打印
[单片机类]

比赛前必看贴子 帮你梳理各大考点 应对考试

[复制链接]
24242|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一路向北lm|  楼主 | 2018-2-23 10:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 一路向北lm 于 2018-2-23 11:14 编辑

2018 届的各位参加蓝桥杯比赛的同学们你们好,感谢21IC给这样一个平台可以为大家分享知识,即将开始省赛了大家准备的怎么样?今天来通过一个试题来帮大家梳理一下。
01.温度监控器
功能简述
“温度监控器”可以实现环境温度检测及报警功能;通过 EEPROM 存储上下限温度数值,温度上下限数值可通过外部按键更改。系统硬件电路主要有单片机控制电路、数码管显示电路、EEPROM 存储电路、直流电机驱动电路(设计部分)、键控制电路及继电器组成,系统框图如图 所示:


设计任务及要求
1.  数码管显示单元
通过 8 位共阳数码管显示温度信息,包括设定的温度上下限数值和当前温度值,显示格式如图所示:


2.  温度测量 单元
通过 DS18B20 数字温度芯片测量环境温度。
3.  按键控制 单元
独立按键 S4 设定为“加上限”按键;每按下一次,温度上限值增加 1℃;
独立按键 S5 设定为“加下限”按键;每按下一次,温度下限值增加 1℃;
独立按键 S6 设定为“减上限”按键;每按下一次,温度上限值减少 1℃;
独立按键 S7 设定为“减下限”按键;每按下一次,温度下限值减少 1℃。
4. EEPROM  记录单元
系统通过 AT24C02 存储温度信息,AT24C02 内部存储地址 0x00 和 0x01 分别存储温度上下限数据信息;温度上下限数据可通过外部按键进行修改,并通过数码管实时显示。数据存储格式如图所示:


5.  直流电机驱动电路设计
设计一个直流电机驱动电路与单片机 P34 引脚连接。
6.  温控 单元
若当前温度数值超过 EEPROM 中存储的温度上限数据,通过单片机 P34 口产生周期为 1KHz 占空比为 30%的 PWM 信号驱动直流电机工作。待温度恢复到上下限阈值内时,P34 口输出高电平,直流电机停止工作若当前温度低于 EEPROM 中存储的温度下限数据,继电器打开,当温度恢复到上3下限阈值内时,继电器自动关闭。
7.  系统初始状态说明
上、下限温度值需要设定在 0℃到 99℃范围内,下限值不大于上限值。系统上电后,从 EEPROM 中读取温度上、下限数值,并实时显示当前温度。

相关帖子

沙发
一路向北lm|  楼主 | 2018-2-23 10:59 | 只看该作者
本帖最后由 一路向北lm 于 2018-2-23 11:08 编辑

考点分析:  数码管
注意点:
01.数码管的显示  (包括动态与静态显示)必须熟练掌握,比赛时熟练写出自己平时封装好的函数我这里有几个给大家提供参考。
02.数码管的消隐问题(有些位置不该亮却出现了一点点亮度)解决办法分享给大家参考

03.最后一位数码管比较亮(最后显示的过长导致最后一位显示过亮)

下面是官网提供给我们的原理图,关于数码管这个模块我们可以看到,主控芯片是通过 74hc138控制74hc02再去控制74hc573  为了节约单片机的io 官网采取了这种方式。

如果想让数码管显示我们可以通过p25 p26 p27 三个io控制  其中我们可以这样写 :

            P2=0xc0;  138的Y6端输出低电平 WR端一直是低电平,Y6C输出高电平 使u8(573)使能端打开,打开数码管位选端。

            P0=0x01;   选择第一位数码管。

            P2=0x1f;      数据锁存。

            P0=0xff ;  用于消隐。

            P2=0xe0;   U7(573)使能,打开数码管段选端。

            P0=table【】;  显示数字相应的段码  。  

            P2=0x1f;      数据锁存。

            P0=0x00;     消隐 。   

我封装了几个函数 方面调用

#ifndef _display_h
#define _display_h
#include<define.h>
#include<intrins.h>
uchar flag,vol;
uchar shi,fen,miao;
uchar table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
uchar yi=11,er=11,san=11,si=11,wu=11,liu=11,qi=11,ba=11;
void delayms();
void display1();
void display2();
void display3();
void display4();
void delayms(uint z)
{
   uint x,y;
   for(x=z;x>0;x--)
      for(y=114;y>0;y--);

}

void display1(uchar yi,er)
{
P2=0xc0;
P0=0x01;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[yi];
P2=0x1f;
P0=0x00;
delayms(1);

P2=0xc0;
P0=0x02;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[er];
P2=0x1f;
P0=0x00;
delayms(1);
}

void display2(uchar san,si)
{
P2=0xc0;
P0=0x04;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[san];
P2=0x1f;
P0=0x00;
delayms(1);

P2=0xc0;
P0=0x08;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[si];
P2=0x1f;
P0=0x00;
delayms(1);
}

void display3(uchar wu,liu)
{
P2=0xc0;
P0=0x10;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[wu];
P2=0x1f;
P0=0x00;
delayms(1);

P2=0xc0;
P0=0x20;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[liu];
P2=0x1f;
P0=0x00;
delayms(1);
}

void display4(uchar qi,ba)
{
P2=0xc0;
P0=0x40;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[qi];
P2=0x1f;
P0=0x00;
delayms(1);

P2=0xc0;
P0=0x80;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=table[ba];
P2=0x1f;
P0=0x00;
delayms(1);

P2=0xc0;
P0=0x80;
P2=0x1f;
P0=0xff;
P2=0xe0;
P0=0xff;
P2=0x1f;
P0=0x00;
delayms(1);

}



使用特权

评论回复
板凳
一路向北lm|  楼主 | 2018-2-23 11:07 | 只看该作者
必考点按键
01.矩阵按键
02.独立按键
可以通过跳线帽来切换最左边一排是独立按键还是矩阵按键。
本人写的矩阵按键 已经调试完毕 ,独立按键没什么含量大家自由发挥即可。
#ifndef _keyscan_h
#define _keyscan_h
#include<define.h>
sfr P4=0xc0;
uchar key_value;
void keyscan()
{
uchar temp;
P3=0x7f; P4=0xef;
temp=P3;
temp=temp&0x0f;
if(temp!=0x0f)
        {
                delayms(5);
                temp=P3;
                temp=temp&0x0f;
                if(temp!=0x0f)
                {
                    temp=P3;
                  switch(temp)
                        {
                                case 0x7e:key_value=4;break;
                                case 0x7d:key_value=3;break;
                                case 0x7b:key_value=2;break;
                                case 0x77:key_value=1;break;
                        }
                }
           while(temp!=0x0f)
           {
             temp=P3;
                 temp=temp&0x0f;
           }

   }
           P3=0xbf; P4=0xfb;
   temp=P3;
   temp=temp&0x0f;
   if(temp!=0x0f)
        {
                delayms(5);
                temp=P3;
                temp=temp&0x0f;
                if(temp!=0x0f)
                {
                    temp=P3;
                  switch(temp)
                        {
                                case 0xbe:key_value=8;break;
                                case 0xbd:key_value=7;break;
                                case 0xbb:key_value=6;break;
                                case 0xb7:key_value=5;break;
                        }
                }
           while(temp!=0x0f)
           {
             temp=P3;
                 temp=temp&0x0f;
           }
        }

   P3=0xdf;
   temp=P3;
   temp=temp&0x0f;
   if(temp!=0x0f)
        {
                delayms(5);
                temp=P3;
                temp=temp&0x0f;
                if(temp!=0x0f)
                {
                    temp=P3;
                  switch(temp)
                        {
                                case 0xde:key_value=12;break;
                                case 0xdd:key_value=11;break;
                                case 0xdb:key_value=10;break;
                                case 0xd7:key_value=9;break;
                        }
                }
           while(temp!=0x0f)
           {
             temp=P3;
                 temp=temp&0x0f;
           }
        }

}
#endif               

使用特权

评论回复
地板
一路向北lm|  楼主 | 2018-2-23 11:12 | 只看该作者
必考点  IIC
IIC是每年的必考点 大家对这个要特别重视

本人当年比赛调试完毕的IIC公共驱动代码
#include <intrins.h>
#include <define.h>
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();}
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

//函数声明
void IIC_Start(void);
void IIC_Stop(void);  
void IIC_Ack(unsigned char ackbit);
void IIC_SendByte(unsigned char byt);
bit IIC_WaitAck(void);  
unsigned char IIC_RecByte(void);

//总线启动条件
void IIC_Start(void)
{
        SDA = 1;
        SCL = 1;
        somenop;
        SDA = 0;
        somenop;
        SCL = 0;       
}

//总线停止条件
void IIC_Stop(void)
{
        SDA = 0;
        SCL = 1;
        somenop;
        SDA = 1;
}

//应答位控制
void IIC_Ack(unsigned char ackbit)
{
        if(ackbit)
        {       
                SDA = 0;
        }
        else
        {
                SDA = 1;
        }
        somenop;
        SCL = 1;
        somenop;
        SCL = 0;
        SDA = 1;
        somenop;
}

//等待应答
bit IIC_WaitAck(void)
{
        SDA = 1;
        somenop;
        SCL = 1;
        somenop;
        if(SDA)   
        {   
                SCL = 0;
                IIC_Stop();
                return 0;
        }
        else  
        {
                SCL = 0;
                return 1;
        }
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
        unsigned char i;
        for(i=0;i<8;i++)
        {   
                if(byt&0x80)
                {       
                        SDA = 1;
                }
                else
                {
                        SDA = 0;
                }
                somenop;
                SCL = 1;
                byt <<= 1;
                somenop;
                SCL = 0;
        }
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
        unsigned char da;
        unsigned char i;
       
        for(i=0;i<8;i++)
        {   
                SCL = 1;
                somenop;
                da <<= 1;
                if(SDA)
                da |= 0x01;
                SCL = 0;
                somenop;
        }
        return da;
}




使用特权

评论回复
5
一路向北lm|  楼主 | 2018-2-23 11:18 | 只看该作者
必考点   AT24C02 EEPROM存储芯片
01.通过对AT24C02写入要保存的重要参数
02.通过对AT24C02读出想要的参数值
本次的AT24C02是基于IIC驱动之下的操作通过调用IIC的驱动函数来实现对AT24C02的操作
//at24c02发送数据
void send_out(uchar adder,uchar dat)
{
   IIC_Start();                      //iic起始信号
   IIC_SendByte(0xa0+0);  //写at24c02地址加读写方向位0
   IIC_WaitAck();         //从机应答
   IIC_SendByte(adder);    //写入首地址
   IIC_WaitAck();                  //从机应答
   IIC_SendByte(dat);     //iic发数据
   IIC_WaitAck();                  //从机应答
   IIC_Stop();                          //iic停止信号
}
//at24c02接收数据
uchar receive_dat(uchar adder)
{
    uchar dat;
        IIC_Start();            //iic 起始信号
        IIC_SendByte(0xa0+0);   //写at24c02地址加读写方向位0
        IIC_WaitAck();                        //从机等待
        IIC_SendByte(adder);         // 写入首地址
        IIC_WaitAck();                        //从机等待
        IIC_Start();                        //iic 起始信号
        IIC_SendByte(0xa0+1);         //写at24c02地址加读写方向位1
        IIC_WaitAck();                        //从机等待
        dat = IIC_RecByte();        //从at24c02读数据
        IIC_Ack(1);                                //主机非应答
        IIC_Stop();                                //iic停止信号
        return dat;
}


使用特权

评论回复
6
一路向北lm|  楼主 | 2018-2-23 11:22 | 只看该作者
必考点  PCF8591
01.通过PCF8591的AD功能对外部电压 电流 以及光照值得采集
02通过PCF8591的DA功能实现对外部输出不同电压变化值
PCF8591也是基于IIC的驱动下完成的,对于AD DA的操作都要建立在IIC之上的。
//DA 发送数据流程
void da_out(uchar dat)
{
   IIC_Start();                      //iic起始信号
   IIC_SendByte(0x90+0);  //写pcf8591地址加读写方向位0
   IIC_WaitAck();         //从机应答
   IIC_SendByte(0x40);   // DA 控制位
   IIC_WaitAck();                  //从机应答
   IIC_SendByte(dat);     //iic发数据
   IIC_WaitAck();                  //从机应答
   IIC_Stop();                          //iic停止信号
}
//AD  读数据流程
uchar adc_receive(uchar cnt)
{
    uchar dat;
        IIC_Start();            //iic 起始信号
        IIC_SendByte(0x90+0);   //写pcf8591地址加读写方向位0
        IIC_WaitAck();                        //从机等待
        IIC_SendByte(cnt);                 //发送控制字节
        IIC_WaitAck();                        //从机等待
        IIC_Start();                        //iic 起始信号
        IIC_SendByte(0x90+1);         //写pcf8591地址加读写方向位1
        IIC_WaitAck();                        //从机等待
        dat = IIC_RecByte();        //从pcf8591读数据
        IIC_Ack(1);                            //主机非应答
        IIC_Stop();                                //iic停止信号
        return dat;
}

使用特权

评论回复
7
caijie001| | 2018-2-23 11:26 | 只看该作者
赞,支持我北哥

使用特权

评论回复
评论
一路向北lm 2018-2-23 11:49 回复TA
感谢大佬支持 
8
gujiamao12345| | 2018-2-23 11:33 | 只看该作者

赞,支持我北哥

使用特权

评论回复
评论
一路向北lm 2018-2-23 11:49 回复TA
感谢大佬 
9
一路向北lm|  楼主 | 2018-2-23 11:48 | 只看该作者
必考点  DS1302时钟
01.对时钟的初始值设置
02.可以实现对时钟的调节,包括时分秒的调节(重点)
03.增加闹钟的功能,实现时钟的多样化
04.考虑数码管时钟的显示 消除亮度不均匀与鬼影现象。
以下是自己的 调试完毕的DS1302底层的驱动代码
/********************************************************************/
sbit SCK=P1^7;               
sbit SD=P2^3;               
sbit RST=P1^3;
/********************************************************************/
/*复位脚*/
#define RST_CLR        RST=0        /*电平置低*/
#define RST_SET        RST=1        /*电平置高*/
/*双向数据*/
#define SDA_CLR        SD=0        /*电平置低*/
#define SDA_SET        SD=1        /*电平置高*/
#define SDA_R        SD        /*电平读取*/       
/*时钟信号*/
#define SCK_CLR        SCK=0        /*时钟信号*/
#define SCK_SET        SCK=1        /*电平置高*/
/*********************************************************/
/*单字节写入一字节数据*/
void Write_Ds1302_Byte(unsigned char dat)
{
        unsigned char i;
        SCK = 0;
        for (i=0;i<8;i++)
        {
                if (dat & 0x01)         // 等价于if((addr & 0x01) ==1)
                {
                        SDA_SET;                //#define SDA_SET SDA=1 /*电平置高*/
                }
                else
                {
                        SDA_CLR;                //#define SDA_CLR SDA=0 /*电平置低*/
                }                 
                SCK_SET;
                SCK_CLR;               
                dat = dat >> 1;
        }
}
/********************************************************************/
/*单字节读出一字节数据*/
unsigned char Read_Ds1302_Byte(void)
{
        unsigned char i, dat=0;       
        for (i=0;i<8;i++)
        {       
                dat = dat >> 1;
                if (SDA_R)           //等价于if(SDA_R==1)    #define SDA_R SDA /*电平读取*/       
                {
                        dat |= 0x80;
                }
                else
                {
                        dat &= 0x7F;
                }
                SCK_SET;
                SCK_CLR;
        }
        return dat;
}

/********************************************************************/
/*向DS1302 单字节写入一字节数据*/
void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat)
{

        RST_CLR;                        /*RST脚置低,实现DS1302的初始化*/
        SCK_CLR;                        /*SCK脚置低,实现DS1302的初始化*/

        RST_SET;                        /*启动DS1302总线,RST=1电平置高 */
        addr = addr & 0xFE;         
        Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是写操作,写之前将最低位置零*/       
        Write_Ds1302_Byte(dat);         /*写入数据:dat*/
        RST_CLR;                                 /*停止DS1302总线*/
        SDA_CLR;
}

/********************************************************************/
/*从DS1302单字节读出一字节数据*/
unsigned char Ds1302_Single_Byte_Read(unsigned char addr)
{
        unsigned char temp;
        RST_CLR;                        /*RST脚置低,实现DS1302的初始化*/
        SCK_CLR;                        /*SCK脚置低,实现DS1302的初始化*/
                                       
        RST_SET;                      /*启动DS1302总线,RST=1电平置高 */       
        addr = addr | 0x01;         
        Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是读操作,写之前将最低位置高*/
        temp=Read_Ds1302_Byte(); /*从DS1302中读出一个字节的数据*/               
        RST_CLR;                     /*停止DS1302总线*/
        SDA_CLR;
        return temp;
}
//十进制转BCD
uchar dat_bcd(uchar dat)  //10进制转bcd
{
  uchar dat1,dat2;
  dat1=dat/10;
  dat2=dat%10;
  dat=dat1*16+dat2;
  return dat;
}
//BCD转十进制
uchar bcd_dat(uchar dat)
{
   uchar dat1,dat2;
   dat1=dat/16;
   dat2=dat%16;
   dat=dat1*10+dat2;
   return dat;

}
//初始化时间
void ds1302init()  //ds1302初始化
{
        Ds1302_Single_Byte_Write(0x8e,0x00);//关掉写保护
        Ds1302_Single_Byte_Write(0x80,0x55);//秒
        Ds1302_Single_Byte_Write(0x82,0x59);//分
        Ds1302_Single_Byte_Write(0x84,0x23);//时
        Ds1302_Single_Byte_Write(0x8e,0x80);//打开写保护
}

//读取时间
void readtime()
{
  Ds1302_Single_Byte_Write(0x8e,0x00);//关掉写保护
  miao= bcd_dat(Ds1302_Single_Byte_Read(0x81));//秒
  fen= bcd_dat(Ds1302_Single_Byte_Read(0x83)) ;//分
  shi= bcd_dat(Ds1302_Single_Byte_Read(0x85) );//时
  Ds1302_Single_Byte_Write(0x8e,0x80);//打开写保护
}
//关闭写保护
void guan1302()
{
Ds1302_Single_Byte_Write(0x8e,0x00);//关掉写保护
Ds1302_Single_Byte_Write(0x80,0x80);//关ds1302
Ds1302_Single_Byte_Write(0x8e,0x80);//开掉写保护

}


使用特权

评论回复
10
一路向北lm|  楼主 | 2018-2-23 11:54 | 只看该作者
必考点  DS18B20 温度传感器
01.实现对温度的采集 一般比赛要求显示温度的两位 不要求小数点后显示
02.采用中断 来实现间断采集温度 大概在200ms左右即可。
以下是封装好的ds18b20驱动程序 方便大家调用
/*
  程序说明: 单总线驱动程序
  软件环境: Keil uVision 4.10
  硬件环境: CT107单片机综合实训平台
  日    期: 2011-8-9
*/

//IC引脚定义
sbit DQ = P1^4;

//单总线延时函数
void Delay_OneWire(unsigned int t)
{
  unsigned char i;
  while(t--)   
  {
          for(i=0;i<12;i++);
  }
}

//DS18B20芯片初始化
bit Init_DS18B20(void)
{
        bit initflag = 0;
        DQ = 1;
        Delay_OneWire(12);
        DQ = 0;
        Delay_OneWire(80);
        DQ = 1;
        Delay_OneWire(10);
        initflag = DQ;   
        Delay_OneWire(5);

        return initflag;
}

//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
        unsigned char i;
        for(i=0;i<8;i++)
        {
                DQ = 0;
                DQ = dat&0x01;
                Delay_OneWire(5);
                DQ = 1;
                dat >>= 1;
        }
        Delay_OneWire(5);
}

//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
        unsigned char i;
        unsigned char dat;

        for(i=0;i<8;i++)
        {
                DQ = 0;
                dat >>= 1;
                DQ = 1;
                if(DQ)
                {
                        dat |= 0x80;
                }            
                Delay_OneWire(5);
        }
        return dat;
}
//读取温度
unsigned char Ds18b20Read()
{
        unsigned char Temp,Th,Tl;
        Init_DS18B20();
        Write_DS18B20(0xcc);
        Write_DS18B20(0x44);
        Delay_OneWire(200);

        Init_DS18B20();
        Write_DS18B20(0xcc);
        Write_DS18B20(0xbe);

        Tl = Read_DS18B20();
        Th = Read_DS18B20();

        Temp =(Th<<4)|(Tl>>4);
        return Temp;

}

使用特权

评论回复
11
21ic小喇叭| | 2018-2-23 16:51 | 只看该作者
赞,感谢楼主分享~

使用特权

评论回复
评论
一路向北lm 2018-2-23 19:26 回复TA
不辛苦 
12
619888476| | 2018-2-24 22:01 | 只看该作者
不错

使用特权

评论回复
评论
一路向北lm 2018-2-27 23:33 回复TA
感谢支持 
13
ankeseng| | 2018-2-27 09:08 | 只看该作者
赞,感谢楼主分享~

使用特权

评论回复
评论
一路向北lm 2018-2-27 23:33 回复TA
应该的 
14
cute3| | 2018-3-15 12:29 | 只看该作者
感谢楼主,很有帮助。

使用特权

评论回复
15
lihui567| | 2018-3-19 13:21 | 只看该作者
挺好的赞一个

使用特权

评论回复
评论
一路向北lm 2018-3-19 21:16 回复TA
感谢支持。 
16
atz1000| | 2018-4-10 13:32 | 只看该作者
牛X,程序写得很整齐,感谢分享。实际产品中,图方便EEPROM都完全不用了,就用单片机内部的flash好了,按键输入也简化成AD采样了,一个管脚输入就实现N多按键了,外围也就是电阻而已。至于RTC也使用单片机内部的了,DS1302好多年不用了,呵呵。温度变化也采用NTC电阻,DS18B20很贵的也用AD采样。STM32的AD采样太好用了

使用特权

评论回复
17
rime| | 2018-4-13 09:21 | 只看该作者
感谢

使用特权

评论回复
18
一路向北lm|  楼主 | 2018-4-13 18:18 | 只看该作者

没事的。

使用特权

评论回复
19
asean| | 2018-11-3 09:17 | 只看该作者
感谢分享。

使用特权

评论回复
20
左神之殇| | 2019-3-17 23:30 | 只看该作者
厉害

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

257

主题

3640

帖子

73

粉丝