C 语言实现 I.MX6ULL 点灯(续上一篇)、SDK、deep及bsp工程管理

目录

一、汇编点灯转 C 语言实现

1. 关键字:volatile

2. 寄存器地址定义(两种方式)

(1)直接宏定义地址

(2)结构体封装寄存器(优化访问)

3. 核心功能代码

(1)时钟初始化:打开所有时钟门

(2)LED 初始化:引脚复用 + GPIO 方向配置

(3)LED 控制:亮、灭、闪烁

二、SDK 库文件

1. SDK文件选择

2. 基于 SDK 的点灯程序优化

三、BSP 工程管理与构建

1. 工程目录结构(模块化管理)

2. 蜂鸣器裸机驱动(S8550 PNP 三极管)

3. Makefile 优化(多目录编译)

4. 链接脚本(imx6ull.lds)

1.链接脚本的作用

2.各段及存储数据类型

一、汇编点灯转 C 语言实现

1. 关键字:volatile

  • 作用:告诉编译器,被修饰的变量值可能会被程序之外的因素(如硬件寄存器、中断服务函数)意外修改,禁止编译器对该变量进行优化

在访问硬件寄存器时必须使用,确保每次对寄存器的读写都是直接操作物理地址,而非操作缓存值

2. 寄存器地址定义(两种方式)

(1)直接宏定义地址

通过#define将寄存器物理地址强制转换为对应类型的指针,直接访问寄存器:

(2)结构体封装寄存器(优化访问)

将同组寄存器(如 GPIO1 的 DR、GDIR、PSR 等)封装为结构体,通过结构体指针映射到基地址:

3. 核心功能代码

(1)时钟初始化:打开所有时钟门

I.MX6ULL 外设默认时钟关闭,需通过 CCM 寄存器开启对应外设时钟,此处为简化操作,直接打开所有时钟门:

(2)LED 初始化:引脚复用 + GPIO 方向配置
  • 引脚复用:将GPIO1_IO03配置为 GPIO 功能(复用值 0x05)
  • 电气属性:配置引脚驱动能力、上下拉等(0x10B0 为常用配置)
  • GPIO 方向:将GPIO1_IO03设为输出模式(GDIR 寄存器对应位写 1)
void led_init(void)
{// 方式1:直接操作寄存器(无SDK)IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05;    // 引脚复用为GPIOIOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;  // 配置引脚电气属性GPIO1_GDIR |= (1 << 3);                     // GPIO1_IO03设为输出(第3位写1)// 方式2:结构体访问(优化后)// IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05;// IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;// GPIO1->GDIR |= (1 << 3);
}
(3)LED 控制:亮、灭、闪烁

通过操作 GPIO1 的 DR 寄存器(数据寄存器)控制引脚电平:

  • LED灯亮:对应位写 0(拉低电平,假设 LED 为共阳极)
  • LED灯灭:对应位写 1(拉高电平)
  • LED灯闪烁:对应位异或(电平翻转)
// LED点亮
void led_on(void)
{GPIO1->DR &= ~(1 << 3);  // 第3位清0(拉低电平)
}// LED熄灭
void led_off(void)
{GPIO1->DR |= (1 << 3);   // 第3位置1(拉高电平)
}// LED闪烁(电平翻转)
void led_flicker(void)
{GPIO1->DR ^= (1 << 3);   // 第3位异或(0变1,1变0)
}// 延时函数(软件延时,时间与参数time相关)
void led_delay(unsigned int time)
{while (time--);
}

二、SDK 库文件

1. SDK文件选择

  • SDK(Software Development Kit):NXP 提供的 I.MX6ULL 开发工具包,包含完整 IDE(需额外设备:下载器、仿真器)和底层驱动头文件;
  • 移植核心:仅使用 SDK 中的头文件(无需完整 IDE),路径为IMAX6ULL/SDK/,关键头文件包括:
    • cc.h:时钟相关定义;
    • core_ca7.h:ARM Cortex-A7 内核相关定义;
    • fsl_common.h:通用工具函数定义;
    • fsl_iomuxc.h:引脚复用配置函数定义;
    • MCIMX6Y2.h:I.MX6ULL 寄存器映射结构体定义。

2. 基于 SDK 的点灯程序优化

SDK 提供了封装好的引脚配置函数(如IOMUXC_SetPinMuxIOMUXC_SetPinConfig),简化寄存器操作:

// 时钟初始化(SDK中CCM已封装为结构体,可通过CCM->CCGRx访问)
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. 引脚复用:将GPIO1_IO03配置为GPIO功能,第2个参数为ALT引脚(0表示无ALT)IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);// 2. 引脚电气属性配置:驱动能力、上下拉等(0x10B0为标准配置)IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0);// 3. GPIO方向配置:设为输出GPIO1->GDIR |= (1 << 3);
}

注意:

时钟使能:I.MX6ULL外设时钟默认关闭,初始化外设前必须开启对应时钟门;

三、BSP 工程管理与构建

1. 工程目录结构(模块化管理)

将代码按功能拆分到不同目录,提高可维护性,目录结构如下:

2. 蜂鸣器实现(S8550 PNP 三极管)

  • 原理:S8550 为 PNP 型三极管,基极高电平时导通,控制蜂鸣器发声;
  • 核心代码:与 LED 驱动逻辑类似,仅需修改对应引脚:
//beep.h#ifndef _BEEP_H_
#define _BEEP_H_extern void beep_init(void);
extern void beep_on(void);
extern void beep_off(void);
extern void beep_nor(void);#endif//beep.c#include "beep.h"
#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"void beep_init(void)
{IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01 , 0);   //复用功能IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01 , 0x10B0);     //电气特性       GPIO5->GDIR |= (1 << 1);         //引脚方向配置beep_off();
}void beep_on(void)    //蜂鸣器发声
{GPIO5->DR &= ~(1 << 1);    //置0
}void beep_off(void)   //蜂鸣器停止发声
{GPIO5->DR |= (1 << 1);     //置1
}void beep_nor(void)   //蜂鸣器交替发声
{GPIO5->DR ^= (1 << 1);
}//main.c                         实现LED灯和蜂鸣器的共同操作#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"
#include "led.h"
#include "beep.h"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;   
}void led_delay(unsigned int t)
{while(t--);
}int main(void)
{clock_init();led_init();beep_init();while (1){led_nor();beep_nor();led_delay(0x7FFFF);}
}

3. Makefile 优化(多目录编译)

