正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-8.1--C语言LED驱动程序

news2024/11/23 11:25:09

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]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/1640473.html

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

相关文章

对stm32F103RCT6原理图解析(详细)

最近想了解一下原理图为什么这样设计&#xff0c;又发现网上虽然有相关的解析&#xff0c;但是不全面&#xff0c;所以唉还是自己动手&#xff0c;丰衣足食吧。 MCU部分 核心芯片STM32F103RCT6 (8条消息) stm32命名规则_BachelorTse的博客-CSDN博客 这里有芯片相关的命名规则…

在Windows中,matplotlibcpp的使用

0 前言 本篇文章记录一个c库的使用&#xff1a;matplotlib-cpp&#xff0c;其作用是在c中调用python的matplotlib&#xff0c;实现绘图操作。 对于Windows环境下使用该库的文章&#xff0c;发现文章依然存在一些问题&#xff0c;总是存在报错不能解决&#xff0c;花费了较多的…

QT的TcpServer

Server服务器端 QT版本5.6.1 界面设计 工程文件&#xff1a; 添加 network 模块 头文件引入TcpServer类和TcpSocket&#xff1a;QTcpServer和QTcpSocket #include <QTcpServer> #include <QTcpSocket>创建server对象并实例化&#xff1a; /*h文件中*/QTcpServer…

Python根据预设txt生成“你画我猜”题目PPT(素拓活动小工具)

Python根据预设txt生成“你画我猜”题目PPT&#xff08;素拓活动小工具&#xff09; 场景来源 去年单位内部的一次素拓活动&#xff0c;分工负责策划设置其中的“你画我猜”环节&#xff0c;网络上搜集到题目文字后&#xff0c;想着如何快速做成对应一页一页的PPT。第一时间想…

观察者模式实战:解密最热门的设计模式之一

文章目录 前言一、什么是观察者模式二、Java实现观察者模式2.1 观察者接口2.2 具体观察者2.3 基础发布者2.4 具体发布者2.5 消息发送 三、Spring实现观察者模式3.1 定义事件类3.2 具体观察者3.3 具体发布者3.4 消息发送 总结 前言 随着系统的复杂度变高&#xff0c;我们就会采…

Google 发布 CodeGemma 7B,8K上下文,性能超CodeLlama 13B

CodeGemma简介 CodeGemma模型是谷歌的社区开放编程模型&#xff0c;专门针对代码领域进行优化。一系列功能强大的轻量级模型&#xff0c;能够执行多种编程任务&#xff0c;如中间代码填充、代码生成、自然语言理解、数学推理和指令遵循。CodeGemma模型是在大约500B个主要为英语…

【算法入门教育赛1D】环形密码 - 字符串 | C++题解与代码

题目链接&#xff1a;https://www.starrycoding.com/problem/161 题目描述 小 e e e有一个宝箱&#xff0c;这个宝箱有一个长度为 n n n的密码&#xff0c;但是这个密码校验器是一个环形&#xff0c;意思是只要密码从任意一位开始读&#xff08;读到最后一位回到第一位继续&a…

每日OJ题_贪心算法二⑤_力扣870. 优势洗牌(田忌赛马)

目录 力扣870. 优势洗牌&#xff08;田忌赛马&#xff09; 解析代码 力扣870. 优势洗牌&#xff08;田忌赛马&#xff09; 870. 优势洗牌 难度 中等 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引…

Redis - Zset 有序集合

前言 它保留了集合不能有重复成员的特点&#xff0c;但与集合不同的是&#xff0c;有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数&#xff08;score&#xff09;与之关联&#xff0c;有序集合中的元素是可以维护有序性的&#xff0c;但这个有序不是⽤下标作为排序依据⽽是…

笔记13-OSError: [Errno 24] Too many open files

文章目录 参考文献失败尝试系列查看发现&#xff0c;似乎是因为线程数有限制 修改配置先查查看 增加文件数限制&#xff0c;然后使用命令运行&#xff08;成功&#xff09; 参考文献 Linux 最大可以打开多少文件描述符&#xff1f; OSError: [Errno 24] Too many open files错…

Redis-单机安装

试图从官网注册不了我也不知道什么情况。 网盘自取吧&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1KERBQaH9gCT10AGt9z0_jg?pwdyjen 安装比较简单&#xff0c;照着敲就完了每一步都试过了&#xff0c;先单机安装&#xff0c;后面搭建集群。 1.将安装包放到/usr/…

一文带你了解MySQL的索引分类

文章目录 ☃️分类☃️演示图☃️思考☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开…

C++之set/map相关实现

看着上面的图片&#xff0c;你可能对set和map的多样变化产生疑惑&#xff0c;下面我们就来详细讲解他们的区别以及实现 一.set/map 首先&#xff0c;在这里我们要声明&#xff0c;如果你对二叉搜索树一点都不了解的话&#xff0c;建议你先去将搜索二叉树学会再来学习这里的内…

MFC 列表控件删除实例(源码下载)

1、本程序基于前期我的博客文章《MFC下拉菜单打钩图标存取实例&#xff08;源码下载) 》 2、程序功能选中列表控件某一项&#xff0c;删除按钮由禁止变为可用&#xff0c;点击删除按钮&#xff0c;选中的项将删除。 3、首先在主界面添加一个删除参数按钮。 4、在myDlg.cpp 文件…

Python语言零基础入门——文件

目录 一、文件的基本概念 1.文件 2.绝对路径与相对路径 3.打开文件的模式 二、文件的读取 三、文件的追加 四、文件的写入 五、with语句 六、csv文件 1.csv文件的读取 2.csv文件的写入 七、练习题&#xff1a;实现日记本 一、文件的基本概念 1.文件 文件是以计算…

win10禁止自动更新的终极方法

添加注册表值 1.运行&#xff0c;输入regedit 2.打开注册表编辑器依次进入以下路径“计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings”。 3.在Settings项中&#xff0c;新建DWORD&#xff08;32位&#xff09;值(D)&#xff0c;重命名为以下命名“Fl…

python判断大图中包含小图并输出位置总结

python判断大图中包含小图并输出位置总结 没啥可说的&#xff0c;项目遇到了就直接上代码&#xff0c;可以减轻劳动力&#xff0c;花最少得时间实现应用功能。 import cv2 # 读取大图片和小图片的路径 img_big cv2.imread(big_image.png) img_small cv2.imread(small_image…

使用protoc-jar-maven-plugin生成grpc项目

在《使用protobuf-maven-plugin生成grpc项目》中我们使用protobuf-maven-plugin完成了grpc代码的翻译。本文我们将只是替换pom.xml中的部分内容&#xff0c;使用protoc-jar-maven-plugin来完成相同的功能。总体来说protoc-jar-maven-plugin方案更加简便。 环境 见《使用proto…

数据结构--顺序表经典OJ题

例1&#xff1a;合并有序顺序表 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff…

R可视化:分组频率分布直方图和密度图

介绍 ggplot2绘制分组频率分布直方图和密度图 加载R包 knitr::opts_chunk$set(message FALSE, warning FALSE) library(tidyverse) library(patchwork) library(ggpubr) library(rstatix)# rm(list ls()) options(stringsAsFactors F) options(future.globals.maxSize …