第十五届蓝桥杯单片机模拟考试实战题目

目录

一、硬件框图

二、功能描述

2.1基本功能描述

2.2显示功能

1.测距界面

2.参数界面

3.记录界面

4.显示要求

2.3按键功能

2.4旋钮模式

2.5LED指示灯功能

2.6初始状态

三、代码实现

1>主函数:

2>测距驱动函数

3>PCF8591驱动函数

4>矩阵按键驱动函数

5>CT107D头文件

四、题目链接:第十五届单片机模拟考试III (4t.wiki)


一、硬件框图

二、功能描述

2.1基本功能描述

1、通过超声波传感器完成距离测量功能; 2、通过 PCF8591完成试题要求的模拟功能: 3、通过LED指示灯完成试题要求的状态指示功能; 4、通过数码管、按键完成试题要求的数据显示、界面切换和参数设置等功能。

2.2显示功能

1.测距界面

测距界面如图2所示,显示内容包括标识符(A)和距离值组成。

距离值固定使用3位数码管显示,数码管显示不足3位时,高位(左侧)数码管熄灭。

距离值单位为:cm。

2.参数界面

参数界面如图3所示,由标识符(P))、模式值(按键模式:1;旋钮模式:2),参数下固定使用2位数码管显示参数下限/上限。限、间隔符和参数上限组成。参数上限、参数下限单位为:cm。

3.记录界面

记录界面如图4所示,由标识符(E)和报警次数组成。

测量的距离值由“参数下限≤测量的距离值≤参数上限”变为“测量的距离值>参数上限”或“测量的距离值<参数下限”时,报警次数加1;

注意:持续处于测量的距离值>参数上限或测量的距离值<参数下限时,报警次数不改变

4.显示要求

(1)按照题目要求的界面格式和切换方式进行设计。 (2)数码管显示无重影、闪烁、过暗、亮度不均匀等严重影响显示效果的缺陷。

2.3按键功能

(1)S4:定义为“切换”,按下S4按键,切换“测距界面”、“参数界面”和“记录界面”

(2)S5:在“参数界面”下,定义为“模式”,按下按键S5,切换参数调整模式。切换顺序如图6所示。

(3)S9:在“参数界面”下“按键模式”下,定义为“上限调整”,按下按键S9,参数上限加10。参数上限调整顺 序:50 60 70 80 90 50.“旋钮模式”下,定义为“上限”,按下按键S9,可使用旋钮调整当前参数上限。调整顺序详见2.4(1)。

(4)S8:在“参数界面”下, “按键模式”下,定义为“下限调整”,按下按键S8,参数下限加10。参数下限调整顺序: 0 10 20 30 40 0 10 . “旋钮模式”下,定义为“下限”,按下按键S8,可使用旋钮调整当前参数上限。调整顺序详见2.4(2)。

(5)按键要求 1、按键应做好消抖处理,避免出现一次按键动作导致功能多次触发。

2、按键动作不影响数码管显示等其他功能。

3、当前界面或模式下无功能的按键按下,不触发其它界面的功能。

4、参数上限调整范围“50-90” 参数下限调整范围“0-50”

2.4旋钮模式

使用PCF8591的ADC功能采集RB2的模拟信号,模拟旋钮功能调整参数上限或参数下限:

(1)“上限”调整过程如图7所示:

(2)“下限”调整过程如图8所示:

2.5LED指示灯功能

1>测距界面下指示灯L1 点亮,否则指示灯L1 熄灭。 2>参数界面下,指示灯 L2 点亮,否则指示灯 L2 熄灭。 3>记录界面下,指示灯L3 点亮,否则指示灯 L3 熄灭。 4>参数下限≤测量的距离值≤参数上限,指示灯L8点亮,否则L8以0.1s为间隔切换亮灭状 5>除L1、L2、L3和L8指示灯外,其余指示灯均处于熄灭状态。

2.6初始状态

请严格按照以下要求设计作品的上电初始状态。

1、处于测距界面 2、按键模式 3、参数上限60;参数下限10

三、代码实现

1>主函数:

