2017-03-29 22:53:23 gxupter 阅读数 2130
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4000 人正在学习 去看看 朱有鹏

矩阵键盘是单片机应用中的一个重要的输入设备,单矩阵键盘扫描会占用MCU大量的时间,为了节约时间使程序运行更加流畅,一般实用中断方式进行矩阵键盘的扫描工作。


上图中从上往下前8个管脚连接单片机的8个I/O,最后一个连接单片机的外部中断管脚,用来触发中断。

2019-07-12 14:08:38 qq_36369267 阅读数 15
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4000 人正在学习 去看看 朱有鹏

(1)运行程序,打开数据窗口,观察地址 30H、31H、32H、33H的数据变化。用键盘输入改变地址 30H、31H、32H、33H 的
值,观察其实验效果。修改源程序中给 30H~33H 的赋值,观察实验效果。
(2)打开 CPU 窗口,选择单步或跟踪执行方式运行程序,观察 CPU 窗口各寄存器的变化。
(3)教材第 55 页 5、7、8 题。

;实验2-1
ORG 0000H
LJMP START
FUNC0: MOV 30H,#6
RET
FUNC1: MOV 31H,#7
RET
FUNC2: MOV 32H,#8
RET
FUNC3: MOV 33H,#9
RET
FUNCENTER:
ADD A,ACC ;AJMP 为二字节指令,调用号×2
MOV DPTR,#FUNCTAB
JMP @A+DPTR
FUNCTAB:
AJMP FUNC0
AJMP FUNC1
AJMP FUNC2
AJMP FUNC3
START:
MOV A,#0
CALL FUNCENTER
MOV A,#1
CALL FUNCENTER
MOV A,#2
CALL FUNCENTER
MOV A,#3
CALL FUNCENTER
SJMP $
END

第五题

MOV A,#56H
ADD A,#74H
ADD A,ACC

Cy=1, OV=0, A=94H

分析:

  01010110

+01110100

————————

11001010  (第二条指令执行完,CY=0,CY-1=1, OV=CY⊕CY-1=1)

 

  11001010 

+11001010 

————————

110010100(第三条指令执行完,CY=1,CY-1=1, OV=CY⊕CY-1=0)

而10010100=94H,由此得以上答案

 

第七题

MOV A,59H
MOV R0,A
MOV A,#0
MOV @R0,A
MOV A,#25H
MOV 51H,A
MOV 52H,#70H

A=25H, (50H)=0H, (51H)=25H, (52H)=70H


第八题

PUSH 30H  ;SP=(61H)     (SP)=(24H)
PUSH 31H  ;SP=(62H)     (SP)=(10H)
POP DPL    ;SP=(61H)      DPL=(10H)
POP DPH   ;SP=(60H)      DPH=(24H)
MOV A,#00H
MOVX @DPTR,A

最后的执行结果是:将0送到地址为24H的单元

2012-12-09 12:54:14 zhangxxxww 阅读数 2417
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4000 人正在学习 去看看 朱有鹏

            一开始接触单片机中断,或许大部分人不知道什么时候用的上,又该怎么样用。其实,中断的运用极大地提高了工作的效率,中断在自动化行业是时时刻刻都在用,就像我们的计算机,一开始按照内置程序运行,但是当我们敲打键盘或者是点击鼠标时,它就必须停下当前的工作来响应现在的工作。

    学习中断,就必须了解中断的寄存器和中断的优先级及中断如何使用如何定义等一系列的问题。

  一个简单的单片机中断初始化程序

  TMOD=0x01;//工作方式选择,选用T0

  设置初值

TH0=(65536-50000)/256;//高八位

TL0=(65536-50000)%256;//低八位

EA=1;//开总中断

ET0=1;//开定时器0中断

TR0=1;//启动定时器中断



2018-10-24 01:00:59 qq_42352666 阅读数 3661
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4000 人正在学习 去看看 朱有鹏

认识

中断

