I.MX6U C语言运行环境构建及驱动开发格式

2024-02-17 18:28

本文主要是介绍I.MX6U C语言运行环境构建及驱动开发格式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.设置处理器模式

设置6ULL处于SVC模式下。设置下CPSR寄存器的bit4-0,也就是M[4:0]为10011=0x13.。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。

2.设置SP指针

SP可以指向内部RAM,也可以指向DDR,我们将其指向DDR。SP设置到哪里?512MB的范围0x80000000---0x9FFFFFFF。栈大小,0x20000000=2MB。处理器栈增长方式,对于A7而言是向下增长的。

3.跳转到C语言

使用b指令,跳转到C语言函数,比如MAIN函数

1汇编部分实验程序编写
I.MX6U 的汇编部分代码和 STM32 的启动文件 startup_stm32f10x_hd.s 基本类似的,只是本
实验我们不考虑中断向量表,只考虑初始化 C 环境即可。在前面创建的 start.s 中输入如下代码:

2 C 语言部分实验程序编写

 main.h

#ifndef __MAIN_H
#define __MAIN_H/* 
* CCM 相关寄存器地址
*/
#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)/* 
* IOMUX 相关寄存器地址
*/
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
#define SW_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)
#define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)
#define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)
#define GPIO1_IMR *((volatile unsigned int *)0X0209C014)
#define GPIO1_ISR *((volatile unsigned int *)0X0209C018)
#define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)#endif
main.c
1 #include "main.h"
2 
3 /*
4 * @description : 使能 I.MX6U 所有外设时钟
5 * @param : 无
6 * @return : 无
7 */
8 void clk_enable(void)
9 {
10 CCM_CCGR0 = 0xffffffff;
11 CCM_CCGR1 = 0xffffffff;
12 CCM_CCGR2 = 0xffffffff;
13 CCM_CCGR3 = 0xffffffff;
14 CCM_CCGR4 = 0xffffffff;
15 CCM_CCGR5 = 0xffffffff;
16 CCM_CCGR6 = 0xffffffff;
17 }
18 
19 /*
20 * @description : 初始化 LED 对应的 GPIO
21 * @param : 无
22 * @return : 无
23 */
24 void led_init(void)
25 {
26 /* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */
27 SW_MUX_GPIO1_IO03 = 0x5; 
28 
29 /* 2、配置 GPIO1_IO03 的 IO 属性 
30 *bit 16:0 HYS 关闭
31 *bit [15:14]: 00 默认下拉
32 *bit [13]: 0 kepper 功能
33 *bit [12]: 1 pull/keeper 使能
34 *bit [11]: 0 关闭开路输出
35 *bit [7:6]: 10 速度 100Mhz
36 *bit [5:3]: 110 R0/6 驱动能力
37 *bit [0]: 0 低转换率
38 */
39 SW_PAD_GPIO1_IO03 = 0X10B0; 
40 
41 /* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */
42 GPIO1_GDIR = 0X0000008;
43 
44 /* 4、设置 GPIO1_IO03 输出低电平,打开 LED0 */
45 GPIO1_DR = 0X0;
46 }
47 
48 /*
49 * @description : 打开 LED 灯
50 * @param : 无
51 * @return : 无
52 */
53 void led_on(void)
54 {
55 /* 
56 * 将 GPIO1_DR 的 bit3 清零 
57 */
58 GPIO1_DR &= ~(1<<3);
59 }
60 
61 /*
62 * @description : 关闭 LED 灯
63 * @param : 无
64 * @return : 无
65 */
66 void led_off(void)
67 {
68 /* 
69 * 将 GPIO1_DR 的 bit3 置 1
70 */
71 GPIO1_DR |= (1<<3);
72 }
73 
74 /*
75 * @description : 短时间延时函数
76 * @param - n : 要延时循环次数(空操作循环次数,模式延时)
77 * @return : 无
78 */
79 void delay_short(volatile unsigned int n)
80 {
81 while(n--){}
82 }
83 
84 /*
85 * @description : 延时函数,在 396Mhz 的主频下延时时间大约为 1ms
86 * @param - n : 要延时的 ms 数
87 * @return : 无
88 */
89 void delay(volatile unsigned int n)
90 {
91 while(n--)
92 {
93 delay_short(0x7ff);
94 }
95 }
96 
97 /*
98 * @description : main 函数
99 * @param : 无
100 * @return : 无
101 */
102 int main(void)
103 {
104 clk_enable(); /* 使能所有的时钟 */
105 led_init(); /* 初始化 led */
106
107 while(1) /* 死循环 */
108 { 
109 led_off(); /* 关闭 LED */
110 delay(500); /* 延时大约 500ms */
111
112 led_on(); /* 打开 LED */
113 delay(500); /* 延时大约 500ms */
114 }
115
116 return 0;
117 }
clk_enable 函数是使能
CCGR0~CCGR6 所控制的所有外设时钟。 led_init 函数是初始化 LED 灯所使用的 IO ,包括设置
IO 的复用功能、 IO 的属性配置和 GPIO 功能,最终控制 GPIO 输出低电平来打开 LED 灯。
led_on led_off 这两个函数看名字就知道,用来控制 LED 灯的亮灭的。 delay_short() delay()
这两个函数是延时函数, delay_short() 函数是靠空循环来实现延时的, delay() 是对 delay_short()
的 简 单 封 装 ,在 I.MX6U 工作 在 396MHz(Boot ROM 设 置的 396MHz) 的 主 频 的 时候

