ARM-LED灯实验
-
一、查看原理图
-
二、查看芯片手册(DataSheet)
-
三、操作GPIO
-
四、编写代码(VS Code中新建工程)
-
如果要进入C语言执行环境,那么就必须设置栈空间,函数调用参数和返回值会压栈,需要写汇编代码
-
//head.h #define GPX2_CON 0x11000C40 //控制输入/输出 LED2 #define GPX2_DAT 0x11000C44 //控制高/低电平 LED2 #define GPX1_CON 0x11000C20 //控制输入/输出 LED3 #define GPX1_DAT 0x11000C24 //控制高/低电平 LED3 #define GPF3_CON 0x114001E0 //控制输入/输出 LED4、LED5 #define GPF3_DAT 0x114001E4 //控制高/低电平 LED4、LED5
-
//led.c #include "head.h" //GPX2_7:GPX2的第7个引脚(每个引脚4位)LED2 //GPX1_0:GPX1的第0个引脚(每个引脚4位)LED3 //GPF3_4:GPF3的第4个引脚(每个引脚4位)LED4 //GPF3_5:GPF3的第5个引脚(每个引脚4位)LED5 //LED2初始化 void led2_init(void) { //将GPX2CON寄存器配置为输出模式 int *p = (int *)GPX2_CON; *p = *p & ~(0xf << 28); //[31,28]置0 *p = *p | (0x1 << 28); //[31,28]设置为0x1 return ; } //LED3初始化 void led3_init(void) { //将GPX1CON寄存器配置为输出模式 int *p = (int *)GPX1_CON; *p = *p & ~0xf; //[3,0]置0 *p = *p | 0x1; //[3,0]设置为0x1 return ; } //LED4初始化 void led4_init(void) { //将GPF3CON寄存器配置为输出模式 int *p = (int *)GPF3_CON; *p = *p & ~(0xf << 16); //[19,16]置0 *p = *p | (0x1 << 16); //[19,16]设置为0x1 return ; } //LED5初始化 void led5_init(void) { //将GPF3CON寄存器配置为输出模式 int *p = (int *)GPF3_CON; *p = *p & ~(0xf << 20); //[23,20]置0 *p = *p | (0x1 << 20); //[23,20]设置为0x1 return ; } //点亮LED2 void led2_on(void) { //将GPX2DAT寄存器配置为高电平 int *p = (int *)GPX2_DAT; *p = *p | (0x1 << 7); //将[7,0]第7位设置为1(高电平) return ; } //熄灭LED2 void led2_off(void) { //将GPX2DAT寄存器配置为低电平 int *p = (int *)GPX2_DAT; *p = *p & ~(0x1 << 7); //将[7,0]第7位设置为0(低电平) return ; } //点亮LED3 void led3_on(void) { //将GPX1DAT寄存器配置为高电平 int *p = (int *)GPX1_DAT; *p = *p | 0x1; //将[7,0]第0位设置为1(高电平) return ; } //熄灭LED3 void led3_off(void) { //将GPX1DAT寄存器配置为低电平 int *p = (int *)GPX1_DAT; *p = *p & ~0x1; //将[7,0]第0位设置为0(低电平) return ; } //点亮LED4 void led4_on(void) { //将GPF3DAT寄存器配置为高电平 int *p = (int *)GPF3_DAT; *p = *p | (0x1 << 4); //将[5,0]第4位设置为1(高电平) return ; } //熄灭LED4 void led4_off(void) { //将GPF3DAT寄存器配置为低电平 int *p = (int *)GPF3_DAT; *p = *p & ~(0x1 << 4); //将[5,0]第4位设置为0(低电平) return ; } //点亮LED5 void led5_on(void) { //将GPF3DAT寄存器配置为高电平 int *p = (int *)GPF3_DAT; *p = *p | (0x1 << 5); //将[5,0]第5位设置为1(高电平) return ; } //熄灭LED5 void led5_off(void) { //将GPF3DAT寄存器配置为低电平 int *p = (int *)GPF3_DAT; *p = *p & ~(0x1 << 5); //将[5,0]第5位设置为0(低电平) return ; } //同时初始化LED2~LED5 void led_all_init(void) { led2_init(); led3_init(); led4_init(); led5_init(); return ; } //同时点亮LED2~LED5 void led_all_on(void) { led2_on(); led3_on(); led4_on(); led5_on(); return ; } //同时熄灭LED2~LED5 void led_all_off(void) { led2_off(); led3_off(); led4_off(); led5_off(); return ; } //简易延迟函数(单位为ms) void delay(int ms) { int i = 0, j = 0; while (ms--) { for(i = 0; i < 5; i++) { for(j = 0; j < 514;j++) { ; } } } return ; } //流水灯函数 void waterfall_light(void) { led2_on(); delay(500); led2_off(); delay(500); led3_on(); delay(500); led3_off(); delay(500); led4_on(); delay(500); led4_off(); delay(500); led5_on(); delay(500); led5_off(); delay(500); return ; } //LED测试 void led_test(void) { led_all_init(); while (1) { /*灯的点亮和熄灭 *led_all_on(); *delay(1000); *led_all_off(); *delay(1000); */ waterfall_light(); //开启流水灯 } return ; }
-
//main.c extern void led_test(void); //函数声明 int main(int argc, char const *argv[]) { led_test(); return 0; }
-
//start.s启动文件 .global _start _start: ldr sp,=0x40100000 @0x40000000为起始地址,偏移1M(2^20)空间 bl main stop: b stop
-
-
五、编译代码
-
//编译(VS Code终端输入以下指令) arm-none-eabi-gcc -c start.s -o start.o arm-none-eabi-gcc -c led.c -o led.o arm-none-eabi-gcc -c main.c -o main.o //链接生成elf文件,start.o一定要放在最开始的位置 arm-none-eabi-ld -Ttext=0x40000000 start.o main.o led.o -o led.elf //去掉头部信息(O必须是大写) arm-none-eabi-objcopy -O binary led.elf led.bin
-
-
-
elf(executable and link format)文件和bin(binary)文件的区别?
-
gcc 编译出来的是elf文件。通常 gcc test.c 生成的 a.out 文件就是elf格式的,在 linux shell 下输入 ./a.out 可以执行。elf文件里面包含了符号表、汇编等。
-
bin 文件是经过压缩的可执行文件,去掉elf格式的东西。是直接的内存映像的表示。在系统没有加载操作系统的时候可以执行。bin文件是将 elf 文件中的代码段、数据段,还有一些自定义的段抽取出来做成的一个内存镜像。
-
在嵌入式(Embedded)中,上电开始运行,没有 OS 系统,如果将 elf 格式的文件烧写进去,包含一些 elf 格式的东西,ARM 运行碰到这些指令,就会导致失败,如果用 objcopy 生成纯粹的汇编 bin 文件,程序就可以一步一步运行。机器最终只认 bin,elf 格式是在有操作系统时,操作系统会根据 elf 解析出代码、数据等,最终仍是以 bin 运行。
-
-
-
六、下载代码到开发板上