STM32使用DMA实现GPIO的高速翻转

这篇具有很好参考价值的文章主要介绍了STM32使用DMA实现GPIO的高速翻转。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

接着上一篇博客的内容,上一篇博客实现了定时器输出pwm,这次我们使用DMA来驱动gpio高速翻转,来探索一下stm32h750的gpio翻转极限

二、原理

这里大家可能有点疑惑,为什么要用dma来控制gpio
使用DMA的原因有两点:
第一点就是资源消耗问题,我的项目中有个数组,该数组有13240个元素,每个元素都是16位的,分别对应16个gpio口的状态,当然我可以通过一个循环来发送这些成员,但是这样对cpu的资源消耗非常大,发送的时候cpu就干不了其他事了,如果中断处理其他任务,那么gpio的控制就不连续;尝试过多线程,效果不尽人意
第二点就是速度问题,通过一个循环来发送这些成员,最终结果还是把这些数据写入GPIO的寄存器中,用dma直接将数据搬运到GPIO的寄存器中当然是最快的了

解答一下疑惑,什么是DMA
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

顺带提一下GPIO的寄存器,我们一会要用到的是ODR
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

三、配置IO口

接着上一次的工程,配置GPIO口
我的项目中用到 GPIOD0 —— D15和 GPIOE0 —— E15 共32个io,本例程中只用到GPIOE0 —— E15,读者可只配置GPIOE0 —— E15

有两种配置方法
方法一:在cubemx中配置PD0PE0为输出口,再在keil中修改整个PBPE口。此方法的缺点是每次在CUBEMX中修改了功能重新生成之后,都需要在keil中修改
方法二:直接在cubemx中将需要的32个io全部配置为输出。此方法的缺点是工作量较大,选32个io比较辛苦,优点是一劳永逸

在这里我用方法一,读者可按自己的喜好自行选择

根据stm32官方手册介绍,GPIO每次翻转最快只需要两个时钟周期
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

四、配置DMA

添加一个dma,选择存储器到存储器,模式选normal

为什么选存储器到存储器,而不选存储器到外设?GPIO不是外设吗?
关于这个问题,好多博主都只讲了怎么配置dma,并没有详细的讲原理,这里我简单的讲一下:其实选存储器到存储器和选存储器到外设都是一样的,区别在使用的时候绑定外设GPIO寄存去的地址。接下来我们再添加一个存储器到外设的,试验对比一下
但是外设一般是指在stm32芯片之外的设备,我们要控制的GPIO是在stm32芯片上的,所以理论上它不属于外设(但是我们当作是有一个外设以并口的方式连接到GPIO上,也是可以的),这里更推荐选memory to memory模式,即选存储器到存储器

关于循环模式的问题,也简单讲一下,先看图
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
看完这个知道你要选normal还是circular了吧,很多人说选了normal,只能发送一次,第二次再发的时候没反应了,至于选normal怎么触发第二次发送的问题,在下一篇博客会讲到

stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

五、程序内容

5.1 gpio配置

由于我们刚刚只配置了PD0PE0,所以还需要配置一下使用整个PBPE

打开 main.c 找到 MX_GPIO_Init()函数,将 GPIO_PIN_0改成GPIO_PIN_All

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_All, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_All, GPIO_PIN_RESET);

  /*Configure GPIO pin : PD0 */
  GPIO_InitStruct.Pin = GPIO_PIN_All;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /*Configure GPIO pin : PE0 */
  GPIO_InitStruct.Pin = GPIO_PIN_All;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

}

stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

5.2 keil中添加.C和.h文件(需要DMA发送控制GPIO的数组波形文件)如不想这么麻烦的可以略过,在main函数中定义一个数组即可,因为我的数组太大了,而且为了方便更改,故单独存了一个文件

打开工程所在目录,在目录下新建一个文件夹(文件夹名随意,中文没试过,不知道会不会报错)存放.C.h文件,我这里建的文件夹命名为User
可以直接将.C.h文件放置在新建的文件夹里,也可以在文件夹里再新建个文件夹分类存放,总之按个人喜好就行
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
我的.C.h文件内容:

先是WaveData.C文件

#include "WaveData.h"

const unsigned int wave_data[13240] = { 
0X98EC,0X58EC,0X98EC,0X58EC,0X98D4,0X58D4,0X98D4,0X58D4,0X98BC,0X58BC,0X98BC,0X58BC,0X98A4,0X58A4,0X98A4,0X58A4,0X988C,0X588C,
0X9888,0X5888,0X9870,0X5870,0X9870,0X5870,0X9858,0X5858,0X9858,0X5858,0X9840,0X5840,0X9840,0X5840,0X9828,0X5828,0X9828,
0X582C,0X9814,0X5814,0X9814,0X5814,0X97FC,0X57FC,0X97FC,0X57FC,0X97E4,0X57E4,0X97E4,0X57E4,0X97CC,0X57CC,0X97CC,0X57CC,
0X97B0,0X57B0,0X97B0,0X57B0,0X9798,0X5798,0X9798,0X5798,0X9780,0X5780,0X9780,0X5780,0X9768,0X5768,0X9768,0X5768,0X9750,
0X5754,0X9754,0X5754,0X973C,0X573C,0X973C,0X573C,0X9724,0X5724,0X9724,0X5724,0X970C,0X570C,0X970C,0X570C,0X96F4,0X56F4,
0X96F0,0X56F0,0X96D8,0X56D8,0X96D8,0X56D8,0X96C0,0X56C0,0X96C0,0X56C0,0X96A8,0X56A8,0X96A8,0X56A8,0X9690,0X5690,0X9690,
0X5694,0X967C,0X567C,0X967C,0X567C,0X9664,0X5664,0X9664,0X5664,0X964C,0X564C,0X964C,0X564C,0X9634,0X5634,0X9634,0X5634,
0X9618,0X5618,0X9618,0X5618,0X9600,0X5600,0X9600,0X5600,0X95E8,0X55E8,0X95E8,0X55E8,0X95D0,0X55D0,0X95D0,0X55D0,0X95B8,
0X55BC,0X95BC,0X55BC,0X95A4,0X55A4,0X95A4,0X55A4,0X958C,0X558C,0X958C,0X558C,0X9574,0X5574,0X9574,0X5574,0X955C,0X555C,
//...太多了这里不贴了...
}

下面是是WaveData.h文件

#ifndef _IMAGEDATA_H_
#define _IMAGEDATA_H_

// WaveData.c
/* --------------------------------------- */
extern const unsigned int wave_data[];

#endif
/* FILE END */

接下来将WaveData.C文件添加到工程里,并添加路径
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
可以看到.c文件就添加进来了,别急,现在还用不了,还需要添加的.h文件所在的文件夹到工程路径中
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
build一下就会发现自动找到.h文件了
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

5.3 DMA实现GPIO的高速翻转代码实现 (memory to memory模式)

main.c文件中添加如下代码:

包含头文件

/* USER CODE BEGIN Includes */
#include "WaveData.h"
/* USER CODE END Includes */

如果略过了 < 5.2 keil中添加.C.h文件” > 的可以定义一个dma缓冲区:

/* USER CODE BEGIN PV */
uint16_t dma_buff[12] = {0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0055, 0x00AA, 0x0000, 0xFFFF, 0xAA55, 0x55AA};
/* USER CODE END PV */

启动 DMA 传输

/* USER CODE BEGIN 2 */
HAL_DMA_Start(&hdma_memtomem_dma1_stream0, (uint32_t)(dma_buff), (uint32_t)(&GPIOE->ODR), sizeof(dma_buff)/sizeof(dma_buff[0]));
/* USER CODE END 2 */

5.4 输出结果

== 需要注意,因为我们前面配置DMA选的是normal模式,该模式只发送一次即停止发送了,所以只有在开发板商店的一瞬间或触发一次DMA搬运,搬运完即停止搬运了,后面就没有输出了,看图 ==
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
我这个是用的我的数组,13240个元素,如果用的是uint16_t dma_buff[12] = {0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0055, 0x00AA, 0x0000, 0xFFFF, 0xAA55, 0x55AA};,则转瞬即逝,如图
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
发现和我们预设的数组并不一致,这里可能是dma的总线频率设置太高了,降一下频率试试
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

