资讯详情

嵌入式arm(三)arm裸机程序点灯+流水灯+环境文件解释

本节我们用点灯体验arm裸机程序开发;

cortex-A虽然系统比M系统更复杂,但对于裸机的开发,即寄存器寄存器寄存器,仍然很容易启动(指示灯),然后体验它

文章目录

  • 一 环境介绍
  • 二 简略了解SFR特殊功能寄存器
  • 三 裸机开发点灯流程
    • 1 看硬件原理图
    • 2 查手册配置Soc的引脚
    • 3 编程
  • 四 点灯
    • 1 查原理图
      • 1.1 查引脚连接
      • 1.2 看驱动方式
    • 2 查手册
      • 2.1 查找到的信息
      • 2.2 搜索过程(手册截图)
        • 2.2.1 GPX1_0
        • 2.2.2 GPF3_4、GPF3_5
    • 3 编程
      • 3.1 点灯C文件
      • 3.2 start.s文件
      • 3.3 map.lds
      • 3.4 Makefile文件
      • 3.5 运行
  • 五 流水灯
    • 1 原理
    • 2 程序

一 环境介绍

使用开发板:FS4412,Soc:Exynos内核型号4412:cortex-A9,架构:armv7; 文件:开发板原理图,Soc数据手册; 环境:文件编写与编译:Linux;下载:超级终端hypertrm;串口下载 编译器:交叉编译器arm-linux-gcc。

二 简略了解SFR特殊功能寄存器

在这里插入图片描述 这张图应该很好理解(除了LED灯的方向相反),汇编中的寄存器是cpu操作中的寄存器只能驱动cpu如果要操作,应进行相应的操作cpu外于外设,您必须操作相应的寄存器,因为每个外设都有自己的寄存器来实现不同的功能,所以这些寄存器也被称为特殊功能寄存器,英文缩写SFR,sfr;

三 裸机开发点灯流程

1 看硬件原理图

这个实验是为了驱动一个LED,让它发光,首先要知道这一点LED如何在电路中连接,只有知道电路连接,才能知道如何使灯亮灭。一般来说,这取决于LED控制引脚连接Soc哪个引脚;

2 查手册配置Soc的引脚

对于引脚的相应配置,需要设置相应的寄存器。在芯片手册对应的模块中,需要找到寄存器的地址和用途(哪些是什么)

3 编程

也可以用C语言汇编,但用C更方便;

四 点灯

1 查原理图

1.1 查引脚连接

如图,LED三、四、五分别连接Soc的GPX1_0,GPF3_4,GPF3_5上; 注:GPX1_0:X组第一组第0引脚;

1.2 看驱动方式

(1) 只要三极管导通,二极管的阳极就会连接到高电平,阴极就会通过电阻和三极管接地,LED就亮,三极管截止,LED就灭; (2) NPN三极管发射极接地,基极给高导通,给低截止; (3) 可知:Soc引脚输出高电平灯,低电平灯;

2 查手册

2.1 找到的信息

使用两个寄存器:引脚模式设置GPX1CON,引脚输出电平设置GPX1DAT; (1) GPX1_这个引脚对应的寄存器是GPX1CON; (2) GPX1CON寄存器是32位,一个引脚需要4位配置,32位对应8个引脚,所以GPX1CON可以管理GPX1_0 GPX1_1…GPX1_7; (3) GPX1_这个引脚对应的是GPX1CON(地址为0x11000c20)寄存器0-3位,引脚输出,设置为0x其他位置保持不变; (4)GPX1DAT这个寄存器是管理的GPX1_0-GPX1_7的输出值,不是0就是1,GPX1_0对应的就是GPX1DAT第0GPX1_0输出1,就让GPX1DAT第0位为1;

2.2 搜索过程(手册截图)

2.2.1 GPX1_0

2.2.2 GPF3_4、GPF3_5

3 编程

3.1 点灯C文件

先点亮LED三、思路是配置GPX1_0为输出,输出1; 手册中查到GPX1CON的基地址为0x11000000,偏移量为0x0c所以它的地址是0x11000c20;因为是32位地址,可以转换成int然后访问型指针;也就是说(int *)0x11000c20.访问时再引用*((int *)0x11000c20);同理,GPX1DAT的地址为:0x11000c24; 程序为:

//没有任何头文件,所以连寄存器都要自己定义; //用volatile不要让编译器优化,以防止更改值,但是cache没更新cpu不更新导致无现象; #define GPX1CON *((volatile unsigned int *)0x11000c20) #define GPX1DAT *((volatile unsigned int *)0x11000c24) 
       
        int 
        main
        (
        ) 
        { 
          
        //设置GPX1_0引脚为输出功能,将GPX1CON的0-3位设置为0x01 GPX1CON 
        &= 
        ~
        (
        0xf 
        << 
        0
        )
        ; 
        //将0-3位清0 GPX1CON 
        |= 
        (
        0x1 
        << 
        0
        )
        ; 
        //将0-3设置为0x1 
        //设置GPX1_0引脚输出1 GPX1DAT 
        |= 
        (
        0x1 
        << 
        0
        )
        ; 
        //将第0位设置为1 
        return 
        0
        ; 
        } 
       

3.2 start.s文件

.global _start	@.global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用;
_start:
b main			@从_start进入后执行跳转到main;

3.3 map.lds

链接脚本是简单易用的,但包含了很多东西,本次实验用到的是很简单的一个;

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")	 /*elf32-littlearm指定输出可执行文件是elf格式,32位ARM指令,小端,写的三个分别表示为默认为小端;如果是小端,改为小端;如果是大端,改为小端*/ 
OUTPUT_ARCH(arm)/*指定输出可执行文件的平台为ARM*/ 
ENTRY(_start) /*指定输出可执行文件的起始代码段为_start*/ //即程序入口
SECTIONS				//使用'SECTIONS'来描述输出文件的内存布局.
{ 
        
/*指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。必须使编译器知道这个地址,通常都是修改此处来完成*/ 
	. = 0;	 			 /*从0x0位置开始,代码应当被载入到地址'0x10000'处*/
	. = ALIGN(4);		/*代码以4字节对齐*/
	.text :
	{ 
        
		*(.text)		/*其它代码部分*/ 
	}
    . = ALIGN(4);
    .data : 			 
	{ 
         *(.data) }		/*指定读/写数据段*/ 
    . = ALIGN(4);
    .bss :
    { 
         *(.bss) }
}

简单讲解: (1) .=0x0;对一个特殊的符号’.‘赋值, 这是一个定位计数器. 如果你没有以其它的方式指定输出节的地址, 那地址值就会被设为定位计数器的现有值. 在’SECTIONS’命令的开始处, 定位计数器拥有值’0’; 每个输出节之前都可以加上对定位计数器进行赋值,来表示这个输出节在内存中的起始位置; 如果不写,那么默认输出节跟在上一个之后,.bss就紧跟在.data内存区后; (2) . = ALIGN(4)表示以4字节对齐; (3) .text :定义一个输出节,‘.text’. 冒号是语法需要,现在可以被忽略. 节名后面的花括号中,你列出所有应当被放入到这个输出节中的输入节的名字. '‘是一个通配符,匹配任何文件名. 表达式’(.text)‘意思是所有的输 入文件中的’.text’输入节. (4) 运行一个程序时第一个被执行到的指令称为"入口点",使用’ENTRY’连接脚本命令来设置入口点;

3.4 Makefile文件

下面的这个makefile文件将start.s和mian.c编译为目标文件,然后再加上链接脚本,链接成一个.elf系统文件,再用objcopy生成.bin可执行的裸机程序,最后用objdump生成反汇编文件,以便查看; 注意:start的所有操作应该在main之前;因为程序入口是start而不是main;

CROSS = arm-none-linux-gnueabi-	 #交叉编译器
CC=$(CROSS)gcc					#交叉编译命令
LD=$(CROSS)ld					#交叉编译链接命令,将所有的.o文件链接生成可执行文件(.elf)
OBJCOPY=$(CROSS)objcopy			#被用来复制一个目标文件的内容到另一个文件中,可用于不同源文件的之间的格式转换,实际上elf文件中的调试信息被删掉了;
all:
	$(CC) -g -c -o start.o start.s 				#-g支持gdb,-c生成目标文件,-o改目标文件名
	$(CC) -g -c -o main.o main.c  
	$(LD) start.o main.o -Tmap.lds -o led.elf	#-T指定链接脚本,
	$(OBJCOPY)  -O binary -S led.elf led.bin	# 用.elf文件生成.bin文件
	$(CROSS)objdump -D led.elf > led.dis		#objdump 将.elf文件进行反汇编生成反汇编文件(.dis),-D是反汇编所有部分,“>”是把结果写入到.dis文件中,如果没有">",那么反汇编语句直接到命令行窗口;
	 
clean:
	rm -f *.o *.elf *.bin *.dis

3.5 运行

使用make启动makefile生成bin文件,下载到开发板就可以看到现象了;不同开发板下载方式和软件都不一样,这里不做讨论;

五 流水灯

1 原理

在四中,我们已经点亮了一个灯,接下来点亮LED4和LED5,再让它们循环地亮和灭即可;

2 程序

这个程序只是把前面的东西组合包装了以下,实现了流水灯,不再作解释;

#define GPX1CON *((volatile unsigned int *)0x11000c20)
#define GPX1DAT *((volatile unsigned int *)0x11000c24)
#define GPF3CON *((volatile unsigned int *)0x114001e0)
#define GPF3DAT *((volatile unsigned int *)0x114001e4)

#define led3on GPX1DAT |= (0x1 << 0) //将第0位设置为1
#define led3of GPX1DAT &=~(0x1 << 0) //将第0位清0
#define led4on GPF3DAT |= (0x1 << 4) 
#define led4of GPF3DAT &=~(0x1 << 4) 
#define led5on GPF3DAT |= (0x1 << 5) 
#define led5of GPF3DAT &=~(0x1 << 5) 

void delay()
{ 
        
	int i=0xfffff;
	while(i--);
}

int main()
{ 
        
	//设置GPX1_0引脚为输出功能,将GPX1CON的0-3位设置为0x01
	GPX1CON &= ~(0xf << 0);  //将0-3位清0
	GPX1CON |=  (0x1 << 0);  //将0-3设置为0x1

	GPF3CON &= ~(0xff << 16);  //将16-23位清0
	GPF3CON |=  (0x11 << 16);  //将16-23设置为0x11

	while(1)
	{ 
        
		led3on;delay();		
		led3of;delay();		
		led4on;delay();		
		led4of;delay();		
		led5on;delay();
		led5of;delay();
	}
	return 0;
}

标签: sot89三极管npn

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台