中断:中断是指单片机在运行别的代码是,系统可以通过中断方式打断连续的运行,先运行中断服务函数,开中断就是指系统可以在连续运行是中断,去运行中断服务函数,关中断就是指关闭系统中断,不允许系统打断连续的运行。
单片机的中断是一种非常有用的设置。我们在编写程序时都会在中断程序内设置一些简单的操作,比如对键盘的扫描程序、传感器返回数据读取程序等等。而且大多数中断都是有固定触发条件和时间的,我们可以利用这些进入中断的特定时间完成很多特定的事件。比如有1Hz的中断,还有2Hz的中断等等~~
当我们在程序里面设置成将所有中断打开,那么这个单片机的所有类型的中断都会在这个它特有的时间和条件下,进入它自己的中断程序去执行工作。如果我们关中断,那么所有类型的中断不论什么条件和时间都不会进入它自己的中断程序去执行工作了。很多单片机的中断是可以有区别的选择开关中断,而且可以有些开有些关闭。
提醒一下,在中断执行程序里面一定要把堆栈设置好哦,瑶及时保护各个常用寄存器内的数据,不然出了中断后的数据就乱套了~~

/*!
 *  @brief      main函数
 *  @since      v50
  *  @note       山.外摄像头 LCD 测试实验
 */
void  main(void)
{
    DisableInterrupts;        //关闭总中断

    PLL_Init(PLL200);         //初始化PLL为200M,总线为100MHZ   决定单片机处理速度
    camera_init(imgbuff);       //摄像头初始化
    UART_Init(UART_4,115200);   //蓝牙或串口初始化


    FTM_PWM_Init(FTM3,FTM_CH1,312,250);//PTE8    //电机1-A
    GPIO_Init(PTE,8,GPO,1);
    FTM_PWM_Init(FTM3,FTM_CH5,312,250);//PTE12    //电机2-A
    GPIO_Init(PTE,12,GPO,1);

    FTM_PWM_Init(FTM0 ,FTM_CH1,62500,duty);//PTC2 50000-125HZ

    EnableInterrupts;

    while(1)
    {
        camera_get_img();                                   //摄像头获取图像
        find_line();
        SendPicture();//调试时  一定要注释这一行!!!
    }
}



void SendPicture(void)
{
  int i=0,j=0;
   UART_Put_Char(UART_4,0x01);
   UART_Put_Char(UART_4,0xFE);
    for(i=0;i<OV7725_EAGLE_H;i++)
    {
      for(j=0;j<OV7725_EAGLE_W;j++)
      {
          UART_Put_Char(UART_4,Image_data[i][j]);
      }
    }
   UART_Put_Char(UART_4,0xFE);
   UART_Put_Char(UART_4,0x01);
}

寻线

上海交大的飞思卡尔寻线算法https://wenku.baidu.com/view/9d90428dd0d233d4b14e690f.html

串口

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。

蓝牙串口

1、无线蓝牙串口:
在这里插入图片描述
2、有线蓝牙串口:每队蓝牙串口后面都有排插,可以蓝牙间直接通过导线直接连接。
在这里插入图片描述
3、蓝牙串口硬件及程序部分

UART_Init(UART_4,115200);   //蓝牙或串口初始化 

以下是K66蓝牙串口示例:
在这里插入图片描述
在这里插入图片描述
其中RX和TX分别代表发送和接收,而单片机上的发送端和蓝牙串口的接收端相连接。
比如:单片机上的RX(发送端)和蓝牙串口上的TX(接收端)连接,单片机上的TX(接收端)和蓝牙串口上的RX(发送端)连接。如果在分线的时候连接错误,蓝牙就不会传出据,更不会出图像。

单工,半双工和全双工

1.单工数据传输只支持数据在一个方向上传输;在同一时间只有一方能接受或发送信息,不能实现双向通信,举例:电视,广播。

2.半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;在同一时间只可以有一方接受或发送信息,可以实现双向通信。举例:对讲机。
在这里插入图片描述

3.全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;在同一时间可以同时接受和发送信息,实现双向通信,举例:电话通信。

4.网卡的全双工(Full Duplex)是指网卡在发送数据的同时也能够接收数据,两者同步进行,这好像我们平时打电话一样,说话的同时也能够听到对方的声音.目前的网卡一般都支持全双工.

