目录

1. 串口通信基本概念

1.1 通信基础概念

1.1.1 电平信号 & 差分信号

1.1.2 串行通信 & 并行通信

1.1.3 异步串行通信 & 同步串行通信

1.2 串口通信格式

1.2.1 RS232电平和TTL电平

1.2.2 波特率

1.2.3 数据帧构成

2. S5PV210 UART模块解析

2.1 概述

2.2 构成部件

2.2.1 Buad-rate Generator

2.2.2 Trsansmitter

2.2.3 Receiver

2.2.4 Control unit

2.3 自动流控(Auto Flow Control,AFC)

2.3.1 概述

2.3.2 实现

2.4 中断与DMA请求

2.4.1 UART的7种状态

2.4.2 中断产生列表

2.4.3 UART DMA请求

2.5 错误状态FIFO

2.5.1 概述

2.5.2. 注意事项

2.6 红外模式

3. S5PV210 UART程序设计

3.1 核心寄存器解析

3.1.1 ULCONn

3.1.2 UCONn

3.1.3 UFCONn & UFSTATn

3.1.4 UTRSTATn & UERSTATn

3.1.5 UTXHn & URXHn

3.1.6 UBRDIVn & UDIVSLOTn

3.1.7 UINTMn & UINTSPn & UINTPn

3.2 串口初始化&收发数据代码解析

3.2.1 串口初始化代码解析

3.2.2 串口收发函数解析

3.3 基于中断的Rx代码解析

3.3.1 中断初始化

3.3.2 ISR

4. printf移植解析

4.1 什么是stdio

4.2 C语言对可变参函数的支持

4.3 移植printf/scanf函数

4.3.1 移植代码来源

4.3.2 printf/scanf工程的组织和编译

4.4 printf/scanf函数工作原理

4.4.1 printf函数工作原理

4.4.2 scanf函数工作原理

5. 自建bootloader超过BL1长度限制的解决方案

5.1 dnw下载

5.2 SD卡烧写


1. 串口通信基本概念

1.1 通信基础概念

1.1.1 电平信号 & 差分信号

电平信号 & 差分信号用于说明信号线上如何表示数据0和1

① 电平信号

电平信号中有一根电平参考线(一般为GND),信号线上的数据值由信号线和参考线上的电压差决定

② 差分信号

差分信号中没有参考电平,都是信号线,只是2路信号振幅相同/相位相同/极性相反。信号线上的数据值由信号线之间的电压差决定。

说明:电平信号的2根通信线之间的电平差值容易受到干扰;差分信号抗干扰能力较强(尤其是共模干扰),因此传输质量比较稳定。

1.1.2 串行通信 & 并行通信

① 串行通信

计算机与IO设备之间数据传输的各位按顺序依次传送,通常数据在一根数据线或一对差分线上传输。

一般特点:速度慢/成本低/适合远距离传输

② 并行通信

计算机与IO设备之间通过多条传输线交换数据,数据各位同时传送。

一般特点:速度快/成本高/适合近距离传输

注意1:一些差分串行通信总线(e.g. RS485/RS422/USB)抗干扰能力强,传输距离远,速度也比较快~~

注意2:CPU内部均是采用并行传输(数据总线)

1.1.3 异步串行通信 & 同步串行通信

① 异步串行通信

特点:

a. 以字符为单位传送信息

b. 相邻2个字符之间的间隔为任意长

c. 因为一个字符的比特位长度有限,所以接收时钟和发送时钟只要相近即可(即发送方和接收方可以各自维护时钟)

d. 字符间异步,字符内同步

补充:如何理解字符内同步?

每个字符的传输通过起始位 & 停止位进行界定

数据格式:

a. 1位起始位,规定为低电平(空闲时为高电平,将信号线拉低表示起始位)

b. 5 ~ 8位数据位,即要传送的有效信息,一般设置为8位。从LSB开始传输,靠时钟定位。

c. 1位奇偶校验位,可以构成奇校验(使“1”的位数为奇数)或偶校验(使“1”的位数为偶数)

d. 1 ~ 2位停止位,规定为高电平

② 同步串行通信

特点:

a. 以数据块为单位

b. 在一个数据块(信息帧)内,字符与字符之间无间隔

c. 因为一次传输的数据块中数据较多,所以接收时钟和发送时钟必须严格同步,通常要有同步时钟(e.g. SPI & I2C)。

d. 字符内部与字符之间均同步

说明:经过多年发展,最终胜出的是异步/串行/差分通信,如USB和网络通信

1.2 串口通信格式

串口通信的基本特点:异步/串行/电平信号

1.2.1 RS232电平和TTL电平

① RS232电平

逻辑1:-3 ~ -15V

逻辑0:+3 ~ +15V

② TTL电平

逻辑1:+3.5 ~ +5V

逻辑0:< 0.4V

③ 使用场景

RS232电平幅值范围较大,适合干扰大/距离远的情况。原先PC机以及工业应用中,均使用RS232串口

TTL电平赋值范围较小,适合干扰小/距离近的情况。TTL电平一般用在电路板内部2个芯片之间

④ 电平转换

X210开发板(包括OK210开发板)使用管脚引出的串口均为TTL电平;同时也使用了TTL转RS232芯片,并使用DB9接口引出。

注意:串口电路中仅使用了DB9接口中的Tx/Rx接口(同时GND接地),其余接口并未使用(也就是说,流控及其他功能均无法使用)

说明:编程时并不考虑电平标准

1.2.2 波特率

① 波特率和比特率

波特率:信号变化的次数(即每秒采样的次数),也称为码元速率(即得到一个信号的频率)

比特率:数据传输速率,单位为bps(bit per second)

② 波特率和比特率的关系

如果信号分为两级:0、1,那么一次信号变化可表示1bit,所以比特率 = 波特率

如果信号分为八级:0 ~ 7,那么一次信号变化可表示3bit,所以比特率 = 3 * 波特率

因此,如果信号分为v级,则比特率 = log2v * 波特率

上图中两个信号的波特率相同(即信号到达的速率相同,竖虚线等宽),但下面的信号每次携带2 bit(信号分为四级)信息,所以比特率是上面信号的2倍

③ 串口波特率

由于在串口中信号只分为2级,所以比特率 = 波特率,也就是串口通信时每秒可以传输多少个二进制位。

由于串口通信属于异步传输,所以收发双方各自维护波特率(也就是串口通信的时钟)

1.2.3 数据帧构成

串口通信按帧传输,每个周期由起始位 + 数据位 + 奇偶校验位 + 停止位构成。具体格式见上文异步串行通信部分。

2. S5PV210 UART模块解析

2.1 概述

① UART模块负责CPU和UART之间的数据传输(包含了串并转换和时序控制)

② S5PV210共有4路UART接口,且均可基于中断/DMA工作

③ 最高支持3Mbps速率

④ 每条UART通道包含2条FIFO(分别用于收/发),其中ch0 - 256B/ch1 - 64B/ch2 - 16B/ch3 - 16B

⑤ ch0 - ch3支持红外模式

⑥ ch0 - ch2支持自动流控(AFC)

2.2 构成部件

2.2.1 Buad-rate Generator

波特率发生器使用PCLK或SCLK_UART分频生成所需的波特率

在我们的示例中,使用PCLK_PSYS作为UART模块的时钟源

2.2.2 Trsansmitter

发送部件由发送FIFO和发送移位器组成,在非FIFO模式下,相当于只用FIFO的一个字节。

程序将要发送的数据写入Tx FIFO后,将自动拷贝到Tx shifter并逐位传输到TxD引脚。

2.2.3 Receiver

接收部件由接收FIFO和接收移位器组成,在非FIFO模式下,相当于只用FIFO的一个字节。

从RxD引脚接收到的数据逐位移入Rx shifter,然后自动拷贝到Rx FIFO中。

说明:通过FIFO和shifter的配合,实现了UART的串并转换(CPU内部数据传输均为并行)

2.2.4 Control unit

控制整个UART收发流程

2.3 自动流控(Auto Flow Control,AFC)

2.3.1 概述

① 流控的目的是让串口通信可靠,在发送方速率比接收方处理能力强时,流控可以确保不丢帧(所谓流控,就是控制发送端的发送速率,而主导调控发送速率的是接收端)。

当前不使用流控,是因为串口主要用于输出调试信息,作为接收方的PC,处理能力远比SoC强。

② S5PV210的UART0和UART1支持AFC;如果将TxD3/RxD3引脚设置为nRTS2/nCTS2功能,则UART2也支持AFC(当然,此时UART3无法使用~~)

2.3.2 实现

S5PV210为实现AFC,需要用到RTS/CTS引脚,其中RTS为output,CTS为input

术语说明:

RTS:Require To Send,发送请求,为输出信号,用于指示本设备准备好接收数据

CTS:Clear To Send,允许发送,为输入信号,用于判断是否可以向对方发送数据

一般使用硬件流控时,将两端设备的RTS & CTS交叉连接

① 发送数据

在AFC中,nCTS信号表示对端UART的接收FIFO已准备好接收数据。所以当发送端nCTS信号生效时,可以将数据写入自身发送FIFO(传输则由Control Unit控制完成)

② 接收数据

在AFC中,nRTS信号表示自身接收FIFO准备好接收数据。所以在接收端接收数据前,需要使能nRTS信号

说明:根据上文分析,通信两端的CTS/RTS信号交叉连接,整个流控过程实际是由接收端控制(因为只有RTS信号是输出信号)。

当接收端接收FIFO无法继续接收数据时,使得RTS信号失效,则发送端无法收到CTS信号,所以不再继续发送。

当接收端接收FIFO出现空闲,可以继续接收时,将RTS信号使能,则发送端收到CTS信号,将继续发送数据。

在S5PV210中,RTS和CTS均是低电平有效,所以拉低RTS使能该信号时,对端连接的CTS也将生效。(注意寄存器值与高低电平的关系~~)

注意:如果将S5PV210的UART与Modem连接,需要禁止UMCONn寄存器的AFC位,并且通过软件控制nRTS信号

2.4 中断与DMA请求

2.4.1 UART的7种状态

在S5PV210中,UART共有7种状态,分别记录在UTRSTAT和UERSTAT寄存器中,可以分为3类:

① Tx

Tx buffer empty / Tx shifter empty

② Rx

Rx buffer data ready

③ Error

Overrun error:缓冲区溢出错误,即新数据已经覆盖旧的未读数据

Parity error:奇偶校验错误

Frame error:帧错误,即收到的数据没有有效的停止位

Break condition:UART发送端可以产生break信号,将串口输出状态强制为logic 0 state,并保持一个数据帧的长度(后文可知,发出break信号有寄存器可供设置)。如果break信号超过一个数据帧的长度,接收端将检测到break condition。

2.4.2 中断产生列表

Type

FIFO Mode

Non-FIFO Mode

Rx interrupt

① Rx FIFO数据量 >= Rx FIFO阈值

② Rx FIFO数据量 < Rx FIFO阈值,同时

3个字长时间没有收到数据(处理尾字节)

Rx holding register中有数据

Tx interrupt

Tx FIFO数据量 <= Tx FIFO阈值

Tx holding register中无数据

Error interrupt

frame error/parity error/break condition/

overrun error触发。且当多个错误同时发生

时,只触发一次中断

同FIFO Mode

注意事项:

① 产生Tx/Rx中断的配置

UART工作在中断或轮询模式 && 中断mask寄存器对应位打开

② 产生Error中断的配置

使能Rx Error中断 && 中断mask寄存器对应位打开

③ 中断的共享与识别

S5PV210中,每个UART只有一个中断源,所以如果同时使能UART的多种中断,需要在ISR中进行识别。

④ Tx中断注意点

根据Tx中断的触发条件,只要Tx FIFO数据量 <= Tx FIFO阈值就会触发,因此建议先向Tx FIFO中写入数据再使能Tx中断。

个人:使用中断模式发送数据时,应该,

a. 默认先关闭中断

b. 有数据要发送时,先向Tx FIFO中写入数据,然后使能中断

c. 在ISR中判断是否还有数据要发送,如果有,则向Tx FIFO中继续写入;如果没有,则关闭中断

这套流程不仅适用于基于中断的串口发送,其他基于中断的接口发送也遵循该流程(e.g. SPI)

上述流程的核心在于中断使能和关闭的时机,如果是基于中断的数据接收,要点也在于此,但是接收流程的设计相对简单,可以设置阈值坐等数据到来。如果等待过程中发生timeout,比如发生尾字节中断,可以在ISR中设置再次进入等待

⑤ 由于S5PV210的中断控制器为电平触发,因此UART的中断信号类型只能设置为电平模式

2.4.3 UART DMA请求

① 如果设置UART工作在DMA模式,那么在上文中产生Tx/Rx中断的条件下就不会产生中断,而是产生DMA请求

② DMA模式一般配合FIFO使用(否则意义不大~~)

③ DMA burst size可设置为1B或4B,建议FIFO阈值与DMA burse size匹配

④ DMA传输会一直持续到满足FIFO阈值

2.5 错误状态FIFO

2.5.1 概述

① 错误状态FIFO用于标识Rx FIFO中哪个数据是错误的及其错误类型

② 每个错误状态FIFO记录一种错误,并与Rx FIFO的位置对应

2.5.2. 注意事项

① 如果使能了Error中断,只有当错误数据被读取时(通过读取URXHn寄存器)才会触发中断

② 为清除错误FIFO中的状态,需要同时读取URXHn和UERSTATn寄存器

2.6 红外模式

① UART的红外模式需要在串口之外结合红外编解码器实现

② 红外模式的通信的原理是发送方在固定时间间隔向接收方发送红外信号(表示1)或者不发送红外信号(表示0)

当串口启用IrDA模式,我们向串口写数据时,这些数据就以红外光的方式向外发射出去。

3. S5PV210 UART程序设计

3.1 核心寄存器解析

3.1.1 ULCONn

ULCONn(UART Line Control)寄存器主要用于设置数据帧格式,一般设置为:

8位数据位 + 无奇偶校验 + 1位停止位

3.1.2 UCONn

UCONn(UART Control)寄存器主要用于设置UART的工作模式,需要注意以下字段:

① Tx/Rx/Error中断使能设置(注意,Tx/Rx的中断类型只能是电平)

② 此处选择PCLK_PSYS作为UART的时钟源

③ 注意Rx time-out中断,该中断就是用于处理尾字节,类型为Rx中断

个人:从该寄存器可知,要实现可靠的通信协议,一定是需要硬件层面保障的(e.g. 各种错误状态的标识与通知机制)

而软件层面的设计,需要充分利用这些硬件机制

3.1.3 UFCONn & UFSTATn

UFCONn(UART FIFO Control)寄存器主要用于使能FIFO及设置Tx/Rx FIFO触发中断的阈值。

注意不同channel的UART FIFO深度不同~~

UFSTATn(UART FIFO Status)寄存器表征了当前FIFO的状态,主要关注Tx/Rx FIFO中的数据量,在使能FIFO和中断的情况下,需要在ISR中根据该寄存器读写相应数量的数据。

3.1.4 UTRSTATn & UERSTATn

上文中说明的UART的7种状态就保存在这2个寄存器中

UTRSTATn(UART Tx/Rx Status)寄存器标识Tx/Rx相关状态,需要注意以下字段:

① Transmitter empty是指Tx buffer和Tx shifter均为空

② Transmit buffer empty仅指Tx buffer为空,在Non-FIFO模式中,此时就触发中断或DMA请求

③ 当使能FIFO时,应该检查UFSTAT寄存器而不是该寄存器

UERSTATn(UART Error Status)寄存器标识Rx过程中发生的各种错误。

注意:读取UERSTATn寄存器就可将相关标志位自动清零

3.1.5 UTXHn & URXHn

UTXHn(UART Transmit Buffer[Holding Register & FIFO register])和URXHn(UART Receive Buffer Register[Holding Register & FIFO register])是读写串口数据时实际操作的寄存器。

在Non-FIFO模式下,就是Holding Register;在FIFO模式下,则是FIFO register

3.1.6 UBRDIVn & UDIVSLOTn

UBRDIVn(UART Channel Baud Rate Division)和UDIVSLOTn(UART Channel Dividing Slot)寄存器共同实现波特率的设置。其中UBRDIVn负责主分频,UDIVSLOTn用于辅助校准,以提高波特率的精度。

计算公式如下:

UBRDIVn = (PCLK / (bps * 16)) - 1的整数部分

UDIVSLOTn = (PCLK / (bps * 16)) - 1的小数部分 * 16 的整数部分构造而来

在本示例中,PCLK = 66.7MHz,波特率设置为115200,因此

UBRDIVn = (66700000 / (115200 * 16)) - 1 = 35.18的整数部分,所以UBRDIVn寄存器值为35

UDIVSLOTn = 0.18 * 16 = 2.88 ≈ 3,即UDIVSLOTn寄存器的值中必须有3个1,为确保串口工作的稳定性,可以根据手册查表得到。

注意事项:

① 当选择PCLK_PSYS为时钟源时,UBRDIVn的值必须 >= 0

② 当选择SCLK_UART为时钟源时,计算方法与上文相同,但是UBRDIVn的值可以为0(即在时钟模块已经分频完毕)。当UBRDIVn的值为0时,波特率不受UBRDIVn和UDIVSLOTn寄存器的影响

3.1.7 UINTMn & UINTSPn & UINTPn

UINTMn(UART Interrupt Mask)寄存器用于屏蔽指定的中断,当某位置1时即使对应的中断情况发生,也不会触发中断。

UINTSPn(UART Interrupt Source Pending)寄存器记录了中断源请求的情况,即使在UINTMn寄存器中屏蔽了某个中断,如果该中断发生,依然会将UINTSPn中的对应位置1。

注意:UINTSPn寄存器只是反映中断源的原始状态,不需要清pend

UINTPn(UART Interrupt Pending)寄存器是真正的中断源请求寄存器,在UART的ISR中需要清pend;清pend的方式是向对应位写1。

说明:UINTMn/UINTSPn/UINTPn寄存器的关系如下图所示

3.2 串口初始化&收发数据代码解析

3.2.1 串口初始化代码解析

串口初始化代码需要完成以下步骤:

① 设置UART对应的GPIO管脚模式

UART0对应的GPIO管脚由GPA0 group管理

② 设置UART模块相关寄存器

ULCON:8位数据位 + 无奇偶校验 + 1位停止位

UCON:收发采用中断或轮询模式

UMCON:禁用modem和AFC相关(使用默认值即可)

UFCON:暂时不用FIFO(使用默认值即可)

UFCON/UDIVSLOT:根据波特率计算

3.2.2 串口收发函数解析

使用轮询方式实现数据收发,就是不断查询UTRSTATn寄存器对应位的值,并据此执行相关操作。

说明:

① 使用轮询方式实现收发能很好地和printf/scanf函数配合(尤其是在当前没有操作系统的环境下)。

② 此处getc函数中没有进行回显,是因为回显实现在了scanf函数中,可参见下文相关内容。

3.3 基于中断的Rx代码解析

3.3.1 中断初始化

中断的整个流程涉及中断源 + VIC + CPU三个部分,

① 中断源

在中断源端使能中断

② VIC

注册ISR + 在VIC端使能中断

③ CPU

为IRQ流程准备环境,包括将CPSR中的I/F位清零、设置IRQ栈、中断源的识别与ISR的调用等

3.3.2 ISR

ISR需要完成2个任务,

① 实际的中断业务流程

② 在中断源端清pend(VIC端清pend由总的中断处理程序完成)

4. printf移植解析

4.1 什么是stdio

① stdio(standard input output),即标准输入输出

② 标准输入输出是操作系统定义的默认的输入和输出通道。一般在PC中,标准输入是键盘,标准输出是屏幕。在操作系统中可以定义标准输入/输出/错误流。

在我们的程序中,标准输入/输出就是串口。

③ printf/scanf函数可以和底层的输入/输出函数绑定,然后这两个函数就可以和stdio绑定起来。

4.2 C语言对可变参函数的支持

所需头文件:<stdarg.h>

<stdarg.h>头文件提供的工具可以使我们能够编写可变数量参数的函数,下面结合示例加以说明。

运行结果:

<stdarg.h>提供了1种类型和4个宏用于处理可变参数列表,

va_list类型:用于处理可变参数列表的类型,在实现中一般就是char *类型

va_start宏:指出参数列表中可变长度部分开始的位置。带有可变数量参数的函数必须至少有一个"正常的"形式参数,省略号总是出现在形式参数列表的末尾,在最后一个"正常"参数之后。

实现中就是将参数列表中可变长度部分的起始地址赋值给va_list类型的变量,同时注意4B对齐,具体原因后文会有论述。

va_end宏:用于对可变参数列表的"清理",实现中可以什么都不做~~

va_start和va_end宏必须成对调用

va_arg宏:用于遍历可变参数列表,可以根据指定的类型取出对应的参数。

其中_bnd宏就是用于实现4B对齐(其实是按int型大小对齐)

特别注意:每从可变参数列表中取出一个参数,ap会指向下一个参数

va_copy宏:va_copy(va_list dest, va_list src)在C99中提出,用于拷贝可变参数列表

va_copy也必须和va_end成对使用

说明:在可变参数函数中,需要根据"正常"参数解析可变参数列表(e.g. printf函数中的格式字符串)

特别注意:关于参数类型提升

当调用带有可变参数列表的函数时,编译器会在省略号对应的所有参数上执行默认的实际参数提升。e.g. char类型和short类型会被提升为int类型,float类型会被提升为double类型。因此把char、short或float类型作为参数传递给va_arg是没有意义的,提升后的参数不可能具有这些类型。

从实现中分析,从可变参数列表中取出参数时均要求4B对齐,因此在示例中处理字符时也是按int类型取出参数,然后强制类型转换为char类型。

进阶:深度分析va_start & va_arg宏的实现

要理解va_start & va_arg宏的工作方式,需要结合函数参数入栈的顺序,参考下图一目了然

① va_start宏中,通过&(A)取得的就是形参fmt变量的地址,在示例中就是栈中的0x100地址

然后在该地址的基础上,就可以计算出可变参数的起始位置,也就是0x104地址

② va_arg宏则是根据可变参数的类型,逐个遍历可变参数,此时需要格式字符串标识的类型与实际可变参数的类型一致,否则会解析错误

③ 由于va_start & va_arg的运行过程高度依赖参数入栈的顺序(只有函数参数是从右向左入栈时,根据"sdc"字符串的解析才是正确的),这就体现了对编译器设置入栈规范的重要性。

4.3 移植printf/scanf函数

4.3.1 移植代码来源

① 最原始来源:Linux内核中的printk函数

② 简化方法:从uboot中移植(也是从linux中得来)

③ 最简单方法:直接使用别人移植好的~~(源自友善之臂裸机代码)

4.3.2 printf/scanf工程的组织和编译

将printf/scanf工程相关文件单独编译成静态库供整个工程链接使用,需要注意在CFLAGS变量中添加如下编译选项:

-nostdinc:不使用标准头文件

-fno-builtin:不使用内建函数

在C语言标准中,有些通用函数被定义为built-in function(内建函数),像printf/strchr/memset等等,这些函数不需要包含头文件中的声明,就可以编译链接该函数。增加-fno-builtin选项后,就可以自己实现这些函数,而不会导致冲突。

-nostdlib:不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。这个选项常用于编译内核、bootloader等程序,它们不需要启动文件、标准库文件。

4.4 printf/scanf函数工作原理

4.4.1 printf函数工作原理

① 定义发送缓存区g_pcOutBuf

② 调用va_start宏取出可变参数列表

③ 调用vsprintf函数,根据格式字符串fmt解析可变参数列表,并将解析结果输出到发送缓冲区

④ 调用putc函数(此处为串口输出函数)将发送缓冲区中的内容输出到串口

说明:printf函数调用链

printf --> vsprintf --> vsnprintf

在vsnprintf函数中会根据格式字符串解析可变参数列表,并调用va_arg宏逐个取出参数并打印到发送缓存区中。

4.4.2 scanf函数工作原理

① 定义接收缓冲区

② 从串口接收数据直到遇到换行(0x0a)或回车(0x0d),此时将接收缓冲区截零(且换行和回车符并不放入接收缓冲区)

③ 调用va_start宏取出可变参数列表

④ 调用vsscanf函数,根据格式字符串解析可变参数列表,并从g_pcInBuf中取出要赋给变量的值,然后向设定的地址赋值。

5. 自建bootloader超过BL1长度限制的解决方案

S5PV210在启动过程中对BL1的长度有16KB的限制,但是随着片内外设使用的增加,尤其是相关库的引入(e.g. printf/scanf),目前自建的bootloader已经超过16KB的限制。

为实现程序的正确下载/烧写与运行,有如下2种解决方案:

5.1 dnw下载

先用dnw下载x210_usb.bin,该文件可以完成内存初始化等操作,这样就可以将自建bootloader下载到内存运行。

注意:经上机验证,通过dnw下载到iRAM中运行的代码可以超过16KB。根据iRAM的内存布局,Reserved部分其实可供自建bootloader下载运行。

特别注意:重复初始化内存问题

问题现象:先将x210_usb.bin下载到0xd0020010,再将key_interrupt.bin(自建bootloader)下载到0x20000000,程序没有正确运行。

原因分析:x210_usb.bin会初始化内存,而自建bootloader中也会初始化一次内存,第二次初始化会使得下载到其中的bootloader失效,所以无法运行。

解决方案1:直接禁用reset流程中的内存初始化

经过测试,reset流程中不再调用sdram_asm_init函数初始化内存后,可以将自建bootloader下载到内存中运行(当然,此前要先下载x210_usb.bin运行)。

需要注意的是,如果下载的内存地址与链接地址(目前为0x20000000)不符,仍需要做重定位。但是要注意其中的一个小限制,为避免异常错误,如果下载地址与链接地址不符,则需要间隔一个bootloader镜像的长度(即需要避免拷贝目标地址与源地址区域的重叠)。

举个例子,如果自建bootloader链接地址为0x20000000,文件大小为20KB。如果下载地址为0x20000010,会导致异常错误。因为此时重定位相当于把文件整体向前移动16B,而目标地址与源地址区域的重叠会导致拷贝过程中程序就跑飞了~~

解决方案2:将内存初始化加入code_relocate子过程

其实这个方案是有逻辑小漏洞的~~需要注意的问题有2个,

① 重定位的条件是运行地址和链接地址不匹配;调用sdram_asm_init的条件是内存尚未初始化。

其中的逻辑漏洞在于,如果此时自建bootloader就是运行在内存中,但是运行地址和链接地址不匹配,此时仍会进行内存初始化,还是会导致问题。也就是说,并不是需要重定位的情况都需要内存初始化。

解决方法就是,确保自建bootloader的下载地址与链接地址一致

② 不能在code_relocate中简单调用bl sdram_asm_init,因为code_relocate为非叶子函数,再次调用bl时,必须将当前LR的内容入栈,否则无法返回code_relocate的上级函数。

个人:对于自建bootloader大于16KB的情况,建议仍使用dnw下载(SD卡烧写插拔太尼玛麻烦~~)。对于下载x210_usb.bin后下载自建bootloader,建议直接禁用重复的内存初始化即可~~

5.2 SD卡烧写

由于mkv210_image.c代码中对BL1镜像限制在16KB,因此可以将自建bootloader工程拆分为2部分。第一部分在16KB以内,第二部分放置剩余部分(存储在SD卡后面的某个扇区中,比如第45个扇区)。然后在第一部分中进行重定位(从SD卡中重定位到内存中,使用S5PV210提供的存储设备操作函数可以完成)。

BL1的一般任务:

① 环境初始化,尤其是内存初始化。

BL1是由CPU在启动过程中自动从SD卡加载到iRAM中运行,需要为BL2的运行准备环境。

其实BL2有2个运行场所,如果BL2 < 80KB则可以加载到iRAM中运行;如果BL2 > 80KB则只能加载到内存中运行。

虽然S5PV210手册中推荐使用iRAM运行BL2,但是Uboot在实现过程中一般直接使用内存运行BL2,我们也采取这种方式。

② 将BL2从SD卡加载到内存中并启动运行

考虑到地址相关操作的影响,BL2的链接地址需要和加载的地址匹配(当然不匹配也可以,就是再在内存中进行一次重定位,但是这么做就木有什么意义了~~)。

剧透:最终使用的方式,是从完整的自建bootloader中截取出最前端的16KB,而非拆分工程

S5PV210体系结构与接口06:串口编程相关推荐

  1. S5PV210体系结构与接口07:中断系统编程

    目录 1. 什么是按键 1.1 按键原理与连接 1.2 按键的2种响应方式 1.2.1 轮询方式 1.2.2 中断方式 1.3 轮询方式处理按键解析 1.4 按键消抖 1.4.1 按键抖动 1.4.2 ...

  2. S5PV210体系结构与接口12:I2C编程

    目录 1. I2C总线工作原理 1.1 概述 1.2 总线寻址 1.2.1 7位地址模式 1.2.2 从设备地址的确定 1.2.3 10位地址模式 1.3 总线时序 1.3.1 空闲态 & s ...

  3. S5PV210体系结构与接口11:NandFlash SD卡编程

    目录 1. Flash ROM简介 1.1 概述 1.2 Nor & Nand Flash比较 1.2.1 接口对比 1.2.2 容量和成本对比 1.2.3 可靠性对比 1.2.4 使用寿命对 ...

  4. S5PV210体系结构与接口08:定时器 计数器编程

    目录 1. S5PV210 PWM模块解析 1.1 结构概述 1.1.1 时钟选择 1.1.2 两级分频 1.1.3 输出特征 1.2 工作模式 1.2.1 工作流程 1.2.2 TCNT & ...

  5. S5PV210体系结构与接口05:时钟系统编程

    目录 1. 时钟概念解析 1.1 什么是时钟? 1.2 为什么需要时钟? 1.3 如何获得时钟? 1.3.1 外部晶振 1.3.2 外部晶振 + SoC内部CMU(Clock Management U ...

  6. S5PV210体系结构与接口03:GPIO编程

    目录 1. GPIO功能简介 1.1 什么是GPIO? 1.2 GPIO作用 1.3 GPIO的构成(data sheet层面) 2. S5PV210芯片GPIO控制器简介 2.1 总额及分类 2.2 ...

  7. S5PV210体系结构与接口02:ARM编程模型 汇编指令

    目录 1. ARM的基本设定 1.1 ARM数据类型 1.1.1 基本数据类型 1.1.2 浮点数据类型 1.1.3 存储器大小端 1.2 支持的指令集 2. Cortex-A8编程模型 2.1 处理 ...

  8. S5PV210体系结构与接口10:MMU编程

    目录 1. MMU概述 1.1 VMSA概述 1.2 MMU作用 2. MMU地址转换详解 2.1 总体流程图示 2.1.1 概述 2.1.2 设置TTB相关寄存器 2.2 一级页表解析 2.2.1 ...

  9. S5PV210体系结构与接口09:SD卡启动详解

    目录 1. MMC技术演进 1.1 NandFlash & NorFlash芯片 1.2 MMC卡 & SD卡 & MicroSD卡(TF卡) 1.2.1 代际关系 1.2.2 ...

最新文章

  1. 和12岁小同志搞创客开发:如何驱动LCD1602液晶显示屏?
  2. Java的小实验——各种测试以及说明
  3. Linux企业运维人员最常用192个命令汇总
  4. 【Linux】一步一步学Linux——dhclient命令(156)
  5. 分享25个新鲜出炉的 Photoshop 高级教程
  6. linux vlc流媒体服务器,vlc media server rtsp 流媒体服务器搭建成功经验分享
  7. java二级缓存技术_Java二级缓存
  8. 最长公共子序列LCS(动态规划)—详解
  9. nohttp网络框架
  10. LoadRunner 11压测时碰到错误Error: missing newline in *:\*****\*.dat
  11. python编程成果_20192217 2019-2020-2 《Python程序设计》实验四报告
  12. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
  13. [转]android刷新后R.java不见了
  14. C# 找出实现某个接口的所有类
  15. 此笔记只作为自身笔记,结构比较混乱,不建议参考,如有需要请访问其他文献,servlet的基础知识和使用
  16. Lemon OA_Lemon OA(开源OA系统)
  17. 冯诺依曼体系结构浅析
  18. 82个加密数字货币遭交易所下线,全球区块链ICO代币进入强监管
  19. emp和emn是什么文件,emnemp是什么文件
  20. ja_charity模板研究_contin_1

热门文章

  1. 埋点用例管理_API管理平台之系统设计篇
  2. css 一些好玩的属性,推荐一些比较有用的css3新属性
  3. 2021-09-01
  4. java坐标代码_java实现计算地理坐标之间的距离
  5. Oracle Rownum 伪列详解
  6. mysql java 社工库_社工库源码
  7. 树莓派 ubuntu gpio_树莓派学习笔记(一)输入输出GPIO
  8. wireshark找不到接口_wireshark网络小故障分析定位
  9. avue-crud 会多出来空白的一列
  10. Android开发笔记(一百五十四)OpenGL的画笔工具GL10