简体   繁体   English

PWM DMA 到整个 GPIO

[英]PWM DMA to a whole GPIO

I've got an STM32F4, and I want to PWM a GPIO port that's been OR'd with a mask..我有一个 STM32F4,我想 PWM 一个 GPIO 端口,该端口已与掩码进行了 OR 运算。

So, maybe we want to PWM 0b00100010 for awhile at 200khz, but then, 10khz later, we now want to PWM 0b00010001 ...then, 10kHz later, we want to PWM some other mask on the same GPIO.所以,也许我们想在 200khz 上 PWM 0b00100010一段时间,但是然后,10khz 之后,我们现在想要 PWM 0b00010001 ...然后,10kHz 之后,我们想在同一个 GPIO 上 PWM 一些其他掩码。

My question is, how do you do this with DMA?我的问题是,你如何用 DMA 做到这一点? I'm trying to trigger a DMA transfer that will set all the bits on a rising edge, and then another DMA transfer that will clear all the bits on a falling edge.我试图触发一个 DMA 传输,它将在上升沿设置所有位,然后另一个 DMA 传输将在下降沿清除所有位。

I haven't found a good way to do this, (at least with CubeMX and my limited experience with C & STM32's) as it looks like I only get a chance to do something on a rising edge.我还没有找到一个好的方法来做到这一点,(至少使用 CubeMX 和我对 C 和 STM32 的有限经验),因为看起来我只有机会在上升的边缘做一些事情。

One of my primary concerns is CPU time, because although I mention hundreds of kilohertz in the above example, I'd like to make this framework very robust in-so-far as it isn't going to be wasteful of CPU resources...That's why I like the DMA idea, since it's dedicated hardware doing the mindless lifting of a word here to a word there type of stuff, and the CPU can do other things like crunch numbers for a PID or something.我最关心的问题之一是 CPU 时间,因为虽然我在上面的例子中提到了数百 kHz,但我想让这个框架非常健壮,因为它不会浪费 CPU 资源。 .这就是我喜欢 DMA 想法的原因,因为它是专用硬件,可以无意识地将这里的一个词提升为一个词那里类型的东西,而 CPU 可以做其他事情,例如处理 PID 的数字或其他东西。

Edit For clarity : I have a set of 6 values that I could write to a GPIO.编辑为清楚起见:我有一组 6 个可以写入 GPIO 的值。 These are stored in an array.这些存储在一个数组中。 What I'm trying to do is set up a PWM timer to set the GPIO during the positive width of the PWM and then I want the GPIO to be set to 0b00000000 during the low period width if the pwm.我想要做的是设置一个 PWM 计时器以在 PWM 的正宽度期间设置 GPIO,然后我希望在低周期宽度期间将 GPIO 设置为 0b00000000 如果 pwm. So, I need to see when the rising edge is, quickly write to the gpio, then see when the falling edge is, and write 0 to the gpio.所以,我需要看上升沿是什么时候,快速写入gpio,然后看下降沿是什么时候,然后将0写入gpio。

Limited solution without DMA没有 DMA 的有限解决方案

STM32F4 controllers have 12 timers with up to 4 PWM channels each, 32 in total. STM32F4 控制器有 12 个定时器,每个定时器最多 4 个 PWM 通道,总共 32 个。 Some of them can be synchronized to start together, eg you can have TIM1 starting TIM2 , TIM3 , TIM4 and TIM8 simultaneously.其中一些可以同步启动,例如您可以让TIM1启动TIM2TIM3TIM4TIM8 That's 20 synchronized PWM outputs.那是 20 个同步 PWM 输出。 If it's not enough, you can form chains where a slave timer is a master to another, but it'd be quite tricky to keep all of them perfectly synchronized.如果这还不够,您可以形成链,其中从计时器是另一个计时器的主计时器,但要保持所有计时器完全同步会非常棘手。 Not so tricky, if an offset of a few clock cycles is acceptable.如果几个时钟周期的偏移是可以接受的,那就没那么棘手了。

