当前位置: 首页 > news >正文

硬件开发(7)—IMX6ULL裸机—led进阶、SDK使用(蜂鸣器拓展)、BSP工程目录

1.led进阶

1.volatile关键字

C 语言编译器会对代码进行优化,若某变量未被显式修改,可能会被编译器认为 “值不变” 并缓存到寄存器中。此时必须用volatile关键字,可以避免编译器忽略的部分语句

2.定义寄存器地址
1.直接定义单个寄存器

 //GPIO相关寄存器

#define GPIO1_DR *((volatile unsigned int *)0x0209C000) // 数据寄存器:控制引脚输出高低电平

#define GPIO1_GDIR *((volatile unsigned int *)0x0209C004) // 方向寄存器:配置引脚为输出

// 引脚复用与电气属性寄存器(GPIO1_IO03引脚)

#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 *((volatile unsigned int*)0x020E0068) // 引脚功能复用:选择GPIO功能

#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 *((volatile unsigned int*)0x020E02F4) // 引脚电气属性

// 时钟使能寄存器(CCM:时钟控制模块)

#define CCM_CCGR0 *((volatile unsigned int *)0x020C4068)

#define CCM_CCGR1 *((volatile unsigned int *)0x020C406C)

#define CCM_CCGR2 *((volatile unsigned int *)0x020C4070)

#define CCM_CCGR3 *((volatile unsigned int *)0x020C4074)

#define CCM_CCGR4 *((volatile unsigned int *)0x020C4078)

#define CCM_CCGR5 *((volatile unsigned int *)0x020C407C)

#define CCM_CCGR6 *((volatile unsigned int *)0x020C4080)

2.结构体封装:优化寄存器访问

// 定义GPIO外设寄存器结构体

struct GPIO_t

{

unsigned int DR; // 0x00:数据寄存器

unsigned int GDIR; // 0x04:方向寄存器

unsigned int PSR; // 0x08:状态寄存器

unsigned int ICR1; // 0x0C:

unsigned int ICR2; // 0x10:

unsigned int IMR; // 0x14:

unsigned int ISR; // 0x18:

unsigned int EDGE_SEL; 

#define GPIO1 (*((volatile struct GPIO_t *)0x0209C000)) // 后续访问更直观:GPIO1.DR = ...; GPIO1.GDIR |= ...;且更简洁,可以直接对整组进行宏定义

3.初始化及led相关函数
1.时钟初始化:打开外设时钟

void clock_init(void)

{

        CCM_CCGR0 = 0xFFFFFFFF; // 打开所有时钟门

        CCM_CCGR1 = 0xFFFFFFFF;

        CCM_CCGR2 = 0xFFFFFFFF;

        CCM_CCGR3 = 0xFFFFFFFF;

        CCM_CCGR4 = 0xFFFFFFFF;

        CCM_CCGR5 = 0xFFFFFFFF;

        CCM_CCGR6 = 0xFFFFFFFF;

}

2. LED 初始化

void led_init(void)

{

        // 引脚复用

        IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05;

        //  电气属性配置

        IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;

        //GPIO方向:将第3位(对应IO03)设为1,配置为输出模式

        GPIO1.GDIR |= (1 << 3);

}

3. LED 控制与延时函数

// LED点亮:DR寄存器第3位清0(

void led_on(void)

{

GPIO1.DR &= ~(1 << 3);

}

// LED熄灭:DR寄存器第3位置1

void led_off(void)

{

GPIO1.DR |= (1 << 3);

}

// LED闪烁:DR寄存器第3位翻转(异或操作:相同为0,不同为1)

void led_flicker(void)

{

        GPIO1.DR ^= (1 << 3);

}

// 简单延时函数

void led_delay(unsigned int time)

{

        while (time--);

}

4.主函数

int main(void)

{

        clock_init(); // 第一步:使能时钟

        led_init(); // 第二步:初始化LED

        while (1) // 死循环:持续闪烁

        {

                led_flicker(); // 翻转LED状态

                led_delay(0xFFFFFFF); // 延时

        } 

}

4.makefile优化

COMPLITER = arm-linux-gnueabihf-

CC = $(COMPLITER)gcc # C语言编译器

LD = $(COMPLITER)ld # 链接器

OBJCOPY = $(COMPLITER)objcopy # 目标文件格式转换工具

OBJDUMP = $(COMPLITER)objdump # 反汇编工具

OBJS = start.o main.o # 需要编译的目标文件列表,start.o是汇编启动文件,main.o是C语言主程序

TARGET = led # 目标文件,后期可代替使用

# 汇编文件编译规则:将 .S 文件编译为 .o 目标文件

%.o : %.S

