正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-8.1

news2024/11/24 14:13:03

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》第 8.1 章

《正点原子资料_A盘/02开发板原理图/IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》

  • 资料盘 开发板资料链接: https://pan.baidu.com/s/1j5Jzbdx9i-g0cWIi3wf2XA 提取码:ag1u


正文:

本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第8.1讲” 的读书笔记。第8.1讲是如何通过汇编设置 I.MX6U 处理器准备C语言运行环境。位接下来使用C语言来开发 led 点灯驱动程序做准备。

0. 使用汇编准备C语言运行环境

第8章介绍了如何使用汇编语言来编写LED灯实验,但是实际开发过程中汇编用的很少,大部分都是C语言开发,汇编只是用来完成C语言环境的初始化。本章我们就来学习如何使用汇编来完成C语言环境的初始化工作,然后从汇编跳转到C语言代码里面去。

1. 设置处理器运行模式为 SVC 特权模式

在第八章的汇编 LED 灯实验中,我们了解了如何使用汇编来编写LED灯驱动,实际工作中是很少用到汇编去编写嵌入式驱动的,毕竟汇编太难,而且写出来也不好理解,大部分情况下都是使用C语言去编写的。只是在开始部分用汇编来初始化一些C语言环境,也就是C语言代码,一般都是进入 main 函数。

所以我们有两部分文件需要做:

  1. 汇编文件
    汇编文件只是用来完成C语言环境的搭建。
  2. C语言文件
    C语言环境就是万策划给你我们业务层代码的,其实就是实际里程要完成的功能。

在第6章C“ortex-A7 处理器运行模式”一节中,已经说过Cortex-A7有 9 种运行模式。这里我们需要设置处理器运行在  SVC 模式,这是因为只有在 SVC 特权模式下,处理器才有权限访问SoC的全部硬件资源,有些SoC的硬件资源处理器在普通用户模式是访问不到的。

在6.3.2小节已经详细的介绍了 Cortex-A7 的 CPSR (Current Program State Register)寄存器,其中 M[4:0] (CPSR的bit[4:0])就是设置处理器运行模式的,参考表 6.3.2.2,如果要将处理器设置为 SVC 模式,那么 M[4:0] 就要等于 0x13。

在7.2.1 小节已经讲过,设置Cortex-A7 处理器的CPSR寄存器这种特殊寄存器,不能使用 LDR, STR 指令,需要使用 MRS, MSR 指令。MRS读取CPSR到的寄存器,MSR将寄存器里的值传送到CPSR。

  1. 首先我们使用 "mrs r0,cpsr" 将CPSR状态寄存的内容读取到R0寄存器
  2. 参考7.2.6 章节的 “ARM汇编逻辑运算指令”,我们使用 BIC 汇编指令来对R0寄存器的低5位进行清零(也就是CPSR寄存器的 M[4:0]),然后使用 ,ORR  汇编指令来对R0 或上 0x13 ,也就是给CPSR寄存器的M[4;0]位赋值为0x13, 表示SVC模式
  3. 最后使用 "msr cpsr,r0" 将r0的值写回到 CPSR 寄存器,此时处理器就进入了SVC模式。

.global _start

_start:
/* 进入 SVC 模式 */
mrs r0, cpsr         @读取cpsr到r0
bic r0, r0, #0x1f    @将r0的低5位清零,也就是cpsr的M0~M4
orr r0, r0, #0x13    @r0或上0x13,表示使用svc模式
msr cpsr, r0         @将r0写回cpsr

2. 准备C语言运行环境栈指针,SP(R13)寄存器

通过 'ldr rp, =0x8020_0000' 指令设置 SVC 模式下的 SP 栈指针 = 0x8020_0000,因为正点原子 I.MX6U ALPHA/Mini 开发板上的 DDR3 的地址范围是 0x8000_0000 ~ 0xA000_0000 (512MB)或者 0x8000_0000 ~ 0x9000_0000(256MB),不管是512MB还是256MB版本的,其DDR3起始地址都是 0x8000_0000 。由于 Cortex-A7 的堆栈是向下增长的,所以将 SP 指针这是为 0x8020_0000 ,因此SVC模式下的栈大小是 ,0x8020_0000 - 0x8000_0000 = 0x0020_0000 = 2MB, 2MB 的栈空间已经很大了,如果做裸机开发的话绰绰有余。 

ldr sp, =0x8020_0000    @设置栈指针
b main                  @跳转到main函数

然后使用 'b main' 指令跳转到main函数,main 函数就是C语言代码了。

至此汇编程序部分执行完成,就几行代码,用来设置处理器运行到 SVC 模式,然后初始化 SP 指针,最终跳转到 mian 函数中。

如果有玩过三星的 S3C2440 或者 S5PV210 的话会知道我们在使用 SDRAM 或者 DDR之前必须初始化 SDRAM后者DDR。所以三星的 S3C2440 或者 S5PV210的汇编文件里是一定会有SDRAM或DDR初始化代码的。我们上面编写的 start.s 文件中却没有初始化 DDR3 的代码,但是却将 SVC 模式下的SP指针设置到了 DDR3 的地址范围中,这不会出问题么?肯定不会的,DDR3肯定是要初始化的,但是不需要再 start.s 文件中完成。在9.4.2 小节里面分析 DCD (Device Config Data)数据的时候就已经讲过了,DCD数据包含了 DDR 配置参数,I.MX6U 内部的 boot ROM 会读取 DCD  数据中的 DDR 配置参数然后完成DDR初始化的。

3. 编写Makefile

现在已经写好了 main.c 和 start.s 两个文件,我们编写一个 Makeile 来实现对两个文件的编译,链接,转换为 .bin 格式,并且反编译文件用来检查生成的代码是否符合预期。

在Makefile里使用的命令 'arm-linx-gnueabihf-xx, 在上一节编译汇编 led.s 程序的时候都已经使用过,并且解释了这行命令中的选型的含义。这里我们使用了 Makefile 语法总的 makefile 自动变量 ‘$@’, '$<', '$^' ,这些makefile 自动变量的含义可以查考手册中的“3.4 Makefile 语法”章节。

       arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o ledc.elf
        arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
        arm-linux-gnueabihf-objdump -D ledc.elf -m arm > ledc.dis

#objs = main.o start.o
objs = start.o main.o 

ledc.bin : $(objs)
	arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o ledc.elf
	arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
	arm-linux-gnueabihf-objdump -D ledc.elf -m arm > ledc.dis


%.o : %.c
	arm-linux-gnueabihf-gcc -c -Wall -nostdlib -o $@ $<

%.o : %.s 
	arm-linux-gnueabihf-gcc -c -Wall -nostdlib -o $@ $<

clean:
	rm -rf *.o ledc.elf ledc.bin ledc.dis

这里执行 'make' 命令编译之后,需要查看反编译出来的 'ledc.dis' 文件,确认下链接出来的 ledc.elf 文件中 '_start' 符号是不是在文件的最开始,因为 '_start' 是设置准备C语言的运行环境的代码,必须在最开始的时候执行,所以 _start 应该被链接到地址 0x80700000,这个地址也就是我们在上一节已经讨论过了选择的 .bin 文件的加载地址和处理器执行起始地址。

如下图,可以看到反汇编出来的 ledc.dis 文件中,_start 符号没有被链接到 0x80700000 地址位置,这样I.MX6U处理器上电boot ROM上电启动跳转到 0x87800000地址起始地址处开始执行时,执行的就不是准备C语言运行环境的 _start 符号处的指令。这样就会出现错误,因为此时C语言执行环境 SP指针还没有初始化,不能执行C语言函数(C语言函数里有PUSH压栈指令,此时SP指针还没有初始化)。

出现 'clk_enable'符号被链接到文件的起始为止,而 '_start' 没有被链接到文件起始为止的原因是在 Makefile 里链接时 ".o" 文件的列表顺序里先写了 'main.o' 然后是 "start.o",这样main.o 就放在了文件的起始为止。

修改Makefile 文件里 "start.o"和 “main.o”的文件顺序,运行命令'make clean', ‘make ’ 再次编译,重新查看反编译的 ledc.dis 文件,这次可以到到 _start 符号已经被链接到文件的起始位置 0x87800000 地址处,I.MX6U 上电启动跳转到 DDR 地址 0x87800000 处开始执行的时候,第一步执行的命令就是 '_start' 符号处的汇编执行,这行执行配置处理器进入 SVC模式,并且配置 SP 堆栈指针准备好C语言运行环境,然后跳转到main函数,从main函数开始就是C语言了。

4. 实现short_dealy短时延

通过让处理器死循环执行指令的方法(忙等)来实现短时延,正点原子的视频教程里讲到经过正点的原子的测试,在I.MX6U处理器运行在 396MHz 的主频下,处理器循环执行 0x7ff 次循环的时间大概耗时 1ms。所以在正点原子视频教程和文档里通过如下两个函数 'dely_short()' 来让处理器执行指定的循环次数,而'dely_short(0x7ff)' 循环 0x7ff 次处理器的耗时大约是1ms,通过 'delay()' 让处理器等待指定时延的 ms 数,我们就可以实现最简单的处理器忙等的时延函数。

/* short delay */
void delay_short(volatile unsigned int delay){
    while(delay--)
    {
        ;
    }
}

/* delay 指定的 ms 数 */
void delay(volatile unsigned int n){
    while(n--){
        delay_short(0x7ff);
    }
}

5. 在main函数中调用时延函数并且turun on/off LED灯,实现LED灯的闪烁

有了如上的时延函数,然后通过写 GPIO1_DR 寄存器 bit3 位的值的方式来控制输出高电平和低电平来实现LED灯的亮灭。我们把控制LED灯亮灭,写寄存器的语句封装成函数, ‘led_on()’, 'led_off()' 。

/* turn on led */ 
void led_on(void)
{
    GPIO1_DR &= ~(1<<3);    //clean bit3 to 0     
}

/* turn off led */
void led_off(void)
{
    GPIO1_DR |= (1<<3);     //set bit3 to 1
}

在 main 函数的最后 while(1)循环中,我们通过调用 delay 时延函数和 led_on, led_off 函数就可以实现LED的亮灭控制。

int main(void)
{
    clk_enable();
    led_init();

    while(1){
        led_on();
        delay(50);
        led_off();
        delay(50);
    }

    return 0;
}

6. 编译代码并烧录到SD卡中,开发上电验证LED灯是否闪烁

按照上一节中的步骤,编译代码,并且使用正点原子提供的 ‘imxdownload’ 来将编译出来的 led.bin 文件加上 "IVT+BootData+DCD" I.MX6U 要求的 image-heander 头,并烧写到SD卡,把SD卡查到正点原子 I.MX6U ALPHA/Mini 开发板上,开发板上电,验证下LED灯是否闪烁。

./imxdownload ledc.bin /dev/sdb

我开发版的验证结果是LED灯正常的按照预期闪烁。

7. 总结

总结,通过汇编程序准备C语言执行环境,只需要简单的几条汇编执行就可以准备好C语言的执行环境。对于C语言执行环境来说,我个人从这个实验的 start.s 理解,最基本的也是最简单的就是准备好C语言执行需要的栈内存空间,然后把栈的起始地址写到 SP (R13)即寄存器里就可以了。

start.s 通过'MSR', 'MRS' 命令写ARM处理器的CPSP当前状态寄存器其,通过写CPSR M[4:0] bit位,就可以让处理器进入指定的ARM运行模式,例如,本例子中的让I.MX6U 运行在 SVC 模式,然后设置 SVC 模式的 SP指针。因为如之前的"Cortex-A7 寄存器”章节所接收的,在不同的运行模式下,ARM处理器可能有自己模式下的独有的物理SP寄存器。

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

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

相关文章

【工具】--- Adobe Illustrator 下载-入门绘图

文章目录 软件下载入门项目可看课程 尝试使用Adobe Illustrator&#xff08;设计师常用软件&#xff09;进行科研绘图。 软件下载 阿里云盘下载 入门项目 绘制一个箭头并保持为SVG&#xff0c; 直线->画线->窗口->描边->选择想要的箭头样式->颜色->改为蓝…

众筹商城源码 众筹商品平台 商城加共识元富之路 网上商城众筹

众筹商城源码 众筹商品平台 商城加共识元富之路 网上商城众筹 前端是编译后的&#xff0c;后端PHP&#xff0c;带商城 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89161734 更多资源下载&#xff1a;关注我。

mybatis工程需要的pom.xml,以及@Data 、@BeforeEach、@AfterEach 的使用,简化mybatis

对 “mybatis - XxxMapper.java接口中方法的参数 和 返回值类型&#xff0c;怎样在 XxxMapper.xml 中配置的问题” 这篇文章做一下优化 这个pom.xml文件&#xff0c;就是上面说的这篇文章的父工程的pom.xml&#xff0c;即&#xff1a;下面这个pom.xml 是可以拿来就用的 <?…

Node.js 版本升级方法

在构建vue项目时&#xff0c;依赖npm&#xff08;Node Package Manager&#xff09;工具&#xff0c;类似于Java项目需要maven管理。而npm是node.js的管理工具&#xff0c;npm依赖node.js环境才能执行。 有时候使用voscode或者其他工具安装vue项目依赖&#xff0c;显示一直处于…

【自然语言处理】Word2VecTranE的实现

作业一 Word2Vec&TranE的实现 1 任务目标 1.1 案例简介 Word2Vec是词嵌入的经典模型&#xff0c;它通过词之间的上下文信息来建模词的相似度。TransE是知识表示学习领域的经典模型&#xff0c;它借鉴了Word2Vec的思路&#xff0c;用“头实体关系尾实体”这一简单的训练目…

【Vue3】Ref与Reactive

3.1【ref 创建&#xff1a;基本类型的响应式数据】 作用&#xff1a;定义响应式变量。语法&#xff1a;let xxx ref(初始值)。返回值&#xff1a;一个RefImpl的实例对象&#xff0c;简称ref对象或ref&#xff0c;ref对象的value属性是响应式的。注意点&#xff1a; JS中操作数…

【在线名字作画HTML源码】

在线名字作画HTML源码 效果图部分源码领取源码下期更新预报 效果图 部分源码 index.htm <!DOCTYPE html> <html> <head> <title>在线名字作画|民间花鸟字|多彩花鸟虫鱼组合书法|藏字画|字谜语|飞帛板书|意匠文字</title> <meta http-equiv&…

Profinet转Modbus网关接称重设备与1200PLC通讯

Profinet转Modbus网关&#xff08;XD-MDPN100&#xff09;是一种能够实现Modbus协议和Profinet协议之间转换的设备。Profinet转Modbus网关可提供单个或多个RS485接口&#xff0c;使用Profinet转Modbus网关将称重设备与西门子1200 PLC进行通讯&#xff0c;可以避免繁琐的编程和配…

【C++】深入了解C++内存管理

个人主页&#xff1a;救赎小恶魔 欢迎大家来到小恶魔频道 好久不见&#xff0c;甚是想念 今天我们要深入讲述类与对象的初始化列表以及隐式类型转换 目录 1.C的内存分布 2.C/C言中动态内存管理方式 1.C语言的管理方式 2.C的管理方式 new delete 3.operator new与ope…

如何删除BigKey

1.2.3、如何删除BigKey BigKey内存占用较多&#xff0c;即便时删除这样的key也需要耗费很长时间&#xff0c;导致Redis主线程阻塞&#xff0c;引发一系列问题。 redis 3.0 及以下版本 如果是集合类型&#xff0c;则遍历BigKey的元素&#xff0c;先逐个删除子元素&#xff0c;…

为什么选择OpenNJet?OpenNJet下一代云原生应用引擎!OpenNJet开发实战!

前言导读 在当今这个数字化转型加速的时代&#xff0c;云原生技术已成为企业和开发者构建现代应用的首选路径。OpenNJet作为新一代云原生应用引擎&#xff0c;在国内外技术社区受到了广泛关注。 本文将深入探讨OpenNJet的特点、优势以及在开发实践中的应用&#xff0c;带您全…

深度学习基础之《TensorFlow框架(16)—神经网络案例》

一、mnist手写数字识别 1、数据集介绍 mnist数据集是一个经典的数据集&#xff0c;其中包括70000个样本&#xff0c;包括60000个训练样本和10000个测试样本 2、下载地址&#xff1a;http://yann.lecun.com/exdb/mnist/ 3、文件说明 train-images-idx3-ubyte.gz: training s…

vscode 配置与插件记录

vscode插件 python PythonPython DebuggerruffisortPylanceJupyterJupyter KeymapJupyter Slide ShowJupyter Cell TagsautoDocstring - Python Docstring Generator ruff isort pylance autodocsting 在setting.json里这么配置&#xff0c;这样你保存时就会自动format…

【酱浦菌-爬虫项目】爬取学术堂论文信息

1. 首先&#xff0c;代码定义了一个名为 url 的变量&#xff0c;它是一个包含三个网址的集合&#xff08;或者说是一个集合的字典&#xff09;。这些网址分别是&#xff1a; - ‘http://www.xueshut.com/lwtimu/127966.html’ - ‘http://www.xueshut.com/lwtimu/12…

您可知道如何通过`HTTP2`实现TCP的内网穿透???

可能有人很疑惑应用层 转发传输层&#xff1f;&#xff0c;为什么会有这样的需求啊&#xff1f;&#xff1f;&#xff1f;哈哈技术无所不用其极&#xff0c;由于一些场景下&#xff0c;对于一个服务器存在某一个内部网站中&#xff0c;但是对于这个服务器它没有访问外网的权限&…

《ElementPlus 与 ElementUI 差异集合》el-dialog 显示属性有差异

ElementPlus 用属性 v-model ElementUI 用属性 visible 其实也是 Vue2/Vue3 的差异&#xff1a;v-model 指令在组件上的使用已经被重新设计&#xff0c;替换掉了 v-bind.sync

新质生产力实践,我用chatgpt开发网站

是的&#xff0c;我用chatgpt开发了一个网站&#xff0c;很轻松。 我之前一点不懂前端&#xff0c;也没有网站开发的代码基础&#xff0c;纯正的0基础。 从0开始到最后成品上线&#xff0c;时间总计起来大致一共花了2-3周的时间。 初始想法我是想给我公司开发一个网站&#…

C# wpf 运行时替换方法实现mvvm自动触发刷新

文章目录 前言一、如何实现&#xff1f;1、反射获取属性2、定义替换方法3、交换属性的setter方法 二、完整代码1、接口2、项目 三、使用示例1、倒计时&#xff08;1&#xff09;、继承ViewModelBase&#xff08;2&#xff09;、定义属性&#xff08;3&#xff09;、属性赋值&am…

【Redis 开发】Lua语言

Lua Lua语法 Lua语法 Lua是一种小巧的脚本语言&#xff0c;底层用C语言实现&#xff0c;为了嵌入式应用程序中 官网&#xff1a;https://www.lua.org/ 创建lua文件 touch hello.lua 运行lua文件 lua hello.lua 输出语句 print("Hello World!")数据类型 可以通过t…

python与上位机开发day04

模块和包、异常、PyQt5 一、模块和包 1.1 模块 Python中模块就是一个.py文件&#xff0c;模块中可以定义函数&#xff0c;变量&#xff0c;类。模块可以被其他模块引用 1.1.1 导入模块 """ 导入格式1&#xff1a; import 模块名 使用格式&#xff1a; …