扩展资料:
单工、半双工和全双工是电信计算机网络中的三种通信信道。这些通信信道可以提供信息传达的途径。通信信道可以是物理传输介质或通过多路复用介质的逻辑连接。物理传输介质是指能够传播能量波的材料物质,例如数据通信中的导线。并且逻辑连接通常指电路交换连接或分组模式虚拟电路连接,例如无线电信通道。由于通信信道的帮助,信息可以无障碍地传输。

单工模式一般用在只向一个方向传输数据的场合。例如计算机与打印机之间的通信是单工模式,因为只有计算机向打印机传输数据,而没有相反方向的数据传输。还有在某些通信信道中,如单工无线发送等。

回显

所谓的回显,是指发送的数据都在接收端进行显示。

回显,就是显示正在执行的批处理命令及执行的结果等。常常指程序开发中执行命令的结果,就是返回的显示,你输入一个命令,然后给你返回一个值,显示在屏幕上。
在执行有些命令的时候,如果不希望显示返回值,就要消除回显。比如:在用串口处理通信中,使用AT命令,来检测Module与串口是否连通,能否接收AT命令。命令返回:OK (串口通信正常)(无返回,与串口通信未连通)。用 ATE0去除回显。

AT命令

AT命令有两种解释一种是调制解调器命令语言,另一种是Windows中的计划任务命令行。

调制解调器

调制解调器是一种计算机硬件,它能把计算机的数字信号翻译成可沿普通电话线传送的模拟信号,而这些模拟信号又可被线路另一端的另一个调制解调器接收,并译成计算机可懂的语言。这一简单过程完成了两台计算机间的通信。

调制解调器是Modulator(调制器)与Demodulator(解调器)的简称,中文称为调制解调器(港台称之为数据机),根据Modem的谐音,亲昵地称之为“猫”。它是在发送端通过调制将数字信号转换为模拟信号,而在接收端通过解调再将模拟信号转换为数字信号的一种装置。
所谓调制,就是把数字信号转换成电话线上传输的模拟信号;解调,即把模拟信号转换成数字信号。合称调制解调器。

线性CCD

CCD传感器:电荷耦合器件图像传感器CCD(Charge Coupled Device)
CCD传感器是一种新型光电转换器件,它能存储由光产生的信号电荷

COM口

别称:串行通讯端口,com口
COM口( cluster communication port )即串行通讯端口,简称串口。微机上的串口通常是9针,也有25针的接口,最大速率115200bps。通常用于连接鼠标(串口)及通讯设备(如连接外置式调制解调器进行数据通讯或一些工厂的数控机接口)等。一般主板外部只有一个串口,机箱后面和并口一起的那个九孔输出端(梯形),就是COM1口,COM2口一般要从主板上插针引出。并口是最长的那个梯形口。

阈值

阈的意思是界限,故阈值又叫临界值

二值化

图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。二值化(英语:Thresholding)是图像分割的一种最简单的方法。

图像数据

图像数据,都是从上往下,从左往右存储 (这个有点不确定)

上位机和下位机

上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/upper computer,屏幕上会显示各种信号变化。
下位机是指可以直接控制设备获取设备状况的计算机,一般是PLC/单片机之类。上位机发出的命令会给下位机,下位机根据此命令解释成相应时序信号直接控制相应设备。下位机会时不时的读取设备状态数据,然后在转换成数字信号反馈给上位机。
只有下位机发送的数据与上位机配置的格式的长度相同时,才可正确识别图像格式,从而正确显示图像。

摄像头调试助手

在这里插入图片描述
在这里插入图片描述

线性 CCD 调试助手

在这里插入图片描述

虚拟示波器

在这里插入图片描述
在这里插入图片描述

舵机 (电机放置在里面)

舵机,是指在自动驾驶仪中操纵飞机舵面(操纵面)转动的一种执行部件。分有:①电动舵机,由电动机、传动部件和离合器组成。

时钟定义列表

PDB

PDB 可以为内部或外部触发源提供可控制的延时,或者为 ADC 的硬件触发或产生 DAC 提供可编程的间隔。这样就可以为 ADC 转换和 DAC 输出的完成提供精确的时间。PDB 模块还可以提供脉冲输出,就行 CMP 模块 中的采样窗口一样。

弹性定时器(FlexTimer,FTM)

弹性定时器模块(FTM)是一个支持输入捕捉,输出比较和 PWM 信号产生来控制电动机和电源管理应用的 2-8 通道定时器。FTM 时间参考是一个可以作为无符号或有符号的计数器的 16 位计数器。
FTM 模块只有一个时钟域就是系统时钟。

1、计数器同步

在这里插入图片描述

2、 初始化触发器

如果 INITTRIGEN=1,那么当 FTM 计数器在以下情况下更新为 CNTIN 寄存器的值时,FTM产生一个触发器。FTM 计数器被选择为技术模式时,自动的更新为 CNTIN 寄存器的值。当对 CNT 寄存器写入时。当有 FTM 计数器同步时如果(CNT=CNTIN),(CLKS[1:0]=0:0),并且有个不为 0 的值写入 CLKS[1:0]位

3、PIC单片机的定时器寄存器与定时器周期寄存器的区别

定时器寄存器包括控制寄存器和周期寄存器,控制寄存器里可以设置定时器的分频比、使能、振荡源、中断使能等,而周期寄存器是用来存储定时器计数的次数的,比如16位定时器可以计数到65535次。
周期寄存器在TMR2中出现,可以设置最大值为FFH(默认值全1),你设置多少的值,它就在那个值溢出它并不是采用全1后自然溢出,而是取决于TMR2和周期寄存器比较的结果两者的内容一但匹配即刻发出溢出的信息

4、定时器

定时器在开启后周期性触发。定时器加载开始值,如它们 LDVAL 寄存器中描述的,然后递减计数直到为 0。然后再加载各自的开始值。每次定时器计数到 0 时,将产生一个触发脉冲并且置中断标志。所有的中断可以开启或屏蔽(通过设置 TCTRL 寄存器中断 TIE 位)。只有在前者被清除后,新中断才会产生。
如果需要,通过 CVAL 寄存器可以读取定时器的当前计数器的值。先关闭定时器,再通过 TEN 位开启定时器来重启计数周期。

指针

指针是一种特殊的数据类型,在其它语言中一般没有。指针是指向变量的地址,实质上指针就是存储单元的地址。根据所指的变量类型不同,可以是整型指针(int *)、浮点型指针(float *)、字符型指针(char *)、结构指针(struct *)和联合指针(union *)。

void 指针类型

顾名思义,void *为“无类型指针”,即用来定义指针变量,不指定它是指向哪种类型数据,但可以把它强制转化成任何类型的指针。众所周知,如果指针 p1 和 p2 的类型相同,那么可以直接在 p1 和 p2 间互相赋值;如果p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类
型转换为左边指针的类型。
【例如】:

 float *p1; //声明 p1 为浮点型指针
int *p2; //声明 p2 为整型指针 
p1 = (float *)p2;//强制转换整型指针 p2 为浮点型指针值给 p1 赋值
 而 void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转

换:

void *p1; //声明 p1 无类型指针
int *p2; //声明 p2 为整型指针
p1 = p2; //用整型指针 p2 的值给 p1 直接赋值

但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针,也就是说p2=p1 这条语句编译就会出错,而必须将 p1 强制类型转换成“void *”类型。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。

结构体

结构体是由基本数据类型构成的,并用一个标识符来命名的各种变量的组合。结构体中可以使用不同的数据类型。
1)结构体的说明和结构体变量的定义
【例如】定义一个名为 student 的结构体变量类型

struct student //定义一个名为student的结构体变量类型
{ char name[8]; //成员变量“name”为字符型数组
char class[10]; //成员变量“class”为字符型数组
int age; //成员变量“age”为整型
};

这样,若声明 s1 为一个“student”类型的结构体变量,则使用如下语句:
struct student s1; //声明s1为“student”类型的结构体变量
【例如】定义一个名为 student 的结构体变量类型,同时声明 s1 为一个“student”类型的
结构体变量

struct student //定义一个名为student的结构体变量类型
{ char name[8]; //成员变量“name”为字符型数组
char class[10]; //成员变量“class”为字符型数组
int age; //成员变量“age”为整型
}s1; //声明s1为“student”类型的结构体变量

2)结构体变量的使用
结构体是一个新的数据类型,因此结构体变量也可以像其它类型的变量一样赋值运算,
不同的是结构体变量以成员作为基本变量。
结构体成员的表示方式为:
结构体变量.成员名
如果将“结构体变量.成员名”看成一个整体,则这个整体的数据类型与结构体中该成员
的数据类型相同,这样就像前面所讲的变量那样使用。
【例如】:

s1.age=18; //将数据 18 赋给 s1.age(理解为学生 s1 的年龄为 18)

3)结构体指针
结构体指针是指向结构体的指针。它由一个加在结构体变量名前的“*”操作符来声明。
例如用上面已说明的结构体声明一个结构体指针如下:

struct student *Pstudent; //声明 Pstudent 为一个“student”类型指针

使用结构体指针对结构体成员的访问,与结构体变量对结构体成员的访问在表达方式上
有所不同。结构体指针对结构体成员的访问表示为:
结构体指针名->结构体成员
其中"->“是两个符号”-“和”>"的组合,好像一个箭头指向结构体成员。例如要给上面定
义的结构体中 name 和 age 赋值,可以用下面语句:

strcpy(Pstudent->name,"LiuYuZhang");
Pstudent->age=18;

实际上,Pstudent->name 就是(*Pstudent).name 的缩写形式。需要指出的是结构体指针是指向结构体的一个指针,即结构体中第一个成员的首地址,因此在使用之前应该对结构体指针初始化,即分配整个结构体长度的字节空间。这可用下面函数完成:

Pstudent=(struct student*)malloc(sizeof (struct student));
sizeof(struct student)自动求取 student 结构体的字节长度,
malloc()函数定义了一个大小为结构体长度的内存区域,然后将其地址作为结构体指针返回。

GPIO 通用输入输出

1、通用输入

通过引脚数据输入寄存器,可获得每个引脚的逻辑状态,提供的引脚为数字功能,相应的端口控制和中断模块有效而配置。引脚数据输入寄存器在端口控制和输入有效中的任何有效的数字过滤器之后返回异步引脚状态。输入引脚异步器共享端口控制和中断模块,为了如果相应的端口控制和中断模块在异步器失效的同时也失效。当一个端口对于普通目的输入功能不需要时,这减少了电源的消耗。

2、通用输出

每个引脚的逻辑状态可以通过引脚数据输出寄存器和引脚输出允许寄存器控制,提供的引脚被配置作为 GPIO 功能。如果引脚被配置为 PIO 功能并且相应的数据输出允许寄存器位被清除,那么引脚被配置为输入。如果引脚被配置为 GPIO 功能并且相应的数据输出允许寄存器位被置位,那么引脚被配置为输出并且引脚的逻辑状态与相应引脚数据输出寄存器相同。为了利用有效位实现通用输出,通过写单个寄存器,在一个端口里设置、清除或触发,引脚数据设置,引脚数据清除和引脚数据触发寄存器允许一个或多个输出。相应的端口控制和中断模块不需要允许更新引脚输出有效寄存器和引脚数据输出寄存器的状态(包括寄存器的设置/清除/触发)

通用异步接收器/发送器(UART)

UART 允许与外围设备及其他 CPU 进行异步串行通信。

2019-04-08 18:57:38 pro1515151515 阅读数 371
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4000 人正在学习 去看看 朱有鹏

引言

这个办法是我大二搞直立智能车的时候想出来的,当时直立智能车需要用矩阵键盘调PID参数,但一般的矩阵键盘算法都有一个“去抖延时”和“等待按键松开”的步骤,在这个步骤里程序是陷入死循环的,只有松开按键,程序才会继续运行。一般来说,按一下按键再松开,程序就要停顿一秒左右。普通的单片机程序停顿一秒钟没问题,但直立车停顿一秒钟就倒了。

