【裸机开发】按键输入实验

目录

一、硬件原理分析

二、寄存器分析

1、时钟源初始化

2、设置 IO 复用 

3、初始化 IO 复用引脚(设置电气属性)

 4、初始化GPIO

三、汇编代码(start.s)

四、公共头文件(imx6u.h)

四、C 代码编写

1、clk 模块(bsp_clk.c)

2、led 模块(bsp_led.c)

2、delay 模块(bsp_delay.c)

3、key 模块(bsp_key.c)

4、main.c

五、链接脚本(imx6u.lds)

六、Makefile 文件(测试工程的整体结构)


一、硬件原理分析

我们在《imx6ull 底板原理图》中找到按键 KEY 模块。R12 是一个上拉电阻,当按键 KEY0 没有按下时,KEY0 处于高电平;当按键KEY0 按下时,KEY0 接地,此时KEY0 将变为低电平。

接下来我们要去 核心板原理图上找 KEY0 连接的哪个引脚。我们发现是和 UART1_CTS 这个引脚相连。

二、寄存器分析

1、时钟源初始化

这里就不再赘述,可以参考:时钟源初始化

2、设置 IO 复用 

按键属于 IO 的范畴,既然和 IO 有关,那我们就要去找第32章的 IOMUX(IO复用选择器)。根据之前的习惯,我们要找和 UART1_CTS 相关的复用寄存器。

IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B 就是我们要找的寄存器了。UART1_CTS 似乎可以复用为很多功能,我们选择的是复用为 GPIO1_IO18 功能。其他位不变,低四位设为 0101。

寄存器: IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B
基地址: 0x20E008C
初始化值: 0x5    # 低四位为0101,其他位不变

注意:

        虽然找的是 UART,但不代表是复用为 UART功能,UART 是用于数据传输的;GPIO 只是简单控制某个设备的开关,或者读取某个设备的状态。

3、初始化 IO 复用引脚(设置电气属性)

