开发板FS4412-C工程与寄存器封装

目录

一、C语言工程简介

二、启动代码分析

 ​编辑

三、 c语言实现LED实验

四、寄存器的封装方式

五、寄存器操作标准化

一、C语言工程简介

Makefile

#=============================================================================#
NAME = interface
CROSS_COMPILE = arm-none-linux-gnueabi-
#=============================================================================#
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJDUMP = $(CROSS_COMPILE)objdump
OBJCOPY = $(CROSS_COMPILE)objcopy
CFLAGS  += -g -O0 -mabi=apcs-gnu -mfpu=neon -mfloat-abi=softfp -fno-builtin \
		   -nostdinc -I ./common/include      		                                       
#============================================================================#
OBJSss  := $(wildcard start/*.S) $(wildcard common/src/*.S) $(wildcard *.S) \
		   $(wildcard start/*.c) $(wildcard common/src/*.c) 			    \
		   $(wildcard usr/*.c) $(wildcard *.c) 
OBJSs  	:= $(patsubst %.S,%.o,$(OBJSss))
OBJS 	:= $(patsubst %.c,%.o,$(OBJSs))
#============================================================================#
%.o: %.S
	$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<
all:clean $(OBJS)
	$(LD) $(OBJS) -T map.lds -o $(NAME).elf
	$(OBJCOPY) -O binary  $(NAME).elf $(NAME).bin 
	$(OBJDUMP) -D $(NAME).elf > $(NAME).dis 
#============================================================================#
clean:
	rm -rf $(OBJS) *.elf *.bin *.dis *.o
#============================================================================#

map.lds 链接脚本文件:告诉编译器 链接的信息 在什么位置 map.lds中找

  map.lds:代码、数据、未初始化的全局变量放在BSS段,地址和排版,都可以在map.lds 找到信息

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x40008000;
	. = ALIGN(4);
	.text      :
	{
		start/start.o(.text)
		*(.text)
	}
	. = ALIGN(4);
 .rodata : 
	{ *(.rodata) }
 . = ALIGN(4);
 .data : 
	{ *(.data) }
 . = ALIGN(4);
 .bss :
 { *(.bss) }
}

二、启动代码分析

异常向量表32个字节,AR,碰到异常,先跳转到相应异常向量表的位置,异常向量表中写跳转指令,跳转至异常处理程序

b.就是跳转到自身相当于死循环

 1 .text
  2 .global _start
  3 _start:
  4         /*
  5          * Vector table
  6          */
  7         b reset
  8         b .
  9         b .
 10         b .
 11         b .
 12         b .
 13         b .
 14         b .
 15 
 16 reset:
 17         /*
 18          * Set vector address in CP15 VBAR register
 19          */
 20        ldr     r0, =_start //异常向量表地址给R0
 21         mcr     p15, 0, r0, c12, c0, 0  @Set VBAR,mcr将ARM寄存器中的值写入协处理器中的寄存器;此处是修改异常向量表位置,ARM默认0地址,从0地址先找这个表;将R0存入cp15的C12,总体作用就是转存异常向量表
 22 
 23         /*
 24          * Set the cpu to SVC32 mode, Disable FIQ/IRQ
 25          */
 26         mrs r0, cpsr    
 27         bic r0, r0, #0x1f
 28         orr     r0, r0, #0xd3
 29         msr     cpsr ,r0
 30 
 31         /*
 32          * Defines access permissions for each coprocessor
 33          */
 34     mov r0, #0xfffffff
 35     mcr p15, 0, r0, c1, c0, 2
 36 
 37         /*
 38          * Invalidate L1 I/D                                                                                                                       
 39          */
 40         mov     r0, #0                                  @Set up for MCR
 41         mcr     p15, 0, r0, c8, c7, 0   @Invalidate TLBs
 42         mcr     p15, 0, r0, c7, c5, 0   @Invalidate icache
 43 
 44         /*
 45          * Set the FPEXC EN bit to enable the FPU
 46          */
 47         mov r3, #0x40000000
 48         fmxr FPEXC, r3
 49 
 50         /*
 51          * Disable MMU stuff and caches 物理地址与虚拟地址的转换MMu
 52          */
 53         mrc     p15, 0, r0, c1, c0, 0
 54         bic     r0, r0, #0x00002000             @Clear bits 13 (--V-)
 55         bic     r0, r0, #0x00000007             @Clear bits 2:0 (-CAM)
 56         orr     r0, r0, #0x00001000             @Set bit 12 (---I) Icache
 57         orr     r0, r0, #0x00000002             @Set bit 1 (--A-) Align
 58         orr     r0, r0, #0x00000800             @Set bit 11 (Z---) BTB
 59         mcr     p15, 0, r0, c1, c0, 0
 60 
 61         /*
 62          * Initialize stacks   初始化栈指针sp,保证正确压栈和出栈                                                                                                                   
 63          */
 64 init_stack:
 65         /*svc mode stack*/
 66         msr cpsr, #0xd3
 67         ldr sp, _stack_svc_end
 68 
 69         /*undef mode stack*/
 70         msr cpsr, #0xdb
 71         ldr sp, _stack_und_end
 72 
 73         /*abort mode stack*/
 74         msr cpsr,#0xd7
 75         ldr sp,_stack_abt_end
 76 
 77         /*irq mode stack*/
 78         msr cpsr,#0xd2
 79         ldr sp, _stack_irq_end
 80 
 81         /*fiq mode stack*/
 82         msr cpsr,#0xd1
 83         ldr sp, _stack_fiq_end
 84 
 85         /*user mode stack, enable FIQ/IRQ*/
 86         msr cpsr,#0x10
 87         ldr sp, _stack_usr_end
 88 
 89         /*Call main*/
 90         b main
 91 
 92 _stack_svc_end:
 93         .word stack_svc + 512
 94 _stack_und_end:
 95         .word stack_und + 512
 96 _stack_abt_end:
 97         .word stack_abt + 512
 98 _stack_irq_end:
 99     .word stack_irq + 512
100 _stack_fiq_end:
101     .word stack_fiq + 512
102 _stack_usr_end:
103     .word stack_usr + 512
104 
105 .data
106 stack_svc:
107         .space 512    //伪操作把512个字节占了,作为栈的位置。6个有栈指针的模式都有自己的栈空间。
108 stack_und:
109         .space 512
110 stack_abt:
111         .space 512
112 stack_irq:
113         .space 512
114 stack_fiq:
115         .space 512
116 stack_usr:
117         .space 512

 92 _stack_svc_end:
 93         .word stack_svc + 512

用来计算栈指针的起始位置,因为是满减栈,要从高往低压

总结

  • 异常向量表初始化
  • 把r0的值放到协处理器p15的c12
  • 设置cpu为SVC模式,禁用FIQ/IRQ
  • 定义协处理器访问权限
  • TLBs 和icache关闭
  • 使能运算浮点型数据的协处理器
  • MMU负责物理内存和虚拟内存转换,关闭:现在还没涉及系统移植,暂时不使用
  • 初始化栈
  • 申请各模式栈地址
  • 计算栈的起始位置
  • 恢复为user模式,跳转到main

三、 c语言实现LED实验

void Delay(unsigned int Time)
{
	while(Time --);
}
int main()
{
	/*通过设置GPX2CON寄存器来将GPX2_7引脚设置成输出功能*/
	//直接写0x11000c40会报错,这是一个常量,加*会报错
	//,编译器是不知道这个值是地址,这是我们查表得到的,需要强制类型转换成地址
	//指针的数据类型不是自身的数据类型,而是指向数据的类型
	//寄存器地址四个字节,所以转成int或unsigned int
	*(unsigned int *)0x11000c40 = 0x10000000;
	while(1)
	{
		//还没学到系统调用,我们无法使用其他封装好的函数如sleep()和头文件
		//点亮
		*(unsigned int *)0x11000c44 = 0x00000080;
		Delay(1000000);
		//熄灭
		*(unsigned int *)0x11000c44 = 0x00000000;
		Delay(1000000);
	}


	return 0;
}

四、寄存器的封装方式

在上述代码中发现,读写性不高,后期维护成本特别高,特别容易出错。

所以我们可以将寄存器进行封装

1.第一种封装方式:宏定义

#define GPX2CON (*(unsigned int *)0x11000c40)
#define GPX2DAT (*(unsigned int *)0x11000c44)
void Delay(unsigned int Time)
{
	while(Time --);
}
int main()
{
	GPX2CON = 0x10000000;

	while(1)
	{
		/*点亮LED2*/
		GPX2DAT = 0x00000080;
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2DAT = 0x00000000;
		/*延时*/
		Delay(1000000);
	}
	return 0;
}

第二种封装方式:

从芯片手册能看到,GPX2的控制寄存器地址是连续的

C语言中除了数组地址是连续的,结构体的成员地址也是连续的。所以我们可以使用结构体将GPX2的不同寄存器封装起来更加高效

typedef struct
{
	unsigned int CON;
	unsigned int DAT;
	unsigned int PUD;
	unsigned int DRV;
}gpx2;

#define GPX2 (*(gpx2 *)0x11000c40)  //0x11000c40表示为结构体的起始地址
									//指针的数据类型不是自身的数据类型,而是指向数据的类型
									//这里是想告诉编译器这个地址是结构体指针,所以这里要转变
                                    //成结构体的数据类型
void Delay(unsigned int Time)
{
	while(Time --);
}
int main()
{
	GPX2.CON = 0x10000000;

	while(1)
	{
		/*点亮LED2*/
		GPX2.DAT = 0x00000080;
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2.DAT = 0x00000000;
		/*延时*/
		Delay(1000000);
	}
	return 0;
}

第三种:头文件封装

当控制硬件很多的时候,我们可以使用头文件将所有寄存器的封装写好,不同项目的时候直接使用

(头文件已封装)

#include "exynos_4412.h"
void Delay(unsigned int Time)
{
	while(Time --);
}
int main()
{
	GPX2.CON = 0x10000000;

	while(1)
	{
		/*点亮LED2*/
		GPX2.DAT = 0x00000080;
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2.DAT = 0x00000000;
		/*延时*/
		Delay(1000000);
	}
	return 0;
}

五、寄存器操作标准化

在上述代码中,操作寄存器的时候控制GPX2CON[7]位为输出 但将整个寄存器的其他引脚进行了改变,而GPX2CON[7]只占用了寄存器的[31,28],所以需要对单个位进行控制操作

位运算

/*
 * 1.unsigned int a; 将a的第3位置1,其他位保持不变
 * 	******** ******** ******** ********
 * 	******** ******** ******** ****1***
 * 	00000000 00000000 00000000 00001000
 *
 * 	a = a | (1 << 3); //通过移位进行位置的选择,或运算对单独的一位进行操作
 *
 * 2.unsigned int a; 将a的第3位置0,其他位保持不变
 * 	******** ******** ******** ********
 * 	******** ******** ******** ****0***
     a = a &0xFFFFFFFF7 ;//效率低,易出错
 * 	11111111 11111111 11111111 11110111
 *
 * 	a = a & (~(1 << 3));
 *
 * 	3.unsigned int a; 将a的第[7:4]位置为0101,其他位保持不变
 * 	******** ******** ******** ********
 * 	******** ******** ******** 0101****
 *										(操作GPX2CON寄存器是要控制[31,28]位.单独与,或无法完成多位的变化)
 * 	1).四位清零
 * 	11111111 11111111 11111111 00001111
 * 	00000000 00000000 00000000 11110000
 *  00000000 00000000 00000000 00001111
 *
 * 	a = a & (~(0xF << 4));
 *
 * 	2).再置位
 * 	00000000 00000000 00000000 01010000
 * 	00000000 00000000 00000000 00000101
 *
 * 	a = a | (0x5 << 4);
 *
 * 	=> a = a & (~(0xF << 4)) | (0x5 << 4);
 */
#include "exynos_4412.h"
void Delay(unsigned int Time)
{
	while(Time --);
}

int main()
{
	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);

	while(1)
	{
		/*点亮LED2*/
		GPX2.DAT = GPX2.DAT | (1 << 7);
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2.DAT = GPX2.DAT & (~(1 << 7));
		/*延时*/
		Delay(1000000);
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值