        $(CC) -c $^ -o $@ -g # -c 表示只编译不链接,-g 表示生成调试信息

# C文件编译规则:将 .c 文件编译为 .o 目标文件

%.o : %.c

        $(CC) -c $^ -o $@ -g

$(TARGET).bin : $(OBJS)

        $(LD) -Ttext 0x87800000 $^ -o $(TARGET).elf # 链接生成ELF格式文件

        $(OBJCOPY) -O binary -S -g $(TARGET).elf $@ # 转换为二进制文件

        $(OBJDUMP) -D $(TARGET).elf > $(TARGET).dis # 生成反汇编文件用于调试

clean:

        rm $(OBJS) $(TARGET).elf $(TARGET).bin $(TARGET).dis -f

load:

        ./imxdownload $(TARGET).bin /dev/sdb

2.SDK的使用

1.SDK 文件准备

核心文件

  • MCIMX6Y2.h:I.MX6ULL 寄存器基地址与结构体定义(核心)。
  • fsl_iomuxc.h:引脚复用配置工具函数(如IOMUXC_SetPinMux)。
  • fsl_common.h:通用宏定义与工具函数。
  • core_ca7.h:ARM Cortex-A7 内核相关定义
2. 基于 SDK 重构 LED 驱动

#include "led.h"

#include "MCIMX6Y2.h"

#include "fsl_iomuxc.h"

// 时钟初始化:使用SDK的CCM结构体

void clock_init(void)

{

        CCM->CCGR0 = 0xFFFFFFFF;

        CCM->CCGR1 = 0xFFFFFFFF;

        CCM->CCGR2 = 0xFFFFFFFF;

        CCM->CCGR3 = 0xFFFFFFFF;

        CCM->CCGR4 = 0xFFFFFFFF;

        CCM->CCGR5 = 0xFFFFFFFF;

        CCM->CCGR6 = 0xFFFFFFFF; }

// LED初始化:使用SDK的引脚复用函数

void led_init(void)

{

        // 1. 引脚复用:IOMUXC_SetPinMux(引脚功能, 复用选项)         IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);

        // 2. 电气属性:IOMUXC_SetPinConfig(引脚功能, 配置值)         IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0);

        // 3. GPIO方向:SDK的GPIO1结构体

        GPIO1->GDIR |= (1 << 3);

}

// LED控制函数

void led_on(void)

{

         GPIO1->DR &= ~(1 << 3);

}

void led_off(void)

{

        GPIO1->DR |= (1 << 3);

}

void led_nor(void)

{

        GPIO1->DR ^= (1 << 3);

}

3.蜂鸣器驱动

#include "beep.h"

#include "MCIMX6Y2.h"

#include "fsl_iomuxc.h"

void beep_init(void)

{

        GPIO1_IO04 IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0); // 复用

        IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0x10B0); // 电气属性

        GPIO1->GDIR |= (1 << 1); // 配置为输出

        beep_off();

}

void beep_on(void)

{

        GPIO5->DR &= ~(1 << 1); // 导通蜂鸣器

}

void beep_off(void)

{

        GPIO5->DR |= (1 << 1); // 关闭蜂鸣器

}

void beep_nor(void)

{

        GPIO5->DR ^= (1 << 1);//控制蜂鸣器发声

}

3.BSP 工程目录结构

1.bsp工程目录的划分

按 “功能模块化” 划分目录,便于后续扩展