// Header:      
// File Name: 
// Author:
// Date:
/********************预编译、宏和变量********************************************/
#include "main.h"
#include <stdio.h>
#include "iic.h"
#include "key.h"
#include "ultrasonic.h"
​
/*************************************************************
                     宏定义区域
**************************************************************/
#define CONTROL(x,y)    P0=y;P2=x;P2=0      //为锁存器的引脚操作封装一个带参宏    
​
enum PERIPHERAL
{
    LED = 0x80,
    DEVICE = 0xa0,
    BIT = 0xc0,
    SEG = 0xe0
};
​
/*******************************************************************************
                     变量定义
********************************************************************************/
/********************模板驱动变量****************************/
//控制LED0~7的开关量,正逻辑
bit LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7;
//控制蜂鸣器的自定义变量,正逻辑
bit Buzzer;
//控制继电器的自定义变量,正逻辑
bit Relay;
//Dsp_Bit是数码管显示的字符串数组,考虑到小数点的同位显示,所以字符串长度最长为16
uchar Dsp_Bit[16] = {0};
//数码管显示的各位段码内容,由 Dsp_Bit 通过函数Seg_Tran转换更新
uchar Dsp_Code[8] = {0};
//定时器计数
uint    Tick;
​
​
​
​
/********************中断区域***************************************************/
/*******************************************************************************
函数名程:   void Timer_2Init(void)
函数功能:   定时器T2 中断 1ms 配置
参数列表:   
返回值  :
*******************************************************************************/
void Timer_2Init(void)      //1000微秒@12.000MHz
{
    AUXR |= 0x04;       //定时器时钟1T模式
    T2L = 0x20;         //设置定时初值
    T2H = 0xD1;         //设置定时初值
    AUXR |= 0x10;       //定时器2开始计时
​
    IE2 |= 0x04;        //ET2 = 1;
    EA = 1;
}
​
/*******************************************************************************
函数名程:   void Timer2_Service() interrupt 12
函数功能:   定时器T2 中断 1ms 中断处理
参数列表:   
返回值  :
*******************************************************************************/
void Timer2_Service() interrupt 12
{
    static uchar dsp_com;
​
    //定时任务
    Tick++;     
    /***********控制外设的引脚操作*****/
    //更新LED的引脚操作
    CONTROL(LED,~((uchar)LED0|(uchar)LED1<<1|(uchar)LED2<<2|(uchar)LED3<<3|(uchar)LED4<<4|(uchar)LED5<<5|(uchar)LED6<<6|(uchar)LED7<<7));
    //更新蜂鸣器、继电器的引脚操作
    CONTROL(DEVICE,(uchar)Buzzer<<6|(uchar)Relay<<4);   
    //更新一位数码管的引脚操作
    CONTROL(BIT,0);                                 //消影
    CONTROL(SEG,Dsp_Code[dsp_com]);     //放入要显示的数字到段选锁存
    CONTROL(BIT,1 << (dsp_com));                //选择对应的位选锁存
    if(++dsp_com > 7)                               //显示完一次后自增位选,位选到8之后回到1
            dsp_com = 0;
}
​
/*******************************************************************************
函数名程:   void Seg_Tran(void)
函数功能:   将Dsp_Bit[]的内容,转换成对应的段码放入Dsp_Code[];
参数列表:   
返回值  :
*******************************************************************************/
void Seg_Tran(void)
{
  unsigned char i, j=0, temp;
  for (i=0; i<8; i++, j++)
  {
    switch (Dsp_Bit[j]) 
    { // 低电平点亮段,段码[MSB...LSB]对应码顺序为[dp g f e d c b a]
      case '0': temp = 0xc0; break;
      case '1': temp = 0xf9; break;
//    case '1': temp = 0xcf; break;
      case '2': temp = 0xa4; break;
      case '3': temp = 0xb0; break;
//    case '3': temp = 0x86; break;
      case '4': temp = 0x99; break;
//    case '4': temp = 0x8b; break;
      case '5': temp = 0x92; break;
      case '6': temp = 0x82; break;
//    case '6': temp = 0x90; break;
      case '7': temp = 0xf8; break;
//    case '7': temp = 0xc7; break;
      case '8': temp = 0x80; break;
      case '9': temp = 0x90; break;
//    case '9': temp = 0x82; break;
      case 'A': temp = 0x88; break;
      case 'B': temp = 0x83; break;
      case 'C': temp = 0xc6; break;
      case 'D': temp = 0xA1; break;
      case 'E': temp = 0x86; break;
      case 'F': temp = 0x8E; break;
      case 'H': temp = 0x89; break;
      case 'L': temp = 0xC7; break;
      case 'N': temp = 0xC8; break;
      case 'P': temp = 0x8c; break;
      case 'U': temp = 0xC1; break;
      case '-': temp = 0xbf; break;
      case ' ': temp = 0xff; break;
      case '^': temp = 0xfe; break; // 1 1 1 1 1 1 1 0
      case '_': temp = 0xf7; break; // 1 1 1 1 0 1 1 1
        default: temp = 0xff;
    }
    if (Dsp_Bit[j+1] == '.')
    {
      temp = temp&0x7f;             // 点亮小数点
      j++;
    }
    Dsp_Code[i] = temp;
  }
}
​
​
​
​
​
​
/********************应用层变量****************************/
//两个参数
char KeyNum;
//界面
uchar Page;
//按键返回值
uchar Key_Value;
//距离值
uchar Distance;
//参数下限值
int LB;
//参数上限值
int UB;
//报警次数计数
int Count = 0;  
 // 假设初始时Distance在范围内,1表示在范围内,0表示超出范围 