于是有了这么一个矩阵键盘算法,可以用空间换时间,按一下按键程序只需要停顿6~11毫秒。不需要中断,实时性高,支持组合键。

要使用这个矩阵键盘算法,只需要三步。

C51代码(矩阵键盘连接P3口)

1.定义全局变量

uchar key_TTL[16];
uint keys_val=0;
uint last_keys_val=0;
uchar max_TTL=3;

2.在key_spy()函数中添加按键功能

void key_spy()
{
    uint temp=0;
    //双循环扫描法 + 译码
    uchar k,l;
    for(l=0;l<4;l++){
        P3=~(0x01<<l);
        for(k=0;k<4;k++){
            if((P3&(0x10<<k))==0) key_TTL[k+l*4]+=2;
        }
    }
    //TTL去抖
    for(k=0;k<16;k++){
        if(key_TTL[k]>0)key_TTL[k]--;
        if(key_TTL[k]>=max_TTL){
            key_TTL[k]--;
            temp=temp|(1<<k);
        }
    }
    keys_val=temp;
    //按键功能
    if(keys_val!=last_keys_val){
        if(keys_val==((1<<0))){
            //TODO:按下了0号键
			
        }
        else if(keys_val==((1<<3)|(1<<4)|(1<<5))){
            //TODO:同时按下了3号键、4号键、5号键
			
        }
        //TODO:加入更多else if,以扩充矩阵键盘功能

    }
    last_keys_val=keys_val;
}

3.主循环中添加key_spy()函数

void main()
{
	
	while(1)
	{
		key_spy();
		//TODO:主循环的其他事物 
        
	}
}

原理介绍

软件去抖的原理

key_spy()函数是检测矩阵键盘用的,放在主循环里就每隔一段时间检测一次。

检测第一次,发现有个按键被按下了,这有可能是电磁干扰造成的,不管它。

隔不久,检测第二次,发现刚才那个按键还是被按下的,这有可能是其他什么干扰造成的,不管。

相同的间隔,检测第三次,发现刚才那个按键仍被按下,就说明这个按键真的是被按下的。

对每个按键,都需要有一个计数器,就是用于计数的变量,这个变量称为“存活时间”,Time To Live,简称TTL。

一个TTL计数器,就对应一个不确定的事件(按键被按下),这个不确定的事件只有两个状态,发生了和没发生。事件的状态可以被检测,但检测得不一定准。每检测到事件发生了,事件的存活时间TTL就加1。检测到事件没发生,事件的存活时间TTL就减1。TTL的值必须大于等于0,小于等于阈值。当TTL等于阈值,就可以认为事件一定发生了。当TTL等于零,可以认为事件一定没发生。这就可以作为软件去抖的方法。

给矩阵键盘的16个按键分别设置一个TTL计数器,就可以对16个按键进行去抖。

组合键的原理

矩阵键盘是否支持组合键,跟矩阵键盘的扫描方法有很大关系。

线反转法

线反转法不支持组合键:

void key_spy1()
{
	//线反转法
	uchar flag=1,key,k;	
	P3=0xf0;
	key=P3&0xf0;
	P3=0x0f;
	key|=(P3&0x0f);
	switch(key)
	{
		case 0xee: key_TTL[0]+=2; break;
		case 0xde: key_TTL[1]+=2; break;
		case 0xbe: key_TTL[2]+=2; break;
		case 0x7e: key_TTL[3]+=2; break;
		case 0xed: key_TTL[4]+=2; break;
		case 0xdd: key_TTL[5]+=2; break;
		case 0xbd: key_TTL[6]+=2; break;
		case 0x7d: key_TTL[7]+=2; break;
		case 0xeb: key_TTL[8]+=2; break;
		case 0xdb: key_TTL[9]+=2; break;
		case 0xbb: key_TTL[10]+=2; break;
		case 0x7b: key_TTL[11]+=2; break;
		case 0xe7: key_TTL[12]+=2; break;
		case 0xd7: key_TTL[13]+=2; break;
		case 0xb7: key_TTL[14]+=2; break;
		case 0x77: key_TTL[15]+=2; break;
		default:break;
	}

	//check TTL
	for(k=0;k<16;k++)
	{
		if(key_TTL[k]>0)key_TTL[k]--;
		if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
		{
			key_TTL[k]--;
			key_val=k+1;
			flag=0;
		}
	}
	if(flag)key_val=0;//全局变量key_val即为键值
}