led_sdk/                # 工程根目录
├── project/            # 核心代码(入口)
│   ├── main.c          # 主函数
│   └── start.S         # 汇编启动文件
├── imx6ull/            # SDK头文件
│   ├── MCIMX6Y2.h
│   ├── fsl_iomuxc.h
│   ├── fsl_common.h
│   └── core_ca7.h
├── bsp/                
│   ├── led/            # LED驱动模块
│   │   ├── led.c
│   │   └── led.h
│   └── beep/           # 蜂鸣器驱动模块
│       ├── beep.c
│       └── beep.h
├── Makefile            # 编译脚本
└── imx6ull.lds         # 链接脚本
2.makefile工程配置

核心

1.makefile核心代码
# 目标文件名(最终生成的二进制文件前缀)
target = led# 交叉编译器前缀(指定为 ARM 架构编译器)
cross_compiler = arm-linux-gnueabihf-# 定义编译工具链(使用交叉编译器)
cc = $(cross_compiler)gcc      # C 编译器
ld = $(cross_compiler)ld       # 链接器
objcopy = $(cross_compiler)objcopy  # 目标文件格式转换工具
objdump = $(cross_compiler)objdump  # 目标文件反汇编工具# 头文件目录(bsp 和 imx6ull 文件夹)
incdirs = bsp imx6ull
# 源文件目录(bsp 和 project 文件夹)
srcdirs = bsp project# 生成头文件包含参数(-I 选项指定)
include = $(patsubst %, -I%, $(incdirs))# 查找所有源文件(.c 和 .S 汇编文件)
cfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c))  # 所有 C 文件
sfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S))  # 所有汇编文件# 提取文件名(去掉路径)
cfilenodir = $(notdir $(cfiles))
sfilenodir = $(notdir $(sfiles))# 定义目标文件路径(放在 obj 文件夹下)
cobjs = $(patsubst %, obj/%, $(cfilenodir:.c=.o))  # C 文件对应的 .o 文件
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o))  # 汇编文件对应的 .o 文件
objs = $(cobjs) $(sobjs)  # 所有目标文件集合# 指定源文件搜索路径(make 会在这些目录找源文件)
VPATH = $(srcdirs)# 生成 led.bin(最终二进制文件):依赖所有目标文件
$(target).bin : $(objs)$(ld) -Timx6ull.lds -o$(target).elf $^  # 用链接脚本 imx6ull.lds 链接生成 elf 文件$(objcopy) -O binary -S -g $(target).elf $@  # 转换 elf 为二进制文件$(objdump) -D $(target).elf > $(target).dis  # 生成反汇编文件# 编译汇编文件:将 .S 编译为 obj 目录下的 .o
$(sobjs) : obj/%.o : %.S@mkdir -p obj  # 确保 obj 目录存在(@ 表示不显示命令本身)$(cc) -Wall -nostdlib -c $(include) -o $@ $<  # -nostdlib 不链接标准库# 编译 C 文件:将 .c 编译为 obj 目录下的 .o
$(cobjs) : obj/%.o : %.c@mkdir -p obj		$(cc) -Wall -nostdlib -c $(include) -o $@ $<  # -Wall 显示所有警告,-c 只编译不链接# 伪目标:清除编译产物
.PHONY : clean
clean:rm -rf $(objs) $(target).elf $(target).bin $(target).dis  # 删除目标文件和输出文件# 伪目标:下载程序到设备(使用 imxdownload 工具)
load:./../imxdownload $(target).bin /dev/sdb  # 将 bin 文件下载到 /dev/sdb 设备
2.链接脚本(imx6ull.lds)
SECTIONS
{. = 0x87800000;  # 代码加载地址(I.MX6ULL SD启动地址).text : {        # 代码段:先放启动文件obj/start.o*(.text)}.rodata ALIGN(4) : {*(.rodata*)}  # 只读数据段(4字节对齐).data ALIGN(4) : {*(.data)}       # 已初始化数据段__bss_start = .;  # BSS段起始地址.bss ALIGN(4) : {*(.bss) *(COMMON)}  # 未初始化数据段__bss_end = .;    # BSS段结束地址
}
3.BSS 段初始化

未初始化全局变量存.bss段需清 0

