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

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]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.elfarm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@arm-linux-gnueabihf-objdump -D ledc.elf -m arm > ledc.dis%.o : %.carm-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.mzph.cn/bicheng/5029.shtml

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

相关文章

使用opencv改变图片大小

使用opencv改变图片大小 图片的宽度和高度效果代码 图片的宽度和高度 宽度&#xff1a;图片的宽度指的是图像从左边缘到右边缘的水平跨度。在数字图像中&#xff0c;宽度通常是以像素&#xff08;pixels&#xff09;为单位来度量的。高度&#xff1a;图片的高度指的是图像从上…

【go项目01_学习记录day01】

博客系统 1 vscode开发go项目插件推荐1.1 CtrlShiftP&#xff08;俗称万能键&#xff09; &#xff1a;打开命令面板。在打开的输入框内&#xff0c;可以输入任何命令。1.2 开发时&#xff0c;我们需要经常查阅 Go 语言官方文档&#xff0c;可惜因国内访问外网不稳定&#xff0…

[Java EE] 多线程(五):单例模式与阻塞队列

1. 单例模式 单例模式是校招中最长考的设计模式之一,首先我们来谈一谈什么是设计模式: 设计模式就好像象棋中的棋谱一样,如果红方走了什么样的局势,黑方就有一定地固定地套路,来应对这样的局势,按照固定地套路来,可以保证在该局势下不会吃亏. 软件开发也是同样的道理,有很多…

Linux基础-socket详解、TCP/UDP

文章目录 一、Socket 介绍二、Socket 通信模型三、Socket 常用函数1 创建套接字2 绑定套接字3、监听连接4、接受连接5、接收和发送数据接收数据发送数据 6、关闭套接字 四、Socket编程试验1、源码server.cclient.c 2、编译&#xff1a;3、执行结果 五、补充TCP和UDP协议的Socke…

OpenAI 新推出 AI 问答搜索引擎——SearchGPT 震撼登场

您的浏览器不支持 video 标签。 OpenAI-SearchGPT 近日&#xff0c;OpenAI 曝光了自己的一款令人瞩目的 AI 问答搜索引擎——SearchGPT。这款搜索引擎带来了全新的搜索体验&#xff0c;给整个行业带来了巨大的压力。 SearchGPT 支持多种强大的功能。首先&#xff0c;它能够通过…

分布式与一致性协议之Raft算法(三)

Raft算法 如何复制日志 你可以把Raft算法的日志复制理解成一个优化后的二阶段提交(将二阶段优化成了一阶段)。优化后减少了一半的往返消息&#xff0c;也就是降低了一半的消息延迟&#xff0c;那日志复制的具体过程又是什么呢&#xff1f; 首先&#xff0c;领导者进入第一阶段…

【Redis 开发】多级缓存,本地进程缓存Caffeine

多级缓存 多级缓存本地进程缓存CaffeineCaffeine三种缓存驱逐策略 多级缓存 Redis处理并发的能力是非常强大的&#xff0c;但是tomcat的支持并发的能力跟不上Redis的性能&#xff0c;导致整体性能的下降 Redis缓存失效时&#xff0c;会对数据库产生冲击&#xff0c;之间再无屏…

LeetCode1017题:负二进制转换(原创)

【题目描述】 给你一个整数 n &#xff0c;以二进制字符串的形式返回该整数的 负二进制&#xff08;base -2&#xff09;表示。注意&#xff0c;除非字符串就是 "0"&#xff0c;否则返回的字符串中不能含有前导零。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&…

基于Springboot的数字化农家乐管理平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的数字化农家乐管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

踏上R语言之旅:解锁数据世界的神秘密码(四)

文章目录 前言一、多元线性回归1.多元线性回归模型的建立2.多元线性回归模型的检验 二、多元线性相关分析1.矩阵相关分析2.复相关分析 三、回归变量的选择方法1.变量选择准则2.变量选择的常用准则3.逐步回归分析 总结 前言 回归分析研究的主要对象是客观事物变量间的统计关系。…