delay_short(0x7ff)基本能够实现大约 1ms 的延时,所以 delay()函数我们可以用来完成 ms 延时。

main 函数就是我们的主函数了,在 main 函数中先调用函数 clk_enable() led_init() 来完成时钟
使能和 LED 初始化,最终在 while(1) 循环中实现 LED 循环亮灭,亮灭时间大约是 500ms
编写 Makefile
1 行定义了一个变量 objs objs 包含着要生成 ledc.bin 所需的材料: start.o main.o ,也
就是当前工程下的 start.s main.c 这两个文件编译后的 .o 文件。这里要注意 start.o 一定要放到 最前面!因为在后面链接的时候 start.o 要在最前面,因为 start.o 是最先要执行的文件!
3 行就是默认目标,目的是生成最终的可执行文件 ledc.bin ledc.bin 依赖 start.o main.o
如果当前工程没有 start.o main.o 的时候就会找到相应的规则去生成 start.o main.o 。比如
start.o start.s 文件编译生成的,因此会执行第 8 行的规则。
4 行是使用 arm-linux-gnueabihf-ld 进行链接,链接起始地址是 0X87800000 ,但是这一行
用到了自动变量“ $^ ”,“ $^ ”的意思是所有依赖文件的集合,在这里就是 objs 这个变量的值:
start.o main.o 。链接的时候 start.o 要链接到最前面,因为第一行代码就是 start.o 里面的,因
此这一行就相当于:
5 行使用 arm-linux-gnueabihf-objcopy 来将 ledc.elf 文件转为 ledc.bin ,本行也用到了自动变量
$@ ”,“ $@ ”的意思是目标集合,在这里就是“ledc.bin”,那么本行就相当于:
6 行使用 arm-linux-gnueabihf-objdump 来反汇编,生成 ledc.dis 文件。
8~15 行就是针对不同的文件类型将其编译成对应的 .o 文件,其实就是汇编 .s(.S) .c
件,比如 start.s 就会使用第 8 行的规则来生成对应的 start.o 文件。第 9 行就是具体的命令,这 行也用到了自动变量“$@ ”和“ $< ”,其中“ $< ”的意思是依赖目标集合的第一个文件。比如 start.s 要编译成 start.o 的话第 8 行和第 9 行就相当于:
17 行就是工程清理规则,通过命令“ make clean ”就可以清理工程。
Makefile 文件就讲到这里,我们可以将整个工程拿到 Ubuntu 下去编译,编译完成以后可以使用 软件 imxdownload 将其下载到 SD 卡中,命令如下:
链接脚本
在上面的 Makefile 中我们链接代码的时候使用如下语句:
上面语句中我们是通过“ -Ttext ”来指定链接地址是 0X87800000 的,这样的话所有的文件
都会链接到以 0X87800000 为起始地址的区域。但是有时候我们很多文件需要链接到指定的区 域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够 自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函 数应该存放到哪个段里面去。要完成这个功能我们就需要使用到链接脚本,看名字就知道链接 脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目 的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如我 们编译生成的文件一般都包含 text 段、 data 段等等。

TMD,太难了,哪来的missing seperator艹艹 

