基于Linux用C语言来代替部分汇编语言实现LED驱动

基于Linux用C语言来代替部分汇编语言实现LED驱动,第1张

小白参考了正点原子和行稳方能走远两位博主的资料

文章名字:ARM(IMX6U)裸机之I.MX6ULL启动头文件详解(内部BOOT ROM、IVT + Boot data + DCD + led.bin)

作者:行稳方能走远

原文地址:https://blog.csdn.net/zhuguanlin121/article/details/118915190

近几日学习来总结一下学习内容(强烈建议在vim编辑器下面进行程序的编写):

一、硬件启动

要搭建软件环境前提就得先把硬件环境搭建好。

        第一步:要先选择是内部启动还是外部启动。

我现在只会SD卡启动这一方式,但是我觉得只要会如何选择启动设备、启动头文件,这些只是小问题。设置MODE1和MODE0是从内部BOOT启动的,也就是MODE1=1,MODE0=0。这个不用理解暂时,I.MX6ULL数据手册有资料可以查。选择了内部BOOT启动后,我们可以选择的启动设备就很多了,有:OR flash,oneNAND、NAND Flash、QSPI flash、SD/EMMC、EEPROM等等,我们最常用的就是NAND、SD、EMMC,QSPI(六线SPI)因为价格昂贵,使用率并不高。

        第二步:启动头文件

启动头文件:

1.让BOOT ROM使能Cache和MMU(cache是高速缓存,mmu是地址管理单元,将物理地址映射为虚拟地址),然后使能L1Cache L2cache MMU,目的就是为了加速启动。L1Cache打开时会下载镜像,然后L1Cache L2cache MMU全部打开来验证镜像。这个我也不太懂,我猜测可能是验证物理地址。+

2.将BOOT DATA 和 IVT(image vector table)数据块准备好! IVT 里面包含了一系列的地址信息,这些地址信息在ROM中按照固定的地址存放着。而Boot data,就是启动数据,包含了镜像要拷贝到哪个地址,拷贝的大小是多少等等。

3.将DCD(device configuration data)数据配置好,DCD是用来配置I.MX6ULL内部寄存器的。它主要是初始化外设,还有检查数据命令、NOP命令、解锁命令。这些其实也都属于DCD。

4.数据配置好之后,其实我也不知道该干什么。我只是了解到我们平时烧录给SD卡的数据是包括:IVT、BOOT DAT、DCD和.bin文件。他们加起来总共4KB大小,其中头部占3KB(IVT占32B、BOOT DATA占12B、DCD最大占1768B)。

5.正点原子的.imx文件load.imx 最前面的就是 IVT 和 Boot Data, IVT 包含了镜像程序的入口点、指向 DCD 的指针和一些用作其它用途的指针。内部 Boot ROM 要求 IVT 应该放到指定的位置,不同的启动设备位置不同。而 IVT 在整个 load.imx 的最前面,其实就相当于要求 load.imx 在烧写的时候应该烧写到存储设备的指定位置去。整个位置都是相对于存储设备的起始地址的偏移,偏移大概是4KB。

        第三步:C语言运行环境的搭建

1.设置处理器模式:将I.MX6ULL设置为SVC模式。设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0X13。

2.设置sp指针:Sp可以指向内部RAM,也可以指向DDR,我们将其指向DDR(DDR是外部ram),因为对于ARM-A7架构来说,处理器栈增长方式是向下增长,所以要设置sp指向0x80200000。(为什么要设置sp指针指向0x80200000我也没懂,以后知识层面扩大了会重新过来了解)

3.搭建完毕,跳转到C语言的主函数
 

.global _start  @定义一个全局标号

_start:
    
    mrs r0, cpsr          @将CPSR的数据读入r0
    bic r0, r0, #0x1f     @r0=r0&0x1f
    orr r0, r0, #0x13     @r0=r0|0x13
    msr cpsr, r0          @将r0的数据写入CPSR

    
    ldr sp, =0x80200000   @设置栈指针
    b main                @跳转到main函数
        第四步:软件编写

1.先写出main.c的头文件

        

#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

2.编写源文件

#include "main.h"
/* 使能外设时钟 */
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 */
void led_init(void)
{
    SW_MUX_GPIO1_IO03 = 0x5;    //复用为GPIO1_IO03
    SW_PAD_GPIO1_IO03 = 0x10B0; //设置GPIO1_IO03电气属性

    /* GPIO初始化 */
    GPIO1_GDIR = 0x8;           //设置为输出
    GPIO1_DR = 0x0;             //打开LED灯

}
int main(void)
{
    clk_enable();
    //初始化LED
    led_init();

    while(1);
    return 0;
}

代码编写完毕后,按原步骤将这些文件烧录给SD卡,然后将SD卡插进I.MX6ULL,将I.MX6ULL拨码开关选择为SD卡启动就ok了!

arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis

        第四步: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 

download:
	./imxdownload ledc.bin /dev/sdb

这里尝试使用了自动化变量,发现还是理解不了。下次还是一步一步来吧。

总结:

        学裸机开发,因为我有很多的STM32开发经验,所以这方面学起来事半功倍,而且能在这里能有32熟悉感,很亲切。很多地方还有不懂,但我相信经过我的深入学习,这些问题都会被我解决的

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/707063.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存