C语言LED灯
1、汇编文件:C语言环境构建
2、C语言文件:完成工程需要的功能
一、C语言运行环境构建
一、 设置处理器模式
设置6ULL处于SVC模式下,设置CPSR寄存器的bit4-0,即M[4:0]为10011 = 0X13。
读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器,MSR指令将通用寄存器的数据写入到CPSR寄存器中。
二、 设置 SP 指针
SP指针可以指向内部RAM,也可以指向DDR,我们将其指向DDR。
SP指针设置到哪里?DDR内存 512MB的范围0X80000000~0X9FFFFFFF。栈设置大小,0X200000 = 2MB。处理器栈增长方式,对于A7而言是向下增长的(由高地址指向低地址),所以设置SP指针指向0X80200000。
三、 跳转到C语言
使用b指令,跳转到C语言函数,比如main函数。
二、 程序代码
1、 汇编文件,设置处理器模式以及SP指针
start.S
.global _start /* 全局标量 */
/*
描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C运行环境
*/
_start:
/* 进入SVC模式 */
mrs r0, cpsr // 读取cpsr的数据到r0
bic r0, r0, #0X1F // 将r0的低五位清零,即cpsr的M0~M4
orr r0, r0, #0X13 // r0或上0x13,表示使用SVC模式
msr cpsr, r0 // 将r0的数据写入到cpsr_c中
ldr sp, = 0X80200000 // 设置栈指针
b main // 跳转到C语言main函数
2、 驱动文件
main.h
#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
main.c
#include "main.h"
/**********************************
* 函数名称: void clk_enable(void)
* 函数功能: 使能外设时钟
* 输入 : 无
* 输出 : 无
***********************************/
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;
}
/**********************************
* 函数名称: void LED_Init(void)
* 函数功能: 初始化LED
* 输入 : 无
* 输出 : 无
***********************************/
void LED_Init(void)
{
/* 初始化IO复用,复用为GPIO1_IO3 */
SW_MUX_GPIO1_IO03 = 0X5;
/* 设置GPIO1——IO3 电气属性 */
/***************************
*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;
/* 初始化GPIO,GPIO1_IO3设置为输出 */
GPIO1_GDIR = 0X00000008;
/* 设置GPIO1_IO3输出低电平,打开LED灯 */
GPIO1_DR = 0X0;
}
/**********************************
* 函数名称: void delay_short(volatile unsigned int n)
* 函数功能: 短延时
* 输入 : volatile unsigned int n
* 输出 : 无
***********************************/
void delay_short(volatile unsigned int n)
{
while(n--);
}
/**********************************
* 函数名称: void delay(volatile unsigned int n)
* 函数功能: 延时,在396Mhz主频下约1ms
* 输入 : volatile unsigned int n
* 输出 : 无
***********************************/
void delay(volatile unsigned int n)
{
while(n--)
delay_short(0X7FF);
}
/**********************************
* 函数名称: void LED_On(void)
* 函数功能: 打开LED灯
* 输入 : 无
* 输出 : 无
***********************************/
void LED_On(void)
{
GPIO1_DR &= ~(1 << 3); /* bit3清零 */
/* 1 << 3:
* 1左移3位 0000 0001 --> 0000 1000
* ~(1 << 3):
* ~(0000 1000) --> 1111 0111
* GPIO1_DR &= ~(1 << 3) 即 将bit3清零
*/
}
/**********************************
* 函数名称: void LED_Off(void)
* 函数功能: 关闭LED灯
* 输入 : 无
* 输出 : 无
***********************************/
void LED_Off(void)
{
GPIO1_DR |= (1 << 3); /* bit3置1 */
}
int main(void)
{
clk_enable(); /* 使能外设时钟 */
LED_Init(); /* 初始化LED */
while(1) {
LED_On();
delay(500);
LED_Off();
delay(500);
}
return 0;
}
3、Makefile
objs = start.o main.o
ledc.bin : $(objs)
arm-linux-gnueabihf-ld -Ttext 0X87800000 $^ -o ledc.elf
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o : %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
三、 链接脚本
链接脚本描述了要连接的文件,以及链接顺序,和链接首地址。
链接脚本的语法很简单,就说编写一系列的命令,这些命令组成连接脚本 ,每个命令都是一个带有参数的关键字或者一个对符号的赋值,可以使用符号分隔命令。
一般编译出来的代码都包含text、data、bss和rodata这四个段。
假设代码要被链接到0X10000000地址,而数据要被链接到0X30000000地址,下面为完成此功能最简单的链接脚本:
SECTIONS{
. = 0X10000000;
.text : {*(.text)}
. = 0X30000000
.data ALIGN(4) : { *(.data)}
.bss ALIGN(4) : { *(.bss)}
}
第1行 为关键字“SECTIONS”
第2行 对特殊符号 “.” 进行赋值,“.”在链接脚本里称为 定位计数器,默认的定位计数器为0。因此给“.”赋值0X10000000,表示以0X10000000开始,后面的文件或者段都会以0X10000000开始链接。
第3行 “.text”为段名,冒号是语法要求,大括号里可以填上要链接到“.text”这个段的所有文件 “ *(.text)”表示所有输入文件的.text段都放到“.text”上 “ * ”是通配符
ALIGN(4)表示4字节对称,用来对“.data”段的起始地址做字节对称,即“.data”的起始地址要能被4整除。常见的有ALIGN(4)、ALIGN(8)
“.bss”就是那些定义了但是没有初始化的变量
imx6ull.lds
与Makefile同目录,用于Makefile的链接
SECTIONS{
. = 0X87800000;
.text :
{
start.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
其中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 : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
%.o : %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis