目录

​​​​​ 一、中断是什么

1.例1:

2.例2:

3.硬件和软件配合来协同实现中断处理的全过程

3.流程中出现的问题

中断处理经历的保存和恢复过程中的步骤:

二.什么是DMA

1.原理

1).请求

2).响应

3).传输

4).结束

2.传送方式

三.实验代码

四.实验结果

五.总结


​​​​​​​


​​​​​​​

​​​​​ 
一、中断是什么

中断的汉语解释是半中间发生阻隔、停顿或故障而断开。那么,在计算机系统中,我们为什么需要“阻隔、停顿和断开”呢?

1.例1:

举个日常生活中的例子,比如说我正在厨房用煤气烧一壶水,这样就只能守在厨房里,苦苦等着水开——如果水溢出来浇灭了煤气,有可能就要发生一场灾难了。等啊等啊,外边突然传来了惊奇的叫声“怎么不关水龙头?”于是我惭愧的发现,刚才接水之后只顾着抱怨这份无聊的差事,居然忘了这事,于是慌慌张张的冲向水管,三下两下关了龙头,声音又传到耳边,“怎么干什么都是这么马虎?”。伸伸舌头,这件小事就这么过去了,我落寞的眼神又落在了水壶上。

门外忽然又传来了铿锵有力的歌声,我最喜欢的古装剧要开演了,真想夺门而出,然而,听着水壶发出“咕嘟咕嘟”的声音,我清楚:除非等到水开,否则没有我享受人生的时候。

这个场景跟中断有什么关系呢?

如果说我专心致志等待水开是一个过程的话,那么叫声、电视里传出的音乐不都让这个过程“半中间发生阻隔、停顿或故障而断开”了吗?这不就是活生生的“中断”吗?

在这个场景中,我是唯一具有处理能力的主体,不管是烧水、关水龙头还是看电视,同一个时间点上我只能干一件事情。但是,在我专心致志干一件事情时,总有许多或紧迫或不紧迫的事情突然出现在面前,都需要去关注,有些还需要我停下手头的工作马上去处理。只有在处理完之后,方能回头完成先前的任务,“把一壶水彻底烧开!”

中断机制不仅赋予了我处理意外情况的能力,如果我能充分发挥这个机制的妙用,就可以“同时”完成多个任务了。回到烧水的例子,实际上,无论我在不在厨房,煤气灶总是会把水烧开的,我要做的,只不过是及时关掉煤气灶而已,为了这么一个一秒钟就能完成的动作,却让我死死地守候在厨房里,在10分钟的时间里不停地看壶嘴是不是冒蒸气,怎么说都不划算。我决定安下心来看电视。当然,在有生之年,我都不希望让厨房成为火海,于是我上了闹钟,10分钟以后它会发出“尖叫”,提醒我炉子上的水烧开了,那时我再去关煤气也完全来得及。我用一个中断信号——闹铃——换来了10分钟的欢乐时光,心里不禁由衷地感叹:中断机制真是个好东西。

正是由于中断机制,我才能有条不紊地“同时”完成多个任务,中断机制实质上帮助我提高了并发“处理”能力。它也能给计算机系统带来同样的好处:如果在键盘按下的时候会得到一个中断信号,CPU就不必死守着等待键盘输入了;如果硬盘读写完成后发送一个中断信号,CPU就可以腾出手来集中精力“服务大众”了——无论是人类敲打键盘的指尖还是来回读写介质的磁头,跟CPU的处理速度相比,都太慢了。没有中断机制,就像我们苦守厨房一样,计算机谈不上有什么并行处理能力。

跟人相似,CPU也一样要面对纷繁芜杂的局面——现实中的意外是无处不在的——有可能是用户等得不耐烦,猛敲键盘;有可能是运算中碰到了0除数;还有可能网卡突然接收到了一个新的数据包。这些都需要CPU具体情况具体分析,要么马上处理,要么暂缓响应,要么置之不理。无论如何应对,都需要CPU暂停“手头”的工作,拿出一种对策,只有在响应之后,方能回头完成先前的使命,“把一壶水彻底烧开!”

先让我们感受一下中断机制对并发处理带来的帮助。

