一、蜂鸣器学习
代码实现:
二、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 = ledcross_compiler = arm-linux-gnueabihf-cc = $(cross_compiler)gcc
ld = $(cross_compiler)ld
objcopy = $(cross_compiler)objcopy
objdump = $(cross_compiler)objdumpincdirs = 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.Scfilenodir = $(notdir $(cfiles)) #去掉了目录名,得到的是main.c
sfilenodir = $(notdir $(sfiles))#去掉了目录名,得到的是main.Scobjs = $(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。
作用:
- 作用是中断优先级排序;
- 中断屏蔽的控制;
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