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

硬件驱动——I.MX6ULL裸机启动(2)

一.GNU常用工具和说明

1.gcc:功能:编译器

            作用:将C/C++等高级语言源代码转换为汇编代码,再将汇编代码转换为目标文件,支

                       持多种编程语言和目标平台,提供丰富分优化选项和编译控制选项

2.ld:功能:链接器      

         作用:将多个目标文件链接成可执行文件,解析和处理符号引用,进行地址分配和重定位,

                    处理库文件的链接

3.objcopy:功能:目标文件格式转换器

                   作用:在不同格式的目标文件之间进行转换,将ELF格式转换为纯二进制格式(.bin)

                              从目标文件中提取特定的段(sections),修改目标文件的内容,常用于嵌入

                              式开发中的固件生成

4.objdump:功能:目标文件分析器/反汇编器

                    作用:将机器码反汇编为汇编代码,查看目标文件的详情信息,分析程序的执行指令

                               显示各种文件格式信息     

二.在C语言环境下进行LED操作

1.volatile关键字:

作用:告诉编译器该变量的值随时可能发生变化,禁止编译器进行优化

2.将C语言和ARM语言连接起来

代码:

.global _start_start:ldr pc, =_reset_handlerldr pc, =_undefine_handlerldr pc, =_svc_handlerldr pc, =_prefetch_abort_handlerldr pc, =_data_abort_handlerldr pc, =_reserved_handlerldr pc, =_irq_handlerldr pc, =_fiq_handler_undefine_handler:ldr pc, =_undefine_handler_svc_handler:ldr pc, =_svc_handler_prefetch_abort_handler:ldr pc, =_prefetch_abort_handler_data_abort_handler:ldr pc, =_data_abort_handler_reserved_handler:ldr pc, =_reserved_handler_irq_handler:ldr pc, =_irq_handler_fiq_handler:ldr pc, =_fiq_handler_reset_handler:mrs r0,cpsrbic r0, r0, #0x1Forr r0, r0, #0x12   //irp(10010)msr cpsr, r0ldr sp, =0x86000000mrs r0,cpsrbic r0, r0, #0x1Forr r0, r0, #0x1F   //system(11111)msr cpsr, r0ldr sp, =0x84000000bl _bss_clear    b main  //跳转至main函数当中_bss_clear:ldr r0, =__bss_startldr r2, =__bss_end
loop:mov r1, #0str r1, [r0]add r0, r0, #4cmp r0, r2blt loopbx lrfinished:b finished
//定义所有引脚的地址
// #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)))// #define SW_MUX_CTL_PAD_GPIO1_IO03 (*((volatile unsigned int *)(0x020E0068)))
// #define SW_PAD_CTL_PAD_GPIO1_IO03 (*((volatile unsigned int *)(0x020E02F4)))
// #define GPIO1_DR   (*((volatile unsigned int *)(0x0209C000)))
// #define GPIO1_GDOR (*((volatile unsigned int *)(0x0209C004)))// struct GPIO_Type_t
// {
//     volatile unsigned int DR;
//     volatile unsigned int GDIR;
//     volatile unsigned int PSR;
//     volatile unsigned int ICR1;
//     volatile unsigned int ICR2;
//     volatile unsigned int IMR;
//     volatile unsigned int ISR;
//     volatile unsigned int EDGE_SEL;
// };// #define GPIO1   ((struct GPIO_Type_t *)(0x0209C000))
// #define GPIO2   ((struct GPIO_Type_t *)(0x020AC000))#include "led.h"void enable_clocks(void)    //将所有时钟都打开
{CCM->CCGR0 = 0xFFFFFFFF;CCM->CCGR1 = 0xFFFFFFFF;CCM->CCGR2 = 0xFFFFFFFF;CCM->CCGR3 = 0xFFFFFFFF;CCM->CCGR4 = 0xFFFFFFFF;CCM->CCGR5 = 0xFFFFFFFF;CCM->CCGR6 = 0xFFFFFFFF;
}void delay(unsigned int n)
{while(n--);             //进行延时}
int main(void)
{enable_clocks();init_led();
//    led_on();while(1){led_nor();delay(0xFFFFF);}return 0;
}
#include "led.h"void init_led(void)
{IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);  //IO复用模块 配置GPIO3的功能IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0x10B0); //配置GPIO3的电气属性GPIO1->GDIR |= (1 << 3);     //配置GPIO1的引脚为输出方式
}void led_on(void)
{GPIO1->DR &= ~(1 << 3);  //配置GPIO1的引脚为低电平}void led_off(void)
{GPIO1->DR |= (1 << 3);   //配置GPIO1的引脚为高电平}
void led_nor(void)
{GPIO1->DR ^= (1 << 3);   //配置GPIO1的引脚在高低电平之间相互切换}
#ifndef __LED_H__
#define __LED_H__#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"extern void init_led();
extern void led_on();
extern void led_off();
extern void led_nor();
#endif

3.利用IMX6ULL自己的芯片的库函数

代码如上,后缀u是无符号的意思

三.内存段

 1. .bss段

(1)用于存放未初始化或初始化为0的数据
(2) 在运行时会被自动清零
-典型例子:
static int bss_var;           // 静态变量(全局或局部)
int explicit_var = 0;         // 显式初始化为0的全局变量

2. COMMON段

(1) 用于存放未初始化的非静态全局变量
(2) 链接时才确定最终大小和位置
(3)允许多个目标文件定义同名符号
典型例子:

int common_var;               // 未初始化的非静态全局变量

 3. .data段

(1) 用于存放已初始化的全局变量和静态变量(非零值)
(2)需要在程序文件中保存实际的初始值
典型例子:
int global_var = 100;         // 初始化为非零值
int global_array[] = {1,2,3}; // 初始化为非零数组

4. .rodata段(只读数据段)

(1)存放程序的只读数据
(2) 运行时受保护,不可修改
(3)包含的数据类型:
1. 字符串常量
2. const修饰的全局变量
3. 全局只读数组
4. switch跳转表
5. 浮点数常量
典型例子:
const int MAX_VALUE = 100;    // const全局变量
char* str = "Hello World";    // 字符串常量
const int lookup[] = {1,2,3}; // 只读数组

5.各段的特点比较

| 段名    | 初始化 | 运行时可写 | 链接特性 |
|---------|--------|------------|----------|
| .bss    | 自动清零 | 可写 | 编译时确定大小 |
| COMMON  | 自动清零 | 可写 | 链接时确定大小 |
| .data   | 需要初始值 | 可写 | 编译时确定大小 |
| .rodata | 需要初始值 | 只读 | 编译时确定大小 |

6. 在链接脚本中的定义

SECTIONS
{
.text :     { ... }              // 代码段
.rodata ALIGN(4) : {*(.rodata*)} // 只读数据段
.data   ALIGN(4) : {*(.data)}    // 已初始化数据段
.bss    ALIGN(4) : {*(.bss) *(COMMON)} // 未初始化数据段


7.关于段名中的通配符说明


在链接脚本中,经常会看到类似`*(.rodata*)`这样的写法,这里包含两个`*`通配符,它们有不同的含义:

1. 第一个`*`:
- 表示收集所有输入文件中的相关段
- 如果不加这个`*`,就只会收集特定文件的段

2. 第二个`*`(如`rodata*`中的`*`):
- 用于匹配所有以`.rodata`开头的段名
- 实际编译时会产生多种rodata相关的段,例如:
- `.rodata`:基本的只读数据段
- `.rodata.str1.1`:长度为1字节对齐的字符串常量
- `.rodata.str1.4`:长度为4字节对齐的字符串常量
- `.rodata.cst8`:8字节常量(如double类型的常量)
- `.rodata.cst4`:4字节常量(如float类型的常量)

如果只写`*(.rodata)`而不是`*(.rodata*)`:
- 只会收集严格命名为`.rodata`的段
- 其他相关的只读数据段(如`.rodata.str1.1`等)会被遗漏
- 这些被遗漏的数据可能导致程序无法正常工作

这种通配符的使用是链接脚本中的常见做法,类似的还有:
- `*(.text*)`:收集所有代码相关的段
- `*(.data*)`:收集所有数据相关的段

8.链接脚本:

SECTIONS
{. = 0x87800000;.text :{obj/start.o*(.text)}.rodata ALIGN(4) : {*(.rodata*)} .data ALIGN(4) : {*(.data)}. = ALIGN(4) ;__bss_start = .;.bss ALIGN(4) :{*(.bss) *(COMMON)}__bss_end = .;
}

代码解释:

规定程序加载到内存的起始地址(0x87800000),确保与硬件的内存布局匹配;

保证启动代码(start.o)最先执行,这是嵌入式系统启动的必要条件;

合理安排代码、只读数据、初始化数据、未初始化数据的内存位置;

定义 BSS 段的起始和结束符号,供启动代码完成 “未初始化变量清零” 的工作。

四.bps工程管理

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 projectinclude = $(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

五.蜂鸣器实验

源码:

#include "beep.h"
#include "led.h"void enable_clocks(void)    //将所有时钟都打开
{CCM->CCGR0 = 0xFFFFFFFF;CCM->CCGR1 = 0xFFFFFFFF;CCM->CCGR2 = 0xFFFFFFFF;CCM->CCGR3 = 0xFFFFFFFF;CCM->CCGR4 = 0xFFFFFFFF;CCM->CCGR5 = 0xFFFFFFFF;CCM->CCGR6 = 0xFFFFFFFF;
}void delay(unsigned int n)
{while(n--);             //进行延时}
int main(void)
{enable_clocks();init_beep();init_led();while(1){led_nor();beep_nor();delay(0xFFFFF);}return 0;
}
#include "beep.h"void init_beep(void)
{IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0);    //IO复用模块,选择相应的功能IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0x10B0);    //配置蜂鸣器相应的电气属性GPIO5->GDIR |= (1 << 1);    //将GPIO5的引脚设置为输出模式
}void beep_on(void)
{GPIO5->DR &= ~(1 << 1);     //将GPIO5设置为低电平
}void beep_off(void)
{GPIO5->DR |= (1 << 1);      //将GPIO5设置为高电平}void beep_nor(void)
{GPIO5->DR ^= (1 << 1);      //将GPIO5设置为高低电平切换}
#ifndef __DEEP_H__
#define __DEEP_H__#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"extern void init_beep(void);
extern void beep_on(void);
extern void beep_off(void);
extern void beep_nor(void);
#endif

start.S和Makefile同上述LED灯和Makefile

                    

              


文章转载自:

http://Vr86leu3.qzmnr.cn
http://3JplhyQm.qzmnr.cn
http://Y5lrv0V8.qzmnr.cn
http://6G2prCkh.qzmnr.cn
http://RTRwl9bC.qzmnr.cn
http://NhbAp3aW.qzmnr.cn
http://tHhCqct6.qzmnr.cn
http://Eo6E6wxY.qzmnr.cn
http://TJRF3Odu.qzmnr.cn
http://tQVXPSG7.qzmnr.cn
http://yRqrROG0.qzmnr.cn
http://YoydqmVB.qzmnr.cn
http://QrButyee.qzmnr.cn
http://wpWXWu7M.qzmnr.cn
http://cnLt4muj.qzmnr.cn
http://xX6ZVVPg.qzmnr.cn
http://utlYdwdJ.qzmnr.cn
http://qRKGZUGG.qzmnr.cn
http://Sgaqpk4B.qzmnr.cn
http://CIW2X5Xa.qzmnr.cn
http://Wmt4UF9G.qzmnr.cn
http://jBCqThOX.qzmnr.cn
http://cPNfdKn7.qzmnr.cn
http://gzOuVo5z.qzmnr.cn
http://4qKlUdqZ.qzmnr.cn
http://b8nu1eKx.qzmnr.cn
http://uQUCj8vD.qzmnr.cn
http://rW2DMvYa.qzmnr.cn
http://KOSuqmzc.qzmnr.cn
http://juaCb1G9.qzmnr.cn
http://www.dtcms.com/a/379583.html

相关文章:

  • Linux 进程深度解析(6):资源隔离的底层实现 (Namespace、Cgroups 与容器化)
  • 【AI大模型面试宝典60题】1-5
  • AUTOSAR Adaptive Platform 日志与追踪 (Log and Trace) 规范深度解析
  • Claude Code + 自定义模型体验
  • Python 实战:票据图像自动矫正技术拆解与落地教程
  • 【Kubernetes】常见面试题汇总(十四)
  • 【 Rank(列)、DIMM(内存条) 和 DDR颗粒(内存芯片) 的区别】
  • 密钥协商与前向/后向安全性
  • UART 总线核心特性
  • CDN(Content Delivery Network,内容分发网络)
  • EMC电磁兼容进阶3讲培训:专题二 电源线滤波器仿真实践-基于反激电源
  • 2.DSP学习记录之GPIO输出应用实验
  • WSL2 | 一种临时解决在 Windows 10 运行了一段时间 WSL2 之后 WSL2 无响应的方法
  • SPARC方法论在Claude Code基于规则驱动开发中的应用
  • Python编程基础(九) | 文件和异常
  • AWS IAM条件操作符实战指南:从基础到高级应用
  • SW - 无法用此剖切线来剖切此模型/零部件。请确认该剖切线完全通过该模型。
  • 【主页介绍】
  • 数据治理进阶——解读2024 企业数据治理体系和应用场景案例【附全文阅读】
  • 测试的概念
  • Python生物信息学数据处理大全:从FASTA文件到Pandas DataFrame
  • Android 设置禁止截图和禁止长截图
  • VR煤矿实训系统相较于传统煤矿培训方式的独特优势​-广州华锐互动
  • 鸿蒙Next Web组件详解:属性设置与事件处理实战
  • Chaosblade常用命令和范例
  • Linux内存管理章节九: 打通虚拟与实体的桥梁:深入Linux内存映射机制
  • leetcode13:罗马数字转整数(哈希表模拟)
  • TCP协议的相关特性
  • 猎豹移动2025年Q2财报:营收2.952亿,接近盈亏平衡
  • Spring框架1—Spring的IOC核心技术1