嵌入式开发学习日志Day8(ARM体系架构——按键、蜂鸣器及中断)

发布于:2025-06-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、蜂鸣器学习

 

代码实现:

二、BSP工程管理及Makefile

1、BSP工程管理

利用BSP工程管理,使文档显示不杂乱;

        将这些文件分为4类,并保存到4个不同的文件夹里。

        首先在新的工程文件夹里创建一个之后我们编写的类似led驱动,clk驱动等等外设驱动程序都放在这文件夹里面,创建名为bsp

 再创建project文件夹,存放 start.s和 main.c 文件,也就是应用文件;

   一个imx6ull文件夹,用来保存NXP的相关库cc.h、fsl_common.h、fsl_iomuxch和 MCIMX6Y2.h 这四个文件;

最后再创建一个obj文件夹,用来存放编译生成的.o 文件。

2、Makefile 

target = led

cross_compiler = arm-linux-gnueabihf-

cc = $(cross_compiler)gcc
ld = $(cross_compiler)ld
objcopy = $(cross_compiler)objcopy
objdump = $(cross_compiler)objdump

incdirs = bsp imx6ull #所有包含头文件的文件夹
srcdirs = bsp project #所有包含源文件的文件夹

include = $(patsubst %, -I%, $(incdirs))  #处理了头文件之后生成了$(incdirs),然后在生成的每个文件前面加-I
#I是include,-Idsp意思是使用的头文件去dsp文件夹找

cfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c))  #将所有源文件中的.c找出来,但是得到的结果带目录。eg:文件名/main.c
sfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S))   #将所有源文件中的.s找出来,但是得到的结果带目录。eg:文件名/main.S

cfilenodir = $(notdir $(cfiles)) #去掉了目录名,得到的是main.c
sfilenodir = $(notdir $(sfiles))#去掉了目录名,得到的是main.S

cobjs = $(patsubst %, obj/%, $(cfilenodir:.c=.o)) #编译之后生成的结果是main.o,(.c)需要放入obj的目录中去,obj/表示放在obj这个目录中去
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o)) #编译之后生成的结果是main.o,(.S)需要放入obj的目录中去,obj/表示放在obj这个目录中去

objs = $(cobjs) $(sobjs)  #将.c生成的obj和.S生成的obj放在一起

VPATH = $(srcdirs)  #表示如果找源文件找不到的话,就去srcdirs中去查找;

$(target).bin : $(objs)
	$(ld) -Timx6ull.lds -o$(target).elf $^
	$(objcopy) -O binary -S -g $(target).elf $@
	$(objdump) -D $(target).elf > $(target).dis

$(sobjs) : obj/%.o : %.S    #.S生成sobj
	@mkdir -p obj   #如果没有obj文件,创建obj目录,@符号表示,创建的过程中没有回显,,-p表示如果这个目录已存在,则不报错
	$(cc) -Wall -nostdlib -c $(include) -o $@ $<

$(cobjs) : obj/%.o : %.c  #.c生成cobj
	@mkdir -p obj     #如果没有obj文件,创建obj目录,@符号表示,创建的过程中没有回显,,-p表示如果这个目录已存在,则不报错
	$(cc) -Wall -nostdlib -c $(include) -o $@ $<

.PHONY : clean
clean:
	rm -rf $(objs) $(target).elf $(target).bin $(target).dis

三、 按键练习

代码实现:

#include"key.h"
#include"MCIMX6Y2.h"
#include"fsl_iomuxc.h"
#include "core_ca7.h"
#include "gpio.h"
void init_key(void)
{
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_UART1_CTS_B,0x10F0);
    GPIO_Pin_Config_t t = 
    {
        .direction = GPIO_Direction_In
    };
    gpio_pin_config(GPIO1,18,&t);

    GPIO1->ICR2 |= (3 << 18);
    GPIO1->IMR |= (1 << 18);

    GIC_SetPriority(GPIO1_Combined_16_31_IRQn,1);
    GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);

}

int key_pressed(void)
{
    return((GPIO1->DR & (1 << 18)) == 0) ? 1 : 0;

}

四、中断

4.1 GIC控制器(通用中断控制器)

 VFIQ/VIRQ中V指虚拟化的;

        GIC 接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给 ARM 内核,这四个信号的含义分别为: VFIQ:虚拟快速 FIQ。 VIRQ:虚拟 IRQ。 FIQ:快速中断 IRQ。 IRQ:中断 IRQ。