支持多目录(project、bsp/led、bsp/beep)编译,自动生成目标文件(obj 目录),并通过链接脚本生成 bin、elf、dis 文件:

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))// 查找所有C和汇编源文件
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))  // C文件对应的.o
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o))  // 汇编文件对应的.o
objs = $(cobjs) $(sobjs)                           // 所有目标文件// 源文件搜索路径
VPATH = $(srcdirs)// 生成bin文件(依赖所有目标文件)
$(target).bin : $(objs)$(ld) -Timx6ull.lds -o$(target).elf $^       // 链接生成elf$(objcopy) -O binary -S -g $(target).elf $@  // 转换为bin$(objdump) -D $(target).elf > $(target).dis  // 生成反汇编// 汇编文件编译规则
$(sobjs) : obj/%.o : %.S@mkdir -p obj$(cc) -Wall -nostdlib -c $(include) -o $@ $<// C文件编译规则
$(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// 下载程序到SD卡
load:../imxdownload $(target).bin /dev/sdb

总结:

工具(前缀为arm-linux-gnueabihf-作用
gcc(编译器)将源代码(.c/.S)编译为目标文件(.o):预处理→编译→汇编,生成机器码
ld(链接器)按链接脚本,将多个目标文件(.o)合并为可执行文件(.elf)
objcopy(目标文件拷贝工具)将.elf 文件转换为二进制文件(.bin),便于烧写至开发板 Flash/RAM
objdump(目标文件反汇编工具)对.elf 文件反汇编,生成.dis 文件,用于调试(查看指令与地址对应关系)
辅助工具(如imxdownload专用烧写工具,将.bin 文件烧写到开发板存储设备(如 SD 卡 /dev/sdb)

4. 链接脚本(imx6ull.lds)

指定代码加载地址(I.MX6ULL 常用 0x87800000),定义各段(text、rodata、data、bss)的排列顺序,并标记 BSS 段起始 / 结束地址(用于启动文件初始化 BSS 段)

SECTIONS
{. = 0x87800000;  // 代码加载基地址(I.MX6ULL DDR起始地址)// 代码段(text):存放启动文件、主程序代码.text :{obj/start.o  // 启动文件优先加载(需先初始化栈、BSS段)*(._text)    // 所有文件的text段} // 只读数据段(rodata):存放常量,4字节对齐.rodata ALIGN(4) : {*(.rodata*)}// 已初始化数据段(data):存放初始化过的全局变量,4字节对齐.data ALIGN(4) : {*(.data)}// 未初始化数据段(bss):存放未初始化的全局变量,需在启动文件中清0__bss_start = .;  // BSS段起始地址.bss ALIGN(4) : {*(.bss) *(COMMON)}  // 所有bss段和COMMON段__bss_end = .;    // BSS段结束地址
}

注意:

需要在启动代码中加入跳转链接

BSS 段初始化:启动文件(start.S)需在跳转到 main 前,将__bss_start到__bss_end的内存清0

1.链接脚本的作用

链接脚本(上文中的imx6ull.lds)的核心作用是定义程序的内存布局使链接器将多个目标文件(.o)合并为可执行文件(.elf) 的配置文件,核心是定义 “内存布局” 和 “段的排列规则”(不同段对应程序中不同类型的数据),确保代码和数据加载到芯片指定的内存地址(如上文中的-Ttext 0x87800000),达成硬件成功运行的要求

2.各段及存储数据类型
常见段名存储数据类型
.text代码段:存放可执行指令(如 C 函数、汇编指令),只读
.data数据段:存放已初始化且非零的全局变量 / 静态变量,可读可写
.bss未初始化数据段:存放未初始化或初始化为零的全局变量 / 静态变量,运行时由系统清零
.rodata只读数据段:存放只读常量(如字符串常量、const修饰的全局变量)
.stack栈区:用于函数调用时保存局部变量、函数参数、返回地址,由编译器自动管理
.heap堆区:用于动态内存分配(如malloc申请的内存),需手动管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/922455.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/922455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevOps实战(7) - 使用Arbess+GitPuk+sourcefare实现Node.js项目自动化部署

Arbess 是一款国产开源免费的 CI/CD 工具&#xff0c;工具支持一键部署&#xff0c;页面简洁易用。本文将详细介绍如何安装配置使用GitPuk、sourcefare、Arbess系统&#xff0c;使用流水线拉取GitPuk源码、使用sourcefare代码扫描、构建安装包并进行主机部署。 1、GitPuk 安装…

算法,蒜鸟蒜鸟-P1-理解“双指针”

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 有很多很多不足的地方&#xff0c;欢迎评论交流&#xff0c;感谢您的阅读和评论&#x1f604;。 目录引言1 双指针&#xff1a;Two Pointers1.1 左右指…

使用cookiecutter创建python项目

一、关于Python项目结构Python 项目并没有完全统一的 “固定结构”&#xff0c;但行业内有一些广泛遵循的约定俗成的目录结构&#xff08;尤其针对可分发的包或大型项目&#xff09;。同时&#xff0c;确实有工具可以快速生成这些标准化结构&#xff0c;提高开发效率&#xff0…

台积电生态工程深度解析:从晶圆厂到蜂巢的系统架构迁移

当半导体巨头将工厂视为生态系统&#xff0c;用工程思维解决环境问题概述&#xff1a;生态系统的工程化再造台积电近日开展的"积蜜"项目绝非简单的企业CSR行为&#xff0c;而是一场将生态系统视为复杂系统进行工程化改造的技术实践。本文将从系统架构、数据监控、循环…

从零实现一个简易计算器

最近在刷算法题时&#xff0c;遇到了实现计算器的问题。一开始觉得很简单&#xff0c;但真正动手实现时才发现其中有很多细节需要考虑。今天就来分享一下我的实现思路和学到的经验。问题分析我们需要实现一个能够处理加减乘除四则运算的计算器&#xff0c;要正确处理运算符的优…

Actix-webRust Web框架入门教程

文章目录引言Actix-web是什么&#xff1f;准备工作你的第一个Actix-web应用理解代码结构处理请求和响应接收请求数据返回响应中间件 - 增强你的应用状态管理和依赖注入实用示例&#xff1a;构建RESTful API测试你的Actix-web应用部署Actix-web应用结语额外资源引言 嘿&#xf…

若依框架前端通过 nginx docker 镜像本地运行

1. 前言 项目运行过程图&#xff1a;对于前端项目通过命令 npm run build 打包后&#xff0c;无法直接运行。存在如下错误&#xff1a;可以通过配置 nginx 服务器运行前端项目解决如上问题。 2. Nginx 运行 采用 docker 镜像的方式运行&#xff0c;docker-compose.yml 文件内容…

浅聊一下HTTP协议

在日常上网浏览网页、刷视频时&#xff0c;背后都离不开 HTTP 协议的支持。作为 Web 世界的 “交通规则”&#xff0c;它负责服务器和客户端浏览器之间的数据传输。这篇文章就带大家全面了解 HTTP 协议&#xff0c;从基本概念到通信细节&#xff0c;再到安全相关的 HTTPS&#…

机器人控制器开发(定位——cartographer ros2 使用2)

文章总览 1 纯定位模式 当完成建图后&#xff0c;会生成pbstream格式的地图文件 配置纯定位模式的lua脚本 backpack_2d_localization.lua include "backpack_2d.lua"TRAJECTORY_BUILDER.pure_localization_trimmer {max_submaps_to_keep 3, } POSE_GRAPH.optimi…

《大数据之路1》笔记3:数据管理

一 元数据 1.1 元数据概述 定义&#xff1a; 元数据是关于数据的数据&#xff0c;元数据打通了源数据、数据仓库、数据应用&#xff0c;记录了数据从生产到消费的全部过程。元数据主要记录数据仓库中模型的定义、各层级间的映射关系、监控数据仓库的数据状态和ETL的任务运行状态…

排序实现java

排序算法概述Java中实现排序可以通过多种方式&#xff0c;包括内置方法、自定义算法或使用第三方库。常见的排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序等。使用Arrays.sort()方法对于数组排序&#xff0c;Java提供了Arrays.sort()方法&#xff0c;支持对基本…

51c大模型~合集182

我自己的原文哦~ https://blog.51cto.com/whaosoft/14174587 #LaV-CoT 超越GPT-4o&#xff0c;蚂蚁集团与南洋理工大学提出&#xff1a;首个语言感知的视觉思维链 随着大型视觉语言模型&#xff08;VLM&#xff09;的飞速发展&#xff0c;它们在处理复杂的视…

C++ STL之deque的使用和模拟实现

目录 deque 核心本质与定位 与stack和queue的关系: deque的使用 deque的底层实现 deque的原理介绍 deque的缺陷 总结: deque deque文档 : deque 翻译: 双端队列 deque&#xff08;通常发音类似“deck”&#xff09;是“double-ended queue”&#xff08;双端队列&…

布草洗涤厂设备租赁押金原路退回系统—东方仙盟

设备租赁状态设备管理添加设备设备收押金设备退押金在布草洗涤行业的运营版图中&#xff0c;设备租赁是连接厂商与客户的重要纽带&#xff0c;而押金的收取与退还则是这一环节中关乎信任与效率的关键节点。未来之窗布草洗涤厂深谙此道&#xff0c;专为设备租赁业务打造的 “押金…

换源rocklinux和centos

一、Rockylinux换源&#xff0c;国外的源换成国内的源#nmcli connection modify ens33 ipv4.addresses 192.168.121.11 ipv4.gateway 192.168.121.2 ipv4.method manual ipv4.dns 114.114.114.114 connection.autoconnect yes修改地址#systemctl stop firewalld#systemctl diab…

第一部分:服务器硬件配置

目录1.1 服务器上架与连线1.2 启用CPU虚拟化功能&#xff08;BIOS设置&#xff09;1.3 配置RAID存储步骤1&#xff1a;进入RAID配置界面步骤2&#xff1a;确认RAID控制器信息步骤3&#xff1a;创建系统RAID&#xff08;用于安装ESXi&#xff09;步骤4&#xff1a;创建数据RAID&…

手搓一个 DELL EMC Unity存储系统健康检查清单

写在前面对于DELL EMC存储系统Unity的一些深度的健康检查通过Web的Unisphere图形化界面是做不到的&#xff0c;图形化界面只能看到是否有告警&#xff0c;物理的东西是否有问题的&#xff0c;逻辑的Pool和LUN等是否ready&#xff0c;再深入的潜在的问题是查不到的。另外&#x…

【数据结构】二叉树的概念

01 概念定义&#xff1a;二叉树既然叫二叉树&#xff0c;顾名思义即度最大为2的树称为二叉树。 它的度可以为 1 也可以为 0&#xff0c;但是度最大为 2 。 一颗二叉树是节点的一个有限集合&#xff0c;该集合&#xff1a;① 由一个根节点加上两棵被称为左子树和右子树的二叉树组…

【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?

单独编译kernel依赖如下几个源码&#xff1a;【交叉编译工具链】prebuilts/clang/host/linux-x86/clang-r487747c【内核源码】kernel-6.1为什么Android下编译内核使用clang作为交叉编译工具链而不是GCC&#xff1f;Android 14 选择使用预置的 Clang 工具链&#xff08;如 clang…

什么是Redis的Pipeline

介绍Redis的Pipeline是一种网络优化技术&#xff0c;在没有Pipeline的时候&#xff0c;客户端往redis发送请求&#xff0c;客户端需要等到redis响应之后才能发送下一个请求。而Pipeline&#xff0c;使redis可以一次性接收多个请求。减少了通信次数&#xff0c;显著的提高了性能…