_bss_init:ldr r0, =__bss_start  # r0 = BSS起始地址ldr r1, =__bss_end    # r1 = BSS结束地址mov r2, #0            # r2 = 0(用于清0)
loop:str r2, [r0]          # 把0写入r0指向的内存add r0, #4            # 地址+4(32位对齐)cmp r0, r1            # 比较是否到末尾blt loop              # 没到就继续循环bx lr                 # 返回,之后进main


文章转载自:

http://TmlnLdWz.pLqkz.cn
http://YUIWZheD.pLqkz.cn
http://k31IEu3P.pLqkz.cn
http://xdtpQ7sw.pLqkz.cn
http://QbeOZkGn.pLqkz.cn
http://qZCW2nPn.pLqkz.cn
http://NLV9TglM.pLqkz.cn
http://5XDnem5n.pLqkz.cn
http://qX8VhmZJ.pLqkz.cn
http://zvlQFitl.pLqkz.cn
http://F4q9h3rS.pLqkz.cn
http://kYey8tMI.pLqkz.cn
http://NLreLRH5.pLqkz.cn
http://de1EwxzW.pLqkz.cn
http://MUbbasIT.pLqkz.cn
http://Y6WUFlta.pLqkz.cn
http://yQ9Yngru.pLqkz.cn
http://Iwv2fJ5k.pLqkz.cn
http://bh0LAC62.pLqkz.cn
http://bFeJKOrp.pLqkz.cn
http://LeweSsqG.pLqkz.cn
http://m2o0BNJN.pLqkz.cn
http://wHVTs43s.pLqkz.cn
http://ms9i87z8.pLqkz.cn
http://bBHEBdXs.pLqkz.cn
http://r29gU3oy.pLqkz.cn
http://fx0irZ5I.pLqkz.cn
http://5mZAEVLh.pLqkz.cn
http://FHHGAFUS.pLqkz.cn
http://SeNnW0PK.pLqkz.cn
http://www.dtcms.com/a/379011.html

相关文章:

  • 人工智能学习:Transformer结构中的编码器层(Encoder Layer)
  • RISCV中PLIC和AIA的KVM中断处理
  • 掌握梯度提升:构建强大的机器学习模型介绍
  • 全球智能电网AI加速卡市场规模到2031年将达20216百万美元
  • springbook3整合Swagger
  • LMS 算法:抗量子时代的「安全签名工具」
  • CUDA中thrust::device_vector使用详解
  • Python学习-day8 元组tuple
  • 2025主流大模型核心信息
  • skywalking定位慢接口调用链路的使用笔记
  • LeetCode刷题记录----739.每日温度(Medium)
  • eNSP华为无线网测试卷:AC+AP,旁挂+直连
  • 开源多模态OpenFlamingo横空出世,基于Flamingo架构实现图像文本自由对话,重塑人机交互未来
  • 光路科技将携工控四大产品亮相工博会,展示工业自动化新成果
  • matlab实现相控超声波成像仿真
  • 【C】Linux 内核“第一宏”:container_of
  • Dinky 是一个开箱即用的一站式实时计算平台
  • Vue3内置组件Teleport/Suspense
  • Python打印格式化完全指南:掌握分隔符与行结尾符的高级应用
  • 实体不相互完全裁剪,请检查您的输入
  • 分数阶傅里叶变换(FRFT)的MATLAB实现
  • ARM (6) - I.MX6ULL 汇编点灯迁移至 C 语言 + SDK 移植与 BSP 工程搭建
  • unsloth微调gemma3图文代码简析
  • 【ECharts ✨】ECharts 自适应图表布局:适配不同屏幕尺寸,提升用户体验!
  • wpf依赖注入驱动的 MVVM实现(含免费源代码demo)
  • Python的f格式
  • 技术视界 | 末端执行器:机器人的“手”,如何赋予机器以生命?
  • 从零开始使用 axum-server 构建 HTTP/HTTPS 服务
  • 简直有毒!索伯ACL撕裂,雷霆四年报销三个新秀!
  • 从 “模板” 到 “场景”,用 C++ 磨透拓扑排序的实战逻辑