链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是
一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。
最简单的链接脚本可以只包含一个命令“ SECTIONS
,
我们可以在这一个“ SECTIONS ”里面来描述输出文件的内存布局。
我们一般编译出来的代码 都包含在 text data bss rodata 这四个段内,假设现在的代码要被链接到 0X10000000 这个 地址,数据要被链接到 0X30000000 这个地方,下面就是完成此功能的最简单的链接脚本:
1 行我们先写了一个关键字“ SECTIONS ”,后面跟了一个大括号,这个大括号和第 7
的大括号是一对,这是必须的。看起来就跟 C 语言里面的函数一样。
2 行对一个特殊符号“ . ”进行赋值,“ . ”在链接脚本里面叫做定位计数器,默认的定位
计数器为 0 。我们要求代码链接到以 0X10000000 为起始地址的地方,因此这一行给“ .”赋值
0X10000000 ,表示以 0X10000000 开始,后面的文件或者段都会以 0X10000000 为起始地址开 始链接。
3 行的“ .text ”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链
接到“ .text ”这个段里面的所有文件,“ *(.text) ”中的“ * ”是通配符,表示所有输入文件的 .text
段都放到“ .text ”中。
4 行,我们的要求是数据放到 0X30000000 开始的地方,所以我们需要重新设置定位计
数器“ . ”,将其改为 0X30000000 。如果不重新设置的话会怎么样?假设“ .text ”段大小为 0X10000 , 那么接下来的.data 段开始地址就是 0X10000000+0X10000=0X10010000 ,这明显不符合我们的 要求。所以我们必须调整定位计数器为 0X30000000
5 行跟第 3 行一样,定义了一个名为“ .data ”的段,然后所有文件的“ .data ”段都放到
这里面。但是这一行多了一个“ ALIGN(4) ”,这是什么意思呢?这是用来对“ .data ”这个段的起
始地址做字节对齐的, ALIGN(4) 表示 4 字节对齐。也就是说段“ .data ”的起始地址要能被 4
除,一般常见的都是 ALIGN(4) 或者 ALIGN(8) ,也就是 4 字节或者 8 字节对齐。
6 行定义了一个“ .bss ”段,所有文件中的“ .bss ”数据都会被放到这个里面,“ .bss ”数
据就是那些定义了但是没有被初始化的变量。
上面就是链接脚本最基本的语法格式,我们接下来就按照这个基本的语法格式来编写我们本试 验的链接脚本,我们本试验的链接脚本要求如下:
  • ①、链接起始地址为 0X87800000
    ②、 start.o 要被链接到最开始的地方,因为 start.o 里面包含这第一个要执行的命令。
    根据要求,在 Makefile 同目录下新建一个名为 imx6ul.lds ”的文件,然后在此文件里面输
    入如下所示代码:
    2 行设置定位计数器为
    0X87800000 ,因为我们的链接地址就是 0X87800000 。第 5 行设置链接到开始位置的文件为 start.o
    因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。第 6 行是 main.o
    这个文件,其实可以不用写出来,因为 main.o 的位置就无所谓了,可以由编译器自行决定链接 位置。
    在第 11 13 行有“ __bss_start ”和“ __bss_end ”这两个东西?这个是什么呢?“ __bss_start ” 和“__bss_end ”是符号,
    11 13 这两行其实就是对这两个符号进行赋值,其值为定位符“ . ”,
    这两个符号用来保存 .bss 段的起始地址和结束地址。前面说了 .bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋 0 即可完成清零。通过第 11、13 行代码,.bss 段的起始地址和结束地址就保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者 C 文件里面使用这两个符号。
    修改 Makefile
    我们已经编写好了链接脚本文件: imx6ul.lds ,我们肯定是要使用这个链接脚
    本文件的,将 Makefile 中的如下一行代码: 改为:
    其实就是将 -T 后面的 0X87800000 改为 imx6ul.lds ,表示使用 imx6ul.lds 这个链接脚本文 件。修改完成以后使用新的 Makefile 和链接脚本文件重新编译工程,编译成功以后就可以烧写 到 SD 卡中验证了。
    下载验证
    使用软件 imxdownload 将编译出来的 ledc.bin 烧写到 SD 卡中,命令如下:
    烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板,如果代码运行正常的 话 LED0 就会以 500ms 的时间间隔亮灭。

这篇关于I.MX6U C语言运行环境构建及驱动开发格式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/718639

相关文章

Docker 容器中运行Certbot获取和管理 SSL 证书

如果你在 Docker 容器中运行 Nginx 并希望使用 Certbot 获取和管理 SSL 证书,可以使用 Certbot 的官方 Docker 镜像来完成这项工作。以下是使用 Docker 和 Certbot 获取 SSL 证书并配置 Nginx 的详细步骤: 1. 拉取 Certbot Docker 镜像 首先,确保你已经安装了 Docker。然后,拉取 Certbot 的官方 Doc

