嵌入式按键中断控制 LED 及蜂鸣器实验

Posted 啥也不想,只想搞钱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式按键中断控制 LED 及蜂鸣器实验相关的知识,希望对你有一定的参考价值。

1. 操作内容

  1. 掌握中断编程步骤
  2. 掌握中断系统相应寄存器的使用
  3. 掌握中断控制程序的编写、编译、运行
    利用按键SW4和SW5控制LED和蜂鸣器。按下按键SW4时,产生中断,蜂鸣器间隔鸣响10次;按下按键SW5时,产生中断,LED灯从左向右跑马灯闪烁10次。编写系统的启动代码、中断控制程序、头文件以及Makefile文件,编译得到可执行文件,下载至开发板,实现在开发板上启动系统及按键控制功能。

2. 原理解释

2.1 实验箱按键电路

按键使用GPIO接口,但按键本身需要外部的输入。按键硬件驱动原理图如图1所示。在下图的1×6矩阵按键(SW4~SW9)电路中,使用6个输入(EINT2、EINT3、EINT4、EINT8、EINT9和EINT11)。

图 1 按键电路
图 1 按键电路

按键入口对应的核心板接口电路如图2所示。

图 2 核心板按键接口电路
图 2 核心板按键接口电路

按键入口对应于核心板的GPH0接口,当其中一个SW按键被按下,通过查询方式就可以 检测到是哪一个接口有输入信号,从而控制相应的操作。前述的按键工作原理都是在按键 的理想状态下进行的,实际的按键动作会在短时间(几毫秒至几十毫秒)内产生信号抖动。 例如,当按键被按下时,其动作就像弹簧的若干次往复运动,将产生几个脉冲信号。一次按键操作将会产生若干次按键中断,从而会产生抖动现象。因此驱动程序中必须要解决去除抖 动所产生的毛刺信号的问题。

2.2 实验箱LED电路

LED使用GPIO接口,对应于核心板的GPJ2接口,其接口电路如图3所示。当GPJ2接口 输出低电平时,LED亮;输出高电平时,LED灭。

图 3 LED 接口电路
图 3 LED 接口电路

2.3 实验箱蜂鸣器电路

蜂鸣器GPIO接口,对应于核心板的GPD0接口,其接口电路如图4所示。当GPD0接口输出高电平时,蜂鸣器鸣响。

在这里插入图片描述
图 4 蜂鸣器电路

2.3 寄存器说明

按键对应的GPH0相关寄存器以及LED对应的GPJ2相关寄存器功能如下图所示。

图 4 GPH0CON 寄存器功能
图 5 GPH0CON 寄存器功能
图 5 GPJ2CON 和 GPJ2DAT 寄存器地址
图 6 GPJ2CON 和 GPJ2DAT 寄存器地址

图 6 GPJ2CON 寄存器功能
图 6 GPJ2CON 寄存器功能
图 7 GPJ2CON 寄存器功能

图 7 GPJ2DAT 寄存器功能
图 8 GPJ2DAT 寄存器功能

在这里插入图片描述
图 9 PWM 定时器功能引脚与 GPIO 对应引脚

在这里插入图片描述
图 10 GPD0CON 寄存器功能

在这里插入图片描述
图 11 GPD0DAT 寄存器功能

3. 操作步骤

3.1 编写键控制 LED 代码,将代码编译为二进制文

a) 在 ubuntu 系统中,进入共享文件夹 forlinux,新建 key 文件夹。
b) 进入 key 文件夹,新建启动文件 start.S,并添加启动代码(参考附录 1)。
c) 新建按键控制代码文件 key.c,自行编写 c 语言代码。
d) 新建 Makefile 文件,添加编译命令(参考附录 2),由以下命令编译生成二进制文件210.bin。

$ make clean
$ make

在这里插入图片描述

3.2 安装 USB 驱动

将实验箱中的拨码开关 2 拨到 on,长按 Power 键直至电脑提示安装驱动。打开计算机 设备管理器,选择下图所示硬件安装驱动。

在这里插入图片描述
图 8 设备管理器界面
在这里插入图片描述

右键选择更新驱动程序,手动添加 USB 驱动程序路径“D:\\新 509\\04-常用工具\\DNW”。 在图 9 所示驱动安装过程中,关闭开发板的电源,将拨码开关 2 重新置为 OFF 状态。

在这里插入图片描述

图 9 驱动程序安装

驱动安装完成提示如图 10 所示。

在这里插入图片描述

图 10 驱动安装成功

3.2 使用 DNW 软件下载裸机程序至 SRAM 中运行

1)用 USB device 线连接电脑和开发板,设置开发板为 nandflash 启动(拨码开关全部拨 至 OFF 状态)。
2)在目录“D:\\新 509\\04-常用工具\\DNW”中打开 DNW.exe。设置串口:波特率为 115200, USB Port 为 Download,Address 为 0xd0020010。
3)在菜单栏开启 DNW 串口连接(Serial Port ->Connect)。启动开发板后立即在 DNW 窗 口迅速敲击空格键进入 Uboot 状态,可见图 11 启动界面

