Linux裸机开发|C语言点灯(逐个定义寄存器地址)

C语言点灯

1. 使用C语言点灯

实际工作中大部分都是使用C语言来编写驱动,只是在开始部分用汇编来初始化一下C语言环境(比如初始化DDR、设置堆栈指针SP等),这些工作做完后就可以进入C语言环境(进入mian函数),所以有两部分文件需要做:

  • 汇编文件:用来完成C语言环境搭建
  • C语言文件:用来完成业务层代码

2. 硬件设计

硬件设计与汇编点灯实例相同

3. 软件设计

3.1 汇编部分
  • 创建汇编文件start.S,用来完成C语言环境搭建
/***** start.S *****/
.global _start	//全局标号
_start:
	mrs r0, cpsr			//进入SVC模式
	bic r0, r0, #0x1f		//将r0的低5位清零
	orr r0, r0, #0x13		//r0或0x13,表示使用SVC模式
	msr cpsr, r0			//将r0的数据写入到cpsr_c中
	
	ldr sp, = 0x80200000	//设置栈指针
	b main					//跳转到mian函数
3.2 C语言部分
  • 创建main.h文件,用来定义寄存器地址
/* main.h */
#ifndef _MAIN_H_
#define _MAIN_H_

//GPIO1_IO03相关寄存器地址定义
//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
  • 创建main.c文件
/*****main.c*****/
#include "main.h"
//使能I.MX6U所有外设时钟
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){
	/* 1、初始化IO复用 */
	SW_MUX_GPIO1_IO03 = 0x5;	/* 复用为GPIO1_IO03 */
	/* 2、配置GPIO1_IO03的IO属性*/
	SW_PAD_GPIO1_IO03 = 0X10B0;		
	/* 3、初始化GPIO */
	GPIO1_GDIR = 0X0000008;	/* GPIO1_IO03设置为输出 */
	/* 4、设置GPIO1_IO03输出低电平,打开LED0 */
	GPIO1_DR = 0X0;
}
//打开LED灯
void led_on(void){
	GPIO1_DR &= ~(1<<3);  //将GPIO1_DR的bit3清零
}
//关闭LED灯
void led_off(void){
	GPIO1_DR |= (1<<3);	 //将GPIO1_DR的bit3置1
}
//短时间延时函数
void delay_short(volatile unsigned int n){
	while(n--){}
}
//延时函数,在396Mhz的主频下
void delay(volatile unsigned int n){
	while(n--){
		delay_short(0x7ff);
	}
}
//mian函数
int main(void){
	clk_enable();		/* 使能所有的时钟		*/
	led_init();			/* 初始化led 			*/
	while(1)			/* 死循环 				*/
	{	
		led_off();		/* 关闭LED   			*/
		delay(500);		/* 延时大约500ms 		*/
		led_on();		/* 打开LED		 		*/
		delay(500);		/* 延时大约500ms 		*/
	}
	return 0;
}

4. 下载验证

4.1 编写Makefile
objs := start.o main.o

ledc.bin:$(objs)
	arm-linux-gnueabihf-ld -Timx6ul.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 -O2 -o $@ $<
	
%.o:%.S
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
	
%.o:%.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
	
clean:
	rm -rf *.o ledc.bin ledc.elf ledc.dis
4.2 链接脚本

在之前的实验中,是通过“-Ttext”来指定链接地址为0x87800000,但是缺点是所有的文件都会链接到0x87800000位起始地址的区域。有时很多文件需要链接到指定的区域(段),使用链接脚本能够自由定义一些段,自由指定段的起始地址,并且可以指定文件或者函数存放到哪个段里面去

链接脚本用于描述文件应该如何被链接在一起形成最终的可执行文件,其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布

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 = .;
}
4.3 代码验证
  • 将imxdownload拷贝到工程根目录下
  • 赋予imxdownload可执行权限
chmod 777 imxdownload
  • 确定要烧写的SD卡
ls /dev/sd*   #查看当前电脑的存储文件
  • 向SD卡烧写bin文件
./imxdownload ledc.bin /dev/sdcard
  • 烧写成功后,复位开发板,若代码正常,LED0会以500ms的时间间隔亮灭

你可能感兴趣的:(Linux裸机开发,linux,c语言,stm32)