嵌入式LinuxC语言版点亮LED灯实验

C语言环境搭建

1.设置处理器模式

设置6ULL处于SVC模式 下。设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0X13。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。

读写指令为什么不用LDR和STR是因为LDR和STR用在通用寄存器,而MRS和MSR指令用在特殊功能寄存器。

设置成SVC模式是因为我们可以完全操作,如果不在这个模式下,可能有一些操作会受到限制。

2.设置SP指针

SP可以指向内部RAM,也可以指向DDR,我们将其指向DDR。SP设置到哪里?512MB的范围0x80000000~0x9FFFFFFF。栈大小,0x200000=2MB。处理器栈增长方式,对于A7而言是向下增长的。设置SP指向0x80200000。

3.跳转到C语言函数

使用b指令,跳转到C语言函数,比如main函数。

代码解析

start.s

这个文件是设置处理器模式和指针地址

.global _start
_start:
    //进入SVC模式
    mrs r0,cpsr        
    bic r0,r0,#0x1F    //将r0的低5位清零,也就是cpsr的M0~M4
    orr r0,r0,#0x13    //将r0或上0x13,表示使用SVC模式
    msr cpsr,r0        //将r0的数据写入到cpsr_c中
    
    ldr sp,=0x80200000 //设置栈指针
    b main             //跳转到main函数

指令意思如下图:

设置SVC模式配置如下:

main.h

#ifndef __MAIN_H
#define __MAIN_H

/* 
2 * CCM 相关寄存器地址
3 */
#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(){                //使能I.MX6U所有外设时钟
    CCM_CCGR0=0xFFFFFFFF;
    CCM_CCGR1=0xFFFFFFFF;
    CCM_CCGR2=0xFFFFFFFF;
    CCM_CCGR3=0xFFFFFFFF;
    CCM_CCGR4=0xFFFFFFFF;
    CCM_CCGR5=0xFFFFFFFF;
    CCM_CCGR6=0xFFFFFFFF;
}

void led_init(){                //初始化LED对应的GPIO
    SW_MUX_GPIO1_IO03=0x5;      //初始化IO复用,复用为GPIO1_IO03
    SW_PAD_GPIO1_IO03=0x10B0;   //配置GPIO1_IO03的IO属性,这里的配置上一篇文章也有详解
    GPIO1_GDIR=0x8;             //初始化GPIO,GPIO1_IO03设置为输出
    GPIO1_DR=0x0;               //设置GPIO1_IO03输出低电平,打开LED0
}

void led_on(){                  //打开LED灯
    GPIO1_DR &= ~(1<<3);        //将GPIO1_DR的bit3清零
}

void led_off(){                 //关闭LED灯
    GPIO1_DR |= (1<<3);         //将GPIO1_DR的bit3置1
}

//短时间延时函数,要延时循环次数(空操作循环次数,模式延时)
void delay_short(volatile unsigned int n){
    while (n--){}
}

void delay(volatile unsigned int n){    //延时函数,在396Mhz的主频下延时时间大约为1ms
    while (n--)
    {
        delay_short(0x7FF);
    }
}

int main(){
    clk_enable();        //使能所有的时钟
    led_init();          //初始化 led
    while(1){            //死循环
        led_off();       //关闭LED
        delay(500);      //延时500ms
        led_on();        //打开LED
        delay(500);      //延时500ms
    }
    return 0;
}

Makefile

objs=start.o main.o
ledc.bin:$(objs)
	arm-linux-gnueabihf-ld -Ttext 87800000 $^ -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

我们现在来详细解释一下Makefile

objs=start.o main.o

这一行定义了一个名为 objs 的变量,包含了 start.omain.o 这两个目标文件。这些目标文件是从源代码文件编译而来的。

ledc.bin:$(objs)

这一行定义了一个目标 ledc.bin,它依赖于 objs 变量中列出的所有目标文件(即 start.omain.o)。

arm-linux-gnueabihf-ld -Ttext 87800000 $^ -o ledc.elf

这一行使用 arm-linux-gnueabihf-ld 链接器将 objs 中的目标文件链接成一个 ELF 可执行文件 ledc.elf-Ttext 87800000 指定了代码段的加载地址。$^ 是一个自动变量,代表所有的依赖文件(即 start.omain.o)。

arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@

这一行使用 arm-linux-gnueabihf-objcopy 工具将 ledc.elf 文件转换成一个二进制文件 ledc.bin-O binary 指定输出格式为二进制文件,-S 表示去掉所有符号表和重定位信息。$@ 是一个自动变量,代表当前目标(即 ledc.bin)。

arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis

这一行使用 arm-linux-gnueabihf-objdump 工具将 ledc.elf 文件反汇编并输出到 ledc.dis 文件中。-D 表示反汇编所有部分,-m arm 指定目标架构为 ARM。

%.o:%.c

这一行定义了一个模式规则,指定如何将 .c 源文件编译成 .o 目标文件。

arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

这一行定义了具体的编译命令。使用 arm-linux-gnueabihf-gcc 编译器编译 .c 文件。-Wall 打开所有警告,-nostdlib 表示不链接标准库,-c 表示只编译不链接,-o $@ 指定输出文件名,$< 是一个自动变量,代表当前的依赖文件(即 .c 文件)。

%.o:%.s

这一行定义了一个模式规则,指定如何将 .s 汇编文件编译成 .o 目标文件。

arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

这一行定义了具体的编译命令。使用 arm-linux-gnueabihf-gcc 编译器编译 .s 文件。-Wall 打开所有警告,-nostdlib 表示不链接标准库,-c 表示只编译不链接,-o $@ 指定输出文件名,$< 是一个自动变量,代表当前的依赖文件(即 .s 文件)。

clean: rm -rf *.o ledc.bin ledc.elf ledc.dis

这一行定义了一个名为 clean 的目标,用于清理生成的文件。rm -rf 命令删除所有 .o 文件以及 ledc.binledc.elfledc.dis 文件。

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rrrrr木生于

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值