构建基础网站的入门指南

在数字时代,网站已经成为展示个人、企业或组织信息的重要平台。了解如何通过编写代码来创建一个网站是非常有用的技能。在本文中,我们将了解构建一个基础网站所需的步骤和代码知识。 第一步:了解网站的基本组成 一个基本的网站通常包含HTML(超文本标记语言)、CSS(层叠样式表)和有时还包括JavaScript。HTML用于结构化内容,CSS用于设计和布局,JavaScript用于添加交互性。 第二步

C语言 指针——函数指针的典型应用:计算定积分

目录 梯形法计算函数的定积分 函数指针的典型应用 梯形法计算函数的定积分 函数指针的典型应用 用函数指针编写计算任意函数定积分的 通用 函数

C语言数据结构排序、插入排序、希尔排序等的介绍

文章目录 前言打印数组函数一、插入排序二、希尔排序总结 前言 C语言数据结构排序、插入排序、希尔排序等的介绍 打印数组函数 打印数组函数定义 // 打印数组void PrintArray(int* a, int n){int i = 0;for (i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");} 一、

离线环境下安装NVIDIA驱动、CUDA(HUAWEI Kunpeng 920 + NVIDIA A100 + Ubuntu 20.04 LTS)

文章目录 前言 一、基础环境 1.1、处理器型号 1.2、英伟达显卡型号 1.3、操作系统 1.4、软件环境 二、取消内核自动升级 2.1、查看正在使用的内核版本 2.2、查看正在使用的内核包 2.3、禁止内核更新 三、配置本地apt源 3.1、挂载iso镜像文件 3.2、配置apt源 3.3、更新apt源 四、安装NVIDIA驱动 4.1、查看显卡型号 4.2、

构建高效稳定的短视频直播系统架构

随着短视频直播的迅猛发展,构建一个高效稳定的短视频直播系统架构成为了互联网企业的重要挑战。本文将探讨如何构建高效稳定的短视频直播系统架构,以提供优质的用户体验和满足日益增长的用户需求。 ### 1. 短视频直播系统的背景 短视频直播近年来蓬勃发展,吸引了大量用户和广告主的关注。短视频直播系统通过实时的视频流传输和交互功能,让用户能够即时参与、分享和互动,具有极高的用户粘性和商业潜力。 #

3DGS(gaussian-splatting)在windows11、RTX4060环境配置步骤

前言 最近公众号一直在推3DGS方面的内容,之前很早就准备配置一下该环境,奈何一直手懒。趁着空闲,配置了一下Windows环境下的3DGS,按照本文的流程进行配置成功率很大,亲测有效! 准备工作 由于本文参照的是国外某位大佬的视频,再此表示感谢地址https://www.youtube.com/watch?v=UXtuigy_wYc(需要科学上网),这里我将一些下载较慢的安装包之类的都上传到

使用NSA工具环境搭建--win7下python2.6.6安装pip踏过的坑---天坑

前期准备 这两天准备搭建一个NSA工具利用环境,看网上的资料发现一般都是在XP和win7上面运行的,于是我就使用了win7x64系统,刚开始一切都很顺利,安装javajdk,安装python,结果却不能执行成功,这让我很郁闷,会遇到一个“ Fatal error in launcher: Unable to create process using ‘"’”~~~经过多次失败都不能成功运行软件

react-native-background-job——让你的react-native项目实现后台运行

使应用处于后台时运行JavaScript的后台任务。 即使应用程序已关闭,任务也会运行,默认情况下,也会在重新启动后继续存在。 这个库依赖于React Native的HeadlessJS ,目前只支持Android。 在本机端,它使用Firebase JobDispatcher或AlarmManager。 Firebase JobDispatcher(默认值):无法准确计划任务,并且根据

太阳能辐射整车综合性能环境试验舱

产品别名 步入式恒温恒湿试验箱、步入式温湿度试验箱、温度试验室、模拟环境试验室、大型恒温恒湿箱、步入式高低温湿热交变试验箱、大型高低温箱、步入式老化箱、恒温恒湿试验房、步入式高低温试验箱. 整车综合性能环境试验舱:整车综合性能环境试验舱:主要用于整车高低温存放试验、整车除霜、除雾性能试验、整车冷起动性能试验、整车采暖及制冷性能试验、整车热平衡试验、零部件耐高低温试验等。   产品构成: