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

这篇具有很好参考价值的文章主要介绍了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函数I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言

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

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 延时。文章来源地址https://www.toymoban.com/news/detail-827342.html

main 函数就是我们的主函数了,在 main 函数中先调用函数 clk_enable() led_init() 来完成时钟
使能和 LED 初始化,最终在 while(1) 循环中实现 LED 循环亮灭,亮灭时间大约是 500ms
编写 Makefile
I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
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 里面的,因
此这一行就相当于: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
5 行使用 arm-linux-gnueabihf-objcopy 来将 ledc.elf 文件转为 ledc.bin ,本行也用到了自动变量
$@ ”,“ $@ ”的意思是目标集合,在这里就是“ledc.bin”,那么本行就相当于: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
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 行就相当于: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
17 行就是工程清理规则,通过命令“ make clean ”就可以清理工程。
Makefile 文件就讲到这里,我们可以将整个工程拿到 Ubuntu 下去编译,编译完成以后可以使用 软件 imxdownload 将其下载到 SD 卡中,命令如下: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
链接脚本
在上面的 Makefile 中我们链接代码的时候使用如下语句: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
上面语句中我们是通过“ -Ttext ”来指定链接地址是 0X87800000 的,这样的话所有的文件
都会链接到以 0X87800000 为起始地址的区域。但是有时候我们很多文件需要链接到指定的区 域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够 自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函 数应该存放到哪个段里面去。要完成这个功能我们就需要使用到链接脚本,看名字就知道链接 脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目 的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如我 们编译生成的文件一般都包含 text 段、 data 段等等。

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

链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是
一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。
最简单的链接脚本可以只包含一个命令“ SECTIONS
,
我们可以在这一个“ SECTIONS ”里面来描述输出文件的内存布局。
我们一般编译出来的代码 都包含在 text data bss rodata 这四个段内,假设现在的代码要被链接到 0X10000000 这个 地址,数据要被链接到 0X30000000 这个地方,下面就是完成此功能的最简单的链接脚本: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
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 ”的文件,然后在此文件里面输
    入如下所示代码: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
    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 中的如下一行代码: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言改为: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
    其实就是将 -T 后面的 0X87800000 改为 imx6ul.lds ,表示使用 imx6ul.lds 这个链接脚本文 件。修改完成以后使用新的 Makefile 和链接脚本文件重新编译工程,编译成功以后就可以烧写 到 SD 卡中验证了。
    下载验证
    使用软件 imxdownload 将编译出来的 ledc.bin 烧写到 SD 卡中,命令如下: I.MX6U C语言运行环境构建及驱动开发格式,I.MX6U嵌入式linux,c语言,驱动开发,开发语言
    烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板,如果代码运行正常的 话 LED0 就会以 500ms 的时间间隔亮灭。

到了这里,关于I.MX6U C语言运行环境构建及驱动开发格式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux 裸机篇(八)】I.MX6U EPIT 定时器中断、定时器按键消抖

    EPIT 的全称是: Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器,它主要是完成周期性中断定时的。学过 STM32 的话应该知道, STM32 里面的定时器还有很多其它的功能,比如输入捕获、 PWM 输出等等。但是 I.MX6U 的 EPIT 定时器只是完成周期性中断定时的,仅此一

    2024年02月02日
    浏览(64)
  • I.MX6ULL ARM驱动开发---网络设备驱动框架

      网络驱动是 linux 里面驱动三巨头之一,linux 下的网络功能非常强大,嵌入式 linux 中也常常用到网络功能。前面我们已经讲过了字符设备驱动和块设备驱动,本章我们就来学习一下 linux 里面的网络设备驱动。   网络设备驱动程序的体系结构分为4层,依次为网络协议驱

    2023年04月17日
    浏览(38)
  • 012——LED模块驱动开发(基于I.MX6uLL)

    目录 一、 硬件原理图 二、 驱动程序 三、 应用程序 四、 Makefile 五、操作         又是非常经典的点灯环节 ,每次学新语言第一步都是hello world,拿到新板子或者学习新的操作系统,第一步就是点灯。         LED 的驱动方式,常见的有四种。 ① 使用引脚输出 3.3V 点

    2024年04月13日
    浏览(48)
  • 016——DHT11驱动开发(基于I.MX6uLL)

    目录 一、 模块介绍 1.1 简介 1.2 电路描述 1.3 通信协议 二、 驱动程序 三、 应用程序 四、 上机实验         DHT11 是一款可测量温度和湿度的传感器。比如市面上一些空气加湿器,会测量空气中湿度,再根据测量结果决定是否继续加湿。DHT11 数字温湿度传感器是一款含有已

    2024年04月16日
    浏览(40)
  • linux驱动开发 ST7789 LCD驱动移植(I.MX6ULL平台)

    前言 I.MX6ULL的板子未选配RGB的屏幕,无法在板子上进行GUI的开发调试,不过手头上有块控制器为ST7789V3的LCD屏幕(1.3inch),通过简易接线后可以很方便进行驱动的移植 如有异议,欢迎留言指正 ST7789 LCD控制器 ST7789是一款单芯片TFT-LCD控制器,支持并口与SPI通信方式 特性 控制器支

    2023年04月09日
    浏览(84)
  • 018——红外遥控模块驱动开发(基于HS0038和I.MX6uLL)

    目录 一、 模块介绍 1.1 简介 1.2 协议 二、 驱动代码 三、 应用代码 四、 实验 五、 程序优化         红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中,像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统分为发送端和接收端,如图下图所示。

    2024年04月16日
    浏览(46)
  • i.MX6ULL驱动开发 | 27 - 使用WM8960 CODEC播放音频

    WM8960是欧胜公司(wolfson)的一款低功耗、高质量的立体声音频编解码芯片。 其内部集成D类喇叭功放,每个通道可以驱动一个1W喇叭(8Ω),内部集成3个立体声输入源,可以灵活配置,拥有一路完整的麦克风接口。 WM8960内部ADC和DAC都为24位,主要特性如下: DAC的SNR(信噪比)

    2024年02月02日
    浏览(48)
  • 017——DS18B20驱动开发(基于I.MX6uLL)

    目录 一、 模块介绍 1.1 简介 1.2 主要特点 1.3 存储器介绍 1.4 时序 1.5 命令 1.5.1 命令大全    1.5.2 命令使用 1.5.3 使用示例 1.6 原理图 二、 驱动程序 三、 应用程序 四、 测试         DS18B20 温度传感器具有线路简单、体积小的特点,用来测量温度非常简单,在一根通信线上

    2024年04月12日
    浏览(49)
  • I.MX6ULL_Linux_驱动篇(57)linux Regmap API驱动

    我们在前面学习 I2C 和 SPI 驱动的时候,针对 I2C 和 SPI 设备寄存器的操作都是通过相关的 API 函数进行操作的。这样 Linux 内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问 I2C/SPI 设备的时候,为此引入了 Reg

    2024年03月26日
    浏览(60)
  • I.MX6ULL_Linux_驱动篇(42)设备树与platform设备驱动

    上一章我们详细的讲解了 Linux 下的驱动分离与分层,以及总线、设备和驱动这样的驱动框架。基于总线、设备和驱动这样的驱动框架, Linux 内核提出来 platform 这个虚拟总线,相应的也有 platform 设备和 platform 驱动。上一章我们讲解了传统的、未采用设备树的 platform 设备和驱

    2024年02月14日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包