单片机学习笔记 —— 导航按键

  在ADC模数转换的基础上,我们可以设计一个导航按键案例,通过将模拟电压转化为数字电压,根据数字电压值进行相应的处理。

一、导航按键电路

电流图

说明:左边有6个电阻,加起来700Ω,当按下/松开/拨动导航按键时,A/D转换输入的模拟电压有0、100、200、300、400、500、700六种组合。



二、A/D转换目标

实现效果:程序主要是对ADC进行操作,并将寄存器相应位取出分别用8位二极管和数码管显示。第一位数码管显示8位转换结果中前三位值,最后两位数码管显示后五位值。数码管下方的发光二极管与数码管对应显示。

程序运行效果说明
在数码管最高位(命名Seg0)显示转换结果高三位,点亮对应的发光二极管L7—L5。
数码管后两位(命名Seg6-Seg7)显示转换结果低五位,点亮对应的发光二极管L4—L0。
误差来源:电阻的工艺使得电阻会有一定的误差,导致实际数值有一点误差
解决办法:我们在做导航按键的判断时,都只是取高三位的值,也就是数码管Seg0的值。

数码管及二极管显示

操作 seg0 seg6-seg7参考值 (实际值)
无操作 7 31(0-31)
按下KEY3 0 00(0-31)
向右 1 06(0-31)
向下 2 12(0-31)
向里 3 17(0-31)
向左 4 22(0-31)
向右 5 25(0-31)


三、程序实现

变量定义:定义引脚别名,AD值相关变量和数码管显示相关的量。

#include <STC15F2K60S2.H>
#include <intrins.h>              //_cror_(); 

#define uint unsigned int
#define ulint unsigned long
#define uchar unsigned char
#define ADC_FLAG 0x10

/*---------引脚别名---------*/
sbit sbtSel0 = P2 ^ 0;
sbit sbtSel1 = P2 ^ 1;
sbit sbtSel2 = P2 ^ 2;  //位选
sbit sbtLedSel = P2 ^ 3;

/*---------变量定义---------*/
uint uiSampleNum = 0;  //采样次数
ulint uiAdSum = 0;     //AD值累加和
uint uiAdDate8 = 0;   //AD值八位数据
uint uiAdHigh3 = 0;   //AD值高3位
uint uiAdLow5 = 0;    //AD值低5位
uchar ucSegState;     //数码管扫描状态
//段选
char arrSegSelect[] = {
    
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
//位选
uchar arrDigSelect[] = {
    
    0x00, 0x06, 0x07};

初始化函数:设置推挽输出、中断和AD转换控制寄存器

// 初始化单片机SYS
void InitSYS()
{
    
    
    P0M1 = 0x00;
    P0M0 = 0xff;
    P2M1 = 0x00;
    P2M0 = 0x0E;
    ucSegState = 0;

    TMOD = 0x00;        //中断设置
    EA = 1; 
    TH0 = ( 65535 - 1000 ) / 256; 
    TL0 = ( 65535 - 1000 ) % 256;
    ET0 = 1;            //打开定时器
    TR0 = 1;            //启动定时器
}

//初始化ADC
void InitADC()
{
    
    
    P1ASF = 0x80;  //选择P1.7作为A/D使用=>可以检测到KEY3按下后的低电平
    ADC_RES = 0;//清零ADC寄存器
    ADC_CONTR = 0X8F;   //打开power 540周期转换一次 选择P1.7作为A/D输入来用   
    CLK_DIV = 0X00;         //ADRJ = 0  ADC_RES存放高8位结果
    EADC = 1;               //允许ADC中断
    PADC = 1;               //ADC中断优先级为1//ADCÖжÏÓÅÏȼ¶Îª1
}

处理子函数:控制数码管显示、ADC的数据采集和处理

void Divide3and5()		//分离AD转换结果的高3位
{
    
    
    uiAdHigh3 = uiAdDate8 & 0xE0;         //将8位转换结果的低5位清零
    uiAdHigh3 = _cror_( uiAdHigh3, 5 );      //循环右移5位
    uiAdLow5 = uiAdDate8 & 0x1F;          //将8位转换结果高5位清零
}

说明:_cror_循环右移函数,包含在头文件<intrins.h> 中:
_crol_(var, n):将var循环左移n位
_cror_(var, n):将var循环右移n位

// 定时器T0中断
void T0_Process() interrupt 1
{
    
    
    ucSegState++;
    if( ucSegState == 4 )
        ucSegState = 0;
    P0 = 0x00;
    switch( ucSegState )
    {
    
    
        case 0:
            sbtLedSel = 0;              //选择数码管亮
            P2 = arrDigSelect[ucSegState];
            P0 = arrSegSelect[uiAdHigh3];   //显示高三位对应的十进制
            break;
        case 1:
            sbtLedSel = 0;
            P2 = arrDigSelect[ucSegState];
            P0 = arrSegSelect[uiAdLow5 / 10]; //显示低5位对应的十进制的十位
            break;
        case 2:
            sbtLedSel = 0;
            P2 = arrDigSelect[ucSegState];
            P0 = arrSegSelect[uiAdLow5 % 10]; //显示低5位对应的十进制的个位
            break;
        case 3:
            sbtLedSel = 1;	// 发光二极管亮
            P0 = uiAdDate8;
            break;
    }
}

// ADC中断处理
void ADC_Process() interrupt 5
{
    
    
    IE = 0x00;                // 关闭中断
    ADC_CONTR &= ~0X10;       // ADC_FLAG清零
    uiSampleNum++;
    if( uiSampleNum > 1000 )          //采集数据1000次后
    {
    
    
        uiAdDate8 = ( uiAdSum + 500 ) / 1000; //四舍五入
        Divide3and5();               //分离高3位AD值
        uiSampleNum = 0;
        uiAdSum = 0;
    }
    uiAdSum += ADC_RES;
    ADC_CONTR |= 0X08;       // 将ADC_START置位
    IE = 0xa2;               //重新打开中断
}

主函数:初始化+死循环

void main()
{
    
    
    InitSYS();
    InitADC();
    while( 1 ) ;
}

将持续烧写到实验板中,当我们拨动导航按键时,数码管第一位显示不同的数字,最后两位显示30/31(存在工艺误差),由于选择P1.7作为A/D输入来用,所以当按下KEY3时,数码管显示0 00



完结 cheers! ??

猜你喜欢

转载自blog.csdn.net/qq_41140138/article/details/100086653