There are several examples in the STM32CubeF4 library example projects section, from which you can puzzle together your setup, look in Projects/*_EVAL/Examples/TIM/*Synchro* . STM32CubeF4 库示例项目部分中有几个示例,您可以从中拼凑您的设置,请查看Projects/*_EVAL/Examples/TIM/*Synchro*

General solution通用解决方案

A general purpose or an advanced timer (that's all of them except TIM6 and TIM7 ) can trigger a DMA transfer when the counter reaches the reload value (update event) and when the counter equals any of the compare values (capture/compare event).当计数器达到重新加载值(更新事件)计数器等于任何比较值(捕获/比较事件)时,通用或高级定时器(除TIM6TIM7之外的所有定时器)可以触发 DMA 传输。

The idea is to let DMA write the desired bit pattern to the low (set) half of BSRR on a compare event, and the same bits to the high (reset) half of BSRR on an update event.这个想法是让 DMA 在比较事件中将所需的位模式写入BSRR的低(设置)一半,并在更新事件中将相同的位写入BSRR的高(复位)一半。

There is a problem though, that DMA1 cannot access the AHB bus at all (see Fig. 1 or 2 in the Reference Manual), to which the GPIO registers are connected.但是有一个问题, DMA1根本无法访问 AHB 总线(参见参考手册中的图 1 或 2),GPIO 寄存器连接到该总线。 Therefore we must use DMA2 , and that leaves us with the advanced timers TIM1 or TIM8 .因此,我们必须使用DMA2 ,这给我们留下了高级计时器TIM1TIM8 Things are further complicated because DMA requests caused by update and compare events from these timers end up on different DMA streams (see Table 43 in the RM).事情变得更加复杂,因为来自这些定时器的更新和比较事件引起的 DMA 请求最终会出现在不同的 DMA 流上(参见 RM 中的表 43)。 To make it somewhat simpler, we can use DMA 2, Stream 6 or Stream 2, Channel 0, which combine events from 3 timer channels.为了更简单,我们可以使用 DMA 2, Stream 6 或 Stream 2, Channel 0,它们结合了来自 3 个定时器通道的事件。 Instead of using the update event, we can set the compare register on one timer channel to 0.我们可以不使用更新事件,而是将一个定时器通道上的比较寄存器设置为 0。

Set up the DMA stream of the selected timer to将所选定时器的 DMA 流设置为

  • channel 0通道 0
  • single transfer (no burst)单次传输(无突发)
  • memory data size 16 bit内存数据大小 16 位
  • peripheral data size 16 bit外设数据大小 16 位
  • no memory increment内存增量
  • peripheral address increment外设地址增量
  • circular mode循环模式
  • memory to peripheral内存到外围设备
  • peripheral flow controller: I don't know, experiment外围流量控制器:我不知道,实验
  • number of data items 2数据项数 2
  • peripheral address GPIOx->BSRR外设地址GPIOx->BSRR
  • memory address points to the output bit pattern内存地址指向输出位模式
  • direct mode直接模式
  • at last, enable the channel.最后,启用通道。

Now, set up the timer现在,设置定时器

  • set the prescaler and generate an update event if required如果需要,设置预分频器并生成更新事件
  • set the auto reload value to achieve the required frequency设置自动重载值以达到所需频率
  • set the compare value of Channel 1 to 0将通道 1 的比较值设置为 0
  • set the compare value of Channel 2 to the required duty cycle将通道 2 的比较值设置为所需的占空比
  • enable DMA request for both channels为两个通道启用 DMA 请求
  • enable compare output on both channels在两个通道上启用比较输出
  • enable the counter启用计数器

This way you can control 16 pins with each timer, 32 if using both of them in master-slave mode.通过这种方式,您可以使用每个定时器控制 16 个引脚,如果在主从模式下同时使用它们,则可以控制 32 个引脚。

To control even more pins (up to 64) at once, configure the additional DMA streams for channel 4 compare and timer update events, set the number of data items to 1, and use ((uint32_t)&GPIOx->BSRR)+2 as the peripheral address for the update stream.要一次控制更多引脚(最多 64 个),请为通道 4 比较和定时器更新事件配置额外的 DMA 流,将数据项数设置为 1,并使用((uint32_t)&GPIOx->BSRR)+2作为更新流的外设地址。

Channels 2 and 4 can be used as regular PWM outputs, giving you 4 more pins.通道 2 和 4 可用作常规 PWM 输出,为您提供 4 个引脚。 Maybe Channel 3 too.也许第 3 频道也是如此。

You can still use TIM2 , TIM3 , TIM4 , and TIM5 (each can be slaved to TIM1 or TIM8 ) for 16 more PWM outputs as described in the first part of my post.您仍然可以使用TIM2TIM3TIM4TIM5 (每个都可以从属于TIM1TIM8 )以获得另外 16 个 PWM 输出,如我帖子的第一部分所述。 Maybe TIM9 and TIM12 too, for 4 more, if you can find a suitable master-slave setup.也许TIM9TIM12也是,如果你能找到合适的主从设置,还有 4 个。

That's 90 pins toggling at once.那是一次切换 90 个引脚。 Watch out for total current limits.注意总电流限制。

what PWM 0b00100010 means? PWM 0b00100010 是什么意思? PWM is a square wave with some duty ratio. PWM 是具有一定占空比的方波。 it wil be very difficult to archive using DMA but you will need to have table with already calculated values.使用 DMA 存档将非常困难,但您需要具有已计算值的表格。 For example to have 2kHz PWM with 10% ratio you will need to have 10 samples one with bit set, nine with bit zeroed.例如,要具有 10% 比率的 2kHz PWM,您将需要有 10 个样本,其中一个位设置,九个位为零。 You configure the timer to 20k / sec trigger mem-to-mem (GPIO has to be done this way) DMA transmission in the circular mode.您将定时器配置为 20k/sec 触发 mem-to-mem(GPIO 必须以这种方式完成)以循环模式进行 DMA 传输。 On the pin you will have 2kHz 10% wave.在引脚上,您将有 2kHz 10% 的波形。 The PWM resolution will be 10%. PWM 分辨率将为 10%。 If you want to make it 0.5% you will need 200 samples table and DMA triggered 400k times per second.如果你想让它达到 0.5%,你需要 200 个样本表和 DMA 每秒触发 400k 次。

IMO it is better to use timer and DMA to load new values to it (read about the burst DMA mode in the timer documentation in the Reference Manual) IMO 最好使用计时器和 DMA 向其加载新值(在参考手册的计时器文档中阅读有关突发 DMA 模式的信息)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM