I.MX6U 裸机开发5.准备C环境并用C语言控制LED

I.MX6U 裸机开发5.准备C环境并用C语言控制LED

  • 一、C运行环境
    • 1. 设置处理器模式
    • 2. CPSR 寄存器
      • CPSR 寄存器结构
      • 模式位
      • MRS 指令
      • MSR 指令
    • 3. 设置SP指针
      • 设置 SP 指针
        • 示例
      • 保存和恢复 SP 指针
        • 示例
    • 4. 跳转到C语言
  • 二、程序编写
    • 1. 启动文件 start.S
    • 2. main.h 定义寄存器
    • 3. 主程序main.c
    • 4. Makefile
    • 5. 链接文件 linker.ld
      • 定位计数器
      • .text
      • .rodata 段
      • .data数据段
      • .bss段
    • 6. 编译烧写测试

在这里插入图片描述

一、C运行环境

1. 设置处理器模式

在Cortex-A 的架构中一共有9种处理模式,如下表所示:

模式描述
USR (User Mode)用户模式,非特权模式,大部分程序运行的时候就处于此模式。
FIQ (Fast Interrupt Request Mode)快速中断模式,进入 FIQ 中断异常。
IRQ (Interrupt Request Mode)一般中断模式。
SVC (Supervisor Mode)超级管理员模式,特权模式,供操作系统使用。
MON (Monitor Mode)监视模式,用于安全扩展模式。
ABT (Abort Mode)数据访问终止模式,用于虚拟存储以及存储保护。
HYP (Hypervisor Mode)超级监视模式,用于虚拟化扩展。
UND (Undefined Mode)未定义指令终止模式。
SYS (System Mode)系统模式,用于运行特权级的操作系统任务。

I.MX6ULL 处理器中,可以通过修改 CPSR(Current Program Status Register)寄存器的模式位来设置处理器模式。以下是一些常见模式的设置方法:
进入用户模式 (User Mode)

MRS R0, CPSR        ; 读取当前 CPSR 寄存器值到 R0
BIC R0, R0, #0x1F   ; 清除模式位
ORR R0, R0, #0x16   ; 设置模式位为 Monitor Mode (0b10110)
MSR CPSR_c, R0      ; 将修改后的值写回 CPSR 寄存器

2. CPSR 寄存器

CPSR(Current Program Status Register)用于存储当前程序的状态信息。它包含了处理器模式、条件标志、中断屏蔽位等信息。

CPSR 寄存器结构

CPSR 是一个 32 位的寄存器,其各个位的含义如下:

名称描述
31N (Negative)负标志,表示最近一次运算结果为负数。
30Z (Zero)零标志,表示最近一次运算结果为零。
29C (Carry)进位标志,表示最近一次运算结果产生了进位或借位。
28V (Overflow)溢出标志,表示最近一次运算结果产生了溢出。
27-8Reserved保留位,未使用。
7I (IRQ disable)IRQ 中断禁用位,设置为 1 时禁用 IRQ 中断。
6F (FIQ disable)FIQ 中断禁用位,设置为 1 时禁用 FIQ 中断。
5T (Thumb)Thumb 状态位,设置为 1 时处理器处于 Thumb 状态。
4-0Mode模式位,表示当前处理器的工作模式。

模式位

模式位用于设置处理器的工作模式,常见的模式及其对应的值如下:

模式描述
User0b10000用户模式,非特权模式。
FIQ0b10001快速中断模式。
IRQ0b10010一般中断模式。
Supervisor0b10011超级管理员模式,特权模式。
Monitor0b10110监视模式,用于安全扩展模式。
Abort0b10111数据访问终止模式。
Hypervisor0b11010超级监视模式,用于虚拟化扩展。
Undefined0b11011未定义指令终止模式。
System0b11111系统模式,用于运行特权级的操作系统任务。

MRS 指令