在这里插入图片描述
图 11 开发板 Uboot 启动界面

4)在 DNW 窗口中输入“dnw 0xd0020010”设置下载地址。如果 DNW 驱动安装失 败或首次使用 DNW,会提示安装驱动,请正确安装驱动,等到提示硬件可使用从进行下一 步。
5)在 DNW 菜单中,选择 usbport->Transmit->Transmit 发送生成的 210.bin 文件,DNW 自动下载 210.bin 文件至开发板。 (210.bin的生成和下载,参考实验九。)

在这里插入图片描述

6)在 DNW 窗口中输入“go 0xd0020010”,即可开始运行 210.bin 程序。 注意,将二进制文件下载到 SRAM 中不会破坏开发板中现有文件与程序,但是掉电后所下载的文件将丢失。

4. 操作结果

在这里插入图片描述

附件一:start.S 文件

.global _start 
.global key_isr                   
_start:
	ldr sp, =0x40000000             
	mrs r0, cpsr      
	bic r0, r0, #0x00000080     
	msr cpsr, r0  	       
	bl main                    
halt:
	b halt    
key_isr:      
	sub lr, lr, #4     
	stmfd sp!, {r0-r12, lr}     
	bl key_handle    
	ldmfd sp!, {r0-r12, pc}^    

附件二:addheader.c 文件

/* 
** 在BL0阶段,iROM内固化的代码读取nandflash或SD卡前面最大16K的内容(即BL1)到iRAM, 
** 并比对前16字节中的校验和是否正确,正确则继续,错误则尝试启动下一个设备。 
** BL1的头信息规定如下 
** 0x0:BL1的大小(最大16K,包括BL1头信息的大小) 
** 0x4: 0(规定) 
** 0x8:校验和 
** 0xC:0(规定) 
*/  
#include <stdio.h>  
#include <string.h>  
#include <stdlib.h>  
  
#define IMG_SIZE                (16*1024)  
#define HEADER_SIZE             16  
#define BLKSIZE                 512  
  
int main (int argc, char *argv[])  
{  
    FILE            *fp;  
    unsigned        char *Buf;  
    int             BufLen;  
    int             nbytes, fileLen;  
    unsigned int    checksum, count;  
    int             i;  
  
    if (argc != 3)  
    {  
        printf("Usage: %s <source file> <destination file>\\n", argv[0]);  
        return -1;  
    }  
  
    /* 分配16K的buffer */  
    BufLen = IMG_SIZE;  
    Buf = malloc(BufLen);  
    if (!Buf)  
    {  
       perror("Alloc buffer failed!");  
        return -1;  
    }  
    memset(Buf, 0x00, BufLen);  
  
    /* 读源bin到buffer */  
    fp = fopen(argv[1], "rb");  
    if( fp == NULL)  
    {  
        perror("source file open error");  
        free(Buf);  
        return -1;  
    }  
    /* 获取源bin长度 */  
    fseek(fp, 0L, SEEK_END);  
    fileLen = ftell(fp);  
    fseek(fp, 0L, SEEK_SET);  
  
    /* 源bin长度不得超过16K-16byte */  
    fileLen = (fileLen < (IMG_SIZE - HEADER_SIZE)) ? fileLen : (IMG_SIZE - HEADER_SIZE);  
  
    /* 读源bin到buffer[16] */  
    nbytes = fread(Buf + HEADER_SIZE, 1, fileLen, fp);  
    if (nbytes != fileLen)  
    {  
        perror("source file read error\\n");  
        free(Buf);  
        fclose(fp);  
        return -1;  
    }  
    fclose(fp);  
      
    /* 计算校验和 */  
    for(i = 0, checksum = 0; i < fileLen; i++)  
        checksum += Buf[HEADER_SIZE + i];  
  
    /* 计算BL1的大小: 
    ** BL1的大小包括BL1的头信息 
    ** 另外iROM从SD卡拷贝是按块拷贝的,因此这里需要调整大小为512字节的整数倍 
    */  
    fileLen += HEADER_SIZE;  
    count = fileLen / BLKSIZE * BLKSIZE;  
    if (count < fileLen)  
        count += BLKSIZE;  
    memcpy(Buf, &count, 4);     // 保存BL1的大小到Buf[0-3]  
  
    // 将校验和保存在buffer[8~15]  
    memcpy(Buf + 8, &checksum, 4);  
  
    fp = fopen(argv[2], "wb");  
    if (fp == NULL)  
    {  
        perror("destination file open error");  
        free(Buf);  
        return -1;  
    }  
    // 将count + HEADER_SIZE字节的buffer拷贝到目的bin中  
    nbytes  = fwrite(Buf, 1, count, fp);  
    if (nbytes != count)  
    {  
        perror("destination file write error");  
        free(Buf);  
        fclose(fp);  
        return -1;  
    }  
  
    free(Buf);  
    fclose(fp);  
  
    return 0;  
}  

附件三:Makefile 文件

key.bin:start.o key.o
	arm-linux-ld -Ttext 0xd0020010 -o key.elf $^
	arm-linux-objcopy -O binary key.elf key.bin
	arm-linux-objdump -D key.elf > key.dis
key.o:key.c
	arm-linux-gcc -nostdlib -c $< -o $@
start.o:start.S
	arm-linux-gcc -nostdlib -c $< -o $@
clean:
	rm *.o *.elf *.bin *.dis

附件四:key.c 文件

#define		GPD0CON   (*(volatile unsigned long *)0xE02000A0)
#define		GPD0DAT   (*(volatile unsigned long *)0xE02000A4)
#define    GPJ2CON             *((volatile unsigned int *)0xE0200280)
#define    GPJ2DAT             *((volatile unsigned int *)0xE0200284)
#define    GPH0CON             *((volatile unsigned int *)0xE0200C00)  
#define    GPH0DAT             *((volatile unsigned int *)0xE0200C04)  
#define    EXT_INT_0_CON       *((volatile unsigned int *)0xE0200E00) 
#define    EXT_INT_0_MASK      *((volatile unsigned int *)0xE0200F00)
#define    VIC0IRQSTATUS       *((volatile unsigned int *)0xF2000000)
#define    VIC0INTSELECT       *((volatile unsigned int *)0xF200000C) 
#define    VIC0INTENABLE       *((volatile unsigned int *)0xF2000010)  
#define    VIC0VECTADDR2       *((volatile unsigned int *)0xF2000108)  
#define    VIC0VECTADDR4       *((volatile unsigned int *)0xF2000110) 
#define    VIC0ADDRESS         *((volatile unsigned int *)0xF2000F00)  
#define    EXT_INT_0_PEND      *((volatile unsigned int *)0xE0200F40)
 
//extern void buzzer_on(void); 
//extern void buzzer_init(void); 
//extern void buzzer_off(void); 

extern void key_isr(void);
 
void buzzer_init(void)
{ 
      GPD0CON |= 1<<8;
}

void buzzer_on(void)
{
      GPD0DAT |= 1<<2;
}

void buzzer_off(void)
{
      GPD0DAT &= ~(1<<2);

}

void delay(unsigned long times) 
{ 
    unsigned long i= times; 
    while (i--) 
     ; 
}

void led_init(void)
{
	GPJ2CON &= ~(0xFFFF << 0);
    GPJ2CON |= ((0x1 << 0) | (0x1 << 4) | (0x1 << 8) | (0x1 << 12));
    GPJ2DAT |= 0xF << 0;
}

void key_init(void)
{
	GPH0CON |= 0xF0F << 8;                  
    EXT_INT_0_CON &= ~(0xF0F << 8);  		
    EXT_INT_0_CON |= (2 << 8) | (2 << 16);          
    EXT_INT_0_MASK &= ~0x14;
}

void int_init(void)
{
	VIC0INTSELECT &= ~0x14;                     
    VIC0INTENABLE |= 0x14;                        
    VIC0VECTADDR2 = (int)key_isr;          
    VIC0VECTADDR4 = (int)key_isr; 
}

void key_handle()  
{     
    volatile unsigned char key_code = VIC0IRQSTATUS & 0x14;  
    VIC0ADDRESS = 0;        
    EXT_INT_0_PEND |= 0x14;    
    
    if (key_code == 0x4)      /* sw4 */
    {
	int num = 10;
	while (num--)
	{

		buzzer_on(); 
	        delay(0x50000); 
		buzzer_off(); 
	        delay(0x50000); 
		
	}
    } 
    else if( key_code == 0x10 )		/* SW6 */
    {
	int num=10;
	while(num--)
	{
	    GPJ2DAT ^= 1 << 0; 
		delay(0x50000);
		GPJ2DAT ^= 1 << 1;
	 	delay(0x50000);
		GPJ2DAT ^= 1 << 2;
		delay(0x50000);
		GPJ2DAT ^= 1 << 3;
		delay(0x50000);
	}
   }
    
}

int main()  
{  
    buzzer_init();
	led_init();
	key_init();
	int_init();
     
    while(1);    
    return 0;  
}

以上是关于嵌入式按键中断控制 LED 及蜂鸣器实验的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式按键中断控制 LED

STM32——外部中断

嵌入式开发学习之--抢答器

ESP327.按键实验(中断)

嵌入式系统外部中断实验(按下按键,LED灯依次熄灭)

嵌入式按键控制 LED 实验