逐行扫描法

逐行扫描法支持跨行组合键,但不支持同行组合键:

void key_spy2()
{
	//逐行扫描法
	uchar flag=1,k;
	P3=0xfe;	
	switch(P3)
	{
		case 0xee: key_TTL[0]+=2; break;
		case 0xde: key_TTL[1]+=2; break;
		case 0xbe: key_TTL[2]+=2; break;
		case 0x7e: key_TTL[3]+=2; break;
	}
	P3=0xfd;	
	switch(P3)
	{
		case 0xed: key_TTL[4]+=2; break;
		case 0xdd: key_TTL[5]+=2; break;
		case 0xbd: key_TTL[6]+=2; break;
		case 0x7d: key_TTL[7]+=2; break;
	}
	P3=0xfb;	
	switch(P3)
	{
		case 0xeb: key_TTL[8]+=2; break;
		case 0xdb: key_TTL[9]+=2; break;
		case 0xbb: key_TTL[10]+=2; break;
		case 0x7b: key_TTL[11]+=2; break;
	}
	P3=0xf7;	
	switch(P3)
	{
		case 0xe7: key_TTL[12]+=2; break;
		case 0xd7: key_TTL[13]+=2; break;
		case 0xb7: key_TTL[14]+=2; break;
		case 0x77: key_TTL[15]+=2; break;
	}

	//check TTL
	for(k=0;k<16;k++)
	{
		if(key_TTL[k]>0)key_TTL[k]--;
		if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
		{
			key_TTL[k]--;
			key_val=k+1;
			flag=0;
		}
	}
	if(flag)key_val=0;//全局变量key_val即为键值
}

双循环扫描法

双循环扫描法支持同行组合键,但不支持三角键

void key_spy3()
{
	//双循环扫描法
	uchar flag=1,k;
	P3=0xfe;P3=0xfe;//高四位设为输入
	OLED_write_bchar(0,0,P3);
	if((P3&0x80)==0) key_TTL[0]+=2;
	if((P3&0x40)==0) key_TTL[1]+=2;
	if((P3&0x20)==0) key_TTL[2]+=2;
	if((P3&0x10)==0) key_TTL[3]+=2;
	P3=0xfd;P3=0xfd;//高四位设为输入
	if((P3&0x80)==0) key_TTL[4]+=2;
	if((P3&0x40)==0) key_TTL[5]+=2;
	if((P3&0x20)==0) key_TTL[6]+=2;
	if((P3&0x10)==0) key_TTL[7]+=2;
	P3=0xfb;P3=0xfb;//高四位设为输入
	if((P3&0x80)==0) key_TTL[8]+=2;
	if((P3&0x40)==0) key_TTL[9]+=2;
	if((P3&0x20)==0) key_TTL[10]+=2;
	if((P3&0x10)==0) key_TTL[11]+=2;
	P3=0xf7;P3=0xf7;//高四位设为输入
	if((P3&0x80)==0) key_TTL[12]+=2;
	if((P3&0x40)==0) key_TTL[13]+=2;
	if((P3&0x20)==0) key_TTL[14]+=2;
	if((P3&0x10)==0) key_TTL[15]+=2;


	//check TTL
	for(k=0;k<16;k++)
	{
		if(key_TTL[k]>0)key_TTL[k]--;
		if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
		{
			key_TTL[k]--;
			key_val=k+1;
			flag=0;
		}
	}
	if(flag)key_val=0;//全局变量key_val即为键值
}

双循环扫描法经过简化代码,并且附加上按键处理代码以后,就是上文的key_spy()函数。

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