用C语言的LED实验,有汇编哦!
2021/12/3 22:06:13
本文主要是介绍用C语言的LED实验,有汇编哦!,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
C语言LED实验
1、汇编激活CPU
首先要明白对于没有系统开发板(也就是裸机)来说,是没办法直接对C进行识别。所以需要一段汇编语言,来配置CPU的资源,选择CPU运行模式,初始化指针位置。
代码如下:
.global _start /* 全局标号 */ _start: /*进入SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f/* 将 r0 的低 5 位清零,也就是 cpsr 的 M0~M4 */ orr r0, r0, #0x13/* r0 或上 0x13,表示使用 SVC 模式 */ msr cpsr, r0/* 将 r0 的数据写入到 cpsr_c 中 */ ldr 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 的栈空间已经很大了, 如果做裸机开发的话绰绰有余。 */ b main/*跳转到main函数 */
这里又学到了两个新的汇编指令:
BIC——位清除指令
指令格式:
BIC{cond}{S} Rd,Rn,operand2
BIC指令将Rn 的值与操作数operand2 的反码按位逻辑”与”,结果存放到目的寄存器Rd 中。指令示例:BIC R0,R0,#0x0F ;将R0最低4位清零,其余位不变。
ORR——位置为1
ORR 指令的格式为:
ORR{条件}{S} 目的寄存器,操作数 1,操作数 2
ORR 指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数 1
应是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数 1 的某些位。
指令示例:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
2、头文件配置相关的寄存器
也就是把相关寄存器的地址,用某个变量名存起来
代码如下:
#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
这里可以复习一下关键字volatile的作用:
volatile:
指的是“易变的”意思,可以避免在编译的时候被编译器优化,而导致意想不到的结果。
一般还有下面的作用:
- 表明变量可以被后台程序修改
- 防止编译器优化操作变量的语句
- 防止编译器优化变量的存取对象
- 访问被volatile修饰的变量时,强制访问内存中的值,而不是缓存中的。
3、C文件指令
这个时候环境已经搭建好了,可以开始写自己的应用程序了
代码如下:
#include "main.h" /*时钟使能函数*/ 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) { /*IO复用为GPIO1_IO03*/ SW_MUX_GPIO1_IO03 = 0x5; /*配置GPIO1_IO03的相关属性*/ /* *bit 16:0 HYS 关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper 功能 *bit [12]: 1 pull/keeper 使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度 100Mhz *bit [5:3]: 110 R0/6 驱动能力 *bit [0]: 0 低转换率 */ SW_PAD_GPIO1_IO03 = 0X10B0;//上面寄存器按配置要求输入相应数字后,就变成了0X10B0 GPIO1_GDIR = 0X0000008;//初始化 GPIO, GPIO1_IO03 设置为输出 GPIO1_DR = 0X0;//设置 GPIO1_IO03 输出低电平,打开 LED0 } /*开灯函数*/ void led_on(void) { GPIO1_DR &= ~(1<<3);//将 GPIO1_DR 的 bit3 清零 } /*关灯函数*/ void led_off(void) { GPIO1_DR |= (1<<3);// 将 GPIO1_DR 的 bit3 置 1 } /*短时间延时函数*/ void short_delay(volatile unsigned int n) { while(n--){} } /*延时函数 1ms*/ void delay(volatile unsigned int n) { while(n--) { short_delay(0x7ff); } } /*主函数*/ int main() { clk_enable(); led_init(); while (1) { led_off(); delay(500); led_on(); delay(500); } return 0; }
这里只点一点:就是开灯关灯函数,用的运算符。
GPIO1_DR &= ~(1<<3);//将 GPIO1_DR 的 bit3 清零 开灯
- 先对1左移3位:0001 -> 1000
- 再按位取反:1000 -> 0111
- 最后按位与运算
就相当于把第3位清零了。
GPIO1_DR |= (1<<3);// 将 GPIO1_DR 的 bit3 置 1 关灯
这个就更简单了,先移位,在按位或运算就行了。
4、makefile文件
先看代码:
objs := start.o main.o ledc.bin:$(objs) arm-linux-gnueabihf-ld -Timx6ull.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
有几个变量符需要解释一下:
“$^”的意思是所有依赖文件的集合。
“$<”的意思是依赖目标集合的第一个文件。
“$@”的意思是目标集合。
还有一点就是进行连接的时候,一定要把start.o放在前面。
objs := start.o main.o
大家有没有注意这个连接命令:
arm-linux-gnueabihf-ld -Timx6ull.lds -o ledc.elf $^
这个imx6ull.lds是什么?
这其实就是一个连接的脚本。
5、连接脚本
承接上面,还记得这样一条命令吗?
arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^
上面语句中我们是通过“-Ttext”来指定链接地址是 0X87800000 的,这样的话所有的文件
都会链接到以 0X87800000 为起始地址的区域。
但是,现实是我们会有很多文件,要放在不同的地址里面(或者说段里),这些段的地址也由我们自由指定。
还有大家常说的text(程序段)、data(数据段)、bss段(没有初始化的变量)等,就是这个意思。(想更直观的话,可以看看很多项目的.map文件,相信你会有新的感受。)
可能有点抽象,这里先举一个简单点的例子:
SECTIONS{ . = 0X10000000; .text : {*(.text)} . = 0X30000000; .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) } }
有下面几个关键点:
1.关键字“SECTIONS”,是必不可少的。
2.特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为 0,要求代码链接到以 0X10000000 为起始地址的地方,后面的文件或者段都会以 0X10000000 为起始地址开始链接。
3.“.text”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text段都放到“.text”中。
4.需要重新设置定位计数器“.”,将其改为 0X30000000。
5.来对“.data”这个段的起始地址做字节对齐的,ALIGN(4)表示4字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。
掌握这些之后,再来看我们需要的连接脚本文件:
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 = .; }
这个脚本里也有几点需要注意:
就是这个__bss_start和__bss_end,这个就相当于记录下了bss的首尾地址。因为我们后面是需要对bssZ段进行清零的,所以要记录下它的的首尾地址。
END!!!
最后千万别忘了使用imxdownload烧到SD卡里边,还有makefile的文件格式,一定要注意了!!好了就这么多。
这篇关于用C语言的LED实验,有汇编哦!的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享