让我们用程序来探讨一下烧水问题,如果没有“中断”(注意,我们这里只是模仿中断的场景,实际上是用异步事件——消息——处理机制来展示中断产生的效果。毕竟,在用户空间没有办法与实际中断产生直接联系,不过操作系统为用户空间提供的异步事件机制,可以看作是模仿中断的产物),设计如下:

void StayInKitchen()

{

bool WaterIsBoiled = false;

while ( WaterIsBoiled != true )

{

bool VaporGavenOff = false;

if (VaporGavenOff )

WaterIsBoiled = true;

else

WaterIsBoiled = false;

}

// 关煤气炉

printf(“Close gas oven./n”);

// 一切安定下来,终于可以看电视了,10分钟的宝贵时间啊,逝者如斯夫…

watching_tv();

return;

}

可以看出,整个流程如同我们前面描述的一样,所有工作要顺序执行,没有办法完成并发任务。

2.例2:

如果用“中断”,在开始烧水的时候设定一个10分钟的“闹铃”,然后让CPU去看电视(有点难度,具体实现不在我们关心的范围之内,留给读者自行解决吧:>)。等闹钟响的时候再去厨房关炉子。

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

// 闹钟到时会执行此程序

void sig_alarm(int signo)

{

//关煤气炉

printf(“Close gas oven./n”);

}

void watching_tv()

{

while(1)

{

// 呵呵,悠哉悠哉

}

}

int main()

{

// 点火后设置定时中断

printf(“Start to boil water, set Alarm”);

if (signal( SIGALRM, sig_alrm ) == SIG_ERR)

{

perror("signal(SIGALRM) error");

return -1;

}

// 然后就可以欣赏电视节目了

printf(“Watching TV!/n”);

watching_tv();

return 0;

}

这两段程序都在用户空间执行。第二段程序跟中断也没有太大的关系,实际上它只用了信号机制而已。但是,通过这两个程序的对比,我们可以清楚地看到异步事件的处理机制是如何提升并发处理能力的。

Alarm定时器:alarm相当于系统中的一个定时器,如果我们调用alarm(5),那么5秒钟后就会“响起一个闹铃”(实际上靠信号机制实现的,我们这里不想深入细节,如果你对此很感兴趣,请参考Richard Stevens不朽著作《Unix环境高级编程》)。在闹铃响起的时候会发生什么呢?系统会执行一个函数,至于到底是什么函数,系统允许程序自行决定。程序员编写一个函数,并调用signal对该函数进行注册,这样一旦定时到来,系统就会调用程序员提供的函数(CallBack函数?没错,不过在这里如何实现并不关键,我们就不引入新的概念和细节了)。上面的例子里我们提供的函数是sig_alarm,所做的工作很简单,打印“关闭煤气灶”消息。

上面的两个例子很简单,但很能说明问题,首先,它证明采用异步的消息处理机制可以提高系统的并发处理能力。更重要的是,它揭示了这种处理机制的模式。用户根据需要设计处理程序,并可以将该程序和特定的外部事件绑定起来,在外部事件发生时系统自动调用处理程序,完成相关工作。这种模式给系统带来了统一的管理方法,也带来无尽的功能扩展空间。

计算机系统实现中断机制是非常复杂的一件工作,再怎么说人都是高度智能化的生物,而计算机作为一个铁疙瘩,没有程序的教导就一事无成。而处理一个中断过程,它受到的限制和需要学习的东西太多了。

首先,计算机能够接收的外部信号形式非常有限。中断是由外部的输入引起的,可以说是一种刺激。在烧水的场景中,这些输入是叫声和电视的音乐,我们这里只以声音为例。其实现实世界中能输入人类CPU——大脑的信号很多,图像、气味一样能被我们接受,人的信息接口很完善。而计算机则不然,接受外部信号的途径越多,设计实现就越复杂,代价就越高。因此个人计算机(PC)给所有的外部刺激只留了一种输入方式——特定格式的电信号,并对这种信号的格式、接入方法、响应方法、处理步骤都做了规约(具体内容本文后面部分会继续详解),这种信号就是中断或中断信号,而这一整套机制就是中断机制。