int lastInRange = 1;
//是否切换L8的切换状态
bit LED7_Flag;
//切换模式标志位
bit Ms_Flag;
//转化值
uint adc;
//旋钮模式
uchar rot;
​
uint adc;
​
​
/*******************************************************************************
函数名程:   void Display_Task(void)
函数功能:   显示内容执行任务,0.5s刷新一次显示内容
参数列表:   
返回值  :
*******************************************************************************/
void Display_Task(void)
{
    static uint Display_Tick;
    
    if((Tick - Display_Tick) > 500)
    {   
        Display_Tick = Tick;    
        switch (Page)
        {
            case 0://测距界面
                sprintf(Dsp_Bit, "A    %3u",(uint)Distance);//显示参数距离值
                LED0=1;LED1=0;LED2=0;
                break;
            case 1://参数界面
​
            //sprintf(Dsp_Bit, "P1   %3u",adc);//显示参数下限和上限
                if(!Ms_Flag)//模式1,按键
                {
                    sprintf(Dsp_Bit, "P1 %2u-%2u",(uint)LB,(uint)UB);//显示参数下限和上限
                }
                else//模式2,旋钮
                {
                    //sprintf(Dsp_Bit, "P1   %3u",adc);//显示参数下限和上限
                    sprintf(Dsp_Bit, "P2 %2u-%2u",(uint)LB,(uint)UB);//显示参数下限和上限
                }
                LED0=0;LED1=1;LED2=0;
                
                break;
            case 2://记录界面
                if(Count<10)
                {
                    sprintf(Dsp_Bit, "E      %d",(int)Count);//报警次数
                }
                else
                {
                    sprintf(Dsp_Bit, "E      -%d",(int)Count);//报警次数大于9
                }
                LED0=0;LED1=0;LED2=1;   
                break;
            default:
                break;
        }
​
​
        
        //sprintf(Dsp_Bit, "%9.2f", (float)KeyNum);
​
    }
    Seg_Tran();
}
​
/*******************************************************************************
函数名程:   void checkDistance(int currentDistance)
函数功能:   检查Distance并更新Count
参数列表:   
返回值  :
*******************************************************************************/
​
// 检查Distance并更新Count(这里Distance作为参数传入以模拟实际使用场景)  
void checkDistance(int currentDistance) {  
    int currentInRange = ((currentDistance >= LB) && (currentDistance <= UB)); // 检查当前Distance是否在范围内  
  
    // 如果当前状态与上一次状态不同,并且当前状态是超出范围,则增加Count  
    if (currentInRange != lastInRange && !currentInRange) {  
        Count++;  
          
    }  
  
    // 更新上一次的状态  
    lastInRange = currentInRange;   
}
​
//void taskrot(void)
//{
//  if(rot == 1){
//      if((adc >= 0)&&(adc < 20)){
//          LB = 0;
//      }else if((adc >= 20)&&(adc < 40)){
//          LB = 10;
//      }else if((adc >= 40)&&(adc < 60)){
//          LB = 20;
//      }else if((adc >= 60)&&(adc < 80)){
//          LB = 30;
//      }else if((adc >= 80)&&(adc < 100)){
//          LB = 40;
//      }
//  }else if(rot == 2){
//      if((adc >= 0)&&(adc < 20)){
//          UB = 50;
//      }else if((adc >= 20)&&(adc < 40)){
//          UB = 60;
//      }else if((adc >= 40)&&(adc < 60)){
//          UB = 70;
//      }else if((adc >= 60)&&(adc < 80)){
//          UB = 80;
//      }else if((adc >= 80)&&(adc < 100)){
//          UB = 90;
//      }
//  }
//}
/*******************************************************************************
函数名程:   void Key_Task(void)
函数功能:   按键执行任务,每10ms执行一次
参数列表:   
返回值  :
*******************************************************************************/
void Key_Task(void)
{
    static uint Key_Tick;                           
                                
    
    if((Tick - Key_Tick) > 10)
    {
        Key_Tick = Tick;
        
        Key_Value = MatKeyScan_4x4();   
        switch (Page)
        {
            case 0://测距界面
                switch (Key_Value)
                {
                    case 4:
                        Page=1;//切换到参数界面
                        break;
                    //case:
                        //break;
                    default:
                        break;
                }
                break;
            case 1://参数界面
                switch (Key_Value)
                {
                    case 4:
                        Page=2;//切换到记录界面
                        break;
                    case 5://模式切换
                        Ms_Flag=!Ms_Flag;
                        break;
                    case 9://上限调整(+10),初始值为50
                        if(!Ms_Flag)//按键模式
                        {
                            rot=0;
//                          if(UB<90)//参数上限调整顺序:50 60 70 80 90 50.
//                          {
//                              UB=UB+10;
//                          }
//                          else
//                          {
//                              UB=50;
//                          }
                            UB = (UB + 10) % 50 +50 ;
                        }
                        else//旋钮模式
                        {
                            rot = 1;//调整上限                              
                        }
                        break;
                    case 8://下限调整(+10),初始值为0    
                        if(!Ms_Flag)//按键模式
                        {
                            rot=0;
//                          if(LB<=40)//参数下限调整顺序:0 10 20 30 40 0 10 .
//                          {
//                              LB=LB+10;
//                          }
//                          else
//                          {
//                              LB=0;
//                          }
                            
                            LB = (LB + 10) % 50;
                        }
                        else//旋钮模式
                        {
                            rot = 2;//调整下限                  
                        }       
                        
                    default:
                        break;
                }
                break;
            case 2://记录界面
                switch (Key_Value)
                {
                    case 4:
                        Page=0;//切换到测距界面
                        break;
                    //case:
                        //break;
                    case 5:
                        Count=0;//清0功能
                    default:
                        break;
                }
                //if(Distance>UB||Distance<LB)//测量的距离值由“参数下限≤测量的距离值≤参数上限”变为“测量的距离值>参数上限”或“测量的距离值<参数下限”时,报警次数加1;注意:持续处于测量的距离值>参数上限或测量的距离值<参数下限时,报警次数不改变
                //{
                    checkDistance(Distance);
                //}
                break;
            default:
                break;
        }
    }
}   
​
​
/*******************************************************************************
函数名程:   void PCF8591_Data_Task(void)
函数功能:   定时处理数据的采集、处理、传输任务
参数列表:   
返回值  :
*******************************************************************************/
void PCF8591_Data_Task(void)
{
    static uint PCF8591_Data_Task;
    
    if((Tick - PCF8591_Data_Task) > 500)
    {   
        PCF8591_Data_Task = Tick;
        adc =(uint) PCF8591_Adc(3)*100/255;
        
    }
}
​
/*******************************************************************************
函数名程:   void Data_Task(void)
函数功能:   定时处理数据的采集、处理、传输任务
参数列表:   
返回值  :
*******************************************************************************/
void Data_Task(void)
{
    static uint Data_Tick;
    
    if((Tick - Data_Tick) > 100)
    {   
        Data_Tick = Tick;
        Distance=Dist_Meas();
    }
​
}
​
/*******************************************************************************
函数名程:   void Logic_Task(void)
函数功能:   定时按一定逻辑执行任务
参数列表:   
返回值  :
*******************************************************************************/
void Logic_Task(void)
{
    static uint Logic_Tick;
​
    if((LB<=Distance)&&(UB>=Distance))//参数下限≤测量的距离值≤参数上限,指示灯L8点亮
        {
            LED7=1;
        }
    else//否则L8以0.1s为间隔切换亮灭状
        {
            if((Tick - Logic_Tick) > 100)
                {   
                    Logic_Tick = Tick;
                    LED7=!LED7;
                }
        }   
}
​
/*******************************************************************************
函数名程:   void taskrot_Logic_Task(void)
函数功能:   定时按一定逻辑执行任务
参数列表:   
返回值  :
*******************************************************************************/
void taskrot_Logic_Task(void)
{
    static uint taskrot_Logic_Tick;
    if((Tick - taskrot_Logic_Tick) > 300)
    {   
        taskrot_Logic_Tick = Tick;
        if(rot == 2)//调整下限
        {
            if((adc >= 0)&&(adc < 20))
            {
                LB = 0;
            }
            else if((adc >= 20)&&(adc < 40))
            {
                LB = 10;
            }
            else if((adc >= 40)&&(adc < 60))
            {
                LB = 20;
            }
            else if((adc >= 60)&&(adc < 80))
            {
                LB = 30;
            }
            else if((adc >= 80)&&(adc < 100))
            {
                LB = 40;
            }
        }
        else if(rot == 1)//调整上限
        {
            if((adc >= 0)&&(adc < 20))
            {
                UB = 50;
            }
            else if((adc >= 20)&&(adc < 40))
            {
                UB = 60;
            }
            else if((adc >= 40)&&(adc < 60))
            {
                UB = 70;
            }
            else if((adc >= 60)&&(adc < 80))
            {
                UB = 80;
            }else if((adc >= 80)&&(adc < 100))
            {
                UB = 90;
            }
        }
​
    }
}
/*******************************************************************************
函数名程:   void P_Init(void)
函数功能:   外设初始化
参数列表:   
返回值  :
*******************************************************************************/
void P_Init(void)               //外设初始化
{
    CONTROL(LED,0xff);      //初始化LED熄灭
    CONTROL(DEVICE,0x00);   //初始化所有外设不工作
    CONTROL(BIT,0x00);      //初始化数码管位选无效
    CONTROL(SEG,0xff);      //初始化数码管段选无效
    //定时器T2时基1ms初始化
    Timer_2Init();
    LB=10;UB=60;//初始值
}
​
/*************************************************************
                      主函数
**************************************************************/
void main(void)
{
/********************初始化********************************************/
    P_Init();
    
/********************功能函数*******************************************/
    while(1)
    {
        PCF8591_Data_Task();
        Display_Task();
        taskrot_Logic_Task();
        //taskrot();
        Key_Task();
        Data_Task();
        Logic_Task();
    }        
}