作用:

  1. 作用是中断优先级排序;
  2. 中断屏蔽的控制;

    GIC控制器(v2版本)最多处理8个内核;最多有1020个中断源:

    SGI  (软件中断):( 0 - 15 ),,软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR

    写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
    PPIs(私有中断):(16 - 31), GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断;
    SPI(共享中断):(32-1019), (注意!不是 SPI 总线那个中断),这类中断泛指所有的外设中断;如定时器、外部中断、串口中断等。

            实际用到的只有(32-159)128个,这128是imx6ull所支持的

            PS:GIC也可以屏蔽不需要的中断
                    V2版本的GIC不在ARM内部,V3、V4版本的是64位

4.2 异常向量表

        异常向量表重映射是指在处理器发生异常时,通过改变异常向量表的物理地址与逻辑地址之间的映射关系

        目的:确保系统能够正确响应和处理异常。

        各模式下的sp只能在进入对应模式后 才能设置,这是因为每种模式的sp都是分离的。 这里分别设置irq、sys、svc模式下的大小都为2MB。

异常状态返回地址偏移量:当异常状态发生以后,返回地址和lr中保留的地址偏移量

4.3 协处理器

        协处理器:用于减轻系统微处理器特定处理任务的芯片;

        cortex A7 共16个协处理器,CP0~CP15

4.3.1 mcr指令与mrc指令 

用mcr与mrc来访问协处理器;

mcr写入协处理器;

mrc读取协处理器;


协处理器编号p0-p15;

读取出来的寄存器内容:

SCTLR寄存器:


eg:mrc p15, 0, r0, c0, c0, 0(mrc指令读取MIDR(主ID)寄存器,读出来的结果放入了寄存器R0中)

    CPSR分为I位(bit[7](0不屏蔽  1屏蔽))、F位(bit[6] 0不屏蔽 1屏蔽)

CPS指令

        这里的 effect 分为俩个bit[7](IE使能 cpsie(0)、ID失能cpsid(1)),使用了effect的话就不能省略iflags,i位指irq,f位指frq

PS:在汇编中调main.c中的函数时,要先保护现场

获取中断号,并记录 

GPIOx_ICR        //设置中断寄存器 

GPIOx_IMR        //设置中断屏蔽寄存器 ,若为1,开中断

GPIOx_ISR        //设置中断标记寄存器 ,若为1,则说明该位产生中断,但要手动清零

4.4  抢占优先级

        Cortex——A7 有32个抢占优先级(谁数小,谁的优先级就高)

        1 1111 ---> 32个

        0(组优先级) xxxx(子优先级)

五、提高代码的耦合性

gpio.c

#include "gpio.h"

void gpio_pin_config(GPIO_Type *base, int pin, GPIO_Pin_Config_t *config)   //初始化函数,GPIo引脚的配置,   
// 参数分别是GPIO_Type类型的base,将GPIO的组号传入   参数2:引脚号     参数3:初始化引脚
{
    if(config->direction == GPIO_Direction_Out)     //判断  将相关寄存器,方向配置为输出
    {
        base->GDIR |= (1 << pin);
        if(config->defalut_value != 0)   //该引脚的默认值为高电平
        {  
            base->DR |= (1 << pin);   
        }
        else   // 该引脚的默认应该配置为低电平
        {
            base->DR &= ~(1 << pin);   
        }
    }
    else  //需要将该引脚配置为低电平
    {
        base->GDIR &= ~(1 << pin);
    }
}

void gpio_write(GPIO_Type *base, int pin, int value)  //写入函数,参数分别是:参数1:参数分别是GPIO_Type类型的base,将GPIO的组号传入
// 参数2:引脚   参数3:需要写入的值
{
    if(value)       //判断写入的值是不是1,如果是1的话,则在其DR写入1;
    {
        base->DR |= (1 << pin);  //在输入得引脚写入1
    }
    else    // 如果不是,则进行指定位清0
    {
        base->DR &= ~(1 << pin);     //在输入得引脚写入0
    }
}

int gpio_read(GPIO_Type *base, int pin) // 读函数,参数分别是GPIO_Type类型的base,第二个参数:引脚
{
    return (base->DR & (1 << pin)) != 0;
}

gpio.h

#ifndef _GPIO_H_
#define _GPIO_H_
#include "MCIMX6Y2.h"
typedef enum
{
    GPIO_Direction_Out,
    GPIO_Direction_In
}GPIO_Direction_t;   //枚举的方法列举GPIO两种状态,输入或者输出的方向

typedef struct 
{
    GPIO_Direction_t direction;  // 引脚的工作方式,列:输入或者输出
    int defalut_value;       // 初始化引脚后,该引脚的默认值是高电平还是低电平
}GPIO_Pin_Config_t;    //    初始化引脚


extern void gpio_pin_config(GPIO_Type *base, int pin, GPIO_Pin_Config_t *config);
extern void gpio_write(GPIO_Type *base, int pin, int value);
extern int gpio_read(GPIO_Type *base, int pin);


#endif


网站公告

今日签到

点亮在社区的每一天
去签到