接下来我们要初始化复用引脚,此时要找前缀为 IOMUXC_SW_PAD_xxx 而且和 UART 相关的寄存器。然后设置每一位。多数都和驱动LED时的设置一样。(初始化 IO 复用

  • 0:0
  • 2-1:00
  • 5-3:000 (这里要禁用输出,相当于用于输入)
  • 7-6:10
  • 10-8:000
  • 11:0
  • 12:1
  • 13:1(选择 pull 功能)
  • 15-14:11(按键 KEY0 默认是上拉状态)
  • 16:0
  • 31-17:剩余为 0
寄存器: IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B
基地址: 0x20E0318
初始化值: 0xF080    

 4、初始化GPIO

从“设置IO复用”部分我们可以知道,UART_CTS 可被复用为 GPIO1_IO18,因此我们需要去初始化 GPIO1_IO18 引脚,设置的方式和驱动LED使用的方式类似。

寄存器: GPIO1_GDIR
基地址: 0x209C004
初始值: GPIO1_GDIR &= ~(1 << 18)    # 因为是GPIO1的第18个引脚设为输入,即第 18 bit应为0
 
寄存器: GPIO1_DR
基地址: 0x209C000
初始值: 
    - 低电平: GPIO1_DR &= ~(1 << 18)
    - 高电平: GPIO1_DR |= (1 << 18)

三、汇编代码(start.s)

汇编代码用于搭建C语言环境。关于汇编代码,可以参考之前驱动LED的汇编代码:汇编代码解析

四、公共头文件(imx6u.h)

公共头文件保存了要用到的寄存器的基地址。

#ifndef _IMX6U_H
#define _IMX6U_H

typedef unsigned int uint32_t;

/*
 * 时钟相关寄存器地址
 */
#define CCM_CCGR0          *((volatile uint32_t*)0x20C4068)
#define CCM_CCGR1          *((volatile uint32_t*)0x20C406C)
#define CCM_CCGR2          *((volatile uint32_t*)0x20C4070)
#define CCM_CCGR3          *((volatile uint32_t*)0x20C4074)
#define CCM_CCGR4          *((volatile uint32_t*)0x20C4078)
#define CCM_CCGR5          *((volatile uint32_t*)0x20C407C)
#define CCM_CCGR6          *((volatile uint32_t*)0x20C4080)

/*
 * IOMUX 相关寄存器地址
 */
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03   *((volatile uint32_t*)0x020E0068)        // IO复用为 GPIO1_IO03
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03   *((volatile uint32_t*)0x020E02F4)        // 设置复用引脚的电气属性

#define IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B *((volatile uint32_t*)0x20E008C)          // IO 复用为 GPIO1_IO18
#define IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B *((volatile uint32_t*)0x20E0318)          // 设置复用引脚的电气属性

/*
 * 设置GPIO输出相关寄存器地址
 */
#define GPIO1_DR            *((volatile uint32_t*)0x0209C000)         // GPIO输出
#define GPIO1_GDIR          *((volatile uint32_t*)0x0209C004)         // 设置输入还是输出

#endif 

四、C 代码编写

为了验证开关,我们加入之前的 led 代码,之前的 led 代码已经被封装好了,直接调用函数即可。我们的目的是通过开关控制灯的亮灭。

1、clk 模块(bsp_clk.c)

void clk_init()
{
    CCM_CCGR0 = 0xffffffff;
    CCM_CCGR1 = 0xffffffff;
    CCM_CCGR2 = 0xffffffff;
    CCM_CCGR3 = 0xffffffff;
    CCM_CCGR4 = 0xffffffff;
    CCM_CCGR5 = 0xffffffff;
    CCM_CCGR6 = 0xffffffff;
}

2、led 模块(bsp_led.c)

void led_init()
{
    /* 1、设置IO复用为GPIO1_IO03 */
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x5;

    /* 2、初始化复用引脚,设置电气属性 */
    IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;

    /* 3、初始化GPIO1 */
    GPIO1_GDIR |= (1 << 3);
}

void led_on()
{
    GPIO1_DR &= ~(1 << 3);
}

void led_off()
{
    GPIO1_DR |= (1 << 3);
}

void switch_led(uint8_t status)
{
    if (status == ON)
    {
        led_on();
    }
    else
    {
        led_off();
    }
}

2、delay 模块(bsp_delay.c)

void delay_short(volatile unsigned int n)
{
	while(n--){}
}

void delay(volatile unsigned int n)
{
	while(n--)
	{
		delay_short(0x7ff);
	}
}

3、key 模块(bsp_key.c)

void key_init()
{
    /* 1、设置IO复用为GPIO1_IO03 */
    IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B = 0x5;

    /* 2、初始化复用引脚,设置电气属性 */
    IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B = 0xF080;

    /* 3、初始化GPIO1 */
    GPIO1_GDIR &= ~(1 << 18);
}

int read_key()
{
    int ret = 1;
    static unsigned char release = 1;       /* 按键松开 */ 

    if ((release==1)&&((GPIO1_DR >> 18) & 0x01) == 0)
    {
        delay(10);		/* 延时消抖*/
        release = 0;	/* 标记按键按下 */
        if (((GPIO1_DR >> 18) & 0x01) == 0)
        {
            ret = 0;
        }
    }
    else if((GPIO1_DR >> 18) & 0x01 == 1)
    {
        ret = 1;
        release = 1;
    }
    return ret;
}

4、main.c

int main(void)
{
    clk_init();     // 初始化时钟
    led_init();     // 初始化LED
    key_init();     // 初始化按键
    led_on();       // LED 初始为亮

    while (1)
    {
        if (read_key() == 0)
        {
            led_off(); 
        }

        delay(1000);
        led_on();
    }
    
    return 0;
}

五、链接脚本(imx6u.lds)

SECTIONS
{
    . = 0x87800000;
    .text :
    {
        obj/start.o
        *(.text)
    }
    .rodata ALIGN(4) : { *(.rodata) }
    .data ALIGN(4) : { *(.data) }
    . = ALIGN(4);
    __bss_start = . ;
    .bss ALIGN(4) : { *(.bss) *(COMMON) }
    __bss_end = . ;
}

六、Makefile 文件(测试工程的整体结构)

测试工程的结构如下:

# ################################################################
# # 指定编译器、获取源文件、指定目标文件的生成位置
# ################################################################
# 编译器
TOOLCHAIN_PATH		:= /home/pigeon/workspace/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
CC					:= $(TOOLCHAIN_PATH)/arm-linux-gnueabihf-gcc
LD					:= $(TOOLCHAIN_PATH)/arm-linux-gnueabihf-ld
OBJCOPY				:= $(TOOLCHAIN_PATH)/arm-linux-gnueabihf-objcopy
OBJDUMP				:= $(TOOLCHAIN_PATH)/arm-linux-gnueabihf-objdump
 
# 目标文件名
TARGET_NAME		:= key
OBJ_DIR			:= obj

# 引入头文件
INCLUDES		:= -Icommon -Ibsp
 
# 获取到一级子目录下的源文件
DIR1			:= project
SOURCES 		+= $(wildcard ${DIR1}/*.c ${DIR1}/*.s)

# 获取到二级子目录下的源文件
DIR2 			:= bsp
SUBDIRS			= $(shell ls $(DIR2) -l | grep ^d | awk '{print	$$9}')
SOURCES			+= $(foreach subdir,${SUBDIRS}, $(wildcard $(DIR2)/${subdir}/*.c))

# 创建 $(OBJ_DIR) 目录
$(shell \
	if test -e $(OBJ_DIR); then \
		rm -rf $(OBJ_DIR)/*; \
	else \
		mkdir -p $(OBJ_DIR); \
fi)


# 生成 .o 文件
define compile
OBJ = $(if $(findstring .c,$1), $(patsubst %.c,%.o,$1), $(patsubst %.s,%.o,$1))
NODIR_OBJ = $$(notdir $$(OBJ))
$$(shell $$(CC) -o $$(OBJ_DIR)/$$(NODIR_OBJ) -c $1 $$(INCLUDES))
OBJ_FILE	+= $$(OBJ_DIR)/$$(NODIR_OBJ)
endef

OBJ_FILE		:= 
$(foreach src_file, ${SOURCES}, $(eval $(call compile, $(src_file))))	# 遍历每一个源文件,同时生成对应的 .o 文件

# ################################################################
# # 编译生成目标文件、库文件/执行文件
# ################################################################
default:
	$(LD) -Timx6u.lds -o $(OBJ_DIR)/$(TARGET_NAME).elf $(OBJ_FILE)
	$(OBJCOPY) -O binary -S -g $(OBJ_DIR)/$(TARGET_NAME).elf $(OBJ_DIR)/$(TARGET_NAME).bin
	$(OBJDUMP) -D $(OBJ_DIR)/$(TARGET_NAME).elf > $(OBJ_DIR)/$(TARGET_NAME).dis

# ################################################################
# # 伪目标/自定义函数
# ################################################################
.PHONY:clean
clean:
	rm -rf ${OBJ_DIR}/*
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值