STM32cubeIDE学习汇总(六)---串口,DMA
阅读原文时间:2023年07月09日阅读:1

经过学习 我们已经初步了解了软件的使用方法,其中核心功能的基本原理。但是相对浅显,也没有很熟悉,接着要更仔细的去了解具体了。

目录

DMA

DMA定义:

DMA传输方式

DMA的主要特征

STM32多少个DMA资源?

仲裁器

DMA 传输通道

DMA库函数配置过程:

工程创建

UART

STM32的USART简介

演示:

1.UART阻塞方式收发

UART中断方式收发

DMA方式串口收发

printf函数


串口和DMA

DMA

DMA的基本介绍
什么是DMA (DMA的基本定义)
DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,

CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?

因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理.

DMA就是基于以上设想设计的,它的作用就是解决大量数据转移过度消耗CPU资源的问题。有了DMA使CPU更专注于更加实用的操作–计算、控制等.

DMA定义:

DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

DMA传输方式

DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

外设到内存
内存到外设
内存到内存
外设到外设
 

DMA传输参数
我们知道,数据传输,首先需要的是1 数据的源地址 2 数据传输位置的目标地址 ,3 传递数据多少的数据传输量 ,4 进行多少次传输的传输模式 DMA所需要的核心参数,便是这四个

当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。
  
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。 

DMA的主要特征

每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推);
独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐;
支持循环的缓冲器管理;
每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求;
存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输;
闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;
可编程的数据传输数目:最大为65535。

STM32多少个DMA资源?

对于大容量的STM32芯片有2个DMA控制器 两个DMA控制器,DMA1有7个通道,DMA2有5个通道。
每个通道都可以配置一些外设的地址。

①DMA1 controller

从外设(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])产生的7个DMA请求,通过逻辑或输入到DMA1控制器 其中每个通道都对应着具体的外设:
 

 

DMA2 controller

