【笔记】ARM裸机程序开发_part3

Zynq双核开发避坑指南:如何高效使用SDK进行ARM裸机程序调试(基于Vivado 2021.1) 本文详细介绍了Zynq双核开发中的高效调试技巧,重点讲解如何使用SDK进行ARM裸机程序调试。内容涵盖Vivado工程配置优化、SDK调试设置、内存访问诊断、中断调试方法以及双核协同调试策略,帮助开发者提升Zynq-7000系列SoC的开发效率。 阅读详情

7.1 uart

电平通信:绝对的电压是没有意的,电压差是真正的意义。电平信号的传输线中有一个参考电平线(一般是GND)
容易受到干扰,届时传输失败
8位二进制并行通信时:需要9根线(1参考GND+8数据线)
差分信号:也是两条线,但是没有1和0,通过高电平减去其对称的低电平(详情1.7.1 37分钟),可能有9V-0.6V
最显著的特征就是抗干扰能力比较强,传输质量比较稳定,现代通信一般使用差分信号,电平信号很少。
而且电压整体上移或者下移没有影响。
8位二进制并行通信时:需要16根线(8x2数据线)

串行通信和并行通信:看起来并行通信快一些,但是串行通信才是王道,因为省信号线,速度可以由通讯提高。

经过发展,最终胜出的通信:异步、串行、差分(USB和网络)

串口通信的基本概念

本节讲述传输通信涉及到的基本概念,如:波特率、起始位、数据位、奇偶校验位、停止位、RS232电平、TTL电平等。主要学习目的是让大家对串口通信涉及到的主要概念做个了解,方便后续课程中使用。

================================================================

7.2.串口通信的基本概念

1.7.2.1、串口通信的特点:异步、电平信号、串行
(1)、异步:串口通信的发送方和接收方之间是没有统一的时钟信号的。
(2)、电平信号:串口通信出现的时间较早,速率较低,传输的距离较近,所以干扰还不太明显,因此当时使用了电平信号传输。后期出现的传输协议都改成差分信号传输了。
(3)、串行通信:串口通信每次同时只能传输1个二进制位。

1.7.2.2、RS232电平和TTL电平
(1)电平信号是用信号线电平减去参考线电平得到电压差,这个电压差决定了传输值是1还是0.
(2)在电平信号时多少V代表1,多少V代表0不是固定的,取决于电平标准。譬如RS232电平中-3V~-15V表示1;+3~+15V表示0;TTL电平则是+5V表示1,0V表示0.
(3)不管哪种电平都是为了在传输线上表示1和0.区别在于适用的环境和条件不同。RS232的电平定义比较大,适合干扰大、距离远的情况;TTL电平电压范围小,适合距离近且干扰小的情况。
(4)我们台式电脑后面的串口插座就是RS232接口的,在工业上用串口时都用这个,传输距离小于15米;TTL电平一般用在电路板内部两个芯片之间。
(5)对编程来说,RS232电平传输还是TTL电平是没有差异的。所以电平标准对硬件工程师更有意义,而软件工程师只要略懂即可。(把TTL电平和RS232电平混接是不可以的)

1.7.2.3、波特率
(1)波特率(bandrate),指的是串口通信的速率,也就是串口通信时每秒钟可以传输多少个二进制位。譬如每秒种可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600.
(2)串口通信的波特率不能随意设定,而应该在一些值中去选择。一般最常见的波特率是9600或者115200(低端单片机如51常用9600,高端单片机和嵌入式SoC一般用115200).为什么波特率不可以随便指定?主要是因为:第一,通信双方必须事先设定相同的波特率这样才能成功通信,如果发送方和接收方按照不同的波特率通信则根本收不到,因此波特率最好是大家熟知的而不是随意指定的。第二,常用的波特率经过长久发展,就形成了共识,大家常用就是9600或者115200.

1.7.2.4、起始位、数据位、奇偶校验位、停止位

(1)串口通信时,收发是一个周期一个周期进行的,没周期传输n个二进制位。这一个周期就叫做一个通信单元,一个通信单元是由:起始位+数据位+奇偶校验位+停止位组成的。
(2)起始位表示发送方要开始发送一个通信单元;数据位是一个通信单元中发送的有效信息位;奇偶校验位是用来校验数据位,以防止数据位出错的;停止位是发送方用来表示本通信单元结束标志的。
(3)起始位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。
(4)数据位是本次通信真正要发送的有效数据,串口通信一次发送多少位有效数据是可以设定的(一般可选的有6、7、8、9,99%情况下我们都是选择8位数据位。因为我们一般通过串口发送的文字信息都是ASCII码编码的,而ASCII码中一个字符刚好编码为8位。)
(5)奇偶校验位是用来给数据位进行奇偶校验(把待校验的有效数据逐个位的加起来,总和为奇数奇偶校验位就为1,总和为偶数奇偶校验位就为0)的,可以在一定程度上防止位反转。
(6)停止位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。常见的有1位停止位,1.5位停止位,2位停止位等。99%情况下都是用1位停止位。

总结:串口通信时因为是异步通信,所以通信双方必须事先约定好通信参数,这些通信参数包括:波特率、数据位、奇偶校验位、停止位(串口通信中起始位定义是唯一的,所以一般不用选择)

异步通信: 发送方发送消息是想发就发
接收方必须是一直接收

串行性:一次只发一个

DB9接口:
DB9是早期串口通信早期比较常用的一种规范化接口
串行通信在早期是计算机与外界通讯的主要手段。DB9就是那个9针大头,我们只引出了3个
即TX RX GND。以前9针中的其余6根是用于流控的,现在多用于调试,禁用流控 ,所以6根就不再使用了。

================================================

7.4 UART 通用异步收发器
universal asynchronous reciver and transmitter

串口的时钟来源:APB peripheral BUS
串口分两部分:
发送器 和 接收器
由一个控制单元控制,但彼此独立。
发送器:用于210发送信息
接收器:用于从外部收信息到210
transmit shifter 发送移位器:帮助我们自动移位8位2进制,移动1位发1位。

将来计算串口控制器 的源时钟时,是以APB总线来计算的。要设定源时钟频率

transmitter由发送缓冲区和发送移位器构成,先将ASCII信息转为二进制流,然后将1帧数据写入发送缓冲区。剩下的发送部分是硬件自动的,发送移位器自动的读取,将其发送到Tx通信位。
我们只需要知道写 “要发送的信息”到发送缓冲区就行了

reciver由接收缓冲区和接收移位器。有人发送信息时,移位器自动将二进制保存至缓冲区

流控线有两端,发送方一根线拉高或拉低,接受方一根线拉低或拉高
其目的是串口通信非常可靠。发送方速率比接收方快的时候,流控可以保证 不会漏掉东西。
现在不用流控?是因为有了USB和Internet,有了更高级的工作方式。现在串口只用于SoC输出调试信息。而且由于硬件水平的提高,现在的串口的速度基本不会出现“发送方速率比接收方快”的情况,所以流控几乎是没有作用了。
所以我们就不用流控了=v=

==========================================

7.5串口补充:

串口重点在上面,但现在210等SoC设备,串口发展了高级功能,如下
FIFO模式
DMA模式
IrDA模式
这三个51都没有,stm32有一部分

FIFO模式
发送缓冲区是标准8字节,单独一个寄存器。但这里有一个发送效率的问题,要一直发一个字节一个字节不停的与CPU发送请求,CPU要切换进程,OS的效率就会很低。
解决方案:扩展串口的发送/接收缓冲区(如设置为64字节,CPU一次给64字节 待发送数据)
cpu就可以极大缓解反复切换进程的时间。

串口控制器要注意还是1字节缓冲区,这个有硬件问题和兼容问题所以不方便改。那么要实现这个想法就要启用FIFO模式。FIFO的内容依次送到transmit buffer register
FIFO,即先进先出,是一种数据结构。这个大的缓冲区也叫FIFO是因为他类似这种数据结构。我12345传进去,那么出的时候也是12345。

DMA模式
与FIFO要解决的是同一个问题,发送接收折腾CPU的问题,本来是一种DSP技术。
核心是:交换数据时,不需要CPU参与,模块可以自己完成。FIFO是轻量级解决方式,DMA则更根本。
FIFO的寄存器是有限的 可以设置成64Bit 或者128
DMA可以干脆设置个1M,根本解决串口去烦CPU的问题。
DMA就是专门解决大量数据的转移的。

IrDA模式

红外模式
红外编码的原理:发送方固定时间发送或者不发生,因此表示0或者1。接收方固定时间看有无红外来判断1或者0
1 0 0 0 0 1 1
有无无无无有有
这种原理非常类似于串口。

210上的某个串口有一个红外模式(IR mode),开启之后我们只需要向串口方式写数据,这些数据就以红外光的方式发射出去,接收方接收后即可解码得到发送信息。
其实是通过串口实现红外通信的。
(可以兼容红外芯片哦!集成哦!可以收遥控器红外信号哦!!)

============================================================

7.6 串口通信与中断

中断:单片机讲过了
发送方“一般”不需要中断 接收方“必须”使用中断,极端状态也可以不使用(轮询式…)
所以我们使用串口通信是需要中断的

RXD置1 说明串口控制器收到了1帧数据,告诉cpu
TXD中断 1帧数据发出去后,告诉cpu没东西发了,赶紧给我东西
不设置中断是,

发送方设置中断的情景: 允许cpu干别的
1发送方先设置一个中断,绑定处理函数
2发送方丢一帧数据给transmitter
3transmitter发送,发送方cpu去做别的事情
4发送完成返回一个TXD中断
5cpu切换回来给transmitter一帧数据,切换离开;循环。。。

发送方不使用中断: 让cpu盯住他,像监工一样看着他发,不发不干别的
1发送方禁止TXD中断
2发送方给一帧数据到transmitter
3transmitter发送数据,这段时间cpu没有切换,一直在这等
4发送方发送完成后,cpu再给一帧数据直到发完
(状态寄存器:里面有一个 发送缓冲区空标志bit,cpu一直读这个位)
cpu search 1 or 0 to get buffer empty

串口通信是异步的,发送方占据主导权。接收方必须时刻等待,不然后来的数据会冲掉前面的,从而丢数据。
我们一般是让发送方轮询,接收方使用中断。因为发送方可以耗,接收方耗不起。

(2)串口通信的时钟问题
串口需要时钟,需要一个固有波特率,transmitter、receiver都需要一个时钟信号
时钟问题:主要是波特率发生器。时钟从APB总线来,到串口控制器内部给波特率发生器(实质是个分频器),分频后得到一个低频的时钟,这个时钟就是给串口transmitter和rcver的。

波特率在uart channel 波特率 Divison 寄存器中 ,设置16bit分频系数
DIV_VAL = UBRDIVn + SLOT中“1”的个数 /16
DIV_VAL = (PCLK / (bps x 16)) - 1 bps:波特率
…
串口通信中时钟的设置,主要还是看寄存器
重点:
寄存器源设置(为串口控制器选择源时钟,一般PCLK_PSYS,也可以SCLK_UART)

注意:UBRDIVn UDIVSLOTn 这两个寄存器很重要
设置波特率 精确辅助设置
51单片机没这个东西,但210太快了,出错了就GG,所以有这么个寄存器为他校准
波特率不准会导致接受方可能出错,波特率越大越有可能不准!

===============================================================

7.7串口通信编程

分析程序,做准备工作
1 初始化tx rx引脚,参考原理图搜索TX,得知分别在GPA0_1 和 GPA0_0
GPA0CON = 0x?
2 关键寄存器
ULCON0 0x3 //无校验位,8数据位,1停止位
UCON0 0x5 //暂不使用DMA,PCLK作时钟源(bit10 置0),中断类型(开始不弄)
//回环模式:我自己发,自己收。我们不使用
///break signal 一般我们也用不到。。。
//发送接受模式都是 中断或轮询模式(interrupt or polling)
UFCON0 //选择接收和发送FIFO,不用FIFO的时候选默认就行
UMCON0 //AFC,modem中断,我们全都不用

UBRDIV0
UDIVSLOT0
//需要计算

在c语言中定义寄存器对应的宏,很重要的哦
GPA0CON E0200000
UBRDIV0
UDIVSLOT0
ULCON0
…
UTXH0 //管发送的
URXH0 //管接收的

rGPA0CON (*(volatile uint *)GPA0CON)
其他依法炮制

======================================

猜想

uart.c 文件

void uart_sendchar()
{
	while(tx缓冲池满);	//卡死在这里,等待为空
	rUTXH0 = 1	//发送1
}

void uart_init()
{
	rGPA0CON = 
	ULCON0		0x3 	 
	UCON0		0x5	 
	UFCON0		 
	UMCON0		 

	//计算bps
	UBRDIV0		
	UDIVSLOT0	
}

char uart_rcvchar()
{
	while(rx缓冲池满);	//卡死在这里,等待为空	
	return (rURXH0 & 0x0f);		
}

void main()
{
	while1	
	{
		发送1
		delay()
	}
}

==================================================================
8.1按键中断

1.8.1.2、按键的电学原理(结合原理图bv3 cv3分析)
(1)硬件接法: SW5:GPH0_2 SW6:GPH0_3
SW78910:GPH2_0123

(3)总结:按键的工作方法:其实就是按键的按下与弹开,分别对应GPIO的两种电平状态(按下则GPIO为低电平,弹开则GPIO为高电平)。此时SoC内部可以通过检测这个GPIO的电平高低来判断按键有没有被按下,这个判断结果即可作为SoC的输入信号。

1)SoC处理按键有2种思路:轮询方式和中断方式。
(2)轮询方式,就是SoC主动的每隔一段时间去读取(按键所对应的)GPIO的电平高低,以此获得按键信息;缺点在于CPU要一直注意按键事件,会影响CPU做其他事情。
(3)中断方式,就是SoC事先设定好GPIO触发的中断所对应的中断处理程序ISR,当外部按键按下或弹开时会自动触发GPIO对应的外部中断,导致ISR执行,从而自动处理按键信息。

GPH0CON (0xE0200C00)
GPH2DAT (0xE0200C04)
GPH2CON (0xE0200C40)
GPH2DAT (0xE0200C44)
在CON寄存器中将GPIO设置为input模式,然后去读取DAT寄存器

8.2.轮询方式处理按键的程序流程
(1)第一步,先初始化GPIO模式为input;
(2)第二步,循环读取GPIO的电平值,然后判断有无按键按下

8.3.串口输出和按键消抖
1.8.3.1、基于串口标准输出的按键调试
(1)以之前的串口stdio的工程为基础来移植添加轮询方式按键处理。
(2)注意USB下载方式可能有错误(有可能不下载,也有可能下载了执行不对),解决方案是用SD卡启动来替代。【使用那个x210_fusing_tool即可】

简易代码逻辑:轮询按键点亮led+串口打印信息
【main】

include "stdio.h"
{
	uart init();
	key init();
	key_polling();
}

【uart.c】

void uart_init(void)		//串口初始化
void putc(char c)			//串口输出
char getc(void)				//串口输入

【key.c】

void key_init(void)
{
	// 设置GPHxCON寄存器,设置为输入模式
}

void delay20ms(void)
{...}

void key_polling(void)
{
	// 依次,挨个去读出每个GPIO的值,
	//判断其值为1还是0.如果为1则按键按下,为0则弹起

	while(1)
	{
		if(GPH0DAT&(1<<2) )		/// xxxx x1xx,代表无按键 
		{
			led_off();
		}

		else
		{
			delay20ms();		//消抖
			if(!GPH0DAT&(1<<2) )	//延时后,再次检验
			{
				led1();			//按下,亮
				printf("key left");		//并且打印串口信息
			}
		}
	}
}

【led.c】

#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)
// 亮1个led
void led1(void)
{
	rGPJ0CON = 0x11111111;
	rGPJ0DAT = ((0<<3) | (1<<4) | (1<<5));
}

================================================================

8.4.S5PV210的中断体系介绍

(1)中断的发明是用来解决宏观上的并行需要的。宏观就是从整体上来看,并行就是多件事情都完成了。
(2)微观上的并行,就是指的真正的并行,就是精确到每一秒甚至每一刻,多个事情都是在同时进行的。宏观上面的并行并不等于围观的并行,有时候宏观上是并行的,微观上是串行的。
但是因为CPU很快,所以在宏观看来可以并行。
(5)为什么需要中断?因为单核CPU实际无法并行的,但是通过中断机制,可以实现假并行(宏观上的并行,微观上实际还是串行的)。

SoC对中断的实现机制:异常向量表
(1)异常向量表是CPU中某些特定地址的特定定义。当中断发生的时候,中断要想办法通知CPU去处理中断,怎么做到?这就要靠异常向量表。
(2)在CPU设计时,就事先定义了CPU中一些特定地址作为特定异常的入口地址(譬如定义0x00000000地址为复位异常向量地址,则发生复位异常时CPU会自动跳转到0x00000000地址去执行指令。又譬如外部中断对应的异常向量地址为0x30000008,则发生外部中断后,CPU会硬件自动跳转到0x30000008地址去执行指令。)如
(3)以上讲的是CPU硬件设计时对异常向量表的支持,下来就需要软件支持了。硬件已经决定了发生什么异常CPU自动跳转PC到哪个地址去执行,软件需要做的就是把处理这个异常的代码的首地址填入这个异常向量地址。

S5PV210的异常向量表
(1)异常向量表在1.2.14节讲过,可以返回去听一下
(2)异常向量表中各个向量的相对位置是固定的,但是他们的起始地址是不固定的,各种SoC可以不一样,而且复杂ARM中还可以让用户来软件设置这个异常向量表的基地址。
(3)扩展到所有架构的CPU中:所有架构(譬如51单片机、PIC单片机)的CPU实现中断都是通过异常向量表实现的,这个机制是不变的;但是不同CPU异常向量表的构造和位置是不同的。

===================================================

8.5.异常向量表的编程处理

像内存一样去访问异常向量表
(1)S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是目前系统刚启动时,此时DRAM尚未初始化,程序都在SRAM中运行。210在iRAM中设置了异常向量表,供暂时性使用。
(2)查210的iROM application note文档中iRAM的地址分配,可知,iRAM中的异常向量表起始地址为0xD0037400。知道了异常向量表的起始地址后,各个异常对应的入口就很好知道了。

1.8.5.2、函数名的实质就是函数的首地址
拿C语言中的语法来讲,函数名就是这个函数的函数指针。
总结:当我们将异常处理程序的首地址和异常向量表绑定起来后,异常处理初步阶段就完成了。到目前可以保证相应异常发生后,硬件自动跳转到对应异常向量表入口去执行时,可以执行到我们事先绑定的函数。

为什么中断处理要先在汇编中进行??
(1)中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备返回SVC模式前,要将保存的SVC模式下的必要寄存器的值恢复回去,不然到了SVC模式后寄存器的值乱了,SVC模式下原来正在进行的常规任务就被你搞坏了)
(2)保存现场包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12
(3)为什么要保存LR寄存器?
要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。
中断返回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的CPSR给CPSR。
(4)中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中

(1)保护现场关键是保存:中断处理程序的返回地址,r0-r12(cpsr是自动保存的)
(2)恢复现场主要是恢复:r0-r12,pc,cpsr

代码编写
【start.s】

{
设置SVC栈
...
bl  main		//规则性函数:IRQ普通中断的现场保护函数
...
}

IRQ_handle:
	ldr sp, =IRQ_STACK		//设置IRQ下的栈
	sub lr, lr, #4		//保存LR,因为流水线的原因要-4
				// 01 | 02  03
				//我们pc要返回的是02,在01结束后加载,03加载完
	stmfd sp! , {r0-r12 , lr}	//保存 r0-r12和lr 到IRQ模式下的栈上
	bl irq_handler		//调用真正的isr来处理中断
	ldmfd sp! , {r0-r12 , pc}^	//这句是恢复现场,r0-r12,pc
				// "^"的作用是恢复cpsr

【int.c】

	r_exception_irq = (unsigned int)IRQ_handle;	//地址赋值!
	//IRQ函数的地址通过【异常向量表】绑定。这里运用了中断向量表基地址和偏移量!!
	
void irq_handler(void)	//真正的中断处理函数在这里,不考虑现场保护、恢复
{
	printf("irq_handler")
	//如果有多个IRQ中断,那么都会到这个函数里来
	//需要一种方法产生分支
}

注意:这些代码看懂即可,他们是开发中的定式,是套路,大家做都一个样子,不需要自己写

================================================================

1.8.6.S5PV210的向量中断控制器

1.8.6.1、异常处理的2个阶段
(1)可以将异常处理分为2个阶段来理解。
第一个阶段是异常向量表跳转;
第二个阶段就是进入了真正的异常处理程序irq_handler之后的部分。

1.8.6.2、回顾:中断处理的第一阶段(异常向量表阶段)处理。
(1)第一个阶段之所以能够进行,主要依赖于CPU设计时提供的异常向量表机制。第一个阶段的主要任务是从异常发生到响应异常并且保存/恢复现场、跳转到真正的异常处理程序处。
(2)第二个阶段的目的是识别4个中断源中究竟哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。

(1)第一阶段(异常向量表阶段)2440和210几乎是完全相同的。实际上几乎所有的CPU在第一阶段都是相同的。
(2)第二阶段就彼此不同了。各个SoC根据自己对实时性的要求,和支持的中断源的多少,各自发明了各自处理中断,找到中断编号,进一步找到对应isr地址的方式。

8.7.S5PV210中断处理的主要寄存器
8.7.1、
VICnINTENABLE和VICnINTENCLEAR
INTENABLE寄存器负责相应的中断的使能,INTENCLEAR寄存器负责相应的中断的禁止。
VICnINTSELECT
设置各个中断的模式为irq还是fiq。一般都设置成irq
CPU如何保证fiq比irq快?有2个原因:第一,fiq模式有专用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,这就能节省时间;第二,异常向量表中fiq是最后一个异常向量入口。因此fiq的isr不需要跳转,可以直接写在原地,这样就比其他异常少跳转一次,省了些时间。
VICnIRQSTATUS和VICnFIQSTATUS
中断状态寄存器,是只读的 靠查询这个寄存器来得到中断编号的
VICnVECTPRIORITY0~VICnVECTPRIORITY31
中断优先级设置寄存器
VICnVECTADDR0~VICnVECTADDR31、VICnADDR
这三个寄存器和210中断处理第二阶段的第二阶段有关。

=============================================================

8.8.S5PV210中断处理的编程实践1
整个中断的工作分为2部分:
第一部分是我们为中断响应而做的预备工作:
1. 初始化中断控制器,禁止所有中断、选择中断类型、清VICxADDR

	   void intc_init(void)

// 为什么在中断初始化之初要禁止所有中断?
// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
// 则程序很可能跑飞,所以不用的中断一定要关掉。
// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
// 给这个中断提供相应的isr并绑定好。

2. 绑定写好的isr到中断控制器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
		void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
										 物理中断号	 isr函数指针
								==========================================-

    //VIC0
   if(intnum<32)
    {
        *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) =(unsigned)handler;
    }
    //VIC1
    else if(intnum<64)
    {
        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;
    }

//s5pv210的中断支持128个中断,使用VIC0~VIC3四个寄存器表示他们
物理中断号号最大设计为128,但是注意中断在寄存器中的port最大为32

3、相应中断的所有条件使能
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号

void intc_enable(unsigned long intnum)

第二部分是当硬件产生中断后如何自动执行isr:
1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口
2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR
寄存器中取isr来执行即可。
4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。

void irq_handler(void)
{
	// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须
	// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中
	unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
   	 int i=0;
   	 void (*isr)(void) = NULL;

   	 for(i=0; i<4; i++)
  	  {	
		// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0
        		if(intc_getvicirqstatus(i) != 0)
        		{
            			isr = (void (*)(void)) vicaddr[i];
            			break;
        		}
    	}
   	 (*isr)();		// 通过函数指针来调用函数	
}

VIC0INTENABLE这个寄存器写1是使能,写0则没有影响,想要失能需要用VIC0INTENCLEAR
VIC0INTENCLEAR某一位置1,清除某一个中断使能

关于VIC0ADDR和VIC0VECTADDR0-31
前者是当前中断函数的地址,硬件自动获得
后者是VIC0中中断号对应的isr处理函数地址,由我们填写。

TIPS:unsigned long unsigned int unsigned
这三种写法在32位系统中是一样的

===============================================================

8.10.外部中断

1.8.10.1、什么是外部中断?数据手册在哪里?
(1)SoC支持的中断类型中有一类叫外部中断。内部中断就是指的中断源来自于SoC内部(一般是内部外设),譬如串口、定时器等部件产生的中断;外部中断是SoC外部的设备,通过外部中断对应的GPIO引脚产生的中断。
(2)按键在SoC中就使用外部中断来实现。具体实现方法是:将按键电路接在外部中断的GPIO上,然后将GPIO配置为外部中断模式。此时人通过按按键改变按键电路的电压高低,这个电压高低会触发GPIO对应的外部中断,通过引脚传进去给CPU处理。
(3)外部中断相关的介绍和寄存器都在2.2.6章节(属于GPIO部分)

)外部中断的触发模式主要有2种:电平触发和边沿触发。
关键寄存器:CON、PEND、MASK
(1)外部中断的主要配置寄存器有3个:EXT_CON、EXT_PEND、EXT_MASK
(2)EXT_CON 配置外部中断的触发方式。
触发方式就是说外部电平怎么变化就能触发中断
电平触发/边缘触发
(3)EXT_PEND 寄存器是中断挂起寄存器。
这个寄存器中每一位对应一个外部中断,平时没有中断时值为0。 平时0,发生中断时候置1。中断触发之后我们需要将其手动置0。
(4)EXT_MASK 寄存器就是各个外部中断的使能/禁止开关。

分析X210开发板的按键对应的EINT编号:
EINT2、EINT3、EINT16、EINT17、EINT18、EINT19

自己总结中断要注意的几个问题:
1 每次中断完记得清挂起,清中断标志位
这里的中断标志位不仅要清EINT模式的,总中断也要清
bug现场:按键一次后下一次就失效了
rEXT_INT_0_PEND |= (1<<3);
intc_clearvectaddr();

2 中断设置好(向量表、初始化中断控制器、绑定、使能中断、现场保护恢复)之后
若要使用key,我们还需要做:
1按键对应的引脚设置为 外部中断
2main函数中包含需要的函数声明,使用头文件
3编写各个按键对应的isr_eint(void)

===============================================================

9 定时器、看门狗和定时器

这三个放在一起讲是因为他们有内在联系:都与时间相关

9.1定时器
定时器也是个外设,常常和计数器纠缠在一起。
计数器每隔一定时间计一个数 计数值x时间周期 = 定时器值
其实定时器和计数器就是一回事

定时器用来实现定时执行代码的作用。他可以产生中断提醒CPU,并执行定时器中断isr

定时器内部有一个计数器,计数器根据时钟(APB总线)来工作。每隔一个时钟周期计数器+1
定时器内部有一个TCNT寄存器,存放计数值
“TCNT – IFTCNT == 0 THEN BL ISR ”
定时器变量:
1 TCNT计数值
2 时钟周期

system timer 即systick,系统定时器,为操作系统提供时间片。系统移植时由原厂给我们。

===============================================================

9.3 pwm定时器

pwm定时器叫这个名字,是因为他本身就设计为PWM作用的,也是普通定时器。
pwm定时器:
时钟源:PCLK_PSYS
T1/T0 共同使用一个预分频器
T234 共同使用一个预分频器
每个分频器有一个专用的 独立分频器
形成了二级分配系统

定时器输入频率 = PCLK / (预分频值+1)/ 分频值

预分频值+1是为了什么?
为了防止一种情况,编程人是个傻逼把0作为了除数,那么可能导致短路引起cpu烧毁
cpu设计的时候考虑了这种情况,为了程序员的容错。
很多地方都有这种思路

关键点: 时钟源 分频器 预分频器 分频系数分别在TCFG0 TCFG1中设定
两个寄存器:
TCMP TCNT 程序员不能访问,与B对应
TCMPB TCNTB 有地址的寄存器
dead-zone死区
(框图中的 三角+圆圈 的符号代表逻辑取反)
自动重载(自动装载“auto reload”)和双缓冲(double buffering)

分频器 实质是mux开关,最大16分频
预分频器 实质是8位寄存器,最大256分频
分频结果串联,即相乘。最小1分频,最大16x256
x210所能 定时 的最长的时间是266548s,远远够用,TCNTB = 2的32次方

为什么设计两套寄存器?
TCMP TCNT 没有寄存器地址,程序员不能访问
TCMPB TCNTB
tips : TCMP的系列作用是生成pwm波形

我们想给定时器里面 放 300
TCNT 负责减
TCNTB 负责让程序员往里面存数

在减的过程中TCNTB永远不变。如果不这样设计,你总不能一直减基准值吧?
全过程:
start >> init
TCNT = TCNTB = 300
start >> timer
loop(TCHT – )

这里要讲一下以后要讲的重装载。TCNT这种称为“影子寄存器”

TCNTO 是能访问的TCNT,observeable,可观察的

自动重载
定时器一次定时算一个工作循环,如果我们想要反复加载定时器的值:
最简单最笨的方法:人为用代码制造循环

现在的高级SoC:默认了内置自动装载,发生了一次中断,到了下一个周期会自动装载。

======================================================
PWM

两个重要参数:
周期 T
占空比 duty 高电平的时间 / 总周期时间

eg编程:拉高电平,定时到达T x duty时间,把电平拉低,T x (1 - duty),把电平拉高。

以前的51单片机是没有专用的pwm定时器的,需要自己结合GPIO和定时器中断编程。
现在我们不用中断就可以生成pwm波形了,而是使用两个寄存器:TCNTB TCMPB。

TCNTB	决定周期
TCMPB	决定占空比

TCNTB x 时钟周期 可以得到pwm波形的周期

输出电平翻转器
TCMPB 可以制造一次电平翻转
可以规定:当TCNT > TCMPB 时为高电平
当TCNT < TCMPB 时为低电平
基于上述,如果duty从30%变到70%,TCMPB随之要从210变化到90.
在这两种规定下,TCMPB中的值会发生变化。于是x210为我们设计了输出电平翻转器。

在编程上反映为一个寄存器位!TCON中的output inverter就可以开启电平翻转
输出电平一翻转,30%的占空比就变成70%了

死区生成器
死区的设计时为了pwm应用,用在我们的功率电路中,给电路进行整流
交流整成直流
整流需要两路电流,两路分别在正电平和负电平导通工作,两路不能同时导通,否则会短路。大功率的开关电源、逆变器广泛使用了整流技术。“这边高那边低”,两路如果同时改变的话很有可能导致短暂的“处于同一个电平”,这个时候就短路了。
死区就是为了解决这种情况,让另一流滞后一点,有点像“按键消抖”,为了安全
死区在设计产品的时候不能多也不能少。多了精度小,没有则很危险。
我们的210提供了自带的死区生成器,用户直接用死区控制功能就好了=v=

ARM裸机编程系列--nand NAND Flash 关于NANDFlash S5PV210的NANDFlash控制器有如下特点: 1) 支持512byte,2k,4k,8k的页大小 2) 通过各种软件模式来进行NANDFlash的读写擦除等 3) 8bit的总线 4) 支持SLC和MCL的NAND Flash 5) 支持1/4/8/12/16bit的ECC 6) 支持以字节/半字/字为单位访问数据/ECC寄存器 阅读详情

相关推荐

ARM裸机的知识点总结---------16、shell简易实战(cmd形式)

Author: 想文艺一点的程序员 自动化专业 工科男 再坚持一点,再自律一点 优快云@想文艺一点的程序员 来自朱有鹏嵌入式的学习笔记 目录一、前期梳理1、字符串的知识补缺。二、shell 小项目梳理 (在 arm裸机 16部分 shell_cmd_led)1、start.s2、makefile3、shell.h (避免重复宏定义)4、string.c (自己写的字符串处理函数)5、uart5、stdio.c(其实就是对 uart 进行改造为标准输入输出)6、cmd.c7、main.c 一、前期梳理 .

vincent3678的博客 419

ARM中断和异常

中断的控制器太多,内容太多复杂,另起一片文章来专门收集。 触发以及CUP的处理流程:产生中断--->mov VICxADDRESS(x=0,1,2,3) VICxVECTADDRy(x=0,1,2,3,y=0-31)--->excute VICxADDRESS(x=0,1,2,3) 各种寄存器直接的关系: 其他一些资料:

hrdzkj的专栏 1046

GD32F105RBT6 keil工程模板

创建GD32F105RBT6的keil初始基础工程,未来的所有外设驱动都是基于这个基础工程进行扩展。

RMDYBW的博客 1925

ARM中C语言编程中断实验总结,ARM11外部中断C语言编程实现总结(5页)-原创力文档...

ARM11(3)——外部中断WF 2013.1.24最近正在学习ARM11(S3C6410开发板)的外部中断部分,稍有心得,特此总结一些学习笔记,分享出来,献给那些像我一样正在ARM11上裸奔的嵌入式学徒们,同时也希望看到此文的业界前辈多多指教。外部中断基础知识总结:ARM11(S3C6410开发板)共有187根多功能I/O口,其中127根可用于外部中断,而这127个外部中断引脚又被分为十组:E...

weixin_39955829的博客 501

Cadence OrCAD: 层次化设计 Hierarchical Design

详细介绍OrCAD的原理图层次化设计方法。

Fantasy237的博客 1万+

PADS VX2.4 缝合孔(阵列过孔)的添加方法

缝合孔的主要目的是:①:增加底层和顶层的地铜皮的连接性,②:起到屏蔽外界信号干扰的作用。铺完铜后,右击空区域选择“选择形状”命令,选取整板中的一块大铜皮,右击选择“覆铜区域内过孔阵列模式”→“沿周边”指令后,再右击选择“覆铜区域内过孔阵列”,便能看到已选区域内添加的缝合孔(过孔阵列),也可以通过铜皮显示或隐藏的无模命令:PO进行查看。完成上面的“防止错误”和“过孔样式”的设置后,开始对整板进行铺铜处理,点击“工具栏”→覆铜平面指示器,在弹出的对话框点击“开始”,便能观察铺铜后的板子。

tianpu2320959696的博客 3030

【笔记】ARM裸机程序开发_part1

ARM裸机开发的一些基础知识,基于x210开发板

一枚菜菜子的充电站 2626

从零到一:DAMASK与ABAQUS联合仿真的环境配置艺术

本文详细介绍了在Ubuntu 16.04系统中配置DAMASK与ABAQUS2017联合仿真环境的完整流程,涵盖系统基础环境搭建、Python科学计算环境构建、DAMASK核心安装与配置,以及ABAQUS集成与联合调试。通过实战案例和高级调试技巧,帮助用户快速掌握有限元分析中的晶体塑性模拟技术。

weixin_29253711的博客 856

【笔记】ARM裸机程序开发_part2

四、GPIO和LED 4.1usb配置DNW启动DNW驱动安装需要数字签名,我们装好驱动后,连接USB线,配置DNW 下载地址是0xd0020010(这个地址是BL1的地址,约定好辣~) 按住power,DNW中选择Transmit,下载要装载的裸机程序其原理也就是说,我们把裸机程序当成了BL1,上电后运行BL1也就是运行了写好的裸机程序。关于0xd0020010的由来,参考官方的iROM手册

一枚菜菜子的充电站 1186

VulnHub靶机渗透实战(详细思路讲解)

使用kali中的namp工具扫描开放端口,这时kali会用一个端口号向目标主机的65535个端口发送TCP连接建立请求,回应了ack=1,syn=q的端口就是开放端口号,这里可以看到21,22,80,3306号端口是开放的。可以看到这里得到了很多哈希,带*号的是禁用的或者是空密码,将三个不带星号的密码复制下来,创建一个shell.txt文档,将三个密码复制进去。这里看到我们是没有成功进入这个网页的,应该是目录的问题,我们将目录修改一下,但是没有显示出来,可能是编码问题,再回到我们找到的源码。

weixin_65891791的博客 2543

Protege新手入门:5分钟搞定本体建模基础(附实战案例)

本文为Protege本体建模新手提供了一份快速入门指南。通过构建一个关于“披萨”的实战案例,详细讲解了从环境准备、定义核心类(Classes)、建立对象属性(Object Properties)到设置数据属性(Data Properties)和创建个体的完整流程。文章旨在帮助读者绕过复杂理论,在5分钟内直观掌握本体建模的基础操作,快速建立知识图谱构建的信心。

weixin_28335283的博客 281

【笔记】ARM裸机程序开发_part5

总线上的其他设备全部都能接收到,总线上的从设备都到地址后,要和自己的地址进行比较看是否相等。主设备先释放总线,然后从设备回应ACK。如果从设备没有拉低总线,则主设备看到的现象是总线在一直高,没有收到ACK,主设备就认为刚刚发送的8bit接收失败。这里有一个隐情:gsensor中发送多个DATA数据不会写到一个寄存器里,而是gsensor有逻辑,每当发送一个额外的DATA,寄存器地址都会向后移动。每个I2C设备都有一个从地址,该地址是唯一的,是设备本身固有的设备,在通信中通过地址来甄别各个I2C设备。

一枚菜菜子的充电站 1082

基于Cesium实现,动态水面+大坝排水动画

一、绘制面状水域,使用水波纹理,进行渲染,水体透明,可以看到底色。三、使用粒子系统,将喷泉改变方向,加上重力设置,实现排水动画。二、绘制线状三维体,用作模拟大坝拦截效果。四、最后,模拟水位上升,进行淹没分析,

一醉千秋的专栏 1418

【笔记】ARM裸机程序开发_part4

10 SD卡详解内存和外存的区别:一般是把这种RAM(random access memory,随机访问存储器,特点是任意字节读写,掉电丢失)叫内存,把ROM(read only memory,只读存储器,类似于Flash SD卡之类的,用来存储东西,掉电不丢失,不能随机地址访问,只能以块为单位来访问)叫外存1.10.1.1、软盘、硬盘、光盘、CD、磁带 (1)存储原理大部分为磁存储,缺点是读写速

一枚菜菜子的充电站 746

Qt 【window udp 高频采集丢包问题】

使用Asio + 无锁队列或者环形缓存区方案可以解决 qt udp 在window平台高速采集 (128k)丢包问题。

weixin_45397344的博客 177

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ARM Cortex-A9双核裸机通信:共享内存与中断同步实战指南

多核处理器通信是现代嵌入式系统的核心技术,其原理基于共享内存机制实现高效数据交换。共享内存通过零拷贝技术直接在物理内存中划分特定区域,使多个核心能够并行访问同一数据空间,大幅降低通信延迟。在ARM Cortex-A9架构中,这种技术结合中断同步机制,通过通用中断控制器(GIC)管理核间中断(SGI),确保数据访问的线程安全性。其技术价值在于为实时嵌入式应用提供低功耗、高可靠性的并行处理能力,广泛应用于工业控制、汽车电子和物联网设备等场景。本文以Xilinx Zynq-7000平台为例,详细解析裸机环境下双核

chengyixian7877的博客 289

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Eclipse开发调试ARM裸机程序(七)sd卡读写

Eclipse开发调试ARM裸机程序(七)sd卡读写          主要用到了sd card官网上的几个文档,《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10 January 22, 2013》里边讲了SD卡内部的寄存器,一般的时序。还有《SD Specifications

Android/Linux的专栏 3865

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Linux内核移植 part1:arm gcc 编译与链接参数

虽然当前android啥的是主流,但是在进行裸机开发的时候,熟悉编译器这些参数就很重要了。下面以uboot的编译和链接过程为实例介绍arm gcc相关的参数。具体内容可以参考官方文档: https://gcc.gnu.org/onlinedocs/ pdf版本下载:https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc.pdf编译参数arm-none-linux-

叶玄青的博客 3111

树莓派4裸机操作系统开发(Part5):帧缓冲显示技术详解

在之前的教程中,我们通过UART实现了基本的输入输出功能。本文将带领大家探索更激动人心的图形显示技术,实现"Hello world!"在屏幕上的显示。我们将深入理解帧缓冲(Framebuffer)的工作原理,并掌握与VideoCore多媒体处理器通信的邮箱机制。 ## 邮箱机制:与VideoCore的通信桥梁 在树莓派系统中,ARM处理器需要通过**邮箱**(Mailbox)机制与VideoC...

gitblog_01136的博客 560

Using QEMU for Embedded Systems Development, Part 3

In embedded systems, especially in mobile devices, ARM processor-based devices are leading the market. For ARM, U-Boot is the best choice for a bootloader. The good thing about it is that we can use i

iltgcl的专栏 834

Part2:S3C2440裸跑一个LED程序

0 写在前面 目标:写一个跑在ARM9为核心的s3c2240开发板的LED程序 三步走:1)读原理图,明确操作对象——即找到LED输出引脚 2) 明确操作,即如何控制LED 3)代码编写——重点!分别用汇编和C实现 这里,我把1)2)步合成一部分讲解,重点讲解代码编写(涉及到ARM汇编指令、C函数调用栈机制) 1 明确“操作对象”和“操作过程” 操作对象——LE...

MK 589

Linux内核移植 part1:全面介绍ARM Linux启动流程

本文介绍arm核cpu裸机启动过程。在cpu reset之后,pc会指向reset vector (地址位于0x00000000 or 0xFFFF0000),此时的代码需要做以下几件事情: 在多核系统中,让非主要核睡眠。 初始化异常向量 初始化memory,包括MMU 初始化不同模式下的栈以及寄存器 初始化关键I/O设备 针对NEON或VFP做些必要的初始化 使能中断 改变核心模式或者状态 如果需

叶玄青的博客 9068

嵌入式系统的移植

在大学时一直在玩单片机开发,但是都是裸机在跑,一直没有能够跑系统,当时可能觉得很难,或者觉得项目没有必要上系统。。。。。 操作系统与裸机的区别 裸机运行的程序代码,一般由一个main函数中的while死循环和各种中断服务程序组成,平时CPU执行while循环中的代码,出现其他事件时,跳转到中断服务程序进行处理,没有多任务、线程的概念。 而引入操作系统后,程序执行时可以把一个应用程序分割为多个任...

专心扫地 889

Power Architecture平台调试实战:从裸机到Hypervisor的CodeWarrior全流程指南

调试是嵌入式开发中确保软件可靠性的核心环节,其本质是调试器与目标系统之间建立可控的通信与观察机制。在Power Architecture这类高性能处理器平台上,调试面临裸机、Linux应用及Hypervisor虚拟化等多重复杂场景的挑战。CodeWarrior Development Studio通过其Target Resident Kernel(TRK)协议,为这些场景提供了统一的调试框架,其技术价值在于能够实现对目标程序执行状态的精确“看见”与“操纵”。无论是通过JTAG/USB TAP直接控制处理器,

weixin_30628077的博客 366

告别裸机双核打架:手把手教你用ZYNQ-7000 OCM实现CPU0与CPU1的AMP通信(Vivado 2023.1实战)

本文详细介绍了如何在ZYNQ-7000 SoC上实现双核AMP通信,解决裸机环境下双核资源冲突问题。通过OCM(On-Chip Memory)信箱机制和防冲突设计,确保CPU0与CPU1的高效通信,适用于Vivado 2023.1开发环境。文章包含硬件配置、软件架构及实战案例,帮助开发者快速掌握ZYNQ双核通信技术。

weixin_30252709的博客 277
上一篇: 【笔记】ARM裸机程序开发_part2
下一篇: 【笔记】ARM裸机程序开发_part4
睡不醒的小泽
博客等级 码龄11年 1426粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值