看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用C语言编写》) 2023.4.18

2023-05-16

目录

    • 前言
    • 汇编文件编写 + 分析
    • C语言文件编写 + 分析
    • 编写Makefile文件
    • 编写链接脚本
    • 最终编译验证

前言

在上一篇文章中我们已经使用汇编语言编写LED灯实验,但是在实际项目中我们如果全部使用汇编,未免难度太大同时也增加了开发难度,这并不是我们想看到的,但是我们也不能完全逃避使用汇编,只不过是最大化的简化汇编文件的编写,大部分是用C语言开发,只是汇编用来完成C语言环境的初始化,然后从汇编跳转到C语言代码里面去。

汇编文件编写 + 分析

汇编:完成C语言环境的初始化

先上完整汇编代码:

.global _start

_start:

    mrs r0, cpsr  
    bic r0, r0, #0x1f  
    orr r0, r0, #0x13  
    msr cpsr, r0  

    ldr sp, =0X80200000  
    b main 

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

.global _start

//相当于声明_start为全局变量

_start:

//代表程序开始运行入口

mrs r0, cpsr  
bic r0, r0, #0x1f  
orr r0, r0, #0x13  
msr cpsr, r0 

//这一部分主要的作用是代表进入SVC模式,至于具体怎么设置成SVC模式的可见下面讲解:

  1. 以前的ARM处理器有7种运行型:User,FIQ,IRQ,SVC,Abort,Undef,System。其中User是非特权模式,其余6种都是特权模式,所以本篇文章中使用SVC模式
    在这里插入图片描述

所有的处理器模式都共用一个CPSR物理寄存器,也就意味着我们需要配置该寄存器,使其功能为SVC模式
在这里插入图片描述
其中第0~4位为设置处理器模式控制位。具体含义如下所示:

在这里插入图片描述

所以由上图可知我们需要将M[4:0]设置为10011即可,也就是低5位

  1. 因为我们需要使用汇编命令来访问存储器,所以需要提前介绍几个汇编命令,分别如下:
    在这里插入图片描述
    对应于上面命令mrs r0, cpsr相当于是将特殊寄存器CPSR里面的数据复制到r0中。
    在这里插入图片描述
    对应于上面命令bic r0, r0, #0x1f 相当于是
    0x1f相当于0x00011111r0 = r0 & (~0x00011111)~0x00011111 = 0x11100000,所以相当于是将r0低5位0

在这里插入图片描述

对应于上面命令orr r0, r0, #0x13 相当于是
0x13相当于0x00010011r0 = r0 | 0x00010011,所以相当于是将r0低5位10011

在这里插入图片描述

对应于上面命令msr cpsr, r0 相当于是经r0的值赋值给CPSR中,即至此成功配置输出模式为SVC模式。

ldr sp, =0X80200000 

因为 I.MX6U的 DDR3 地址范围是0X80000000 ~ 0XA0000000(512MB)或者0X80000000~0X90000000(256MB),不管是 512MB 版本还是 256MB 版本的,其 DDR3 起始地址都是 0X80000000。由于 Cortex-A7 的堆栈是向下增长的,所以将 SP 指针设置为 0X80200000,
因此 SVC 模式的栈大小 0X80200000-0X80000000=0X200000=2MB,2MB 的栈空间已经很大了,如果做裸机开发的话绰绰有余。

b main 

//跳转到main函数,main函数就是C语言代码

C语言文件编写 + 分析

main.c文件

#include "main.h"
//使能 I.MX6U 所有外设时钟
void clk_enable(void)
{
    CCM_CCGR0 = 0xffffffff;
    CCM_CCGR1 = 0xffffffff;
    CCM_CCGR2 = 0xffffffff;
    CCM_CCGR3 = 0xffffffff;
    CCM_CCGR4 = 0xffffffff;
    CCM_CCGR5 = 0xffffffff;
    CCM_CCGR6 = 0xffffffff;
}
//初始化 LED 对应的 GPIO
void led_init(void){
		/* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */
    SW_MUX_GPIO1_IO03 = 0x5;
    /* 2、配置 GPIO1_IO03 的 IO 属性*/
    SW_PAD_GPIO1_IO03 = 0X10B0;
    /* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */
    GPIO1_GDIR = 0X0000008;
    /* 4、设置 GPIO1_IO03 输出低电平,打开 LED0 */
    GPIO1_DR = 0X0;
}

void led_on(void){
	  /* 将 GPIO1_DR 的 bit3 清零*/
    GPIO1_DR &= ~(1<<3);
}


void led_off(void){
	 /*将 GPIO1_DR 的 bit3 置 1*/
    GPIO1_DR |= (1<<3);
}
/*
* @description : 短时间延时函数
* @param - n : 要延时循环次数(空操作循环次数,模式延时)
* @return : 无
*/
void delay_short(volatile unsigned int n){
    while(n--){}
}
/*
 * @description : 延时函数,在 396Mhz 的主频下延时时间大约为 1ms
 * @param - n : 要延时的 ms 数
 * @return : 无
 *
*/
void delay(volatile unsigned int n){
    while(n--){
        delay_short(0x7ff);
    }

}

int main(void)
{
	clk_enable(); 
	led_init(); 
	
	while(1) 
	{ 
		led_off(); 
		delay(500); 
				
		led_on(); 
		delay(500); 
	}
	
	return 0;
}

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

clk_enable();

//其中是使能所有的时钟

led_init(); 

//初始化led

led_off(); 

//关闭 LED

delay(500);

// 延时大约 500ms

编写Makefile文件

Makefile文件

objs := start.o main.o

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

%.o:%.s
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.S
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
	rm -rf *.o ledc.bin ledc.elf ledc.dis

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

objs := start.o main.o

//相当于定义了一个变量objs,只不过生成objs需要依赖start.omain.o,但是生成start.omain.o需要之后的语句来还生成。

ledc.bin:$(objs)

//生成ledc.bin需要依赖start.omain.o,但是生成start.omain.o需要之后的语句来还生成。

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

//相当于针对.s文件类型将其编译成对应的.o文件,其实就是汇编.s(.S).c文件。其中$@代表是目标集合,在这里代表start.o$<代表依赖目标集合的第一个文件,这里的依赖目标集合为(start.o和main.o),所以第一个文件为start.s。至此应该生成了start.omain.o,既然我们已经成功生成了依赖目标文件,那么下一步就是要进行链接操作。

arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^

//使用arm-linux-gnueabihf-ld进行链接,只不过这里的链接为我们自己编写的链接脚本,至于这个链接脚本的内容是什么,之后会做详细讲解。这里先简单介绍下功能。相当于将start.omain.o生成ledc.elf,也就是链接文件。

arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@

//使用arm-linux-gnueabihf-objcopy来将ledc.elf文件转为ledc.bin

arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis

//使用arm-linux-gnueabihf-objdump来反汇编,生成ledc.dis文件。

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

//工程清理规则,通过命令make clean就可以清理工程。

编写链接脚本

脚本文件imx6ul.lds

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

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

SECTIONS

//相当于是我们C语言中的函数

. = 0X87800000;

//“.”相当于是特殊符号,即相当于是pc指针,指向了其地址,在这个代码中相当于是将代码链接到以
0X87800000为起始地址的地方。

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

//.text相当于是段名,其中start.omain.o代表可以链接到段名为.text的所有文件。*(.text)中的“*”是通配符,表示所有输入文件的.text段都放到.text中。

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

定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。但是这一行多了一个“ALIGN(4)”,这是什么意思呢?这是用来对“.data”这个段的起始地址做字节对齐的,ALIGN(4)表示 4 字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。

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

//所有文件中的“.bss”数据都会被放到这个里面,“.bss”数据就是那些定义了但是没有被初始化的变量。就是因为有这个特性,所以我们需要手动对.bss段进行变量清零,因此我们需要知道.bss段的起始和结束地址,这样我们直接对这段内存赋值为0即可完成清零。

最终编译验证

我们将编译出来的ledc.bin烧写到SD卡中,最终结果就是LED0就会以500ms的时间间隔亮灭。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用C语言编写》) 2023.4.18 的相关文章

  • 【ros】7.ros导航navigation(定位规划)

    物竞天择 xff0c 优胜劣汰 xff1b 苟不自新 xff0c 何以获存 梁启超 文章目录 smirk 1 ros导航 blush 2 2d导航 satisfied 3 3d导航 x1f60f 1 ros导航 ros机器人有个导航功能 x
  • 【两周年】我的创作纪念日(水)

    机缘 两年前的今天 xff0c 处于离职状态 xff0c 准备去另一个城市工作 xff0c 同时开始学习编程知识 IT技能 xff0c CSDN让我发现了一群热爱学习和分享的小伙伴 xff0c 也萌发了在这里扎根的想法 收获 不知不觉已经两
  • AI模型部署概述

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 AI模型部署方法 blush 2 AI模型部署框架ONNXNCNNOpenVINOTensorRTMediapipe如何选择 satisfied 3 AI模
  • 【C++】1.语言基础:八股文

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 语言基础内存分配指针参数传递和引用参数传递四种强制转换面向对象的三大特性并举例 define 和别名 typedef 的区别 blush 2 标准库STL介
  • 【VSLAM】ORB-SLAM3安装部署与运行

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 ORB SLAM3介绍 blush 2 代码安装部署1 安装ros与opencv2 安装Pangolin作为可视化和用户界面3 安装Eigen3一个开源线性
  • 【Linux运维】ACPI BIOS Error问题解决

    今天帮朋友装个ubuntu系统 xff0c 遇到一个问题记录一下 报错与现象 xff1a ACPI BIOS Error 电脑花屏 解决方法 xff1a 插入启动盘 xff0c 当进入引导界面后 xff0c 键盘输入 e xff0c 编辑L
  • catkin_make的时候发生了什么

    原链接http community bwbot org topic 182 运行测试平台 小强ROS机器人 这是一个比较复杂的问题 xff0c 但是有时候会有莫名其妙的编译错误 xff0c 在找错误的过程中会非常需要了解这个过程 首先说一下
  • 【ros】8.有限状态机

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 有限状态机认识 blush 2 一个简单的示例 satisfied 3 自动驾驶如何用有限状态机 x1f60f 1 有限状态机认识 有限状态机 xff08
  • 【C++】8.编译:CMake工具入门

    x1f60f xffe3 xffe3 x1f60f 这篇文章主要介绍CMake工具的入门使用 学其所用 xff0c 用其所学 梁启超 欢迎来到我的博客 xff0c 一起学习知识 xff0c 共同进步 x1f95e 喜欢的朋友可以
  • lwip --- (十六)TCP建立流程

    这一节我们就看看如何在我们的LWIP上实现一个http服务器的过程 xff0c 结合连接建立过程来理解TCP状态转换图和TCP控制块中各个字段的意义 这里先讲解一些与TCP相关的最基础的函数 xff0c 至于是怎样将这些函数合理高效的组织起
  • TCP和串口间的互相通信(透传)

    TCP和串口间的互相通信 xff08 透传 xff09 Tcp作为服务端 xff0c 接收消息 xff0c 通过串口发送 span class token keyword private span span class token retu
  • CONTINUING||重启

    现在是20年的8月13日 这是一个让自己非常难忘的一天 此时的我已经实现了当时自己曾经许下的诺言 xff0c 实现了自己当时年少无知的梦想 找到了一个好公司 xff0c 有了一份好工作 xff08 tx xff09 但是这不是自己的梦想的终
  • c语言| |strstr函数的源代码以及自我实现

    strstr函数 strstr函数 xff1a strstr str1 str2 函数用于判断字符串str2是否是str1的子串 如果是 xff0c 则该函数返回str2在str1中首次出现的地址 xff1b 否则 xff0c 返回NULL
  • 如何在Linux下用vim编写代码

    1 首先进入到一个目录下 xff0c 输入命令 vim test c 2 便会在该目录下 xff0c 创建一个test c xff08 test c不存在 xff09 的文件 xff0c 如果test c存在的话 xff0c 那么就打开该文
  • C语言| |c语言下如何输出彩色的字

    c语言下如何输出彩色的字 使用格式 xff1a 样式开始 43 被修饰字符串 43 样式结束 样式开始 xff1a 033 43 参数1 43 xff1a 43 参数2 43 xff1a 43 参数3 43 m 参数1 xff1a 代表背景
  • Linux| |对于UDP的学习

    UDP 前序 UDP xff08 用户数据报协议 xff09 没有连接的 xff0c 是面向数据报的 xff0c 是不可靠 套接字 就是IP地址 43 端口号 IP地址 xff1a 4字节 端口号 xff1a 2字节 xff0c 也就是说范
  • 数据结构| |各类排序的时间复杂度以及稳定性

    各类排序的时间复杂度以及稳定性 插入排序 xff1a 直接插入排序 xff1a O N 2 稳定 希尔排序 xff1a O N 1 3 不稳定 选择排序 xff1a 选择排序 xff1a O N 2 不稳定 堆排序 xff1a O Nlog
  • 安装Nvidia显卡驱动和CUDA

    原链接 http community bwbot org topic 152 网上看到的 xff0c 但是原链接 不过这里安装的是CUDA7 5 xff0c 现在最新的是8 0 可以到官网进行下载 xff0c 记住一定不要选择deb方式 x
  • Linux| |IP地址的三类私有地址

    IP地址的三类私有地址 对于IP地址来说有着三种私有地址 三种私有地址如下 xff1a 10 0 0 0 10 255 255 255 172 16 0 0 172 16 255 255 192 168 0 0 192 168 255 25
  • Linux| |如何高效切换目录

    Linxu如何高效切换目录 前言 Linux下对于目录的切换 xff0c 大家肯定会想到一个命令 xff1a cd命令 cd命令确实方便 xff0c 但是当需要频繁的切换目录的时候 xff0c cd命令可能比较麻烦了 比如 xff1a ho

随机推荐