ARM嵌入式学习(四)--- C语言应用:led、beep、key

目录

一、代码部分

1.汇编部分

2.主函数main.c

3.LED、BEEP、KEY功能代码

(1)led灯

(2)beep蜂鸣器

(3)key按键

二、makefile

[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 执行流程

  1. 找到所有 .c 和 .S
  2. 编译成 .o 文件(只编译不链接)
  3. 把所有 .o 链接成 .elf
  4. 转成 .bin 可烧录文件
  5. 生成反汇编文件
  6. 可以 clean 清理、load 烧录

2.Makefile 关键注意事项

  1. Makefile 编译时必须生成 .o 文件(只编译不链接),不能直接使用 .c 文件编译,否则会导致重复编译、效率低下,且无法实现按需更新和正确的链接流程。

  2. Makefile 的 OBJ 目标文件列表中,start.o 必须放在最前面,main.o 等文件放在后面。因为程序启动需要先执行汇编启动代码完成硬件初始化、栈设置等工作,若 main.o 在前,链接器会先执行 main 函数,导致程序运行崩溃。

  3. Makefile 内的通配符区分规则:%.s和 %.o中的 %是 Makefile 自带的通配符语法;*.o中的 * 属于 Shell 命令通配符,二者使用场景和规则不同,不能混用。

  • 所有文件先编译成 .o,最后才链接
  • .o 放在 obj 目录,不污染源码
  • 自动找源码,不用手动写文件列表
  • 最终生成 .bin 用于烧录
  • 自动包含头文件路径
相关推荐
liliangcsdn24 分钟前
派息率指标的应用探索和分析
大数据·算法
xh didida32 分钟前
Linux -- 基础IO
linux·服务器·开发语言·c++
垆边人似月.1 小时前
华为机试题 :两数之和
c++·算法·华为
默默开发1 小时前
物流行业可用算法方向
算法
垆边人似月.1 小时前
华为机试题 :有效的括号
c++·算法·华为
心易行者1 小时前
Agent应用+API端点商业化进阶实战:从单体智能体到可付费调用的API全流程
运维·服务器·人工智能·python·apache
科技研学社1 小时前
精度决胜品质:羽绒服缝制工艺标准与自动化精度对标解析
运维·自动化
夜之眷属1 小时前
Core dump 崩溃排查:JVM 宕机后,那份 core 文件怎么用 gdb 还原现场
java·运维·服务器·jvm
Initialize293081 小时前
Redis 内存打满、连接数飙红?运维排查四步法
运维
zly35001 小时前
centos7 mount设备挂载 /dev/sda1/硬盘挂载
linux·运维·服务器