选择内核注意力 SK | Selective Kernel Networks

论文名称&#xff1a;《Selective Kernel Networks》 论文地址&#xff1a;https://arxiv.org/pdf/1903.06586.pdf 代码地址&#xff1a;https://github.com/implus/SKNet 在标准的卷积神经网络中&#xff0c;每层人工神经元的感受野被设计为具有相同的大小。神经科学界已经广…

使用OkHttp 缓存 API 调用提高Android应用性能

使用OkHttp 缓存 API 调用提高Android应用性能 坦率地说&#xff0c;我们都遇到过这样的情况——焦急地刷新应用&#xff0c;看着加载图标不停地旋转&#xff0c;等待那个至关重要的 API 响应。这样的等待我们已经是炉火纯青了&#xff0c;是吧&#xff1f;手指有节奏地轻敲屏…

《R语言与农业数据统计分析及建模》——多重共线性和逐步回归

一、多重共线性 多重共线性&#xff1a;在多元线性回归时&#xff0c;多个自变量之间存在高度相关关系&#xff0c;时模型估计失真或难以估计准确的情况。 一般地&#xff0c;多元线性回归中自变量间应尽量相互独立。常规模型诊断方法难以检测多重共线性。 1、案例解释 作物产…

ActiveMQ 反序列化漏洞 (CVE-2015-5254)

一、漏洞描述 Apache ActiveMQ 是由美国阿帕奇&#xff08;Apache&#xff09;软件基金会开发的开源消息中间件&#xff0c;支持 Java 消息服务、集群、Spring 框架等。属于消息队列组件(消息队列组件&#xff1a;分布式系统中的重要组件&#xff0c;主要解决应用耦合、异步消息…

数据库|TiDB-Server API的高效应用指南

一、API介绍 1.Status 显示TiDB 连接数、版本和git_hash 信息 tidb-server_ip:status_port/status { "connections": 0, "version": "5.7.25-TiDB-v6.1.1", "git_hash": "5263a0abda61f102122735049fd0dfadc7b7f822" } 2.St…

mysql-sql-练习题-4-标记

标记 连续登录2-7天用户建表排名找规律 最大连胜次数建表只输出连胜结果输出所有连续结果 连续登录2-7天用户 建表 create table continuous_login(user_id1 integer comment 用户id,date_login date comment 登陆日期 ) comment 用户登录表;insert into continuous_login val…

LT2611UX四端口 LVDS转 HDMI2.0,带音频

描述LT2611UX 是一款面向机顶盒、DVD 应用的高性能 LVDS 至 HDMI2.0 转换器。LVDS输入可配置为单端口、双端口或四端口&#xff0c;具有1个高速时钟通道和3~4个高速数据通道&#xff0c;工作速率最高为1.2Gbps/通道&#xff0c;可支持高达19.2Gbps的总带宽。LT2611UX 支持灵活的…

002 springCloudAlibaba Sentinel流控-关联

当与A关联的资源B达到阀值后&#xff0c;就限流A自己 文章目录 FlowLimitController.javaSentinelServerApplication.javaServletInitializer.javaapplication.yamlpom.xmlpom.xml 启动Sentinel8080 - java -jar sentinel-dashboard-1.7.0.jar 启动微服务8401 启动8401微服务…

72、栈-每日温度

思路&#xff1a; 第一种方法&#xff0c;双循环&#xff0c;第一层循环拿出一个元素&#xff0c;第二层循环寻找最近比当前大的元素位置。 第二种方法&#xff1a;使用栈来实现。 初始化&#xff1a; int[] ans 用来存储每一天之后多少天温度会升高。Stack<Integer> 用…

Java | AI+编程 | 如何使用通义灵码提升开发效率

大家好&#xff0c;我是程序员影子 | 全网同名 一名致力于帮助更多朋友快速入门编程的程序猿 今天&#xff0c;我将以小白入门的视角带着大家学会如何在Idea上使用通义灵码&#xff0c;提高开发效率&#xff0c;减少重复工作&#xff1b;话不多说&#xff0c;我们直接进入正题…