DMA挂在AHB1总线上的,将AHB1由原来的240MHz降低至120MHz
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件
再次测试
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

看起来没什么问题,可以看到翻转频率达到了20MHz,导出数据解析一下
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

和我们的数组uint16_t dma_buff[12] = {0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0055, 0x00AA, 0x0000, 0xFFFF, 0xAA55, 0x55AA};匹配,没有问题

如果要方便观察,可以将DMA选为CIRCULAR模式,或者直接在void MX_DMA_Init(void)函数中将DMA模式由 DMA_NORMAL 更改为 DMA_CIRCULAR 模式,在DMA_CIRCULAR 模式下,会自动循环发送dma_buff[8]的内容

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* Configure DMA request hdma_memtomem_dma1_stream0 on DMA1_Stream0 */
  hdma_memtomem_dma1_stream0.Instance = DMA1_Stream0;
  hdma_memtomem_dma1_stream0.Init.Request = DMA_REQUEST_MEM2MEM;
  hdma_memtomem_dma1_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY;
  hdma_memtomem_dma1_stream0.Init.PeriphInc = DMA_PINC_ENABLE;
  hdma_memtomem_dma1_stream0.Init.MemInc = DMA_MINC_DISABLE;
  hdma_memtomem_dma1_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_memtomem_dma1_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_memtomem_dma1_stream0.Init.Mode = DMA_CIRCULAR;//DMA_NORMAL;  //改为DMA_CIRCULAR模式
  hdma_memtomem_dma1_stream0.Init.Priority = DMA_PRIORITY_LOW;
  hdma_memtomem_dma1_stream0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
  hdma_memtomem_dma1_stream0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdma_memtomem_dma1_stream0.Init.MemBurst = DMA_MBURST_SINGLE;
  hdma_memtomem_dma1_stream0.Init.PeriphBurst = DMA_PBURST_SINGLE;
  if (HAL_DMA_Init(&hdma_memtomem_dma1_stream0) != HAL_OK)
  {
    Error_Handler( );
  }

  /* Configure DMA request hdma_dma_generator0 on DMA1_Stream1 */
  hdma_dma_generator0.Instance = DMA1_Stream1;
  hdma_dma_generator0.Init.Request = DMA_REQUEST_GENERATOR0;
  hdma_dma_generator0.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_dma_generator0.Init.PeriphInc = DMA_PINC_ENABLE;
  hdma_dma_generator0.Init.MemInc = DMA_MINC_DISABLE;
  hdma_dma_generator0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_dma_generator0.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_dma_generator0.Init.Mode = DMA_NORMAL;
  hdma_dma_generator0.Init.Priority = DMA_PRIORITY_LOW;
  hdma_dma_generator0.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  if (HAL_DMA_Init(&hdma_dma_generator0) != HAL_OK)
  {
    Error_Handler( );
  }

}

stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

可以看到,改成循环模式后就会一直循环发送

为了方便观察,我们把数组改成如下,dma改成循环发送

uint16_t dma_buff[12] = {0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF};

结果如图:
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件

总结

最后探了一下STM32H750VBT6DMA控制GPIO的极限翻转频率,可以看到虽然可以到40Mhz,但是已经不稳定了,比较稳定的是在10MHz以下

图片中dma缓冲区为 uint16_t dma_buff[12] = {0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF};
可以看到后面多了一些东西
stm32 gpio dma,STM32,stm32,单片机,嵌入式硬件文章来源地址https://www.toymoban.com/news/detail-768631.html

到了这里,关于STM32使用DMA实现GPIO的高速翻转的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32实现DMA接收串口数据

    一..首先我们得配置DMA和USARAT,我们的原理是DMA1的通道5为USART1的RX引脚。  1.USART1的配置 2.DMA的配置 二.中断进行数据处理(stm32f10x_it.c) 我们可以串口打印出数组中的数据,验证DMA是否正常工作。可以到数据处理那个地方进行处理。USART1在初始化中就已经波特率为115200.我们可以

    2024年02月16日
    浏览(43)
  • STM32:GPIO配置和使用

    目录 一、GPIO简介 1.1 GPIO的输入输出模式 1.1.1 输入模式 1.1.2 输出模式 二、GPIO的使用 2.1 引脚初始化 2.2 引脚使用 注:型号:STM32F407ZET6     GPIO:通用输入输出接口     STM32上有A~H共8组,其中A~G7组每组有0~15号共16个引脚,H组有两个引脚。 1.1.1 输入模式      1. 上拉输入  

    2024年02月08日
    浏览(34)
  • STM32F407-- DMA使用

    目录 1. DMA结构体  STM32F103: STM32F407:  2. F4系列实现存储器到存储器数据传输 1)结构体配置初始化 2)主函数 补充知识点:关于变量存储的位置,关于内部存储器一般存储什么内容 3. F4系列实现存储器到串口4数据传输 1)结构体配置 初始化 2)主代码:实现串口4DMA请求,以

    2024年02月16日
    浏览(32)
  • STM32通过DMA方式实现串口通信

    目录 一、DMA工作原理  二、创建工程项目 三、编写代码 1.在main.c写入以下函数 2.main函数中的while循环中写入以下代码

    2024年02月15日
    浏览(42)
  • STM32使用SPI+DMA(标准库)

    DMA DMA原理 DMA通道资源分配 SPI+DMA配置 主函数编写如下 1.CPU配置好DMA。 2.SPI发出DMA请求。(在DMA_Mode_Normal模式下,该请求实际上需要CPU命令SPI发出请求) 3.若该通道有多个请求,DMA控制器通过仲裁器判断,根据配置的优先级,选择先回应该通道高优先级的请求,再回应低优先

    2024年02月11日
    浏览(44)
  • STM32 -ADC+DMA使用(巨全面)

    在STM32中,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁¹。STM32的ADC为12位,AD最大值是4095,对应最大电压3.3V,可对0-3.3v之间的任意电压量化¹。STM32的ADC有18个输入通道,可测量16个外部和2个内部信号源¹。 在多通道数据

    2024年04月25日
    浏览(40)
  • 【STM32】GPIO——快速IO的使用

    STM32的每个GPIO端口都有两个特别的寄存器,GPIOx_BSRR和GPIOx_BRR寄存器,通过这两个寄存器可以直接对对应的GPIOx端口置\\\'1\\\'或置\\\'0\\\'。 GPIOx_BSRR的高16位中每一位对应端口x的每个位,对高16位中的某位置\\\'1\\\'则端口x的对应位被清\\\'0\\\';寄存器中的位置\\\'0\\\',则对它对应的位不起作用。 GPI

    2024年03月17日
    浏览(52)
  • STM32F429 Discovery开发板应用:使用FreeRTOS队列+DMA双缓存实现串口数据接收

      参考帖子:https://blog.csdn.net/freedompoi/article/details/122350866 目前想要实现STM32F4自带的DMA双缓冲区,尝试过一版,结果不能预期,就使用了RxHalfCplt和RxCplt去实现DMA双缓冲区的效果。 现在有时间了,又重新实现STM32F4自带的DMA双缓冲区,作为参考。   MCU:STM32F429ZIT6 开发环境:

    2024年02月08日
    浏览(48)
  • 【STM32+HAL】DAC+DMA输出波形实现

    有关CUBEMX的初始化配置,参见我的另一篇blog:【STM32+HAL】CUBEMX初始化配置 1、芯片: STM32F407ZGT6 2、STM32CubeMx软件 3、IDE: MDK-Keil软件 4、STM32F4xxHAL库 1、DAC+DMA输出波形 2、按键切换波形及频率 3、串口打印当前波形以及频率值 1、开启DAC 这里开启的是通道一 Output Buffer:关闭后可

    2024年02月02日
    浏览(48)
  • stm32和python实现DMA+串口数据收发

    1-0 串口配置 1-1 DMA发送模式配置 1-2 通过DMA传输数据到USART1的发送寄存器 1-3 串口数据发送 将usart1_dma_tx_data()函数放在main函数中或者中断处理函数中即可,如下所示: 2-1 DMA接收模式配置 2-2 串口结束中断 2-3 对串口接收的数据进行处理 3 完整程序

    2024年02月14日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包