Linux之ARM(MX6U)裸机篇----4.C语言LED驱动实验

一,启动文件

cpp 复制代码
.global _start

_start:
    @设置处理器进入SVC模式
    mrs r0, cpsr        @读取cpsr到r0
    bic r0, r0, #0x1f   @清除cpsr的bit4-0
    orr r0, r0, #0x13   @使用svc模式
    msr cpsr, r0        @将r0写入到cpsr

    ldr sp, =0x80200000 @设置sp指针起始地址,此处已初始化ddr,若开发板未初始化ddr,则要手动初始化
    b main              @跳转到C语言main函数

汇编位运算

设置CPSR寄存器,看M4:0 设置为1001100XB ,时6ULL处于SVC模式,读写状态寄存器需要用MRS(特殊寄存器读写到普通寄存器中)MSR(普通寄存器读写到特殊寄存器中)指令

设置SP来控制出栈和入栈,可以指向内部RAM,也可以DDR,这里指向DDR,设置栈大小为0x200000=2MB,栈是向上增长。

二,main文件

cpp 复制代码
main.c

#include "main.h"
/* 使能外设时钟 */
void clk_enable(void)
{
    CCM_CCGR1 = 0XFFFFFFFF;
    CCM_CCGR2 = 0XFFFFFFFF;
    CCM_CCGR3 = 0XFFFFFFFF;
    CCM_CCGR4 = 0XFFFFFFFF;
    CCM_CCGR5 = 0XFFFFFFFF;
    CCM_CCGR6 = 0XFFFFFFFF;
}
/* 初始化LED */
void led_init(void)
{
    SW_MUX_GPIO1_IO03 = 0X5;       /* 复用为GPIO1-IO03 */
    SW_PAD_GPIO1_IO03 = 0X10B0;    /* 设置GPIO01_IO03电气属性*/

    /* GPIO初始化 */
    GPIO1_GDIR = 0X8;              /* 设置未输出 */
    GPIO1_DR = 0X0;                /* 打开LED灯 */
}

/* 短延时一个循环1ms */
void delay_short(volatile unsigned int n)
{
    while(n--){}
}

/* 延时 */
void delay(volatile unsigned int n)
{
    while(n--)
    {
        delay_short(0x7ff);
    }
}

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

/* 关闭led灯 */
void led_off(void)
{
    GPIO1_DR |= (1<<3); /* bit3置1 */
}
int main(void)
{
    /* 初始化时钟 */
    clk_enable();
    /* 初始化LED */
    led_init();
    /* 设置LED闪烁 */
    while(1){
        led_on();
        delay(500);
        led_off();
        delay(500);
    };
    return 0;
}

main.h
#ifndef _MAIN_H
#define _MAIN_H

/*定义要使用的寄存器,根据操作手册看寄存器地址进行配置*/
#define CCM_CCGR0           *((volatile unsigned int *)0x020C4068)  //volatile是防止被更改,此处对宏读写操作就是直接读写该地址寄存器
#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

三,链接脚本文件

cpp 复制代码
SECTIONS{
    . = 0x87800000;                         /* 链接起始地址 */
    .text :                                 /* 程序段存放所有程序 */
    {
        start.o
        *(.text)
    }       
    .rodata ALIGN(4) : {*(.rodata*)}      /* rodata段存放所有rodata */
    .data ALIGN(4) : {*(.data*)}          /* data段存放所有data */
    _bss_start=.;                         /* .bss的起始地址 */
    .bss ALIGN(4) : {*(.bss) *(COMMON)}   /* 存放.bss数据,定义但未初始化的变量 */
    _bss_end=.;                           /* .bss的终止地址 */
}
/* 这段代码是一个连接脚本文件的片段,将文件分为不同的段,用于描述将各个输入目标文件(object file)的不同部分如.text、.rodata、.data和.bss等合并到最终的可执行文件中的内存布局。 */

四,makefile文件

cpp 复制代码
objs = main.0 start.o
ledc.bin : $(objs)
	arm-linux-gnueabihf-ld -Timx6ull.lds $^ -o ledc.elf
#arm-linux-gnueabihf-ld -Ttext 0X87800000 $^ -o ledc.elf
#这条命令的意思是:使用 arm-linux-gnueabihf-ld 链接器将所有依赖的目标文件($^ 展开后的目标文件列表)链接成一个名为 ledc.elf 的 ELF 文件,并且指定程序的文本段从内存地址 0x87800000 开始。
	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 $@ $<
#Wall表示显示编译时所有警告 -nostdlib表示不链接系统标准启动文件和库文件 -O2表示优化等级
%.o : %.s
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<

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

#-c:这个选项告诉编译器只执行编译步骤,不进行链接。编译器将源代码编译成目标文件(通常是 .o 文件)。
#-o $@:-o 是一个链接器选项,用于指定输出文件的名称。$@ 是一个 Makefile 特殊变量,代表当前规则的 target 名称。在这个上下文中,它将被替换为当前规则的目标文件名,例如 start.o。
#$<:这是另一个 Makefile 特殊变量,代表当前规则依赖的第一个文件。在这个上下文中,它通常指向源代码文件,例如 start.s。
#这串指令的意思是:使用 arm-linux-gnueabihf-gcc 编译器,打开所有警告,不链接标准库,只编译不链接,输出文件名为当前规则的目标文件名,源代码文件为当前规则的第一个依赖文件。这通常用于编译单个源文件到目标文件的过程。
相关推荐
2401_862880821 小时前
ARM 嵌入式开发 ---知识点
arm开发·嵌入式硬件·物联网
AAA代码批发商3 小时前
DAY43 ARM 体系结构学习笔记|嵌入式入门,从硬件组件到流水线、内存与栈帧
arm开发·笔记·学习
女神下凡5 小时前
带NPU的电脑 和不带NPU的电脑 在使用上有什么不样
arm开发·单片机·pcm
T1mzhou19 小时前
ARM64 Linux 6.10 内核驱动(12):dd.c:probe/ remove、延迟 probe
linux·服务器·c语言·arm开发·arm
Doraemomo20 小时前
IMX6ULL裸机开发——系统主频和时钟配置
arm开发·嵌入式硬件·makefile·imx6ull
T1mzhou1 天前
ARM64 平台安全学习记录
linux·服务器·arm开发·arm
云栖梦泽1 天前
网络设备驱动(1)
linux·arm开发·嵌入式硬件
零依赖极客2 天前
Day 9·1 KV 也量化——q8 KV 把缓存与 decode 带宽压到一半
c语言·开发语言·arm开发·人工智能·缓存·矩阵
secondyoung3 天前
I2C通信协议详解
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·51单片机
奔跑的架构师3 天前
[A-50]ARMv9/v8-DVFS系统架构
linux·arm开发·架构·系统架构·arm