目录
[1.Makefile 执行流程](#1.Makefile 执行流程)
[2.Makefile 关键注意事项](#2.Makefile 关键注意事项)
一、代码部分
1.汇编部分
cs
.global _start
_start:
ldr pc, =_reset_handler
ldr pc, =_software_handler
ldr pc, =_undef_handler
ldr pc, =_prefetch_abort_handler
ldr pc, =_data_abort_handler
nop
ldr pc, =_irq_handler
ldr pc, =_fiq_handler
_software_handler:
b _software_handler
_undef_handler:
b _undef_handler
_prefetch_abort_handler:
b _prefetch_abort_handler
_data_abort_handler:
b _data_abort_handler
_irq_handler:
b _irq_handler
_fiq_handler:
b _fiq_handler
_reset_handler:
/*DDR 0x80000000 ~ 0xA0000000*/
cpsid i /*disable irq*/
ldr sp, =0x81000000 /*init system mode stack 16M */
cps #0x12 /*change to irq mode */
ldr sp, =0x82000000 /*init irq mode stack 16M */
cps #0x1f /* change to system mode */
ldr sp, =0x83000000 /*init irq mode stack 16M */
cpsie i /* enable irq */
bl main
code_end:
b code_end
从汇编部分跳转(bl)到main函数
2.主函数main.c
cs
#include "MCIMX6Y2.h"
#include "beep.h"
#include "fsl_iomuxc.h"
#include "key.h"
#include "led.h"
void ccm_ccgr_enable(void)
{
CCM->CCGR0 = 0xffffffff;
CCM->CCGR1 = 0xffffffff;
CCM->CCGR2 = 0xffffffff;
CCM->CCGR3 = 0xffffffff;
CCM->CCGR4 = 0xffffffff;
CCM->CCGR5 = 0xffffffff;
CCM->CCGR6 = 0xffffffff;
}
static void delay(unsigned int num)
{
while (num--)
;
}
int main(void)
{
ccm_ccgr_enable();
led_init();
beep_init();
key_init();
while (1)
{
if (key_get_value())
{
led_on();
beep_on();
}
else
{
led_off();
beep_off();
}
delay(0x80000);
}
return 0;
}
注意:在ARM汇编初始化阶段,切换工作模式后一定要初始化栈 ,这是非常关键且必须遵守的规则。因为ARM处理器的不同工作模式(如IRQ模式、系统模式等)各自拥有独立的栈指针SP,互不共享、互不继承。当CPU进入异常或中断时会自动切换到对应模式,如果该模式下的栈指针SP没有预先设置为合法有效的内存地址,CPU在压栈保存现场时就会使用无效或随机地址,直接导致程序跑飞、死机或数据损坏。因此在启动汇编中,每执行一次cps指令切换到新的工作模式,都必须紧跟一条ldr sp,=地址指令,为当前模式设置合法栈空间,保证中断、异常以及C语言运行时的栈操作安全可靠。
3.LED、BEEP、KEY功能代码
(1)led灯
cs
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "led.h"
void led_init(void)
{
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10b0);
// IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05;
// IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10b0;
GPIO1->GDIR |= (1 << 3);
GPIO1->DR |= (1 << 3);
}
void led_on(void)
{
GPIO1->DR &= ~(1 << 3);
}
void led_off(void)
{
GPIO1->DR |= (1 << 3);
}
(2)beep蜂鸣器
cs
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "beep.h"
void beep_init(void)
{
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0);
IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0x10b0);
//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1 = 0x05;
//IOMUXC_SNVS_SW_PAD_CTL_PAD_SNVS_TAMPER1 = 0x10b0;
GPIO5->GDIR |= (1 << 1);
GPIO5->DR |= (1 << 1);
}
void beep_on(void)
{
GPIO5->DR &= ~(1 << 1);
}
void beep_off(void)
{
GPIO5->DR |= (1 << 1);
}
(3)key按键
cs
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "key.h"
void key_init(void)
{
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0x10b0);
GPIO1->GDIR &= ~(1 << 18);
}
int key_get_value(void)
{
if(GPIO1->DR & (1<<18))
return 0;
else
return 1;
}
新知识:BSP、SDK
BSP 即板级支持包 ,简单来说就是对底层硬件操作进行封装的功能代码集合。它将直接操作寄存器、配置硬件参数等复杂底层逻辑,封装成简洁易用的接口函数,比如 adc_init、i2c_read、led_on 等,让上层程序可以方便地控制 LED、ADC、I2C、UART 等外设,而不用关心具体硬件细节,是衔接上层应用与底层硬件的关键代码层。
SDK**(Software Development Kit,软件开发工具)** 是芯片厂家(如 NXP、ST)官方提供的完整开发资源包,包含已经写好的底层驱动库、寄存器定义头文件、工具、示例代码和驱动 API。它屏蔽了复杂的寄存器操作,让开发者不用研究底层硬件细节,直接调用官方提供的标准化函数就能快速完成开发,是开发芯片的基础软件包,通用性强,适用于同型号的所有芯片。

二、makefile
makefile代码:
cs
target = key
cross_compiler = arm-linux-gnueabihf-
cc = $(cross_compiler)gcc
ld = $(cross_compiler)ld
objcopy = $(cross_compiler)objcopy
objdump = $(cross_compiler)objdump
incdirs = bsp sdk
srcdirs = bsp project
include = $(patsubst %, -I%, $(incdirs))
cfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c))
sfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S))
cfilenodir = $(notdir $(cfiles))
sfilenodir = $(notdir $(sfiles))
cobjs = $(patsubst %, obj/%, $(cfilenodir:.c=.o))
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o))
objs = $(cobjs) $(sobjs)
VPATH = $(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
@mkdir -p obj
$(cc) -Wall -nostdlib -c $(include) -o $@ $<
$(cobjs) : obj/%.o : %.c
@mkdir -p obj
$(cc) -Wall -nostdlib -c $(include) -o $@ $<
.PHONY : clean
clean:
rm -rf $(objs) $(target).elf $(target).bin $(target).dis
load:
./imxdownload ./$(target).bin /dev/sdb
1.Makefile 执行流程
- 找到所有
.c和.S - 编译成 .o 文件(只编译不链接)
- 把所有 .o 链接成 .elf
- 转成 .bin 可烧录文件
- 生成反汇编文件
- 可以 clean 清理、load 烧录
2.Makefile 关键注意事项
-
Makefile 编译时必须生成 .o 文件(只编译不链接),不能直接使用 .c 文件编译,否则会导致重复编译、效率低下,且无法实现按需更新和正确的链接流程。
-
Makefile 的 OBJ 目标文件列表中,start.o 必须放在最前面,main.o 等文件放在后面。因为程序启动需要先执行汇编启动代码完成硬件初始化、栈设置等工作,若 main.o 在前,链接器会先执行 main 函数,导致程序运行崩溃。
-
Makefile 内的通配符区分规则:%.s和 %.o中的 %是 Makefile 自带的通配符语法;*.o中的 * 属于 Shell 命令通配符,二者使用场景和规则不同,不能混用。
- 所有文件先编译成 .o,最后才链接
- .o 放在 obj 目录,不污染源码
- 自动找源码,不用手动写文件列表
- 最终生成 .bin 用于烧录
- 自动包含头文件路径