ARM裸机
汇编LED驱动实验
汇编LED原理分析
- ALPHA开发板LED灯硬件原理分析
stm32初始化流程:- 使能GPIO时钟
- 设置IO复用,将其复用为GPIO
- 配置GPIO 的电器属性
- 使用GPIO输出高低电频
I.MAX6ULL IO初始化流程
- 使能时钟 CCGR0-CCGRU这7个寄存器控制着6ULL所有时钟的使能,为了简单,设置CCGR0-CCGR6这7个寄存器全部设置为0XFFFFFFF,相当于使能所有外设时钟
- IO复用,将寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的比特设置为0101=5,这样GPIO1_IO03就复用为GPIO 。
- 寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 是设置GPIO1_IO03的电气属性,包括压摆率。速度,驱动能力,开漏,上下拉
- 配置GPIO功能,设置输入输出,设置GPIO1_GDIR寄存器bit3为1,也就是设置为输出模式,设置GPIO1_DR寄存器的bits3,为1表示输出高电平,为0表示输出低电平
汇编简介
GNU汇编语法适用于所有架构,并不是ARM架构独享的,GNU汇编由一系列语句组成,每行一条语句,每条语句有三个可选部分,如下:
1 | label: instruction @comment |
- label既是标号,表示地址位置,任何以:结尾的标识符都会被认识是一个标号
- instruction即指令
- @符号表示后面是注释内容
==ARM中的指令,伪指令,伪操作不能大小写混用==
汇编由一条一条指令构成,指令就涉及到汇编指令
处理器内部的数据传输指令
指令 | 目的 | 源 | 描述 |
---|---|---|---|
MOV | R0 | R1 | 将 R1 里面的数据复制到 R0 中。 |
MRS | R0 | CPSR | 将特殊寄存器 CPSR 里面的数据复制到 R0 中。 |
MSR | CPSR | R1 | 将 R1 里面的数据复制到特殊寄存器 CPSR 里中 |
存储器访问指令
指令 | 描述 |
---|---|
LDR Rd, [Rn , #offset] | 从存储器 Rn+offset 的位置读取数据存放到 Rd 中。 |
STR Rd, [Rn, #offset] | 将 Rd 中的数据写入到存储器中的 Rn+offset 位置。 |
1 | LDR: |
1 | LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004 |
例:
1 | 假设a的地址为0x20,b的地址为0x30:对于c语言: |
我们在使用汇编编写驱动的时候最常用的就是LDR,STR指令
编写驱动
1 | .global_start @全局标号 |
==编译程序==
- 将.s文件编译为.o arm-linux-gnueabihf-gcc -g -c led.s -o led.o
- 将所有的.o文件链接为elf文件格式 arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
链接就是将所有.o文件链接到一起,并且链接到指定的地方。本实验链接的时候要指定连接起始地址,链接起始地址就是代码运行的起始地址。
对于6ULL来说,链接的起始地址应该指向RAM地址,RAM分为内部和外部RAM,也就是DDR。6ULL内部RAM地址范围(0X900000~0X91FFFF) 。也可以放到外部DDR中,对于I.MX6U-ALPHA开发板,512MB字节DDR版本的核心板,DDR范围就是0X80000000-oX9FFFFFFF.本系列视频,裸机代码的链接起始地址为0X87800000.要使用DDR,必须要初始化DDR,bin文件不能直接运行,需要添加一个头部,这个头部信息包含DDR的初始化参数。I.MX系列SOC内部boot rom 会从SD卡,EMMC等外置存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到指定的地方。
bin的运行地址一定要和链接起始地址一致,位置无关代码除外 - 将elf文件转为bin文件 arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
- 将elf文件转为汇编,反汇编 arm-linux-gnueabihf-objdump -D led.elf > led.dis
==烧写代码==
使用正点原子提供的imxdownload软件,imxdownload使用方法:
- 确定要烧写的sd卡文件,我的为/dev/sdb
- 给予imxdownload可执行权限:chmod 777 imxdownload
- 烧写:./imxdownload led.bin /dev/sdb
imxdownload会向led.bin添加一个头部,生成新的load.imx文件,就是最终烧写到sd卡中的
==makefile编写==
1 | led.bin : led.s |
IMX 启动方式
启动方式选择
LED灯实验,是从SD卡读取bin文件,说明6ULL支持从SD卡启动。6ULL支持多种启动方式
6ULL是怎么支持从多种外置flash启动程序的:
- 启动方式的选择
- BOOT_MODE0和BOOT_MODE1,这两个是IO来控制的,选择从USB启动还是内部BOOT启动,如果要烧写系统到开发板中可以选择从USB下载,下载到sd卡,EMMC,NADN等外置存储中。烧写完成后从内部BOOT启动,然后从相应的外置存储中启动
- 选择启动设备:前提是,设置MODE1和MODE0是从内部BOOT启动,也就是MODE1和MODE0=0。
支持哪些设备:NOR flash oneFLASH, QSPI flash,SD/EMMC,EEPROM,我们最常用的就是NAND,SD,EMMC,QSPI Flash - 如何选择启动设备:通过BOOT_CFG选择,有BOOT_CFG1,2,4,每个8位,BOOT_CFG是由LCD_DATE0-23设置的,在ALPHA开发板上,大部分默认都接地,BOOT_CFG4的8根线全部接地,BOOT_CFG2全部接地,除了BOOT_CFG2[3],此位用来选择SD卡启动接口,BOOT_CFG1,0,1,2都是顶死的,34567是可以设置的
- 正点原子开发板BOOT电路设置,核心板LCD_DATEA0-23基本默认47k下拉
启动头文件
- BOOT ROM做的事:
- 设置内核时钟为396MHz
- 使能MMU和caches,使能L1cache,L2cacheMMU,目的为加速启动
- 从BOOT_CFG设置的外置存储中,读取image,然后做相应的处理
- IVT Boot Data数据
- bin文件前面要添加头部,可以得到,我们烧写到SD卡中的load.imx文件在SD卡中的起始地址是0X400,也就是1024
- 头部大小为3kb加上偏移的1kb,一共是4kb,因此,在sd卡中,bin文件起始地址为4kb,也就是4096
- IVT大小为32字节
- DCD数据
- DCD数据就是配置6ULL内部寄存器,首先将CCGR0-6全部写为0xFFFFFFFF,表示打开所有的外设时钟,
- 然后就是DDR初始化参数
- 其他数据
- 数据检查命令等也是属于DCD
LED C语言版本
C语言运行环境构建
设置处理器模式:设置6ULL处于SVC模式(特权模式)下,设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0x13,读写状态寄存器需要用到MRS和MSR指令
- MRS将通用寄存器数据读出到通用寄存器里面
- MSR指令将通用寄存器的值写入到CPSR寄存器里面去
设置sp指针:sp可以指向内部RAM,也可以指向DDR,我们将其指向DDR。sp设置到哪里?512MB的范围是0x80000000-0x9fffffff。栈大小,0x200000=2MB.处理器栈增长方式,对于A7而言,是向下增长的。设置sp指向0x80200000
跳转到C语言:使用b指令,跳转到c语言函数,比如main函数
1
2
3
4
5
6
7
8
9
10
11
12
13.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 */
/*设置sp指针 */
ldr sp,=0x80200000
b main /*跳转到哦c语言main函数 */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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//定义要使用的寄存器
/*
13 * IOMUX 相关寄存器地址
14 */
/*
19 * GPIO1 相关寄存器地址
20 */
/*
4 * @description : 使能 I.MX6U 所有外设时钟
5 * @param : 无
6 * @return : 无
7 */
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;
}
/*
20 * @description : 初始化 LED 对应的 GPIO
21 * @param : 无
22 * @return : 无
23 */
void led_init(void)
{
/* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */
SW_MUX_GPIO1_IO03 = 0x5;
/* 2、配置 GPIO1_IO03 的 IO 属性
30 *bit 16:0 HYS 关闭
31 *bit [15:14]: 00 默认下拉
32 *bit [13]: 0 kepper 功能
33 *bit [12]: 1 pull/keeper 使能
34 *bit [11]: 0 关闭开路输出
35 *bit [7:6]: 10 速度 100Mhz
36 *bit [5:3]: 110 R0/6 驱动能力
37 *bit [0]: 0 低转换率
38 */
SW_PAD_GPIO1_IO03 = 0X10B0;
/* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */
GPIO1_GDIR = 0X0000008;
/* 4、设置 GPIO1_IO03 输出低电平,打开 LED0 */
GPIO1_DR = 0X0;
}
/*
49 * @description : 打开 LED 灯
50 * @param : 无
51 * @return : 无
52 */
void led_on(void)
{
/*
56 * 将 GPIO1_DR 的 bit3 清零
57 */
GPIO1_DR &= ~(1 << 3);
}
/*
62 * @description : 关闭 LED 灯
63 * @param : 无
64 * @return : 无
65 */
void led_off(void)
{
/*
69 * 将 GPIO1_DR 的 bit3 置 1
70 */
GPIO1_DR |= (1 << 3);
}
/*
75 * @description : 短时间延时函数
76 * @param - n : 要延时循环次数(空操作循环次数,模式延时)
77 * @return : 无
78 */
void delay_short(volatile unsigned int n)
{
while (n--)
{
}
}
void delay(volatile unsigned int n)
{
while (n--)
{
delay_short(0x7ff);
}
}
/*
98 * @description : main 函数
99 * @param : 无
100 * @return : 无
101 */
int main(void)
{
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化 led */
while (1) /* 死循环 */
{
led_off(); /* 关闭 LED */
delay(500); /* 延时大约 500ms */
led_on(); /* 打开 LED */
delay(500); /* 延时大约 500ms */
}
return 0;
}
BSP工程管理实验
BSP工程管理的目的就是为了模块化整理代码,同一个属性的文件存放在同一个目录里面
- 新建所需的文件夹,将同一属性的文件放到相应的文件夹中
- 修改clk led delay驱动,创建对应的驱动文件,然后放到对应的目录中
- 根据编写的新驱动文件,修改main.c文件内容
设置vscode文件路径,先创建.vscode目录,然后打开c/c++配置器,会在vscode目录下生成一个
==Makefile编写==
Makefile指定头文件路径,需要-I,我们编译源码的时候,需要指定头文件路径,比如/bsp/clk/bsp_clk.h变为-I /bsp/clk/bsp_clk.h
1 | CROSS_COMPILE ?= arm-linux-gnueabihf- |
notdir用于去掉文件的绝对路径,只保留文件名。
wildcard函数是针对通配符在函数或变量定义中展开无效情况下使用的,用于获取匹配该模式下的所有文件列表,<PATTERN…>参数若有多个则用空格分隔。若没有找到指定的匹配模式则返回为空。
- #返回make工作下的所有.cpp以及.c文件
$(wildcard *.cpp *.c)
- #返回make工作下的所有.cpp以及.c文件
patsubst函数返回被替换过后的字符串。patsubst函数判断
中字符串(若多个字符串以空格分隔)是否匹配 模式,若匹配则使用 替换 。 可以包括通配符%表示任意长度的字串。如果 中也包含%,则 中的这个%将是 中的那个%所代表的字符串。若字符串中含有%则可以用反斜杠\来转义,即%来表示真实含义的%字符。 示例:
#把字符串“x.c.c bar.c”符合模式%.c的单词替换成%.o,返回“x.c.o bar.o”。
$(patsubst %.c,%.o,x.c.c bar.c)$(foreach ,
- ,
) 这个函数的意思是,把参数
- ;中的单词逐一取出放到参数;所指定的变量中,然后再执行< text>;所包含的表达式。每一次
;会返回一个字符串,循环过程中, ;的所返回的每个字符串会以空格分隔,最后当整个循环结束时, ;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 所以,;最好是一个变量名,
- ;可以是一个表达式,而
;中一般会使用;这个参数来依次枚举 - ;中的单词。举个例子:
names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
蜂鸣器实验
- 硬件原理图分析:BEEP控制IO为 SNVS_TAMPER1,当输出低电平的时候蜂鸣器响,输出高电平的时候蜂鸣器不响
- 实验程序编写:
- 初始化SNVS_TAMPER1这个IO复用为GPIO5_IO01
- 设置SNVS_TAMPER1这个IO的电器属性
- 初始化GPIO
- 控制GPIO输出高低电平
按键输入实验
- 硬件原理图分析:按键key0链接到了UART1_CTS引脚上,默认情况下,KEY0为高,当按下KEY0以后UART1_CTS为低
- 实验程序编写:
- 设置UART1_CTS复用为GPIO_IO18
- 设置电器属性UART1_CTS的电器属性
- 配置GPIO1_IO08为输入模式
- 读取按键值,也就是GPIO1_IO08的高低电平mk
主频和时钟配置实验
- I.MX6U 系统时钟分析
- 为了方便生成时钟,6从24MHZ晶振生出来了7路PLL,这7路PLL中有的又生出来了PFD
- PLL1:ARM PLL供给ARM内核
- PLL2:system PLL ,538MHZ,528_PLL此路PLL分出了4路PFD分别为 PLL2_PFD0-PFD3
- PLL3: USB1 PLL, 480MHZ,480_PLL,此路PLL分出了4路PFD,分别为PLL3_PFD0-PFD3
- PLL4:Audio PLL, 主供给音频使用
- PLL5:Video PLL,主要供视频外设,比如RGB LCD接口,和图像处理有关的
- PLL6: ENET PLL , 主供网络外设
- PLL7: USB2 PLL,480MHZ,无PFD
- 为了方便生成时钟,6从24MHZ晶振生出来了7路PLL,这7路PLL中有的又生出来了PFD
- 要初始化的PLL和PFD
- PLL1
- PLL2,PLL2_PFD0-PFD3
- PLL3,PLL3_PFD0-PFD3
- 一般按照时钟树里面的值进行设置
- I.MX6U系统配置
- 系统主频配置:
- 要设置ARM内核主频为528MHZ,设置CACRR寄存器的ARM_PODF位为2分频,然后设置PLL1=1056MHZ即可。CACRR的bit3-0为ARM_PODF位,可设置0-7分别对应1-8分频。应该设置CACRR寄存器的ARM_POEF=1
- 设置PLL1=1056MHZ,PLL1=pll1_sw_clk,pll1_sw_clk通过有两路可以选择,分别为pll1_main_clk和step_clk通过CCSR寄存器的pll1_sw_clk_sel位(bit2)来选择,为0的时候选择pll1_mian_clk为1的时候选择step_clk
- 在修改PLL1的时候,也就是设置系统时钟的时候,需要给6ULL一个临时的时钟,step_clk,在修改PLL1的时候,需要将pll1_sw_clk切换到step_clk上
- 设置step_clk:step_clk也有两路来源,由CCSR的step_sel位(bit8)来设置,为0的时候设置step_clk为osc=24mhz,为1的时候不重要
- 时钟切换成功以后就可以修改PLL1的值
- 通过CCM_ANALOG_PLL_ARM寄存器的DIV_SELECT位(bit0-6)来设置PLL1的频率,公式为output=fref * DIV_SEL/2 1056=24 * DIV_SEL/2=>DIV_SEL=88,设置CCM_ANALOG_PLL_ARM寄存器的DIV_SELECT位=88即可。PLL1=1056MHZ,还要设置CCM_ANALOG_PLL_ARM寄存器的ENABLE位(bit13)为1,也就是使能输出
- 在切换慧PLL1之前,设置CACRR寄存器的ARM_PODF位为1!!切记
- 各个PLL时钟配置:PLL2固定为528MHZ,PLL3固定为480MHZ
- 初始化PLL2_PFD0-PFD3,寄存器CCM_ANALOG_PFD_528用于设置4路PFD的时钟,比如PFD0 = 528*18/PFD0_FRAC.设置PFD0_FRAC位即可,比如PLL2_PFD0=352M=528 * 18/PFD0_FRAC,因此PFD0_FRAC=27
- 初始化PLL3_PFD0-PFD3
- 其他外设时钟源配置:AHB_CLK_ROOT,PERCLK_CLK_ROOT以及IPG_CLK_ROOT ,因为PERCLK_CLK_ROOT以及IPG_CLK_ROOT 要用到AHB_CLK_ROOT,所以要初始化AHB_CLK_ROOT:
- AHB_CLK_ROOT的初始化:AHB_CLK_ROOT = 132Mhz,设置CBCMR寄存器寄存器的PRE_PERIPH_CLK_SEL,设置CBCDR寄存器的PERIPH_CLK_SELECT位为0 ,设置CBCDE寄存器的AHB_PODF位为2.也就是3分频,因此396/3=132MHZ
- PERCLK_CLK_ROOT = 66MHz=IPG_CLK_ROOT:
IPG_CLK_ROOT:设置CBCDR寄存器IPG_PODF=1,也就是二分频
PERCLK_CLK_ROOT:设置CSCMR1寄存器寄存器的PERCLK_CLK_SEL位为0,表示PERCLK的时钟源为ipg
- 系统主频配置:
GPIO中断实验
ARM芯片从0x00000000开始运行,执行指令。在程序开始的地方存放着中断向量表。中断向量表主要功能是描述了中断对应的中断服务函数。
对于STM32来说代码最开始的地址存放堆栈栈顶指针
- 中断向量偏移:一般ARM从0x00000000开始运行处理代码,如果代码一定要从0x80000000开始运行,那么需要告诉一下soc内核,也就是设置中断向量偏移,设置SCB的VTOR寄存器为新的中断向量表起始地址即可
- NVIC中断控制器:NVIC就是中断管理机构,使能和关闭指定的中断,设置中断优先级。
- 中断服务函数编写
Cortex-A7中断系统
- Cortex-A7中断向量表有8个中断,其中重点关注IRQ,Cortex-A的中断向量表,需要用户自己去定义。
- 中断向量偏移:裸机历程都是从0x87800000开始的,因此要设置中断向量偏移
- GIC中断控制器:同NVIC一样,GIC用于管理Cortex_A的中断,GIC提供了开关中断,设置中断优先级。
- IMX6U中断号:为了区分不同的中断,引入了中断号ID0-15是给SGI,ID16-31是给PPI,ID32-1019给SPI。也就是按键中断,串口中断。。。。6ULL支持128个中断
- 中断服务函数的编写:一个是IRQ中断服务函数的编写,另一个就是在IRQ中断服务里面去查找并运行的具体的外设中断服务函数
中断实验编写
编写按键中断例程,KEY0,使用UART1_CTS这个IO,本章就编写UART1_CTS的中断代码
修改start.s,添加中断向量表,编写复位中断服务函数和IRQ中断服务函数:
- 关闭I,D Cache和MMU
- 设置处理器进入9种工作模式下对应的sp指针,要使用中断,必须设置IRQ模式下的sp指针,索性直接设置所有模式下的SP指针。
- 清除bss段
- 跳到C函数,也就是main函数
CP15协处理器
MRC:将CP15协处理器中的寄存器数据读到ARM寄存器中
MRC就是读CP15寄存器,MCR就是写CP15寄存器,MCR指令格式如下:
1
2
3
4
5
6
7
8
9
10MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
/*cond:指令执行的条件码,如果忽略的话就表示无条件执行。
opc1:协处理器要执行的操作码。
Rt: ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
CRn: CP15 协处理器的目标寄存器。
CRm: 协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm 设置为 C0,否则结果不可预测。
opc2: 可选的协处理器特定操作码,当不需要的时候要设置为 0*/
假如我们要将 CP15 中 C0 寄存器的值读取到 R0 寄存器中,那么就可以使用如下命令:
MRC p15, 0, r0, c0, c0, 0现在要关闭I,DCache和MMU,打开Cortex-A7参考手册到105页找到SCTLR寄存器,也就是系统控制寄存器,此寄存器bit0用于打开和关闭MMU,bit1,对其控制位,bit2控制Data Cache的打开和关闭,bit11用于控制分支预测,bit12,用于控制 I Cache
1. 中断向量偏移设置:将新的中断向量表首地址写入到CP15协处理器
EPIT定时器实验
- EPIT简介
- EPIT时32位的向下计数器
- EPIT的时钟源可以选择,我们选择ipg_clk=66mhz
- 可以对时钟源进行分频,12位的分频器,0-4095,分别代表1-4096分频
- 开启定时器后,计数寄存器会每个时钟减一,如果和比较寄存器里面的值相等的话就会触发中断
EPIT有两种工作模式,一个时Set-and-forget模式,一个是free-running模式 - 6ULL有两个EPIT定时器
- EPIT_CR寄存器用于配置EPIT
- 实验原理
- EPIT_CRbit0为1,设置EPIT使能,bit1为1设置计数器的初始值为加载寄存器的值,bit2使能比较中断,bit3为1,设置定时器工作在Set-and-forget模式下,bit15-4设置分频值。bit24-25设置时钟源的选择,我们设置为1,那么EPIT的时钟源就为ipg_clock为66hz
- EPIT_SR寄存器只有bit0有效,表示寄存器状态,写1清零,当OCIF位为1的时候表示中断发生,为0的时候表示中断未发生,我们处理完定时器中断后,一定要清除中断标志位
- EPIT_LR寄存器设置计数器的加载值,计数器每次计时到0以后就会读取LR寄存器的值重新开始计时
- CMPR比较计数器,当计数器的值和CMPR相等以后就会产生比较中断
- 使用EPIT实现500ms周期的定时器,我们在EPIT中断服务函数中让LED灯亮灭
高精度延时
- GPT定时器简介 :以前的延时函数就采用空指令执行来实现,延时肯定不准确,当我们修改了6ULL主频以后,延时就不准了,
- GPT定时器是32位向上计数器,
- GPT定时器有捕获的功能,
- GPT定时器支持比较输出或中断功能,
- GPT定时器有一个12位分频器,
- GPT时钟源可以选择,这里我们使用ipg_clock
- GPT定时器有两种工作模式,restart和free-run
restart模式下,定时器计数值和比较寄存器OCR的值相等的话定时器就会重新从0开始计数,==只有比较通道1才有此功能==
free-run模式:所有三个输出比较通道都适用从0开始一直加到0xfffffff然后重新从0开始,周而复始 - GPT_CR寄存器
- bit0为使能位,为0关闭,为1使能
- bit1确定GPT定时器初始值,为0表示计数器默认为上次关闭的时候遗留的值,为1的话计数值为0.
- bit8-6为时钟源的选择,设置为1表示GPT时钟源为ipg_clk=66M
- bit9设置GPT定时器工作模式为0的时候工作在restart模式,为1的时候工作在free-run
- bit15软件复位
- GPT_PR寄存器的bit0-11为分频值
- GPT_SR
- bit5表示溢出发生
- bit4和bit3分别为输入通道2和1的捕获中断标志位,bit2-0,也就是OF3-OF1为比较中断
串口实验
- 6ULL串口UART原理
- 6ULL的UART_URXD寄存器保存着串口接收到的数据
- UART_UTXD为发送数据寄存器,如果需要通过串口发送数据,只需要将数据写入UART_UTXD寄存器中
- UART_UCR1-UCR4都是串口的控制寄存器
- UART_UCR1的
- bit0都是UART的使能位,
- 为1的时候使能UART,
- bit14为自动检测波特率使能位,为1的时候使能波特率自动检测
- UART_UCR2的
- bit0为软件复位位,为0的时候复位UART,
- bit1使能UART的接收,我们配置为1,
- bit2为发送使能,要设置为1,
- bit5设置数据位,0的话表示7位数据位,1的话表示8位数据位,
- bit6设置停止位,0的话表示1位停止位,1的话表示两位,
- bit7为奇偶校验位,为0的时候是偶校验,为1的时候是奇校验,
- bit8校验使能位,为0的时候关闭校验
- UART_UCR3的b==it2必须为1==
- UART_UCR1的
- UART_UFCR寄存器的bit7-9设置分频值UART的时钟源=PLL3/6=480/6=80Mhz。CSCDR1寄存器的UART_CLK_SEL位设置UART的时钟源,为0的时候UART时钟源为80Mhz,为1的时候时钟源为24M晶振,CSCDR1寄存器的UART_CLK_PODF位控制分频,一般设置为1分频,因此为80Mhz
- UART_UFCR,UART_UBIR,UART_UBMR,决定了串口波特率
- UART_USR2寄存器的bit0为1表示有数据可以读取,bit3为1的时候表示数据发送完成
DDR3实验
RAM,ROM
- RAM:随机存储器,可以随时进行读写操作,速度很快,掉电数据丢失
- ROM:只读存储器,例如手机8+256G,8是RAM,256是ROM
SRAM:一开始是芯片内部RAM,后面因为应用需求需要外扩RAM
DDR:本质上还是SDRAM,
- 时间参数
- 传输速率:DDR31600,DDR3 1866 ,DDR4 2400 , DDR4 3200MT/S。
- tRCD :行地址到列地址之间的延时
- CL :列地址选通潜伏期
- tRC : 两个ACTIVE命令之间的周期
- 时钟配置
- DDR使用的时钟源为MMDC_CLK_ROOT=PLL2_PFD2=396MHZ,在前面历程以及设置为396MHZ
- CMCMR寄存器的PRE_PERIPH2_CLK_SEL位来选择,也就是bit22:21设置pre_periph2时钟源,设置为01,也就是PLL2_PFD2作为pre_periph2时钟源
- CBCDR寄存器的PERIPH2_CLK_SEL位,也就是bit26设置为0 ,PLL2作为MMDC时钟源,396MHZ
- CBCDR寄存器的FABRIC_MMDC_PODF位,bit5:3设置为0,也就是1分频,最终MMDC_CLK_ROOT=396MHZ
- 时间参数
MMDC控制器
- 多模支持DDR3,DDR3L LPDDR2*16位
- MMDC最高支持DDR3频率是400MHZ ,800MT/S
- MMDC提供的DDR3链接信号,6ULL提供了专用的IO
校准值:
MMDC registers updated from calibration
Write leveling calibration
MMDC_MPWLDECTRL0 ch0 (0x021b080c) = 0x00000000
MMDC_MPWLDECTRL1 ch0 (0x021b0810) = 0x001E001E
Read DQS Gating calibration
MPDGCTRL0 PHY0 (0x021b083c) = 0x013C0134
MPDGCTRL1 PHY0 (0x021b0840) = 0x00000000
Read calibration
MPRDDLCTL PHY0 (0x021b0848) = 0x40403236
Write calibration
MPWRDLCTL PHY0 (0x021b0850) = 0x40403830
RGBLCD实验
- 像素点:每个像素点相当于一个小灯,不管是液晶屏,手机平板都是由一个个彩色小灯构成的,彩色点阵屏每个像素点有三个小灯,红色绿色和蓝色,也叫RGB,RGB也就是光的三原色。。通过调整RGB三种颜色的比例就可以实现刹紫千红的世界
- 分辨率:要想显示文字,图片,视频等等就需要很多个像素点,分辨率说的就是像素点的个数,1080p,720p,2k,4k,8k 1080=192 * 1080,表示一行有1920个像素点,一列有1080个像素点。显示器有尺寸,尺寸不变的情况下,分辨率越高,显示效果越精细,4k=3840 * 2160 ==正点原子的RGB屏幕有4.3寸 480 * 272 和1024 * 600
- 像素格式:如何将RGB三种颜色进行量化,每种颜色就需要8bit,RGB就需要888共24bit,可以描述出2^24^种颜色,16777216=1677万种颜色,现在流行10bit,HDR10,支持HDR效果的10bit面板,RGB10 10 10 ,在RGB888的基础上再加上8bit的ALPHA通道,ARGB8888 = 32位
- LCD屏幕接口 :RGB格式的屏幕一般叫做RGB接口屏,屏幕接口有MPI,LVDS,MCU,RGB接口。正点原子屏幕ID,使用ID可以识别不同的屏幕,在RGB 屏幕上对R7,G7,B7上焊接上拉或者下拉电阻实现不同的ID,正点原子的ALPHA开发板RGB屏幕接口用了3个3157屏幕开关,原因是防止LCD屏幕上的ID电阻,影响6ULL的启动
- LCD时间参数:
- 水平:HSYNC:水平同步信号,行同步信号,当出现HSYNC的时候表示新的一行开始显示
- 产生HSYNC信号,表示新的一行开始显示,HSYNC信号得维持一段时间,,这个时间叫做HSPW
- HSYNC信号完成以后需要一段时间延时,这段时间叫做HBP
- 显示1024个像素点的数据,1024clk
- 一行像素显示完成以后,到HSYNC下一行信号得产生之间有个延时叫做HFP,因此真正显示一行所需的时间就是(HSPW+HBP+WDTH(屏幕水平像素点个数,比如1024)+HFP)=20+40+1024+160=1344CLK
- 垂直: VSYNC:垂直同步信号,帧同步信号,当出现VSYNC信号的时候表示新的一帧开始显示。
- VSYNC信号持续一段时间为VSPW
- VSYNC信号完成以后需要一段时间叫VBP
- VBP信号结束以后就是要显示的行数,比如600行
- 所有的行显示完成以后,一段VFP延时.
- 像素时钟:(VSPW+VBP+height(600)+VFP)*(HSPW+HBP+WDTH+HFP)
- 水平:HSYNC:水平同步信号,行同步信号,当出现HSYNC的时候表示新的一行开始显示
- 显存:显示存储空间,采用ARGB8888 = 32bit = 4B.这4个字节的数据表示一个像素点的信息,必须要存起来。10246004 = 2.5M.因此需要留出2.5MB的内存给LCD用,方法很简单,直接定义一个32位的数组,u32 lcdframe[1024*600]
RTC实验
6ULL内部自带一个RTC外设,确切的说叫SRTC。6U和6ULL的RTC内容在SNVS章节,6U的RTC分为LP和HP。LT叫SRTC,HP是RTC,但是HP的RTC掉电以后数据就丢失了,即使用了纽扣电池也没用,所以要初始化LP,也就是SRTC。
RTC分为SNVS_LP和SNVS_HP,RTC很类似定时器,外接32.768K的晶振,再开始计时。RTC使用两个寄存器来保存计数值
RTC使用很简单,打开RTC,然后RTC就开始工作,我们要做的就是不断的读取RTC计数寄存器,获取时间值,或者向RTC计数器写入时间值,也就是调整时间SNVS_HPCOMR的bit31置1,表示所有的软件都可以访问SNVS所有寄存器
- bit8也是和安全有关,置1,也可以不置1
SNVS_LPCR寄存器,bit0置1开启SRTC功能
SNVS_LPSRTCMR的bit14:0是高15位RTC计数寄存器
SNVS_LPSRTCLR是低32位RTC计数器,与LPSRTCMR共同组成SRTC数据计数器,每一秒数据加一。
==6U的RTC默认从1970年一月一日0点0时0分0秒==
I2C实验
ALPHA开发板有个AP3216C,这是一个IIC接口的器件,这是一个环境光传感器,AP3216C连接到了I2C1上
- I2C1_SCL:使用的是UART4_TXD这个IO,复用为ALT2
- I2C1_SDA:使用的是UART4_RXD这个IO,复用为ALT2
I2C分为SCL和SDA,这两个必须要接上拉电阻,到VCC,比如3.3V一般是4.7K上拉电阻
I2C总线支持多从机,通过从机地址来区别访问哪个从机
6ULL的I2C时钟频率标准模式100kbit/s,快速模式400kbit/s
时钟源选用perclk_clk_root= ipg_clk= 66M
IADR寄存器bit7:0保存I2C从机设备地址
IFDR寄存器设置I2C频率,bit5:0,设置分频值,假如我们现在需要100kbit的速率,那么66000000/100000 = 660,经过查找,IC位设置为0X38或0x15的时候为640分频,660000000/640 = 103.125kbit
I2CR寄存器,bit7为I2C使能位置1,bit5为主从模式选着位,为0表示从模式,为1表示主机,bit4为发送/接收设置位,为0的时候是接收,为1的时候是发送
I2SR寄存器,bit7为传输完成位,为0表示正在发送,为1表示发送完成,bit5是I2C忙闲位,为0的时候I2C总线空闲,为1的时候I2C总线忙,bit0是读确认位,也就是ACK信号
I2DR寄存器,数据寄存器,
AP3216C:是一个三合一的环境光传感器,ALS(环境光)+Ps(接近传感器)+IRLED(红外LED灯),I2C接口最高400k的频率
环境光,ALC是16位输出
接近传感器:10bit输出,IR传感器也是10bit
AP32167从机地址为0x11
0X0A是IR Ddata low bit7为0的时候表示IR和PS数据有效,为1的时候IR和PS数据无效,bit1和0 是IR的低两位
0X0B是IR Data high ,bit7:0是高字节,与0X0A一起组成10bit的数据
0X0C和0X0D分别为ALS的低8位和高8位
0X0E是ps的bit3:0是低4位数据,0X0F的bit5:0是高6位数据
0X00是系统配置寄存器,bit2:0设置AP3216C开启哪些传感器,我们需要设置为011,也就是0X3表示开启ALS+PS+IR
SPI 协议
- SPI相比I2C最大优势有两点,一个是速度快,最高可达几十M,甚至上百M,第二个是SPI是个全双工。
- SPI接口和I2C一样,一个SPI接口可以连接多个SPI外设,SPI通过CS引脚/数据线,片选和哪个SPI外设通信。SPI通信前先将指定的SPI外设对应的CS引脚拉低来选中此设备
- ALPHA开发板上通过ECSPI3接口连接了一个6轴传感器,引脚如下:
- ECPI3_SCLK:UART2_RX
- ECPI3_MOSI:UART2_CTS
- ECPI3_SS0:UART2_TXD
- ECIP3_MISO:UART2_RTS
6ULL一个SPI主接口有4个硬件片选,分别为SS0-SS3
- 根据CPOI和CPHA可以设置4种工作模式,一般使用CPOL=0,CPHA = 0,
==寄存器==
6ULL的SPI接口叫ECSPI,支持全双工,主从可配置
4个硬件片选信号可以使用软件片选,这样一个SPI接口所能连接的外设就无限制了
RXDATA寄存器为接收到的数据
TXDATA寄存器为发送数据寄存器
CONREG寄存器为配置寄存器
- bit0使能位,bit0置1,使能SPI、
- bit3,当向TXFIFO写入数据以后马上开启SPI突发访问,也就是马上发送数据,置1
- bit7:4:设置SPI通道主从模式,bit7为通道3,bit4为通道0,因此,我们使用到了通道0,也就是我们要设置bit4为1
- bit19:18:通道选择,设置为00,使用到ss0,也就是通道0
- bit31:30:设置突发访问长度,设置为7,也就是8bit的突发长度,也即是一个字节
CONFIGREG寄存器
- bit0:PHA,设置为0,表示串形时钟的第一个跳变沿采集数据
- bit4:POL,设置为0,表示SCLK空闲的时候为低电平
- bit8:设置为0
- bit15:12:bit12设置为0
- bit16:设置为0,表示空闲的时候数据线为高
- bit20:设置为0,表示SCLK空闲的时候为低
STATREG寄存器:状态寄存器:
- bit0:等待TXFIFO为空,在发送数据前要等待TXFIFO为空,也就是等待bit0为·1
- bit3:RXFIFO是否有数据,为1表示RXFIFO至少有一个字的数据,我们在接收数据的时候要等待bit3为1
PERIODREG寄存器,:
- bit14:0:设置wait state时间,设置为0x200
- bit15:设置wait states 时钟源为SPI_CLK,将此位设置为0
- bit21:16:表示片选信号的延时,可设置0-23,设置为0
SPI时钟设置:
- SPI时钟源最终来源于pll3_sw_cl = 480MHZ/8=60M
- 设置CSCDR2寄存器bit18为0,也就是ECSPI时钟源为60MHZ
- bit24:19:设置为0表示1分频,最终进入到SPI外设的时钟源为60M
ECSPI模块还需要对时钟进行两级分频,由ECSPI_CONRELG寄存器设置
- bit15:12:可设置0-0xf表示1-16分频
- bit11:8:,设置2^n^分频,n=0-15
六轴传感器,三轴陀螺仪,三轴加速度计
陀螺仪分辨率可设置,加速度分辨率可设置
陀螺仪和加速度计都是16位ADC
通信接口为I2C或SPI,I2C最大到400kHZ,SPI最大到8MHz
ICM20608G的WHO_AM_I寄存器,地址为0X75,默认值为0XAF,ICM20608D的WHO_AM_I寄存器可能是0XAE。
0X3B~0X48是要读取的传感器数据
多点电容触摸屏实验
正点原子电容触摸屏,4.3寸都是GT9147IC,7寸是FT5206和FT5246
触摸屏IO:
- CT_INT:触摸中断线,连接到了GPIO1_IO09-+
- I2C2_SCL:连接到UART5_TXD
- I2C2_SDA:连接到了UART5_RXD
- RESET:连接到了SNVS_TAMPER9
电容触摸输出的触摸点坐标信息为对应的屏幕像素点信息,因此不需要校准。电阻屏需要校准
IIC接口最大400k频率
正点原子7寸屏幕FT5426的IIC地址为0X38
需要用到的寄存器:
- DEVICE_MODE : 需要设置为0x0,表示工作在正常运行模式
- ID_G_LIB_VERSION_H以及ID_G_LIB_VERSION_L,0XA1 0XA2:表示固件版本号
- ID_G_MOOD 0XA4 :设置为1,表示采用中断方式上报信息
- TD_STATUS 0X02: 当前触摸点的个数1-5
- TOUCH1_XH_0X03 : 开始记录触摸屏的触摸点坐标信息,一个触摸点6个寄存器表示,一共需要5*6 = 30个寄存器。一直读取到0x20
一个触摸点坐标信息用12bit表示,其中H的bit3:0这4个bit为高4位。L寄存器的bit7:0为低8位 - XH寄存器bit7:6表示事件:为0按下,为1抬起,为10表示接触、
- YH寄存器的bit7:4表示触摸ID
PWM背光实验
6UL的PWM是16位计数器
有4个16位的FIFO
一个12位的分频器
正点原子LCK屏幕的背光IO连接到了GPIO1_IO08上,GPIO1_IO08可以复用为PWM_OUT信号
PWM计数器从0x0000开始计数,当计数器的值等于PWMPR+1,定时器就会重新开始下一个周期的运行,因此PWMPR寄存器控制PWM的频率
FIFO保存着采样值,当我们向采样寄存器PWMSAR写采样值的时候会写到FIFO里面,每当读取PWMSAR寄存器,FIFO里面的数据都会减一,或者没产生一个PWM信号,FIFO的数据也会减一,知道FIFO为空,那就无法产生PWM信号,FIFO为空会产生中断,可以在中断中向FIFO写入采样值
PWMCR寄存器:
- bit0:PWM使能信号,
- bit2:1:设置为0,每个周期使用FIFO里面的一个数据
- 15:4:PWM分频设置,可以设置0~4095,对应1 ~ 1096分频
- bit17:16:设置时钟源,设置为1,表示使用ipg_clk=66MHZ
- bit19:18:设置为0
- bit27:16:设置为01,表示当FIFO里面空余位置大于2的时候FIFO为空
PWMIR寄存器:
- bit0设置为1
categories: - Linux