其次,计算机不懂得如何应对信号。人类的大脑可以自行处理外部输入,我从来不用去担心闹钟响时会手足无措——走进厨房关煤气,这简直是天经地义的事情,还用大脑想啊,小腿肚子都知道——可惜计算机不行,没有程序,它就纹丝不动。因此,必须有机制保证外部中断信号到来后,有正确的程序在正确的时候被执行。

还有,计算机不懂得如何保持工作的持续性。我在看电视的时候如果去厨房关了煤气,回来以后能继续将电视进行到底,不受太大的影响。而计算机则不然,如果放下手头的工作直接去处理“意外”的中断,那么它就再也没有办法想起来曾经作过什么,做到什么程度了。自然也就没有什么“重操旧业”的机会了。这样的处理方式就不是并发执行,而是东一榔头,西一棒槌了。

那么,通用的计算机系统是如何解决这些问题的呢?它是靠硬件和软件配合来协同实现中断处理的全过程的。我们将通过Intel X86架构的实现来介绍这一过程。

3.硬件和软件配合来协同实现中断处理的全过程

CPU执行完一条指令后,下一条指令的逻辑地址存放在cs和eip这对寄存器中。在执行新指令前,控制单元会检查在执行前一条指令的过程中是否有中断或异常发生。如果有,控制单元就会抛下指令,进入下面的流程:

1. 确定与中断或异常关联的向量i (0£i£255)

2. 寻找向量对应的处理程序

3. 保存当前的“工作现场”,执行中断或异常的处理程序

4. 处理程序执行完毕后,把控制权交还给控制单元

5. 控制单元恢复现场,返回继续执行原程序

3.流程中出现的问题

  让我们深入这个流程,看看都有什么问题需要面对。

1、异常是什么概念?

在处理器执行到由于编程失误而导致的错误指令(例如除数是0)的时候,或者在执行期间出现特殊情况(例如缺页),需要靠操作系统来处理的时候,处理器就会产生一个异常。对大部分处理器体系结构来说,处理异常和处理中断的方式基本是相同的,x86架构的CPU也是如此。异常与中断还是有些区别,异常的产生必须考虑与处理器时钟的同步。实际上,异常往往被称为同步中断。

2、中断向量是什么?

中断向量代表的是中断源——从某种程度上讲,可以看作是中断或异常的类型。中断和异常的种类很多,比如说被0除是一种异常,缺页又是一种异常,网卡会产生中断,声卡也会产生中断,CPU如何区分它们呢?中断向量的概念就是由此引出的,其实它就是一个被送通往CPU数据线的一个整数。CPU给每个IRQ分配了一个类型号,通过这个整数CPU来识别不同类型的中断。这里可能很多朋友会寻问为什么还要弄个中断向量这么麻烦的东东?为什么不直接用IRQ0~IRQ15就完了?比如就让IRQ0为0,IRQ1为1……,这不是要简单得多么?其实这里体现了模块化设计规则,及节约规则。

首先我们先谈谈节约规则,所谓节约规则就是所使用的信号线数越少越好,这样如果每个IRQ都独立使用一根数据线,如IRQ0用0号线,IRQ1用1号线……这样,16个IRQ就会用16根线,这显然是一种浪费。那么也许马上就有朋友会说:那么只用4根线不就行了吗?(2^4=16)。

这个问题,体现了模块设计规则。我们在前面就说过中断有很多类,可能是外部硬件触发,也可能是由软件触发,然而对于CPU来说中断就是中断,只有一种,CPU不用管它到底是由外部硬件触发的还是由运行的软件本身触发的,因为对于CPU来说,中断处理的过程都是一样的:中断现行程序,转到中断服务程序处执行,回到被中断的程序继续执行。CPU总共可以处理256种中断,而并不知道,也不应当让CPU知道这是硬件来的中断还是软件来的中断,这样,就可以使CPU的设计独立于中断控制器的设计,这样CPU所需完成的工作就很单纯了。CPU对于其它的模块只提供了一种接口,这就是256个中断处理向量,也称为中断号。由这些中断控制器自行去使用这256个中断号中的一个与CPU进行交互,比如,硬件中断可以使用前128个号,软件中断使用后128个号,也可以软件中断使用前128个号,硬件中断使用后128个号,这与CPU完全无关了,当你需要处理的时候,只需告诉CPU你用的是哪个中断号就行,而不需告诉CPU你是来自哪儿的中断。这样也方便了以后的扩充,比如现在机器里又加了一片8259芯片,那么这个芯片就可以使用空闲的中断号,看哪一个空闲就使用哪一个,而不是必须要使用第0号,或第1号中断号了。其实这相当于一种映射机制,把IRQ信号映射到不同的中断号上,IRQ的排列或说编号是固定的,但通过改变映射机制,就可以让IRQ映射到不同的中断号,也可以说调用不同的中断服务程序。

3、什么是中断服务程序?

在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序(interrupt handler)或中断服务程序(interrupt service routine(ISR))。产生中断的每个设备都有相应的中断处理程序。例如,由一个函数专门处理来自系统时钟的中断,而另外一个函数专门处理由键盘产生的中断。

一般来说,中断服务程序要负责与硬件进行交互,告诉该设备中断已被接收。此外,还需要完成其他相关工作。比如说网络设备的中断服务程序除了要对硬件应答,还要把来自硬件的网络数据包拷贝到内存,对其进行处理后再交给合适的协议栈或应用程序。每个中断服务程序根据其要完成的任务,复杂程度各不相同。

一般来说,一个设备的中断服务程序是它的设备驱动程序(device driver)的一部分——设备驱动程序是用于对设备进行管理的内核代码。

4、隔离变化

不知道您有没有意识到,中断处理前面这部分的设计是何等的简单优美。人是高度智能化的,能够对遇到的各种意外情况做有针对性的处理,计算机相比就差距甚远了,它只能根据预定的程序进行操作。对于计算机来说,硬件支持的,只能是中断这种电信号传播的方式和CPU对这种信号的接收方法,而具体如何处理这个中断,必须得靠操作系统实现。操作系统支持所有事先能够预料到的中断信号,理论上都不存在太大的挑战,但在操作系统安装到计算机设备上以后,肯定会时常有新的外围设备被加入系统,这可能会带来安装系统时根本无法预料的“意外”中断。如何支持这种扩展,是整个系统必须面对的。

而硬件和软件在这里的协作,给我们带来了完美的答案。当新的设备引入新类型的中断时,CPU和操作系统不用关注如何处理它。CPU只负责接收中断信号,并引用中断服务程序;而操作系统提供默认的中断服务——一般来说就是不理会这个信号,返回就可以了——并负责提供接口,让用户通过该接口注册根据设备具体功能而编制的中断服务程序。如果用户注册了对应于一个中断的服务程序,那么CPU就会在该中断到来时调用用户注册的服务程序。这样,在中断来临时系统需要如何操作硬件、如何实现硬件功能这部分工作就完全独立于CPU架构和操作系统的设计了。

而当你需要加入新设备的时候,只需要告诉操作系统该设备占用的中断号、按照操作系统要求的接口格式撰写中断服务程序,用操作系统提供的函数注册该服务程序,设备的中断就被系统支持了。

中断和对中断的处理被解除了耦合。这样,无论是你在需要加入新的中断时,还是在你需要改变现有中断的服务程序时、又或是取消对某个中断支持的时候,CPU架构和操作系统都无需作改变。

5、保存当前工作“现场”

在中断处理完毕后,计算机一般来说还要回头处理原先手头正做的工作。这给中断的概念带来些额外的“内涵”。注一“回头”不是指从头再来重新做,而是要接着刚才的进度继续做。这就需要在处理中断信号之前保留工作“现场”。“现场”这个词比较晦涩,其实就是指一个信息集,它能反映某个时间点上任务的状态,并能保证按照这些信息就能恢复任务到该状态,继续执行下去。再直白一点,现场不过就是一组寄存器值。而如何保护现场和恢复场景是中断机制需要考虑的重点之一。

中断处理经历的保存和恢复过程中的步骤:

每个中断处理都要经历这个保存和恢复过程,我们可以抽象出其中的步骤:

1. 保存现场

2. 执行具体的中断服务程序

3. 从中断服务返回

4. 恢复现场

上面说过了,“现场”看似在不断变化,没有哪个瞬间相同。但实际上组成现场的要素却不会有任何改变。也就是说,只要我们保存了相关的寄存器状态,现场就能保存下来。而恢复“现场”就是重新载入这些寄存器。换句话说,对于任何一个中断,保护现场和恢复现场所做的都是完全相同的操作。

既然操作相同,实现操作的过程和代码就相同。减少代码的冗余是模块化设计的基本准则,实在没有道理让所有的中断服务程序都重复实现这样的功能,应该将它作为一种基本的结构由底层的操作系统或硬件完成。而对中断的处理过程需要迅速完成,因此,Intel CPU的控制器就承担了这个任务,非但如此,上面的所有步骤次序都被固化下来,由控制器驱动完成。保存现场和恢复现场都由硬件自动完成,大大减轻了操作系统和设备驱动程序的负担。

二.什么是DMA

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖的CPU的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

1.原理

DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让工作器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。

在实现DMA传输时,是由直接掌管DMA控制器总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

1).请求

CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。

2).响应

DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。

3).传输

DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。

在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要终于处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。

4).结束

当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。

由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。

2.传送方式

DMA技术的出现,使得外围设备可以通过DMA控制器直接访问内存,与此同时,CPU可以继续执行程序。那么DMA控制器与CPU怎样分时使用内存呢?通常采用以下三种方法:(1)停止CPU访问内存;(2)周期挪用;(3)DMA与CPU交替访问内存。

1).停止CPU访问内存

当外围设备要求传送一批数据时,由DMA控制器发一个停止信号给CPU,要求CPU放弃对地址总线、数据总线和有关控制总线的使用权。DMA控制器获得总线控制权以后,开始进行数据传送。在一批数据传送完毕后,DMA控制器通知CPU可以使用内存,并把总线控制权交还给CPU。图(a)是这种传送方式的时间图。很显然,在这种DMA传送过程中,CPU基本处于不工作状态或者说保持状态。

优点: 控制简单,它适用于数据传输率很高的设备进行成组传送。

缺点: 在DMA控制器访问内存阶段,内存的效能没有充分发挥,相当一部分内存工作周期是空闲的。这是因为,外围设备传送两个数据之间的间隔一般总是大于内存存储周期,即使高速I/O设备也是如此。例如,软盘读出一个8位二进制数大约需要32us,而半导体内存的存储周期小于0.5us,因此许多空闲的存储周期不能被CPU利用。

2).周期挪用

当I/O设备没有DMA请求时,CPU按程序要求访问内存;一旦I/O设备有DMA请求,则由I/O设备挪用一个或几个内存周期。

这种传送方式的时间图如下图(b):

I/O设备要求DMA传送时可能遇到两种情况:

(1)此时CPU不需要访内,如CPU正在执行乘法指令。由于乘法指令执行时间较长,此时I/O访内与CPU访内没有冲突,即I/O设备挪用一二个内存周期对CPU执行程序没有任何影响。

(2)I/O设备要求访内时CPU也要求访内,这就产生了访内冲突,在这种情况下I/O设备访内优先,因为I/O访内有时间要求,前一个I/O数据必须在下一个访问请求到来之前存取完毕。显然,在这种情况下I/O 设备挪用一二个内存周期,意味着CPU延缓了对指令的执行,或者更明确地说,在CPU执行访内指令的过程中插入DMA请求,挪用了一二个内存周期。 与停止CPU访内的DMA方法比较,周期挪用的方法既实现了I/O传送,又较好地发挥了内存和CPU的效率,是一种广泛采用的方法。但是I/O设备每一次周期挪用都有申请总线控制权、建立线控制权和归还总线控制权的过程,所以传送一个字对内存来说要占用一个周期,但对DMA控制器来说一般要2—5个内存周期(视逻辑线路的延迟而定)。因此,周期挪用的方法适用于I/O设备读写周期大于内存存储周期的情况。

3).DMA与CPU交替访问内存

如果CPU的工作周期比内存存取周期长很多,此时采用交替访内的方法可以使DMA传送和CPU同时发挥最高的效率。

这种传送方式的时间图如下:

此图是DMA与CPU交替访内的详细时间图.假设CPU工作周期为1.2us,内存存取周期小于0.6us,那么一个CPU周期可分为C1和C2两个分周期,其中C1专供DMA控制器访内,C2专供CPU访内。

这种方式不需要总线使用权的申请、建立和归还过程,总线使用权是通过C1和C2分时制的。CPU和DMA控制器各自有自己的访内地址寄存器、数据寄存器和读/写信号等控制寄存器。在C1周期中,如果DMA控制器有访内请求,可将地址、数据等信号送到总线上。在C2周期中,如CPU有访内请求,同样传送地址、数据等信号。事实上,对于总线,这是用C1,C2控制的一个多路转换器,这种总线控制权的转移几乎不需要什么时间,所以对DMA传送来讲效率是很高的。

这种传送方式又称为“透明的DMA”方式,其来由是这种DMA传送对CPU来说,如同透明的玻璃一般,没有任何感觉或影响。在透明的DMA方式下工作,CPU既不停止主程序的运行,也不进入等待状态,是一种高效率的工作方式。当然,相应的硬件逻辑也就更加复杂。

三.实验代码

dma.h

#ifndef __DMA_H
#define __DMA_H
#include "stm32f10x.h"
void MyDMA_Init(void);
void MyDMA_Enable(void);
#endif

dma.c

#include "dma.h"
DMA_InitTypeDef DMA_InitStructure;
const u8 TEXT_TO_SEND[]={"Hello,world!"};
#define TEXT_LENTH sizeof(TEXT_TO_SEND)-1
u8 SendBuff[TEXT_LENTH+2];
u16 DMA1_MEM_LEN;void MyDMA_Init()
{u16 i;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);DMA_DeInit(DMA1_Channel4);DMA1_MEM_LEN = TEXT_LENTH+2;DMA_InitStructure.DMA_BufferSize = TEXT_LENTH+2;                      //DMA通道的DMA缓存的大小DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                     //数据传输方向,从内存读取发送到外设DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                         //DMA通道没有设置为内存到内存传输DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;                   //DMA内存基地址DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;          //数据宽度为8位DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                   //内存地址寄存器递增DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           //工作在正常缓存模式DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;         //DMA外设基地址DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;      //外设地址寄存器不变DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //DMA通道拥有中优先级DMA_Init(DMA1_Channel4,&DMA_InitStructure);                                //根据DMA_InitSturct中指定的参数初始化DMA的通道USART_Tx_DMA_Channel所标识的寄存器//发送内容for(i=0;i<TEXT_LENTH;i++){SendBuff[i]=TEXT_TO_SEND[i];}SendBuff[TEXT_LENTH]=0x0d; SendBuff[TEXT_LENTH+1]=0x0a;
}/* 开启一次DMA传输 */
void MyDMA_Enable()
{DMA_Cmd(DMA1_Channel4,DISABLE);                        //关闭USART_Tx_DMA1所指示的通道DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);  //DMA通道的DMA缓存的大小DMA_Cmd(DMA1_Channel4,ENABLE);                      //使能USART1_Tx_DMA1所指示的通道
}

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "dma.h"
int main(void)
{delay_init();MyUSART_Init();MyDMA_Init();while(1){USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //串口——DMA使能MyDMA_Enable();                                  //开始MDA传输while(1){if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)  //等待通道4传输完成{DMA_ClearFlag(DMA1_FLAG_TC4);           //清除通道4传输完成标志break;}}delay_ms(1000);                                    //延时1s}
}

四.实验结果

五.总结

本篇博客较详细,有错误请指出。

学习stm32中断、DMA通信原理和编程方法相关推荐

  1. 【嵌入式12】DMA通信原理及编程实验,DMA方式向上位机连续发送数据

    本文介绍DMS通信原理,STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据. 一.DMA介绍 什么是DMA? DMA介绍 DMA传输方式 DMA传输参数 DMA数据传输的 ...

  2. 【嵌入式基础】STM32中断及DMA通信原理编程

    本文主要学习stm32中断.DMA通信原理和编程方法.使用stm32tubemx和HAL库分别完成中断模式编程和串口通信中断实验. 目录 一.STM32中断,DMA通信原理编程 1.STM32中断 ( ...

  3. 51单片机学习--定时器--中断--串口通信

    51单片机学习–定时器–中断–串口通信 定时器–中断–串口通信 中断分类 定时器中断 外部中断 串口中断 基本概念 对于单片机来讲, 中断是指 CPU 在处理某一事件 A 时, 发生了另一事件 B, ...

  4. java学习(173):class反射编程方法

    //class反射编程 //定义一个学生类 public class test124 {public String name;protected int age;double height;priva ...

  5. c语言头文件_C语言学习之头文件的原理和使用方法

    头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享.有两种类型的头文件:程序员编写的头文件和编译器自带的头文件. 在程序中要使用头文件,需要使用 C 预处理指令 #i ...

  6. STM32中断与DMA通信编程

    文章目录 一.中断与DMA通信原理 1.中断 2.DMA通信原理 二.中断控制LED灯点亮熄灭 1.新建工程 2.配置外部中断 3.配置中断优先级 4.完成创建 5.编写中断函数 6.程序烧录 7.运 ...

  7. 学习嵌入式的书籍推荐,嵌入式编程入门教程学习大纲

    嵌入式系统是当前热门.具发展前景的IT应用领域之一,很多数字包括手机.电子字典.可视电话.数字相机.数字摄像机.机顶盒.智能玩具医疗仪器和航空航天设备等都是典型的嵌入式系统.越来越多的人想要了解学习嵌 ...

  8. 现代通信原理与技术 matlab,现代通信原理与技术.pdf

    第 1 页 第 章 绪 论 随着数字通信技术和计算机技术 的快速发展 以及通信 网与计算机 网络 的相互 融合 ,信息科学技术已成为 世纪 国际社会和世界经济发展的强大推动力 .信息 作为一种资源 , ...

  9. stm32中断源有哪些_143条 超详细整理STM32单片机学习笔记(必看)

    点击上方蓝色字关注我们~ 1.AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2>1,意思是APB2接高速设备 2.Stm32f10x.h相当于reg52.h(里面有基本的 ...

最新文章

  1. mysql一些查询方法记录
  2. CentOS 7.4下Redis及集群的安装及配置
  3. (5.0) Tomcat 8 源码, 初始化 bootstrap
  4. 实例演示如何在spring4.2.2中集成hibernate5.0.2并创建sessionFactory
  5. mysql-plus多数据库_IDEA项目搭建九——MybatisPlus多数据库实现
  6. 游标sql server_了解游标并将其替换为SQL Server中的JOIN
  7. 敏捷世界里中层经理的角色
  8. Python04,变量与赋值
  9. 开发环境各个版本的下载
  10. python时间序列函数_Python中的时间序列分解函数
  11. 网络监测用计算机安全管理制度,计算机信息网络安全管理制度
  12. 19电子设计速成实战宝典pdf_ALTIUMDESIGNER19(中文版)电子设计速成实战宝典
  13. Android Netd
  14. Mysql存储过程名规则_数据库对象命名规范一(原则、命名、 表、视图、存储过程、函数、触发器命名规范)...
  15. 什么是架构?架构的本质和作用!
  16. BAT机器学习面试1000题系列(详细版)
  17. 面向对象基础案例(2)
  18. 基于Hi3559A ARM64位嵌入式平台的OpenCV2.4.9+ffmpeg2.0.7移植及应用
  19. Red Hat Enterprise Linux Server 7.4 安装方法
  20. UI设计师—这个世界上最棒的工作!

热门文章

  1. thinkadmin
  2. Python获取列表list中的非零数据、第一个非零元素、最后一个非零元素
  3. 【路由器连接天翼网关2.0问题解决思路】
  4. 下载微信账单用于分析
  5. Android 广播(Broadcast)
  6. 国家司法考试(法考)成功上岸前辈备考经验分享
  7. 使用pytorch自己从头搭建一个Lenet分类器网络及其训练、检测脚本
  8. 3d Max人物动画学习笔记(二)蒙皮
  9. 仅需一个app就能像homeassistant一样实现跨品牌联动?
  10. Java-AQI计算