从外设(TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器,其中每个通道都对应着具体的外设:

DMA工作系统框图 

上方的框图,我们可以看到STM32内核,存储器,外设及DMA的连接,这些硬件最终通过各种各样的线连接到总线矩阵中,硬件结构之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据。
我们对他来进行一点一点的分析:

下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的?

没有DMA

1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:

内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据,

然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。

有DMA传输

有DMA的话,

DMA传输时外设对DMA控制器发出请求。
DMA控制器收到请求,触发DMA工作。
DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中
DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与,
 

 

我们把上面的步骤专业一点介绍:

在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。DMA传输结束,如果有更多的请求时,外设可以启动下一个周期。

总之,每次DMA传送由3个操作组成:

从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。

仲裁器

仲裁器的作用是确定各个DMA传输的优先级

仲裁器根据通道请求的优先级来启动外设/存储器的访问。

优先权管理分2个阶段:

软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:

最高优先级
高优先级
中等优先级
低优先级;
硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。比如:如果软件优先级相同,通道2优先于通道4。

注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。
 

DMA 传输通道

每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据 量是可编程的,大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。

可编程的数据量:
外设和存储器的传输数据量可以通过DMA_CCRx寄存器中的PSIZE和MSIZE位编程。

指针递增模式
根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值

通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。

如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1个数据宽度、2个数据宽度或 4个数据宽度。

存储器到存储器模式
DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。

当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。

这里要注意仅 DMA2 的外设接口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输,DMA1 不支持。

存储器到存储器模式不能与循环模式同时使用。

DMA的内存占用
在STM32控制器中,芯片采用Cortex-MX架构,总线结构有了很大的优化,DMA占用另外的地址总线,并不会与CPU的系统总线发生冲突。也就是说,DMA的使用不会影响CPU的运行速度

但是要注意:
DMA 控制器和Cortex-M3核共享系统数据总线执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求可能会停止 CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。

DMA库函数配置过程:

1、使能DMA时钟:RCC_AHBPeriphClockCmd();

2、初始化DMA通道:DMA_Init();

//设置通道;传输地址;传输方向;传输数据的数目;传输数据宽度;传输模式;优先级;是否开启存储器到存储器。

3、使能外设DMA;

4、使能DMA通道传输;

5、查询DMA传输状态。

工程创建

1设置RCC

设置高速外部时钟HSE 选择外部时钟源

2设置串口


1点击USATR1
2设置MODE为异步通信(Asynchronous)
3基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能
4GPIO引脚自动设置 USART1_RX/USART_TX
5 NVIC Settings 一栏使能接收中断
​​

 

关于串口部分的讲解可以参考: 【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解
3 DMA设置


根据DMA通道预览可以知道,我们用的USART1 的TX RX 分别对应DMA1 的通道4和通道5

点击DMASettings 点击 Add 添加通道
选择USART_RX USART_TX 传输速率设置为中速
DMA传输模式为正常模式
DMA内存地址自增,每次增加一个Byte(字节)

1DMA基础设置
右侧点击System Core 点击DMA

DMA Request : DMA传输的对应外设

注意: 如果你是在DMA设置界面添加DMA 而没有开启对应外设的话 ,默认为MENTOMEN

Channel DMA传输通道设置
DMA1 : DMA1 Channel 0~DMA1 Channel 7
DMA2: DMA2 Channel 1~DMA1 Channel 5

Dirction : DMA传输方向
四种传输方向:

外设到内存 Peripheral To Memory
内存到外设 Memory To Peripheral
内存到内存 Memory To Memory
外设到外设 Peripheral To Peripheral
Priority: 传输速度

最高优先级 Very Hight
高优先级 Hight
中等优先级 Medium
低优先级;Low
2DMA传输模式

Normal:正常模式
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次

Circular: 循环模式

传输完成后又重新开始继续传输,不断循环永不停止

3DMA指针递增设置

Increment Address:地址指针递增(上方有介绍)。

左侧Src Memory 表示外设地址寄存器

功能:设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,

右侧Dst Memory 表示内存地址寄存器

功能:设置传输数据时候内存地址是否递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,

这个Src Memory一样,只不过针对的是内存。

串口发送数据是将数据不断存进固定外设地址串口的发送数据寄存器(USARTx_TDR)。所以外设的地址是不递增。

而内存储器存储的是要发送的数据,所以地址指针要递增,保证数据依次被发出

串口数据发送寄存器只能存储8bit,每次发送一个字节,所以数据长度选择Byte。

就是要注意DMA的传输方向别弄错了,到底是PERIPHERIAL到MEMORY还是MEMORY到PERIPHERIAL或者说是Memory到Memory要配置正确。尤其是在用CubeMx配置时,这里有个默认配置是PERIPHERIAL到MEMORY。如果说你的真实意图根本不是从PERIPHERIAL到MEMORY,而你无意中使用了这个默认配置,结果可想而知,DMA传输根本没法正常运行。

4时钟源设置

​​​​
我的是 外部晶振为8MHz

1选择外部时钟HSE 8MHz
2PLL锁相环倍频9倍
3系统时钟来源选择为PLL
4设置APB1分频器为 /2
5 使能CSS监视时钟
32的时钟树框图 如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》

5项目文件设置

1 设置项目名称
2 设置存储路径
3 选择所用IDE

在main.C中添加:

 /* USER CODE BEGIN Init */
    uint8_t Senbuff[] = "\r\n moyu \r\n";  //定义数据发送数组
  /* USER CODE END Init */

while循环:

  while (1)
  {
    /* USER CODE END WHILE */
            HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
            HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

 uint8_t Senbuff[] = "\r\n moyu \r\n";

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
                HAL_Delay(1000);

 UART

USART与UART的区别:
usart:全双工通用同步/异步串行收发器
uart:全双工通用异步串行收发器
usart支持同步通讯时比uart多一条时钟线,usart用于异步通讯时跟uart一样。

单工: 只能单向传输数据
半双工:某一个时刻只能接受或者发送的双向传输数据
全双工:可以同时进行接收和发送数据

异步:发送方发送一帧数据后不需要接受方应答继续发送下一帧
同步:发送方发送完一帧数据后需要等待接受方应答才能发下一帧

串行:每一传输按位进行,在一个导线上一位一位一次传输
并行:每次传输按两位或者两位以上进行,通过几根导线同一时刻可以进行多位传输

波特率:一秒钟内传输多少位数据,单位:Bps(byte 字节每秒),发送设备与接收设备的波特率必须一致。

USART/UART是一种通用的标准接口,根据导线的电压等不同也分为很多的同类,比如: RS485, RS422,RS232

STM32的USART简介

通用同步异步收发器是一个串行通信设备,可以灵活的与外部设备进行全双工数据交换。有别与USART,还有一个UART,它在USART基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
串口通信一般是以帧格式传输数据,即一帧一帧传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。
USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC规范。
USART支持使用 DMA,可实现高速数据通信。
功能引脚
TX:发送数据输出引脚。
RX:接收。
SW_RX:数据接收引脚,属于内部引脚。
nRTS:请求以发送,n表示低电平有效。如果使能 RTS 流控制,当USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。该引脚只适用于硬件流控制。
nCTS:清除以发送(Clear To Send),n表示低电平有效。如果使能 CTS流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
USART:下图是STM32F103VET6芯片的USART引脚

 

USART1的时钟来源于APB2总线时钟,最大频率为72MHZ,其他4个时钟来源于APB1总线时钟,最大频率36MHZ。UART只有异步传输功能,没有SCLK、nCTS和nRTS功能引脚。

2.数据寄存器
USART说数据寄存器(USART_DR)只有低 9 位有效,并且第 9 位数据是否有效要取决于USART 控制寄存器 1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M位为 1 表示 9 位数据字长,我们一般使用 8位数据字长。
USART_DR包含了已发送的数据或者接收到的数据。USART_DR实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR写入数据会自动存储在 TDR内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。
TDR和RDR都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
USART 支持 DMA 传输,可以实现高速数据传输。

3.控制器
USART有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等。
使用USART之前需要向USART_CR1寄存器的UE位置1使能USART,UE位用于开启供给串口的时钟。发送或者接收数据字长可选8或9位,由USARTT_CR1的M位控制。
1)发送器
当USART_CR1寄存器的发送使能位TE置1时,启动数据发送,发送移位寄存器的数据会在TX引脚输出,低位在前,高位在后。如果是同步模式SCLK也输出时钟信号。
一个字符帧发送需要3部分:起始位、数据帧、停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间 ;数据帧就是我们要发送的8或9位数据,数据是最低位开始传输的;停止位是一定时间周期的高电平。
停止位的时间长短可以通过USART控制寄存器2(USART_CR2)的STOP[1:0]位控制,可选0.5个、1个、1.5个、2个停止位。默认使用1个停止位。2个停止位适用于正常USART模式、单线模式和调制解调器模式。0.5和1.5个停止位用于智能卡模式。
当发使能位TE置1之后,发送器开始会发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往USART_DR寄存器写入要发送的数据。在写入最后一个数据后,需等待USART状态寄存器(USART_SR)的TC位为1,表示数据传输完成。USART_CR1寄存器的TCIE位置1,则产生中断。
发送数据时,几个重要的标志位如下:
TE:发送使能。
TXE:发送寄存器为空,发送单个字节时使用。
TC:发送完成,发送多个字节数据时候使用。
TXIE:发送完成中断使能。
2)接收器
将CR1寄存器的RE位置1,使能USART接收,使得接收器在RX线开始搜索起始位。在确定起始位后,就根据RX线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器的数据移到PDR内,并把USART_SR寄存器的RXNE位置。如果USART_CR2寄存器的RXNEIE置1可以产生中断。
接收数据时,几个重要的标志位如下:
RE: 接收使能。
RXNE:读数据寄存器非空。
RXNEIE:发送完成中断使能。
校验控制
STM32F103系列控制器USART支持奇偶校验。使用校验位时,串口传输的长度将在8位数据帧上加上1位的校验位,总共9位,此时USART_CR1寄存器的M位需要设置位1,即9数据位。将USART_CR1寄存器的PCE位置1就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,发送数据帧时会自动添加校验位,接收数据自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会将USART_SR寄存器的PE置1,并可以产生奇偶校验中断。
使用了奇偶校验控制位后,每个字符帧的格式变成了:起始位+数据帧+校验位+停止位。

演示:

UART阻塞方式收发

UART中断方式收发

UART中断回调函数

UART DMA方式收发

printf()函数串口重定向

1.UART阻塞方式收发

打开串口

 自行配置时钟(注:时钟配置一定要和自己的芯片一致,不然串口输出会乱码!)

代码

 

HAL_Delay(1999);
      HAL_UART_Transmit(&huart1, (uint8_t *)a1, 4, 0xffff);

参数分别是    UATR的别名 需要发送的数据 发送的字节数  最大发送时间,发送数据超过该时间退出发送 

现象:

阻塞方式收

 效果  :发什么收什么

      if(HAL_UART_Receive(&huart1, a2, sizeof(a2), 100)== HAL_OK)
              {
               HAL_UART_Transmit(&huart1, (uint8_t *)a2, 4, 0xffff);
              }

2.

UART中断方式收发

中断方式的收发函数只有三个参数
第一个参数时要使用的串口句柄地址
第二个参数是发送/接受缓冲区的首地址,用于存放要发送/接收的数据
第三个参数是发送/接受缓冲区长度

中断回调函数

  1. 使用HAL_UART_Receive_IT()函数接受指定数量的数据存到指定缓冲区;
  2. 编写接受回调函数,回调函数作用是当回调函数被执行后判断是否是usart1所触发的中断,如果是,就使用HAL_UART_Transmit_IT()函数给主机发送指定内容的应答数据。

实验目的:收什么发什么

 打开串口,打开中断

 代码

 

 先使能中断

在中断回调里面再次使能中断接收,然后把接收到的数组发送出来

 现象

 DMA方式串口收发

 其他配置一样,也已经在上面中介绍过DMA了

这里我们接收选择circular 发送normal

定义数组R0后先使能,然后在中断回调函数里面发送收到内容

 现象

printf函数

printf函数在stm32cubeide里面是无法直接使用的

先引入stdio.h函数然后

写在   main.c 里面 
#ifdef __GNUC__

  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}

之后就可以利用printf函数了

 

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章