MRS(Move Register from Special register)指令用于将特殊寄存器(如 CPSR 或 SPSR)的值移动到通用寄存器中。常用于读取当前程序状态寄存器(CPSR)或保存程序状态寄存器(SPSR)的值。

MSR 指令

MSR(Move Special register from Register)指令用于将通用寄存器的值移动到特殊寄存器中。常用于修改 CPSR 或 SPSR 的值。

3. 设置SP指针

在 ARM 处理器中,SP(Stack Pointer)指针用于指向当前栈的顶部。设置 SP 指针通常用于初始化栈或切换栈。以下是一些常见的设置 SP 指针的方法:

设置 SP 指针

可以使用 MOV 指令将一个值加载到 SP 寄存器中,以设置栈指针的位置。

示例
; 将栈指针设置为地址 0x8000
MOV SP, #0x8000

保存和恢复 SP 指针

在某些情况下,可能需要保存当前的 SP 指针值,并在稍后恢复它。可以使用通用寄存器来保存和恢复 SP 指针。

示例
; 保存当前的 SP 指针值到 R0
MOV R0, SP; 执行一些操作,可能会改变 SP 指针; 恢复 SP 指针值
MOV SP, R0

SP可以指向内部RAM,也可以指向DDR内存。 对于512M的DDR来说,内存范围是 0x80000000~0x9FFFFFFF,栈大小一般设置为2M,由于A7栈是向下增长的,可以将SP设置为 0x80200000。

4. 跳转到C语言

一般跳到 main 函数,下面是一个示例:

.global _start
.extern main_start:/* 设置栈指针 */LDR SP, =0x8000/* 调用 C 语言的 main 函数 */BL main/* 死循环,防止程序返回 */
1:  B 1b

二、程序编写

1. 启动文件 start.S

.global __start__start:/** 设置处理进入 SVC 模式 */MRS R0, CPSR        /** 读取当前的 CPSR */BIC R0, R0, #0x1F   /** 清除 CPSR 的低 5 位 */ORR R0, R0, #0x13   /** 设置 CPSR 为 SVC 模式 */MSR CPSR, R0        /** 设置 CPSR *//** 设置堆栈 */LDR R0, =0x80200000B main

2. main.h 定义寄存器

//
// Created by Xundh on 2024/11/9.
//#ifndef LEARN_I_MX6U_MAIN_H
#define LEARN_I_MX6U_MAIN_H/** 定义要使用的寄存器 **/
#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)/** IOMUXC 寄存器地址 **/
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E0068)
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E02F4)/** GPIO1 寄存器地址 **/
#define GPIO1_DR   (*(volatile unsigned int *)0x0209C000)
#define GPIO1_GDIR (*(volatile unsigned int *)0x0209C004)
#define GPIO1_PSR  (*(volatile unsigned int *)0x0209C008)#endif //LEARN_I_MX6U_MAIN_H

3. 主程序main.c

//
// Created by Xundh on 2024/11/9.
//
#include "main.h"
/*** 使能时钟* @return*/
int enable_clock(void){CCM_CCGR1 = 0xffffffff;CCM_CCGR2 = 0xffffffff;CCM_CCGR3 = 0xffffffff;CCM_CCGR4 = 0xffffffff;CCM_CCGR5 = 0xffffffff;CCM_CCGR6 = 0xffffffff;return 0;
}
/*** 初始化LED* @return*/void init_led(void){IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x5; // 设置复用为GPIO1_IO03IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;  // 设置电气属性GPIO1_GDIR |= (1<<3);   // 设置为输出
}
void led_on(void){GPIO1_DR &= ~(1<<3);
}
void led_off(void){GPIO1_DR |= (1<<3);
}
void delay(volatile unsigned int n){while(n--);
}
int main(void){/** 初始化LED **/enable_clock();// 初始化LEDinit_led();while(1){led_on();delay(500);led_off();delay(500);}return 0;
}

4. Makefile

objs = start.o main.oledc.bin : $(objs)# 把.o文件链接成.elf文件, 其中的 $^ 代表所有的.o文件arm-linux-gnueabihf-ld -T linker.ld $^ -o ledc.elf# 把.elf文件转换成.bin文件arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@# 把.elf文件转换成.dis文件arm-linux-gnueabihf-objdump -D ledc.elf > ledc.dis%.o : %.c# 把.c文件编译成.o文件, 其中的 $@ 代表目标文件, $< 代表第一个依赖文件arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<%.o : %.S# 把.S文件编译成.o文件arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<clean:rm -f *.bin *.o *.dis *.elfdownload:../tools/imxdownload ledc.bin /dev/sdb

5. 链接文件 linker.ld

上面的 Makefile 直接编译的话,会出现 0x87800000 地址放的不是 _start 函数,而是 main 函数。为了确保 0x87800000,需要使用链接脚本 。

链接脚本(linker script)是用于控制链接器(如 ld)如何将目标文件(object files)链接成可执行文件的脚本,链接脚本通常以 .ld 为扩展名,主要有以下作用:

  1. 定义内存布局:指定程序各个段(如 .text.data.bss)在内存中的位置。
  2. 设置入口点:指定程序的入口点,即程序开始执行的地址。
  3. 分配段:将目标文件中的段分配到内存中的特定位置。
  4. 定义符号:可以定义一些符号,用于在程序中引用特定的内存地址。

以下是本示例使用的链接脚本文件 linker.ld

SECTIONS
{. = 0x87800000; /* 起始地址 */.text : {*(.text)}.data : {*(.data)}.bss : {*(.bss)}. = ALIGN(8);__stack_top = .; /* 定义栈顶 */
}ENTRY(_start) /* 指定入口点为 _start */

本次实验使用的链接脚本 文件:

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

定位计数器

链接脚本中的定位计数器(location counter)是一个特殊的符号,用于跟踪当前分配的内存地址。它通常用 . 表示,并在链接脚本中用于定义段的起始地址和大小。上例中:
. = 0x87800000 , 前面的点就是定位计数器,设置定位计数器的初始值为 0x87800000,表示从这个地址开始分配内存。

.text

.text : {start.o*(.text)
}

定义 .text 段,包含 start.o 和所有 .text 段的内容。定位计数器会自动增加,以跟踪分配的内存地址。
*(.text)表示所有程序源代码编译出来的段。

.rodata 段

只读数据段,如字符串变量,使用const关键字定义的全局或静态变量。

.rodata ALIGN(4) : {*(.rodata*)
}

定义 .rodata 段,并将其对齐到 4 字节边界。定位计数器会更新为 .text 段结束后的地址,并对齐到 4 字节。
其中前面一个* 表示匹配所有的输入文件,
(.rodata*)表示匹配所有以.rodata 开头的段,如 .rodata,rodata1,.rodata2。

.data数据段

.data ALIGN(4) : {*(.data)
}

定义 .data 段,并将其对齐到 4 字节边界。定位计数器会更新为 .rodata 段结束后的地址,并对齐到 4 字节。

.bss段

__bss_start = .;
.bss ALIGN(4) : {*(.bss)*(COMMON)
}
__bss_end = .;

记录 .bss 段的起始地址为 __bss_start,然后定义 .bss 段,并将其对齐到 4 字节边界。定位计数器会更新为 .data 段结束后的地址,并对齐到 4 字节。最后,记录 .bss 段的结束地址为 __bss_end。

*(.bss):匹配所有输入文件中的 .bss 段,并将其内容放入输出文件的 .bss 段中。在输入文件中,未初始化的全局变量和静态变量会被放入 .bss 段。 这些变量在编译时会被放入 .bss 段,并在程序启动时被初始化为零。

6. 编译烧写测试

按第3课操作,将程序烧写到SD卡,可以看到LED1在闪烁。
在这里插入图片描述
本文代码开源地址:
https://gitee.com/xundh/learn_i.mx6u.git

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

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

相关文章

c++设计模式demo

模式设计原则 依赖倒置原则 ⾼层模块不应该依赖低层模块&#xff0c;⼆者都应该依赖抽象 &#xff1b; 抽象不应该依赖具体实现&#xff0c;具体实现应该依赖于抽象&#xff1b; ⾃动驾驶系统公司是⾼层&#xff0c;汽⻋⽣产⼚商为低层&#xff0c;它们不应该互相依赖&#x…

【go从零单排】泛型(Generics)、链表

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在Go语言中&#xff0c;泛型&#xff08;Generics&#xff09;允许你编写可以处理…

Web前端开发--HTML语言

文章目录 前言1.介绍2.组成3.基本框架4.常见标签4.1双标签4.1.1.标题标签4.2.2段落标签4.1.3文本格式化标签4.1.4超链接标签4.1.5视频标签4.1.6 音频标签 4.2单标签4.2.1换行标签和水平线标签4.2.2 图像标签 5.表单控件结语 前言 生活中处处都有网站&#xff0c;无论你是学习爬…

数据结构-图的概念

不存在空图现象,顶点集不能为空,边集可以为空 研究链接一个顶点的边有多少条非常有意义 无向图的度边的二倍 有向图的入度出度,度边数 有向图一致 重点 子图必须联通,尽可能多的边和结点 对于一个生成树,他有n个节点就有n-1条边 修路问题将各个村庄相连,由于经费有限,只能选择…

TDengine 签约蘑菇物联,改造通用设备工业互联网平台

在当前工业互联网迅猛发展的背景下&#xff0c;企业面临着日益增长的数据处理需求和智能化转型的挑战。通用工业设备的高能耗问题愈发突出&#xff0c;尤其是由这些设备组成的公辅能源车间&#xff0c;亟需更高效的解决方案来提升设备运行效率&#xff0c;降低能源消耗。为此&a…

LSM-TREE和SSTable

一、什么是LSM-TREE LSM Tree 是一种高效的写优化数据结构&#xff0c;专门用于处理大量写入操作 在一些写多读少的场景&#xff0c;为了加快写磁盘的速度&#xff0c;提出使用日志文件追加顺序写&#xff0c;加快写的速度&#xff0c;减少随机读写。但是日志文件只能遍历查询…

vue3使用easy-player播放hls监控流

easy-player未发布在npm上&#xff0c;只能采用静态引入方式&#xff0c;老版本不支持v3 1. 在public文件夹下放入EasyPlayer-element.min.js 和 EasyPlayer.wasm 文件 2. 在根目录index.html引入 这样在vue文件中可以使用easy-player 标签 附件

【VScode】C/C++多文件夹下、多文件引用、分别编译——仅一个设置【适合新人入手】

【VScode】C/C多文件夹内的多文件引用编译 1、问题2、前提&#xff08;最简环境&#xff09;3、核心&#xff08;关键配置&#xff09;4、成功享用~ 1、问题 在使用 VScode 编写一个简单项目的时候&#xff0c;没有特别配置的情况下&#xff0c;若主文件(.c)引用了自定义的头文…

【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!

数据集介绍 【数据集】道路事故识别数据集 8939 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。数据集中包含2种分类&#xff1a;{0: accident, 1: non-accident}。数据集来自国内外图片网站和视频截图。检测范围道路事故检测、监控视角检测、无人机视角检测、等&…

Scala 的包及其导入

Scala使用包来创建用于模块化程序的命名空间。通过在Scala文件的顶部声明一个或多个包名称可以创建包&#xff0c;另一种声明包的方式是使用0&#xff0c;这种方式可以嵌套包&#xff0c;并且提供更好的范围与封装控制。对于包的导入&#xff0c;Scala与Java的区别之一便是&…

使用 HuggingFace 提供的 Elasticsearch 托管交叉编码器进行重新排名

作者&#xff1a;来自 Elastic Jeff Vestal 了解如何使用 Hugging Face 的模型在 Elasticsearch 中托管和执行语义重新排序。 在这篇简短的博文中&#xff0c;我将向你展示如何使用 Hugging Face 中的模型在搜索时在你自己的 Elasticsearch 集群中执行语义重新排序。我们将使用…

深究JS底层原理

一、JS中八种数据类型判断方法 在JavaScript中&#xff0c;数据类型分为两大类&#xff1a;基本&#xff08;原始&#xff09;数据类型和引用&#xff08;对象&#xff09;数据类型。 基本数据类型&#xff08;Primitive Data Types&#xff09; 基本数据类型是表示简单的数…

C++虚继承演示

在继承中如果出现&#xff1a; 这种情况&#xff0c;B和C都继承了A&#xff0c;D继承了B、C 在D中访问A的成员会出现&#xff1a; 这样的警告 是因为在继承时A出现两条分支&#xff1a;ABD、ACD 编译器不知道访问的A中的元素是经过B继承还是C继承 所以B、C在继承A时要用到…

【1】虚拟机安装

1.安装VMware WorkStation Pro VMware下载地址&#xff1a; 密钥&#xff1a;YF390-0HF8P-M81RQ-2DXQE-M2UT6 2.新建虚拟机 centos7下载地址&#xff1a;centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里云

【机器学习】均方误差根(RMSE:Root Mean Squared Error)

均方误差根&#xff08;Root Mean Squared Error&#xff0c;RMSE&#xff09;是机器学习和统计学中常用的误差度量指标&#xff0c;用于评估预测值与真实值之间的差异。它通常用于回归模型的评价&#xff0c;以衡量模型的预测精度。 RMSE的定义与公式 给定预测值 和实际值 …

python可视化进阶

引用&#xff1a; 首先需要安装 plotnine from plotnine import* import joypy数据可视化进阶操作 3.1 类别数据可视化 【例3-1】——绘制简单条形图 【代码框3-1】——绘制简单条形图 # 图3-1的绘制代码 import pandas as pd import matplotlib.pyplot as plt from cvxpy …

玩的花,云产品也能拼团了!!!

说起拼单大家都不陌生&#xff0c;电商一贯的营销手段&#xff0c;不过确实可以给消费者省下一笔钱。双11到了&#xff0c;腾讯云产品也玩起了拼团&#xff0c;这明显是对开发人员和各企业的福利。 对于有云产品需求的个人或企业&#xff0c;这次绝对是难得的一次薅羊毛机会。…

设计模式-七个基本原则之一-开闭原则 + SpringBoot案例

开闭原则:(SRP) 面向对象七个基本原则之一 对扩展开放&#xff1a;软件实体&#xff08;类、模块、函数等&#xff09;应该能够通过增加新功能来进行扩展。对修改关闭&#xff1a;一旦软件实体被开发完成&#xff0c;就不应该修改它的源代码。 要看实际场景&#xff0c;比如组内…

Flutter 插件 sliding_up_panel 实现从底部滑出的面板

前言 sliding_up_panel 是一个 Flutter 插件&#xff0c;用于实现从底部滑出的面板。它在设计上非常灵活&#xff0c;能够适应多种 UI 场景&#xff0c;比如从底部滑出的菜单、可拖动的弹出面板等。以下是 sliding_up_panel 的详细用法&#xff0c;包括常用的参数说明和示例代…

出海企业如何借助云计算平台实现多区域部署?

云计算de小白 如需进一步了解&#xff0c;请单击链接了解有关 Akamai 云计算的更多信息 在本文中我们将告诉大家如何在Linode云计算平台上借助VLAN快速实现多地域部署。 首先我们需要明确一些基本概念和思想&#xff1a; 部署多区域 VLAN 为了在多区域部署中在不同的 VLAN …