1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
第八章我们讲解了如何用汇编语言编写LED灯实验,但是实际开发过程中汇编用的很少,大部分都是C语言开发,汇编只是用来完成C语言环境的初始化。本章我们就来学习如何用汇编来完成C语言环境的初始化工作,然后从汇编跳转到C语言代码里面去。
10.1 C语言版LED灯简介 第八章的汇编LED灯实验中,我们讲解了如何使用汇编来编写LED灯驱动,实际工作中是很少用到汇编去写嵌入式驱动的,毕竟汇编太难,而且写出来也不好理解,大部分情况下都是使用C语言去编写的。只是在开始部分用汇编来初始化一下C语言环境,比如初始化DDR、设置堆栈指针SP等等,当这些工作都做完以后就可以进入C语言环境,也就是运行C语言代码,一般都是进入main函数。所以我们有两部分文件要做: ①、汇编文件 汇编文件只是用来完成C语言环境搭建。 ②、C语言文件 C语言文件就是完成我们的业务层代码的,其实就是我们实际例程要完成的功能。 其实STM32也是这样的,只是我们在开发STM32的时候没有想到这一点,以STM32F103为例,其启动文件startup_stm32f10x_hd.s这个汇编文件就是完成C语言环境搭建的,当然还有一些其他的处理,比如中断向量表等等。当startup_stm32f10x_hd.s把C语言环境初始化完成以后就会进入C语言环境。 10.2硬件原理分析 本章使用到的硬件资源和第八章一样,就是一个LED0。 10.3实验程序编写 本实验对应的例程路径为:开发板光盘-> 1、裸机例程->2_ledc。 新建VScode工程,工程名字为“ledc”,新建三个文件:start.S、main.c和mian.h。其中start.S是汇编文件,main.c和main.h是C语言相关文件。 10.3.1汇编部分实验程序编写 在STM32中,启动文件 startup_stm32f10x_hd.s就是完成C语言环境搭建的,当然还有一些其他的处理,比如中断向量表等等。startup_stm32f10x_hd.s中堆栈初始化代码如下所示:
示例代码10.3.1.1 STM32启动文件堆栈初始化代码
1 Stack_Size EQU 0x00000400
2
3 AREA STACK, NOINIT, READWRITE, ALIGN=3
4 Stack_Mem SPACE Stack_Size
5 __initial_sp
6
7; Heap Configuration
8; Heap Size (in Bytes)
9;
10
11 Heap_Size EQU 0x00000200
12
13 AREA HEAP, NOINIT, READWRITE, ALIGN=3
14 __heap_base
15 Heap_Mem SPACE Heap_Size
16 __heap_limit
17*******************省略掉部分代码***********************
18 Reset_Handler PROC
19 EXPORT Reset_Handler [WEAK]
20 IMPORT __main
21 IMPORT SystemInit
22 LDR R0,=SystemInit
23 BLX R0
24 LDR R0,=__main
25 BX R0
26 ENDP
第1行代码就是设置栈大小,这里是设置为0X400=1024字节。
第5行的__initial_sp就是初始化SP指针。
第11行是设置堆大小。
第18行是复位中断服务函数,STM32复位完成以后会执行此中断服务函数。
第22行调用SystemInit()函数来完成其他初始化工作。
第24行调用__main,__main是库函数,其会调用main()函数。
I.MX6U的汇编部分代码和STM32的启动文件startup_stm32f10x_hd.s基本类似的,只是本实验我们不考虑中断向量表,只考虑初始化C环境即可。在前面创建的start.s中输入如下代码:
示例代码10.3.1.2 start.s文件代码
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : start.s
作者 : 左忠凯
版本 : V1.0
描述 : I.MX6U-ALPHA/I.MX6ULL开发板启动文件,完成C环境初始化,
C环境初始化完成以后跳转到C代码。
其他 : 无
日志 : 初版 2019/1/3 左忠凯修改
**************************************************************/
1.global _start /* 全局标号 */
2
3/*
4 * 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
5 * 运行环境。
6 */
7 _start:
8
9 /* 进入SVC模式 */
10 mrs r0, cpsr
11 bic r0, r0, #0x1f/* 将r0的低5位清零,也就是cpsr的M0~M4 */
12 orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
13 msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
14
15 ldr sp,=0X80200000 /* 设置栈指针 */
16 b main /* 跳转到main函数 */
第1行定义了一个全局标号_start。
第7行就是标号_start开始的地方,相当于是一个_start函数,这个_start就是第一行代码。
第10~13行就是设置处理器进入SVC模式,在6.2小节的“Cortex-A处理器运行模型”中我们说过Cortex-A有九个运行模型,这里我们设置处理器运行在SVC模式下。处理器模式的设置是通过修改CPSR(程序状态)寄存器来完成的,在6.3.2小节中我们详细的讲解了CPSR寄存器,其中M[4:0](CPSR的bit[4:0])就是设置处理器运行模式的,参考表6.3.2.2,如果要将处理器设置为SVC模式,那么M[4:0]就要等于0X13。11~13行代码就是先使用指令MRS将CPSR寄存器的值读取到R0中,然后修改R0中的值,设置R0的bit[4:0]为0X13,然后再使用指令MSR将修改后的R0重新写入到CPSR中。
第15行通过ldr指令设置SVC模式下的SP指针=0X80200000,因为I.MX6U-ALPHA开发板上的DDR3地址范围是0X80000000~0XA0000000(512MB)或者0X80000000~0X90000000(256MB),不管是512MB版本还是256MB版本的,其DDR3起始地址都是0X80000000。由于Cortex-A7的堆栈是向下增长的,所以将SP指针设置为0X80200000,因此SVC模式的栈大小0X80200000-0X80000000=0X200000=2MB,2MB的栈空间已经很大了,如果做裸机开发的话绰绰有余。
第16行就是跳转到main函数,main函数就是C语言代码了。
至此汇编部分程序执行完成,就几行代码,用来设置处理器运行到SVC模式下、然后初始化SP指针、最终跳转到C文件的mian函数中。如果有玩过三星的S3C2440或者S5PV210的话会知道我们在使用SDRAM或者DDR之前必须先初始化SDRAM或者DDR。所以S3C2440或者S5PV210的汇编文件里面是一定会有SDRAM或者DDR初始化代码的。我们上面编写的start.s文件中却没有初始化DDR3的代码,但是却将SVC模式下的SP指针设置到了DDR3的地址范围中,这不会出问题吗?肯定不会的,DDR3肯定是要初始化的,但是不需要在start.s文件中完成。在9.4.2小节里面分析DCD数据的时候就已经讲过了,DCD数据包含了DDR配置参数,I.MX6U内部的Boot ROM会读取DCD数据中的DDR配置参数然后完成DDR初始化的。
10.3.2 C语言部分实验程序编写 C语言部分有两个文件main.c和main.h,main.h里面主要是定义的寄存器地址,在mian.h里面输入代码:
示例代码10.3.2.1 main.h文件代码
#ifndef __MAIN_H
#define __MAIN_H
/******************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : main.h
作者 : 左忠凯
版本 : V1.0
描述 : 时钟GPIO1_IO03相关寄存器地址定义。
其他 : 无
日志 : 初版V1.0 2019/1/3 左忠凯创建
*****************************************************************/
1 /*
2 * CCM相关寄存器地址
3 */
4 #define CCM_CCGR0 *((volatileunsignedint*)0X020C4068)
5 #define CCM_CCGR1 *((volatileunsignedint*)0X020C406C)
6 #define CCM_CCGR2 *((volatileunsignedint*)0X020C4070)
7 #define CCM_CCGR3 *((volatileunsignedint*)0X020C4074)
8 #define CCM_CCGR4 *((volatileunsignedint*)0X020C4078)
9 #define CCM_CCGR5 *((volatileunsignedint*)0X020C407C)
10#define CCM_CCGR6 *((volatileunsignedint*)0X020C4080)
11
12/*
13 * IOMUX相关寄存器地址
14 */
15 #define SW_MUX_GPIO1_IO03 *((volatileunsignedint*)0X020E0068)
16 #define SW_PAD_GPIO1_IO03 *((volatileunsignedint*)0X020E02F4)
17
18/*
19 * GPIO1相关寄存器地址
20 */
21 #define GPIO1_DR *((volatileunsignedint*)0X0209C000)
22 #define GPIO1_GDIR *((volatileunsignedint*)0X0209C004)
23 #define GPIO1_PSR *((volatileunsignedint*)0X0209C008)
24 #define GPIO1_ICR1 *((volatileunsignedint*)0X0209C00C)
25 #define GPIO1_ICR2 *((volatileunsignedint*)0X0209C010)
26 #define GPIO1_IMR *((volatileunsignedint*)0X0209C014)
27 #define GPIO1_ISR *((volatileunsignedint*)0X0209C018)
28 #define GPIO1_EDGE_SEL *((volatileunsignedint*)0X0209C01C)
29
30 #endif
在mian.h中我们以宏定义的形式定义了要使用到的所有寄存器,后面的数字就是其地址,比如CCM_CCGR0寄存器的地址就是0X020C4068,这个很简单,很好理解。
接下看一下main.c文件,在mian.c里面输入如下所示代码:
示例代码10.3.2.2 main.c文件代码
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : mian.c
作者 : 左忠凯
版本 : V1.0
描述 : I.MX6U开发板裸机实验2 C语言点灯
使用C语言来点亮开发板上的LED灯,学习和掌握如何用C语言来
完成对I.MX6U处理器的GPIO初始化和控制。
其他 : 无
日志 : 初版V1.0 2019/1/3 左忠凯创建
**************************************************************/
1#include "main.h"
2
3 /*
4 * @description : 使能I.MX6U所有外设时钟
5* @param : 无
6 * @return : 无
7 */
8void 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 */
24void 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 */
53void led_on(void)
54{
55/*
56 * 将GPIO1_DR的bit3清零
57 */
58 GPIO1_DR &=~(1
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?