2>测距驱动函数

#include "ultrasonic.h"
​
sbit TX = P1^0;
sbit RX = P1^1;
​
unsigned char Dist_Meas(void)
{
  unsigned int uiNum = 10;                  //每次发射产生10个周期的方波
​
  TMOD &= 0x0f;
  TMOD |= 0x10;                        // 设置T1为16位定时方式
#ifndef SRF04
  /*********TX引脚发送40KHz方波信号驱动超声波发送探头*******/
  TX = 0;
  TL1 = 0xf4;                       // 设置T1低8位定时初值
  TH1 = 0xff;                       // 设置T1高8位定时初值
  TR1 = 1;                          // 定时器1计时,大概为12us,产生40KHz左右的方波
  while (uiNum--)                                       //产生10个周期的方波,比较保险
  {
    while (!TF1);                   // 这里不用中断,阻塞判断TF,等待定时
    TX ^= 1;                                                //  12us时间到,翻转发射引脚,产生40KHz左右的方波
    TL1 = 0xf4;                     // 重装载T1低8位定时初值
    TH1 = 0xff;                     // 重装载T1高8位定时初值
    TF1 = 0;
  }
  TR1 = 0;
 #else
  TX = 1;                          // 触发测距
  while (uiNum--);               // 延时约10us
  TX = 0;
  uiNum = 5000;
  while (!RX && (uiNum>0))     // 等待测距脉冲
    uiNum--;
  if(uiNum==0)                   // 超时返回255
    return 255;
#endif 
  /************* 接收计时**************/
  TL1 = 0;                          // 设置定时初值
  TH1 = 0;                          // 设置定时初值
  TR1 = 1;
  while (RX && !TF1);               // 阻塞等待收到脉冲或定时器溢出
  TR1 = 0;                                                  // 收到脉冲或定时器溢出停止计时
  if(TF1)                           // 超时返回255
    return 255;
  else
    return ((TH1<<8)+TL1)*0.017+1;  // 计算距离:340*100/1000000/2
}

3>PCF8591驱动函数

#include "iic.h"
​
// I2C引脚定义
//sfr  P2 = 0xA0;
sbit SCL = P2^0;                // 时钟线
sbit SDA = P2^1;                // 数据线
​
void I2C_Delay(unsigned char i)
{
  while(i--);
}
void Delay100ms()       //@11.0592MHz
{
    unsigned char data i, j;
​
    i = 180;
    j = 73;
    do
    {
        while (--j);
    } while (--i);
}
// I2C起始条件
void I2C_Start(void)
{
  SDA = 1;
  SCL = 1;
  I2C_Delay(DELAY_TIME);
  SDA = 0;
  I2C_Delay(DELAY_TIME);
}
// I2C停止条件
void I2C_Stop(void)
{
  SDA = 0;
  SCL = 1;
  I2C_Delay(DELAY_TIME);
  SDA = 1;
  I2C_Delay(DELAY_TIME);
}
// I2C发送应答:0-应答,1-非应答
void I2C_SendAck(bit bAck)
{
  SCL = 0;
  SDA = bAck;
  I2C_Delay(DELAY_TIME);
  SCL = 1;
  I2C_Delay(DELAY_TIME);
  SCL = 0; 
  SDA = 1;
  I2C_Delay(DELAY_TIME);
}
// I2C等待应答
bit I2C_WaitAck(void)
{
  bit bAck;
​
  SCL  = 1;
  I2C_Delay(DELAY_TIME);
  bAck = SDA;
  SCL = 0;
  I2C_Delay(DELAY_TIME);
  return bAck;
}
// I2C发送数据
void I2C_SendByte(unsigned char ucData)
{
  unsigned char i;
​
  for (i=0; i<8; i++)
  {
    SCL  = 0;
    I2C_Delay(DELAY_TIME);
    if (ucData & 0x80)
      SDA  = 1;
    else
      SDA  = 0;
    I2C_Delay(DELAY_TIME);
    SCL = 1;
    ucData <<= 1;
    I2C_Delay(DELAY_TIME);
  }
  SCL  = 0;
}
// I2C接收数据
unsigned char I2C_RecvByte(void)
{
  unsigned char i, ucData;
​
  for (i=0; i<8; i++)
  {
    SCL = 1;
    I2C_Delay(DELAY_TIME);
    ucData <<= 1;
    if (SDA)
      ucData |= 1;
    SCL = 0;
    I2C_Delay(DELAY_TIME);
  }
  return ucData;
}
#define AT24C02
#ifdef AT24C02
// AT24C02缓存器写:pucBuf-数据,ucAddr-地址,ucNum-数量
void AT24C02_WriteBuffer(unsigned char *pucBuf,
  unsigned char ucAddr, unsigned char ucNum)
{
  I2C_Start();
  I2C_SendByte(0xa0);           // 发送器件地址及控制位(写)
  I2C_WaitAck();
​
  I2C_SendByte(ucAddr);         // 发送数据地址
  I2C_WaitAck();
​
  while (ucNum--)
  {
    I2C_SendByte(*pucBuf++);    // 发送数据
    I2C_WaitAck();
    I2C_Delay(200);
  }
  I2C_Stop();
  Delay100ms();
  Delay100ms();
}
// AT24C02缓存器读:pucBuf-数据,ucAddr-地址,ucNum-数量
void AT24C02_ReadBuffer(unsigned char *pucBuf,
  unsigned char ucAddr, unsigned char ucNum)
{
  I2C_Start();
  I2C_SendByte(0xa0);           // 发送器件地址及控制位(写)
  I2C_WaitAck();
​
  I2C_SendByte(ucAddr);         // 发送数据地址
  I2C_WaitAck();
​
  I2C_Start();
  I2C_SendByte(0xa1);           // 发送器件地址及控制位(读)
  I2C_WaitAck();
​
  while (ucNum--)
  {
    *pucBuf++ = I2C_RecvByte();     // 接收数据
    if (ucNum)
      I2C_SendAck(0);
    else
      I2C_SendAck(1);
  }
  I2C_Stop();
  Delay100ms();
  Delay100ms();
}
​
#endif
#define PCF8591
#ifdef PCF8591
// PCF8591 ADC:ucAin-ADC通道(0~3),返回值-ADC值
//1:光敏 3:电位器
unsigned char PCF8591_Adc(unsigned char ucAin)
{
  unsigned char ucAdc;
    EA = 0;
  I2C_Start();
  I2C_SendByte(0x90);           // 发送器件地址及控制位(写)
  I2C_WaitAck();
​
  I2C_SendByte(ucAin + 0x40);   // 发送控制字(ADC通道,允许DAC)
  I2C_WaitAck();
​
  I2C_Start();
  I2C_SendByte(0x91);           // 发送器件地址及控制位(读)
  I2C_WaitAck();
​
  ucAdc = I2C_RecvByte();       // 接收ADC值
  I2C_SendAck(1);
  I2C_Stop();
    EA = 1;
  return ucAdc;
}
// PCF8591 DAC: ucDac-DAC值
void PCF8591_Dac(unsigned char ucDac)
{
    EA = 0;
  I2C_Start();
  I2C_SendByte(0x90);            // 发送器件地址及控制位(写)
  I2C_WaitAck();
​
  I2C_SendByte(0x40);            // 发送控制字(允许DAC)
  I2C_WaitAck();
​
  I2C_SendByte(ucDac);           // 发送DAC值
  I2C_WaitAck();
  I2C_Stop();
    EA = 1;
}
#endif
 

4>矩阵按键驱动函数

#include "key.h"
​
​
/*******************************************************************************
函数名程:    KeyEvent_TypeDef KeyScan(void)
函数功能:    检测独立按键是否按下,按下则返回对应键值
返回值  :    S4_PRESS(4):S4按下
            S5_PRESS(5):S5按下
            S6_PRESS(6):S6按下
            S7_PRESS(7):S7按下
            UNPRESS(0):未有按键按下
*******************************************************************************/
uchar KeyScan(void)
{
 
    static uchar S4_value = 0xFF;
    static uchar S5_value = 0xFF;
    static uchar S6_value = 0xFF;
    static uchar S7_value = 0xFF;
    static uchar lock = 0;
    
    //2ms读取一次,读取前左移,将新读取的状态放在最低位,0为按下,1为抬起
    //连续8次都读取到按键按下,keyx_value值变为0x00时方才触发按下事件
    S4_value <<= 1;
    S4_value |= S4;
    S5_value <<= 1;
    S5_value |= S5;
    S6_value <<= 1; 
    S6_value |= S6;
    S7_value <<= 1;
    S7_value |= S7;
    
    if((!S4_value||!S5_value||!S6_value||!S7_value) && !lock)   //如果任意一个按键触发且按键未上锁
    {
        lock = 1;               //上锁,并返回相应键值
        if(!S4_value)
            return 4;
        else if(!S5_value)
            return 5;
        else if(!S6_value)
            return 6;
        else if(!S7_value)
            return 7;
    }
    else if(S4_value && S5_value && S6_value && S7_value && lock)           //如果已上锁且所有按键都不触发(触发按键已抬起)
    {
        lock = 0;                                                   
    }
    else{                                                                   //
    }
//  Delayms(2);
    return 0;  
}
​
/*******************************************************************************
函数名程:    uchar MatKeyScan_4x4(void)
函数功能:    检测矩阵按键是否按下,按下则返回对应键值
返回值  :    S4~S19
*******************************************************************************/
uchar MatKeyScan_4x4(void)
{
    static uchar    pre_scan = 0,    //continue value
                    pre_trg = 0;            //last trigger value
    uchar   scan = 0,            //trigger value
            trg = 0,                //current value
            value = 0,             //必须初始化为3
            key_x = 0,
            key_y = 0;
​
    P3 = 0x0f;
    P4 = 0x00;
    if(!P30)        key_x = 3;          //获取X轴坐标
    else if(!P31)   key_x = 2;
    else if(!P32)   key_x = 1;
    else if(!P33)   key_x = 0;
​
    P3 = 0xf0;
    P4 = 0xff;
    if(!P34)        key_y = 4;          //获取Y轴坐标
    else if(!P35)   key_y = 3;
    else if(!P42)   key_y = 2;
    else if(!P44)   key_y = 1;
​
    scan = key_x + key_y * 4;        //根据按键连线和键值设置得出的键值计算式
    
    if(scan == pre_scan)            //消抖:只有连续两次检测相同才触发,否则无效
        trg = scan;
    else
        trg = 0;
    
    if(trg == pre_trg)              //自锁:连续相同的触发,不再产生键值(0)
        value = 0;
    else 
        value = trg;                //前后不同的触发,更新成后一次的键值。如果是按下S15,即前一次触发0,后一次触发15,更新15
    
    pre_scan = scan;             //每次检测后,保存值至PRE里以便下次比较
    pre_trg = trg;
    
    return value;    //返回独立按键的键值
}
​
#define LONG_PRESS_THRESHOLD 100 // 长按阈值设定,单位为10ms
​
uchar MatKeyScan_2x4(void)
{
    static uchar scanState = 0;   // 扫描状态
    static uchar pressDuration = 0; // 按下持续时间计数器
    static uchar keyValue = 0;            // 按键值局部变量
​
    // 设置列线为高电平
    P44 = 1;
    P42 = 1;
        P34 = 1;
        P35 = 1;
        // 设置行线为高电平
        P33 = 0; 
        P32 = 0; 
    switch (scanState) {
        case 0: // 准备扫描
            if (P44 == 0 || P42 == 0 || P34 == 0 || P35 == 0) { // 检查是否有键被按下
                scanState = 1;
            }
            break;
        case 1: // 确定按键按下
​
            if (P44 == 0) { // 检查第一列
                keyValue = 4;
            } else if (P42 == 0) { // 检查第二列
                keyValue = 8;
            } else if (P35== 0) { // 检查第一列
                keyValue = 12;
            } else if (P34 == 0) { // 检查第二列
                keyValue = 16;
            } else {
                // 如果两行都不是低电平,说明之前的检测可能是干扰
                scanState = 0;
                break;
            }
                        // 设置列线为低电平
                        P44 = 0;
                        P42 = 0;
                        P34 = 0;
                        P35 = 0;
                        // 设置行线为高电平
                        P33 = 1; 
                        P32 = 1; 
            if (keyValue == 4) {    // 按键在第一列
                if (P33 == 0) keyValue = 4; // S4
                if (P32 == 0) keyValue = 5; // S5
            } else if (keyValue == 8) { // 按键在第二列
                if (P33 == 0) keyValue = 8; // S8
                if (P32 == 0) keyValue = 9; // S9
            }else if (keyValue == 12) { // 按键在第三列
                if (P33 == 0) keyValue = 12; // S12
                if (P32 == 0) keyValue = 13; // S13
            }else if (keyValue == 16) { // 按键在第四列
                if (P33 == 0) keyValue = 16; // S16
                if (P32 == 0) keyValue = 17; // S17
            }
                        scanState = 2;
                        pressDuration = 0; // 开始计时
            break;
        case 2: // 按键释放,检测长按
            if (P44 && P42 & P34 & P35) {
//                // 按键释放
//                if (pressDuration == LONG_PRESS_THRESHOLD) { // 长按了
//                                  keyValue = keyValue | 0x80; // 输出长按键值                                               
//                } else {  // 不是长按 键值不变
//                              }
//                              pressDuration = 0; // 计时清零
                                scanState = 0; // 重置状态
                                return keyValue; // 返回短按键值
//            } else {
//                // 按键持续被按下
//                if (pressDuration < LONG_PRESS_THRESHOLD) {
//                                  pressDuration++; // 更新持续时间
//                }
            }
            break;
    }
​
    return 0; // 无按键操作
}
​
 

5>CT107D头文件

#ifndef __MAIN_H
#define __MAIN_H
​
#include "stc15.h"
​
typedef unsigned int uint;  //对系统默认数据类型进行重定义
typedef unsigned char uchar;
​
#endif

四、题目链接:第十五届单片机模拟考试III (4t.wiki)

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值