学习stm32中断、DMA通信原理和编程方法。使用stm32tubemx和HAL库分别完成以下编程练习:
用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
采用串口中断方式重做上周的串口通信作业。
STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据。
不同的计算机其硬件结构和软件指令是不完全相同的,因此,中断系统也是不相同的。计算机的中断系统能够加强CPU对多任务事件的处理能力。中断机制是现代计算机系统中的基础设施之一,它在系统中起着通信网络作用,以协调系统对各种外部事件的响应和处理。中断是实现多道程序设计的必要条件。 中断是CPU对系统发生的某个事件作出的一种反应。 引起中断的事件称为中断源。中断源向CPU提出处理的请求称为中断请求。发生中断时被打断程序的暂停点称为断点。CPU暂停现行程序而转为响应中断请求的过程称为中断响应。处理中断源的程序称为中断处理程序。CPU执行有关的中断处理程序称为中断处理。而返回断点的过程称为中断返回。中断的实现实行软件和硬件综合完成,硬件部分叫做硬件装置,软件部分称为软件处理程序。
程序状态字和向量中断
这是两个与中断响应和处理有密切关系的概念。
① 程序状态字:每个程序均有自己的程序状态字。现行程序的程序状态字放在处理机的程序状态字寄存器中。程序状态字中最主要的内容有指令地址、条件码、地址保护键,中断屏蔽和中断响应时的中断源记录等。中断响应和处理操作的第④步和第⑤步就是交换程序状态字操作。
② 向量中断:对应每一级中断都有一个向量,这些向量顺序存放在主存的指定单元中。向量的内容是:相应的中断服务程序起始地址和处理机状态字(主要是指令地址)。在中断响应时,由中断部件提供中断向量的地址,就可取出该向量。中断响应和处理操作的第⑤步就是取中断向量操作。在采用向量中断的机器中一般不再使用程序状态字。
1)实现中断响应和中断返回
当CPU收到中断请求后,能根据具体情况决定是否响应中断,如果CPU没有更急、更重要的工作,则在执行完当前指令后响应这一中断请求。CPU中断响应过程如下:首先,将断点处的PC值(即下一条应执行指令的地址)推入堆栈保留下来,这称为保护断点,由硬件自动执行。然后,将有关的寄存器内容和标志位状态推入堆栈保留下来,这称为保护现场,由用户自己编程完成。保护断点和现场后即可执行中断服务程序,执行完毕,CPU由中断服务程序返回主程序,中断返回过程如下:首先恢复原保留寄存器的内容和标志位的状态,这称为恢复现场,由用户编程完成。然后,再加返回指令RETI,RETI指令的功能是恢复PC值,使CPU返回断点,这称为恢复断点。恢复现场和断点后,CPU将继续执行原主程序,中断响应过程到此为止。
2)实现优先权排队
通常,系统中有多个中断源,当有多个中断源同时发出中断请求时,要求计算机能确定哪个中断更紧迫,以便首先响应。为此,计算机给每个中断源规定了优先级别,称为优先权。这样,当多个中断源同时发出中断请求时,优先权高的中断能先被响应,只有优先权高的中断处理结束后才能响应优先权低的中断。计算机按中断源优先权高低逐次响应的过程称优先权排队,这个过程可通过硬件电路来实现,亦可通过软件查询来实现。
3)实现中断嵌套
当CPU响应某一中断时,若有优先权高的中断源发出中断请求,则CPU能中断正在进行的中断服务程序,并保留这个程序的断点(类似于子程序嵌套),响应高级中断,高级中断处理结束以后,再继续进行被中断的中断服务程序,这个过程称为中断嵌套。如果发出新的中断请求的中断源的优先权级别与正在处理的中断源同级或更低时,CPU不会响应这个中断请求,直至正在处理的中断服务程序执行完以后才能去处理新的中断请求。
几个中断请求可能同时出现,但中断系统只能按一定的次序来响应和处理。可最先被响应的中断具有最高优先权,按优先级别顺序进行处理。优先权高低是由中断部件的中断排队线路确定的。
中断级
当机器设置很多中断源时,为了简化设计,对中断源分组管理。具有相同中断优先权的中断源构成一个中断级。同一级中断使用同一个中断控制程序起点。
中断屏蔽
对应于各中断级设置相应的屏蔽位。只有屏蔽位为1时,该中断级才能参加中断优先权排队。中断屏蔽位可由专用指令建立,因而可以灵活地调整中断优先权。有些机器针对某些中断源也设置屏蔽位,只有屏蔽位为1时,相应的中断源才起作用。
DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。
DMA
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
停止CPU访问内存
当外围设备要求传送一批数据时,由DMA控制器发一个停止信号给CPU,要求CPU放弃对地址总线、数据总线和有关控制总线的使用权。DMA控制器获得总线控制权以后,开始进行数据传送。在一批数据传送完毕后,DMA控制器通知CPU可以使用内存,并把总线控制权交还给CPU。图(a)是这种传送方式的时间图。很显然,在这种DMA传送过程中,CPU基本处于不工作状态或者说保持状态。
优点: 控制简单,它适用于数据传输率很高的设备进行成组传送。
缺点: 在DMA控制器访问内存阶段,内存的效能没有充分发挥,相当一部分内存工作周期是空闲的。这是因为,外围设备传送两个数据之间的间隔一般总是大于内存存储周期,即使高速I/O设备也是如此。例如,软盘读出一个8位二进制数大约需要32us,而半导体内存的存储周期小于0.5us,因此许多空闲的存储周期不能被CPU利用。
周期挪用
当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设备读写周期大于内存存储周期的情况。
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既不停止主程序的运行,也不进入等待状态,是一种高效率的工作方式。当然,相应的硬件逻辑也就更加复杂。
首先选择STM32F103C8芯片
点击System Core,进入里面的SYS,在debug那里选择Serial Wire
进入上面的RCC,将HSE那里设置为Crystal/Ceramic Resonator
来到Clock Configuration选项
将PB6管脚的引脚模式设置为输出模式:GPIO_Output。
将PA2管脚的引脚模式设置为外部中断:GPIO_EXTI2。
选择对应的外部中断线,点击Enabled。
其次,设置中断的优先级,再设置时钟即可。
然后选择Project Manager,选择MDK/ARM,再创建工程名
在Code Generator中勾选如图选项
即创立完成
随即打开所生成的工程文件,找到stm32f1xx_hal_gpio.c。
可以发现函数前面有个weak,需要进行重写。
其次,我们再找到main.c文件,进行重写下列代码
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if( GPIO_Pin == A2_EXTI_Pin)//判断外部中断源 { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//翻转LED状态 } }
1212
前面步骤与之前相同,只需要增添以下步骤
找到USART1,在Mode中选择Asynchronous
进入NVIC Settings界面,勾选Enabled,使能中断。
最后取好文件名,选择MDK-ARM即可生成工程文件。
找到main.c文件
分别添加以下代码
①
uint8_t a[]="Hello world!\n";
②
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { HAL_UART_Transmit_IT(&huart1, a, sizeof(a)); }
③
HAL_UART_Transmit_IT(&huart1, a, sizeof(a));
勾选生成hex文件
编译无错误
与之前步骤相同,先找到STM32F103C8芯片,设置与上实验相同
此外进行以下操作,可以看到以下参数:波特率为115200 Bits/s;传输数据长度为8 Bit;无奇偶检验;停止位为1
勾选该选项,使其能中断
在同一界面下点击DMA setting->Add添加通道
然后来到Clock Configuration界面
最后,再取号文件名生成工程文件
打开main.c文件
添加以下代码
uint8_t Senbuff[] = "Hello windows!\n"; //定义一个数组,将想要发送的数据放到数组中去
在main函数的while循环语句中加入以下代码
HAL_UART_Transmit_DMA(&huart1,Senbuff, sizeof(Senbuff)); HAL_Delay(1000);
编译未产生错误
参考文献
https://blog.csdn.net/as480133937/article/details/104827639/
https://blog.csdn.net/mosifan/article/details/121101069