.
相关bug可以查看 《bug记录/imx6ull》
从 USB 下载程序
按照正点原子步骤编译链接,更改为二进制文件,
1 2 3 4
| led.bin : 1_leds.s arm-linux-gnueabihf-gcc -g -c 1_leds.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
|
要想在imx上运行,还需要在 bin 文件前加上一个头部,用于初始化 ddr,这个正点原子已经给出 imxdownload。(它程序中配置的是将imx文件拷贝到 SD 卡,需要将这些文件注释掉,保留创建 imx 的文件).
1 2
| gcc -o imxdownload imxdownload.c sudo ./imxdownload led.bin /dev/sdd
|
最后获取 nxp 提供的 uuu 工具(不是用aptget安装的),
1
| sudo snap install universal-update-utility
|
然后将 imx 通过 USB 连接到电脑,注意要将 USB 接到虚拟机中,然后可能还需要执行下面这个命令才能连上 usb 。
1
| sudo apt-get install libusb-1.0.0-dev libzip-dev libbz2-dev
|
在 makefile 中添加下面这一行:
至此,编译运行后,将板子拨码开关拨到 USB 启动,按下 reset , 控制台输入 make run 即可执行, 有个缺点是,只有 手动 下载一次,才能运行一次,按板子上的 reset 好像没用。完整 makefile 如下:
1 2 3 4 5 6 7 8 9 10 11
| led.bin : 1_leds.s arm-linux-gnueabihf-gcc -g -c 1_leds.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 gcc -o imxdownload imxdownload.c sudo ./imxdownload led.bin /dev/sdd run: sudo uuu load.imx clean: rm -rf *.o led.bin led.elf led.dis
|
通用makefile
首先重命名一些变量
1 2 3 4 5 6 7
| CROSS_COMPILE ?= arm-linux-gnueabihf- TARGET ?= ledc
CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump
|
主要就是找头文件路径,源文件路径。
头文件路径交给 gcc 使用,源文件路径赋值给 vpath,也供 gcc 使用。
先找到了各自的文件夹。
1 2 3 4 5 6 7 8 9 10
| INCUDIRS := imx6ull \ bsp/clk \ bsp/delay \ bsp/led \ project
SRCDIRS := project \ bsp/clk \ bsp/delay \ bsp/led
|
然后需要从文件夹中获取头文件和源文件。
1 2 3 4 5 6 7
| INCLUDE := $(patsubst %, -I %, $(INCUDIRS)) #引用头文件时,需要加上 -I
SFILES := $(foreach dir , $(SRCDIRS), $(wildcard $(dir)/*.s)) #通配符在函数中会失效,需要加上 wildcard CFILES := $(foreach dir , $(SRCDIRS), $(wildcard $(dir)/*.c)) #干嘛要加上路径呐,后面也不用路径
SFILESNDIR := $(notdir $(SFILES))# 去除路径,只取文件名, CFILESNDIR := $(notdir $(CFILES))
|
输入文件(头文件,源文件)找完了,需要找输出文件:
1 2 3 4
| SOBJS := $(patsubst %.s, obj/%.o, $(SFILESNDIR)) COBJS := $(patsubst %.c, obj/%.o, $(CFILESNDIR))
OBJS := $(SOBJS)$(COBJS)
|
然后进行编译:
1 2 3 4 5 6 7
| VPATH := $(SRCDIRS) #指定可以在那些文件夹中寻找依赖文件 $(SOBJS) : obj/%.o : %.s#不能这么用 $(SOBJS) : $(SFILES),和之前的匹配方式不一样(%o:%c),之前的会为每个.o 调用一次 gcc # 如果向上面那行使用的话,会把所有同时作为输入编译,结果放到SOBJS的每个目标文件中($<值其实是一个一个取出来的) $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $< $(COBJS) : obj/%.o : %.c # 和正点的式子有区别 $(COBJS) : obj/%.o : %.c $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
|
为什么需要使用 $(SOBJS) 和 $(COBJS) ,不加时编译链接也不会出错。感觉像是做了一个限定,表示从 $(COBJS) 中找,
以 %c, %.c开头时(即以模式定义时),应该会自动为每个.c 编译一次,$< 会依次取出文件(和 $^不一样)
以$(SOBJS) : $(SFILES)开头时,会一次性编译 SFILES第一个文件(不会往后取文件),结果放到每个 SOBJS文件中。产生指令如下,且会报错:
生成最终的目标文件:
1 2 3 4 5
| $(TARGET).bin : $(OBJS) $(LD) -Timx6u.lds -o $(TARGET).elf $^ $(OBJCOPY) -O binary -S $(TARGET).elf $@ $(objdump) -D -m arm $(TARGET).elf > $(TARGET).dis sudo ./imxdownload $@ /dev
|
整个文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| CROSS_COMPILE ?= arm-linux-gnueabihf- TARGET ?= ledc
CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump
INCUDIRS := imx6ull \ bsp/clk \ bsp/delay \ bsp/led \ project
SRCDIRS := project \ bsp/clk \ bsp/delay \ bsp/led
INCLUDE := $(patsubst %, -I %, $(INCUDIRS))
SFILES := $(foreach dir , $(SRCDIRS), $(wildcard $(dir)/*.s)) CFILES := $(foreach dir , $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILESNDIR := $(notdir $(SFILES)) CFILESNDIR := $(notdir $(CFILES))
SOBJS := $(patsubst %.s, obj/%.o, $(SFILESNDIR)) COBJS := $(patsubst %.c, obj/%.o, $(CFILESNDIR))
OBJS := $(SOBJS)$(COBJS)
VPATH := $(SRCDIRS) .PHONY : clean
$(TARGET).bin : $(OBJS) $(LD) -Timx6u.lds -o $(TARGET).elf $^ $(OBJCOPY) -O binary -S $(TARGET).elf $@ $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis sudo ./imxdownload $@ /dev
$(SOBJS) : obj/%.o : %.s $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $< $(COBJS) : obj/%.o : %.c $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
run: sudo uuu load.imx clean : rm $(TARGET).bin $(TARGET).dis $(TARGET).elf $(OBJS)
print:
|
三极管区分
NPN 三极管 输入高电平导通,反之关闭
PNP 三极管 输入低电平导通,反之关闭
正点原子的一个问题
07key实验中,程序没法运行,是因为 清除 .bss时,.bss 段没用四字节对齐,导致误清了后面的数据,将 .bss 四字节对齐即可:
1 2 3 4 5 6 7 8 9 10 11 12 13
| SECTIONS{ . = 0X87800000; .text : { obj/start.o *(.text) } .rodata ALIGN(4) : { *(.rodata) } .data ALIGN(4) : { *(.data) } . = ALIGN(4); <<<<<<<<<加上这里后就可以实现四字节对齐 __bss_start = .; .bss ALIGN(4) : { *(.bss) *(.COMMON) } __bss_end = .; }
|
tips:
左移右移 是两个 << >>
或运算与运算是一个 & |
时钟配置
总共需要配置两个地方,一个是 pll1,一个时 cacrr寄存器。(后面的那个/2 没用,正点原子说的)
接下来详细看 pll1 部分,有两个复用选择器。配置前,要先切换到step_clk上,然后配置 pll1,最后切换回 main_clk上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
void imx6u_clkinit(void){ if(((CCM->CCSR >> 2) & 0X1) == 0){ CCM->CCSR &= ~(1 << 8); CCM->CCSR |= (1 << 2); } CCM_ANALOG->PLL_ARM &= ~(0X7F); CCM_ANALOG->PLL_ARM |= 88; CCM_ANALOG->PLL_ARM |= (1 << 13); CCM->CACRR = 0x01; CCM->CCSR &= ~(1 << 2); }
|
正点原子犯的一个错
如下图配置时钟时:
1 2 3 4
|
CCM->CBCMR &= ~(0X03 << 18); CCM->CBCMR |= (0X01 << 18);
|
这样配置不行,因为第一次配时看似是清零,实际上将寄存器相应区域配为了0,在处理器看来这是有意义的值,这个值可能导致系统(时钟)停止运行。为此,需要一个reg中介,来一次写入。
1 2 3 4 5 6
|
reg = CCM->CBCMR; reg &= ~(0X03 << 18); reg |= (0X01 << 18); CCM->CBCMR = reg;
|
ISB DSB 使用
详见https://zhuanlan.zhihu.com/p/601037646
在大部分场景下,我们不用特意关注内存屏障的,特别是在单处理器系统里,虽然CPU内部支持乱序执行以及预测式的执行,但是总体来说,CPU会保证最终执行结果符合程序员的要求。在多核并发编程的场景下,程序员需要考虑是不是应该用内存屏障指令。下面是一些需要考虑使用内存屏障指令的典型场景。
在多个不同CPU内核之间共享数据。在弱一致性内存模型下,某个CPU乱的内存访问次序可能会产生竞争访问。
执行和外设相关的操作,例如DMA操作。启动DMA操作的流程通常是这样的:第一步,把数据写入DMA缓冲区里;第二步,设置DMA相关寄存器来启动DMA。如果这中间没有内存屏障指令,第二步的相关操作有可能在第一步前面执行,这样DMA就传输了错误的数据。
修改内存管理的策略,例如上下文切换、请求缺页以及修改页表等。
修改存储指令的内存区域,例如自修改代码的场景。
总之,我们使用内存屏障指令的目的是想让CPU按照程序代码逻辑来执行,而不是被CPU乱序执行和预测执行打乱了代码的执行次序。
原子课程中,使用到了数据同步,指令同步
1 2 3 4 5 6 7
| /*设置中断向量偏移 */ LDR R0, =0X87800000 dsb //数据同步 isb //指令同比 MCR p15, 0, R0, c12, c0, 0 dsb //数据同步 isb //指令同比
|
疑问
- 为什么中断保存是保存 r0-r3,这些寄存器,这些不是自动保存的吗
1 2 3
| IRQ_Handler: push {lr} /* 保存lr地址 */ push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */
|
U-BOOT 部分感想
使用u-boot时,u-boot被拷贝到dram 中 0x87800000 开始运行,堆栈指针指向内部 ram,后面为了给linux腾出空间,u-boot 代码要被拷贝到0x9fff0000(大致)开始运行,堆栈指针也指向了dram
一个疑问,如下,正点说应该引用stack 中的arch_reserve_stacks函数,但实际不可能呀。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int arch_reserve_stacks(void) // 和stack的函数重名了,这解释的通吗 { return 0; }
static int reserve_stacks(void) { /* make stack pointer 16-byte aligned */ gd->start_addr_sp -= 16; gd->start_addr_sp &= ~0xf;
/* * let the architecture-specific code tailor gd->start_addr_sp and * gd->irq_sp */ return arch_reserve_stacks(); }
|