I.MX6U-C语言裸机点灯

2517 字
13 分钟
I.MX6U-C语言裸机点灯

一、流程#

  • 编写汇编,跳转到main函数
  • 编写main函数,实现业务代码
  • 编译生成.bin文件
  • 生成imx文件,烧录进入SD卡中

二、汇编跳转到C入口#

从汇编点灯章节可以知道,gcc编译后,汇编程序在 _start: 入口执行汇编代码

因此首先需要定义一个全局标签:.global _start:

然后在标签处执行汇编代码:

_start:

xxx

形如:

.global _start /* 全局标号 */
/********************************************************************
* Function Name : _start
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : 裸机代码启动入口,实现C语言环境跳转
* Note : 2024.09.22 By LMH
**********************************************************************/
_start:
...

_start: 标签下的代码中,需要执行

  • 进入系统管理员权限模式、

    进入此模式,才可以配置一些关键寄存器,否则会没有权限导致配置失败

  • 设置SP指针

    设置栈内存的起始地址,相当于设置栈大小了

  • 跳转到C语言main入口

    跳转到标准C程序入口main

综上,启动程序的简要汇编代码如下:

Terminal window
/************************************************************************
* This source code file is the property of MengHang.Li.
* The contents of this file are intended for non-commercial use only.
* No individual or organization may use this file for commercial purposes
* without the explicit written permission of MengHang.Li.
*
* This source code is provided "as is" without any warranties. The author
* or copyright holder shall not be liable for any consequences, direct or
* indirect, arising from the use of this source code.
*
* Copyright Notice: (C) 2024-2025, MengHang.Li.
*
* File: imx6u_start.s
* Author: LMH
* Creation Date: 2024.09.30
*************************************************************************
*
*************************************************************************/
/* Includes ------------------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Extern variables ----------------------------------------------------------*/
/* Extern functions ----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
.global _start /* 全局标号 */
/* Private function prototypes -----------------------------------------------*/
/*----------------------------------------------------------------------------*/
/********************************************************************
* Function Name : _start
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : 裸机代码启动入口,实现C语言环境跳转
* Note : 2024.09.22 By LMH
**********************************************************************/
_start:
//进入SVC模式(supervisor Call)
//进入SVC模式,便于设置堆栈、配置中断向量表等。需要特权模式才能执行
MRS R0,CPSR //将当前的CPSR值保存到R0中
BIC R0,R0,#0x1F //清除后5位
ORR R0,R0,#0x13 //向R0中写入0x13,
MSR CPSR,R0 //将修改后的值写入CPSR寄存器,实现模式切换
//初始化SP指针(堆栈), IMX文件中已经对DDR进行初始化,所以这里直接设置SP
//256M 512M 的DDR起始地址都是0x80000000
//因此,这里设置的栈大小是 0x80200000-0x80000000 = 0x200000 Byte = 2 Mbyte
LDR SP,= 0x80200000
//跳转到main
B main

类似于STM32的启用文件中的汇编代码,还涉及了设置堆大小、初始化中断向量表等操作,这里暂时用不到,不做配置。

三、C代码编写#

这一部分就没有什么难度了,参考汇编点灯的这一章节,使用C语言实现即可

mian.c 文件:

/************************************************************************
* This source code file is the property of MengHang.Li.
* The contents of this file are intended for non-commercial use only.
* No individual or organization may use this file for commercial purposes
* without the explicit written permission of MengHang.Li.
*
* This source code is provided "as is" without any warranties. The author
* or copyright holder shall not be liable for any consequences, direct or
* indirect, arising from the use of this source code.
*
* Copyright Notice: (C) 2024-2025, MengHang.Li.
*
* File: main.c
* Author: LMH
* Creation Date: 2024.09.30
*************************************************************************
*
*************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private defines -----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Extern variables ----------------------------------------------------------*/
/* Extern functions ----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
void imx_clk_enable();
void imx_led_init();
void led_on(void);
void led_off(void);
void delay(volatile unsigned int n);
/* Private function prototypes -----------------------------------------------*/
/*----------------------------------------------------------------------------*/
/********************************************************************
* Function Name : main
* Input : @Param1 - argc;
* ! 输入的参数个数
* : @Param2 - argv;
* ! 参数列表
* Output : xxx
* Returns : xxx
* Description : C main入口
* Note : 2024.09.30 By LMH
**********************************************************************/
int main( void )
{
imx_clk_enable();
imx_led_init();
while(1)
{
led_on();
delay(100);
led_off();
delay(100);
}
return 0;
}
/********************************************************************
* Function Name : imx_clk_enable
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : imx 时钟使能
* Note : 2024.09.30 By LMH
**********************************************************************/
void imx_clk_enable()
{
CCM_CCGR0 = 0xFFFFFFFF;
CCM_CCGR1 = 0xFFFFFFFF;
CCM_CCGR2 = 0xFFFFFFFF;
CCM_CCGR3 = 0xFFFFFFFF;
CCM_CCGR4 = 0xFFFFFFFF;
CCM_CCGR5 = 0xFFFFFFFF;
CCM_CCGR6 = 0xFFFFFFFF;
}
/********************************************************************
* Function Name : imx_led_init
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : imx led 初始化配置
* Note : 2024.09.30 By LMH
**********************************************************************/
void imx_led_init()
{
imx_gpio_config imx_cfg = {.u32 = 0};
//设置IO复用为GPIO
SW_MUX_GPIO1_IO03 = 0x00000005;
//IO设置
imx_cfg.SRE = SRE_SLOW_SR;
imx_cfg.DSE = DES_LEVEL_5;
imx_cfg.SPEED = IO_SPEED_100Ma;
imx_cfg.ODE = DISABLE;
imx_cfg.PKE = ENABLE;
imx_cfg.PUE = PUE_USED_PUS;
imx_cfg.PUS = PUS_PULL_DOWN_100K;
imx_cfg.HYS = DISABLE;
SW_PAD_GPIO1_IO03 = imx_cfg.u32;
GPIO1_GDIR = 0X0000008;
GPIO1_DR = 0x00;
}
/********************************************************************
* Function Name : led_on
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : imx led 打开
* Note : 2024.09.30 By LMH
**********************************************************************/
void led_on(void)
{
GPIO1_DR &= ~(1<<3);
}
/********************************************************************
* Function Name : led_off
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : imx led 关闭
* Note : 2024.09.30 By LMH
**********************************************************************/
void led_off(void)
{
GPIO1_DR |= (1<<3);
}
/********************************************************************
* Function Name : delay_short
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : 延迟函数-
* Note : 2024.09.30 By LMH
**********************************************************************/
void delay_short(volatile unsigned int n)
{
while(n--){}
}
/********************************************************************
* Function Name : delay
* Input : xxx
* Output : xxx
* Returns : xxx
* Description : 延迟函数-ms
* Note : 2024.09.30 By LMH
**********************************************************************/
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7ff);
}
}

mian.h 文件:

/************************************************************************
* This header code file is the property of MengHang.Li.
* The contents of this file are intended for non-commercial use only.
* No individual or organization may use this file for commercial purposes
* without the explicit written permission of MengHang.Li.
*
* This header code is provided "as is" without any warranties. The author
* or copyright holder shall not be liable for any consequences, direct or
* indirect, arising from the use of this header code.
*
* Copyright Notice: (C) 2024-2025, MengHang.Li.
*
* File: mian.h
* Author: LMH
* Creation Date: 2024.09.30
*************************************************************************
*
*************************************************************************/
#ifndef _MAIN_H_
#define _MAIN_H_
#ifdef __cplusplus
extern "C"
{
#endif
/* Includes ------------------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
#define DISABLE ( 0 )
#define ENABLE ( 1 )
/*
* 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)
/* Private typedef -----------------------------------------------------------*/
/**
* @brief imx gpio 配置结构体
*/
#pragma pack(1)
typedef union
{
struct
{
enum
{
SRE_SLOW_SR = 0b0, //@低压摆率
SRE_FAST_SR = 0b1, //@高压摆率
}SRE:1; /*! IO压摆率设置 */
unsigned res1:2; /*! 预留位 */
enum
{
DES_LEVEL_OFF = 0b000,
DES_LEVEL_0 = 0b001,
DES_LEVEL_1 = 0b010,
DES_LEVEL_2 = 0b011,
DES_LEVEL_3 = 0b100,
DES_LEVEL_4 = 0b101,
DES_LEVEL_5 = 0b110,
DES_LEVEL_6 = 0b111,
}DSE:3; /*! IO驱动能力 */
enum
{
IO_SPEED_50M = 0b00,
IO_SPEED_100M = 0b01,
IO_SPEED_100Ma = 0b10,
IO_SPEED_200M = 0b11,
}SPEED:2; /*! IO速度设置*/
unsigned res2:3; /*! 预留位 */
unsigned ODE:1; /*! 开路输出使能*/
unsigned PKE:1; /*! 启用上下拉/状态保持寄存器(PUE)*/
enum
{
PUE_USED_PUS = 0b0, //@启用上下拉
PUE_USED_STATE_HOLD = 0b1, //@启动状态保持
}PUE:1; /*! 上下拉/状态保持寄存器*/
enum
{
PUS_PULL_DOWN_100K = 0b00, //@100K下拉
PUS_PULL_UP_47K = 0b01, //@47K上拉
PUS_PULL_UP_100K = 0b10, //@100K上拉
PUS_PULL_UP_22K = 0b11, //@22K上拉
}PUS:2; /*! 上下拉配置*/
unsigned HYS:1; /*! 迟滞比较器使能*/
unsigned res3:15; /*! 预留位*/
};
unsigned int u32;
}imx_gpio_config;
#pragma pack()
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#ifdef __cplusplus
}
#endif
#endif

这里就把寄存器用宏定义封装起来,然后使用赋值即可。

为了配置简单,这里单独将gpio的配置寄存器使用位域实现

使用位域时:

  • gcc编译器将首个元素到最后一个元素按照从低到高的字节顺序进行映射。

==但注意的是,位域在C语言标准中并没有被严格定义,因此不同编译器的编译结果是可能不同的。==

四、Makefile编写#

在裸机点灯的 Makefile 文件上进行更改:

# 创建一个变量 link_objs,保存用于生成led.bin所需的.o文件字符串
# imx6u_start.o必须在main.o前面,因为imx6u_start.o先运行
link_objs := imx6u_start.o main.o
# 定义默认目标,即生成led.bin文件
led.bin:$(link_objs)
arm-linux-gnueabihf-ld -Ttext 0X87800000 -o c_led.elf $^
arm-linux-gnueabihf-objcopy -O binary -S c_led.elf $@
arm-linux-gnueabihf-objdump -D -m arm c_led.elf > c_led.dis
%.o:%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o c_led.bin c_led.elf c_led.dis

回顾一下gcc的编译过程:

  1. 编译:

    将代码编译成机器码,如:arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

    arm-linux-gnueabihf-gcc : 这是针对ARM架构的交叉编译器,专门用于编译ARM平台上的代码。 -Wall : 启用所有常见的编译警告,帮助捕获潜在问题。 -nostdlib : 不链接标准库。这通常用于系统级编程或嵌入式开发,特别是在启动代码中。 -c : 仅编译,不进行链接。生成目标文件.o。 -O2 : 启用优化选项,进行较高程度的优化以提高代码性能。 -o start.o : 指定输出文件名为start.o。 $@ : 替换为目标文件字符串 $< : 替换为依赖文件字符串 如编译 imx6u_start.s 时,这段命令相当于:

    imx6u_start.o:imx6u_start.s
    # 通过imx6u_start.s 生成imx6u_start.o文件
    arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o imx6u_start.o imx6u_start.s
  2. 链接:

    .o 文件连接成 .elf 文件,此过程确定了代码的运行地址,如:

    led.bin:$(link_objs)
    arm-linux-gnueabihf-ld -Ttext 0X87800000 -o c_led.elf $^

    效果等同于:

    led.bin:imx6u_start.o main.o
    arm-linux-gnueabihf-ld -Ttext 0X87800000 -o c_led.elf imx6u_start.o main.o

    指定了代码的运行地址是 0x87800000,同时 -o 指定输出文件 c_led.elf

  3. 转码:

    led.bin:$(link_objs)
    arm-linux-gnueabihf-objcopy -O binary -S c_led.elf $@

    效果等同于:

    led.bin:imx6u_start.o main.o
    arm-linux-gnueabihf-objcopy -O binary -S c_led.elf led.bin

    这里将c_led.elf转码生成二进制文件 led.bin

    arm-linux-gnueabihf-objcopy : 一个用于对象文件格式转换的工具。 -O binary : 指定输出文件的格式为纯二进制格式。二进制文件不包含任何符号或调试信息,只包含程序的机器代码和数据。 -S : 去除所有不必要的符号信息。这在生成最终的二进制文件时是常见的做法,因为这样可以减小文件的大小。 ledc.elf : 输入文件,通常是链接后的ELF格式可执行文件。 ledc.bin : 输出文件,转换后的纯二进制格式文件。

  4. 反汇编:

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

    将elf文件生成汇编代码,便于查阅和调试

五、编译及下载验证#

使用make编译

然后在windows上使用写的烧录工具:

插入SD卡,拨码至从SD启动,LED闪烁:

六、使用链接脚本#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

评论区

Profile Image of the Author
lim
Hello, I'm lim.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
站点统计
文章
11
分类
4
标签
5
总字数
14,426
运行时长
0
最后活动
0 天前