历史上的今天

今天是:2024年08月22日(星期四)

正在发生

2019年08月22日 | STM32——使用PWM+DMA实现脉冲发送精确控制

发布者:Xingfu6666 来源: eefocus关键字:STM32  PWM  DMA  脉冲发送  精确控制 手机看文章 扫描二维码
随时随地手机看文章

之前用stm32写过脉冲发送的代码,用来控制步进电机,但是缺点明显,之前是用定时器中断做的,所以一但控制的电机多起来,MCU资源占用就很大,这在大多数情况下是不可接受的,更不用说多轴联动了。

最近做的步进电机CAN总线控制系统,就想顺便重新写驱动。希望做到占用很少的MCU资源,实现脉冲发送的精确控制。既然是用来控制步进电机,那么脉冲的数量和频率一定要可控,要不然怎么实现电机的加减速曲线。于是就想到了DMA。


DMA (直接存储器访问)

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


在记忆里,STM32的数据手册中有提到PWM有DMA触发的模式。那么这一次终于有用武之地了。


show me your code !!!

main.c

#include "delay.h"

#include "key.h"

#include "sys.h"

#include "usart.h"

#include "dma.h"

#include "timer.h"

#define size 100


extern u16 DMA1_MEM_LEN;

extern DMA_InitTypeDef DMA_InitStructure;  

u16 send_buf[size];

 

int main(void)

{

int i;

int feedback;

delay_init();

uart_init(115200);

KEY_Init();

DMA_Config(DMA1_Channel6, (u32)&TIM3->ARR, (u32)send_buf, size);

TIM3_PWM_Init(599,7199);

for(i = 0; i < size; ++i)

{

if(i != size - 1)

send_buf[i] = 100 + 10 * i;

else

send_buf[i] = 0;

}

DMA_Enable(DMA1_Channel6);

while(1)

{

feedback = DMA_send_feedback(DMA1_Channel6);

if(feedback != 0)

{

printf("-> ");

printf("%drn", DMA_send_feedback(DMA1_Channel6));

}

if(KEY_Scan(0) == 1)

{

DMA_Enable(DMA1_Channel6);

}

}

}


main.c中的send_buf[size]是控制信息的来源,你想要如何发送脉冲,全靠这个buffer

这里写图片描述

这是send_buf的数据组成,size 决定了发送脉冲的数量information决定了脉冲的频率。至于脉冲的脉宽,可以在timer.c中的初始化函数中修改。


dma.c

#include "dma.h"


extern u16 send_buf[100];

extern TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

DMA_InitTypeDef DMA_InitStructure;

u16 DMA1_MEM_LEN; /* 保存DMA每次数据传送的长度 */


/*

 *DMA1的各通道配置

 *这里的传输形式是固定的,这点要根据不同的情况来修改

 *从存储器->外设模式/8位数据宽度/存储器增量模式

 *DMA_CHx:DMA通道CHx

 *cpar:外设地址

 *cmar:存储器地址

 *cndtr:数据传输量

 */

void DMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)

{

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* 使能DMA钟源 */

delay_ms(5);

  DMA_DeInit(DMA_CHx);   /* 将DMA的通道1寄存器重设为缺省值 */


DMA1_MEM_LEN=cndtr;

DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  /* DMA外设基地址 */

DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  /* DMA内存基地址 */

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  /* 数据传输方向,从内存读取发送到外设 */

DMA_InitStructure.DMA_BufferSize = cndtr;  /* DMA通道的DMA缓存的大小 */

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  /* 外设地址寄存器不变 */

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  /* 内存地址寄存器递增 */

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  /* 数据宽度为16位 */

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /* 数据宽度为16位 */

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  /* 工作在正常模式 */

DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* DMA通道 x拥有中优先级 */

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  /* DMA通道x没有设置为内存到内存传输 */

DMA_Init(DMA_CHx, &DMA_InitStructure); 


/* 开启一次DMA传输 */

void DMA_Enable(DMA_Channel_TypeDef*DMA_CHx)

{

DMA_Cmd(DMA_CHx, DISABLE );

TIM3->ARR = 2; /* 由于最后一项是0,所以在最后的时刻ARR会被清零,导致下一次启动无效。*/

  DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);

  DMA_Cmd(DMA_CHx, ENABLE);

TIM_Cmd(TIM3, ENABLE);  /* 使能TIM3 */

TIM3->EGR = 0x00000001; /* 由于最后一次ARR值为0,这是为了停止定时器对io口的操作,但是不要忽略了一点:CNT并没有停止计数,而且是不会再停下来,如果没有手动操作的话,所以需要在每次dma使能时加上一句,将EGR里的UG位置1,清零计数器 */

}   


/*

 *进度反馈,返回剩下的数据量

 */

u16 DMA_send_feedback(DMA_Channel_TypeDef* DMA_CHx)

{

return DMA_CHx->CNDTR;


dma.h

#ifndef __DMA_H

#define __DMA_H    

#include "sys.h"

#include "delay.h"

#include "dma.h"

#include "timer.h"

#include "usart.h"

#include "stm32f10x_dma.h"


void NVIC_Configuration(void);         


void DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);//配置DMA1_CHx


void DMA_Enable(DMA_Channel_TypeDef*DMA_CHx);//使能DMA1_CHx


u16 DMA_send_feedback(DMA_Channel_TypeDef* DMA_CHx);


void DMA1_Channel6_IRQHandler(void);

#endif


timer.c

#include "timer.h"

#include "led.h"

#include "usart.h"

 

 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

/*

 *TIM3 PWM部分初始化 

 *PWM输出初始化

 *arr:自动重装值

 *psc:时钟预分频数

 */

void TIM3_PWM_Init(u16 arr,u16 psc)

{  

GPIO_InitTypeDef GPIO_InitStructure;

TIM_OCInitTypeDef  TIM_OCInitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* 使能定时器3时钟 */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  /* 使能GPIO外设 */   

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; /* TIM_CH1*/

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  /* 复用推挽输出 */

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);/* 初始化GPIO */

 

   /* 初始化TIM3 */

TIM_TimeBaseStructure.TIM_Period = arr; /* 设置在下一个更新事件装入活动的自动重装载寄存器周期的值 */

TIM_TimeBaseStructure.TIM_Prescaler =psc; /* 设置用来作为TIMx时钟频率除数的预分频值 */

TIM_TimeBaseStructure.TIM_ClockDivision = 0; /* 设置时钟分割:TDTS = Tck_tim */

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  /*TIM向上计数模式 */

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); /* 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 */

 

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; /* 选择定时器模式:TIM脉冲宽度调制模式1 */

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; /* 比较输出使能 */

//TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  /* 使能TIM3在CCR1上的预装载寄存器*/

TIM_OCInitStructure.TIM_Pulse= 100;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; /* 输出极性:TIM输出比较极性高 */

TIM_OC1Init(TIM3, &TIM_OCInitStructure);  /* 根据T指定的参数初始化外设TIM3 OC1 */

//TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE); /* 如果是要调节占空比就把这行去掉注释,然后注释掉下面那行,再把DMA通道6改为DMA通道3 */

TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);

TIM_Cmd(TIM3, ENABLE);  /* 使能TIM3 */

}


timer.h

#ifndef __TIMER_H

#define __TIMER_H

#include "sys.h"


void TIM3_PWM_Init(u16 arr,u16 psc);


#endif


看看运行效果

这里写图片描述

垃圾示波器,玩玩就好。。。。

篇幅限制,结果是最终将会发送100个脉冲,频率慢慢改变。但是在这个有一点需要提醒小伙伴们注意一下,send_buf的size并不是要多大有多大的,比较STM32的RAM有限,没办法一下子给你分配那么多空间,如果你强行分配的话,那么在编译过程中一定会报错,我在测试的时候,分配到3W+的时候已经差不多是极限了,但是这仅仅只是一个demo程序,在实际应用的时候不可能把RAM都给send_buf,所以如果想要发送大量的脉冲的话,比如我想发409600个脉冲给步进驱动器,那么我需要分多次发送。这部分我后面会继续做,有空会分享给小伙伴们。

关键字:STM32  PWM  DMA  脉冲发送  精确控制 引用地址:STM32——使用PWM+DMA实现脉冲发送精确控制

上一篇:stm32入门——PWM输出控制直流电机变速
下一篇:stm32103R8C6 捕捉2路pwm 串口打印捕捉数据

推荐阅读

据报道,未来几周,重庆小康工业集团股份有限公司(Chongqing Sokon Industry Group)的新电动汽车工厂将采用最先进的制造技术,即首次在中国使用杜尔新一代极具灵活性的7轴涂装机器人EcoRP E043i。该机器人可灵活喷涂不同尺寸的纯电动S车身,无需采用之前的线性行走轨。杜尔是小康电动车涂装的总承包商,其完整的涂装车间还包括了带热回收功能的Eco...
今天复习了之前的串口通信知识,为了巩固,所以想自己总结一下。串口通信配置方法大致是这样的:①:使能相应的时钟②:初始化IO口③:初始化相应的串口参数④:使能串口⑤:开启中断并初始化NVIC(如果需要到中断)⑥:编写中断处理函数⑦:串口数据收发下面我以 USART1为例,写一下代码。#include “stm32f10x.h”void CHUANKOU_Init(void){GPIO_Init...
截止8月21日收盘,A股三大指数今日集体收涨,其中上证指数收报3380.68点,上涨0.50%;深成指收报13478.00点,上涨1.18%;创业板指表现较强,收报2632.45点,上涨1.72%。两市合计成交8442亿元,北向净流入31.86亿元。从盘面上看,食品饮料、云游戏、造纸居板块涨幅居前,农业、数字货币、钢铁居板块跌幅居前。两市2401家个股上涨,1330家个股下跌。其中涨停...
据俄罗斯卫星通讯社报道,芬兰三大移动运营商之一 DNA 宣布,因通信标准提高,将在 2023 年底前关闭 3G 网络。  DNA 公司称,截至 2020 年底,其用 3G 传输的移动数据占比仅略超 2%。3G 网络关停后,将为 4G、5G 和其他新技术腾出频率。  IT之家了解到,根据爱立信在 6 月份的报告,目前全球多地运营商都在考虑推进 2G/3G...

史海拾趣

问答坊 | AI 解惑

IAR5.3(评估版)编译老是提示如下的错误,是怎么一回事啊

Error[Lp021]: the destination for compressed initializer batch "P2 mid-1" is placed at an address that is dependent on the size of the batch, which is not allowed when using packbits compression. Consider using "initialize b ...…

查看全部问答∨

请教:LCD怎样‘横屏’转‘竖屏’

我用的是2450,问一下LCD横屏转竖屏,驱动程序里除了在头文件处修改分辨率外,还要修改什么地方呢? 我只修改了头文件处定义的分辨率的情况下,屏幕变窄了(部分屏幕黑色没图象),竖直方向靠下的部分没被显示出来。 不知道在哪(几)个文件里的函 ...…

查看全部问答∨

linux下中断处理问题

  现有一外部中断,中断来临后要求驱动马上读取数据,现在问题是:中断来临后,怎么通知用户主动读取数据,现在我用的是使用了中断上下部,下部处理中断,一产生中断马上进入上半部分处理接收,并传送到用户空间,那么此时的读如果在没有 ...…

查看全部问答∨

知道timestool的请进(一个实时调度模拟与分析工具)!!!

请教了解瑞典的乌普萨拉(Uppsala)大学开发的times的大侠,还有什么工具与times的功能差不多??? 小弟在此先谢过了!…

查看全部问答∨

电接点双金属温度计维护与检修

1.安装前须仔细核对型号及规格,指示指针不得偏出零位标记的黑框,否则应重新校验或更换。 硅橡胶电缆 2、仪表应安装于周围环境(或介质)温度-40~ 55℃,相对湿度不大于85%,振动或被测压力的急剧脉动对正确读数等无影响的环境下使用。 3151压力变 ...…

查看全部问答∨

对uCOS移植STM32官方例程的一点疑惑

                                 这两天,下了个ucos在STM32F103ZE-SK开发板上移植的官方例程(uCOSII-ST-STM32F103ZE-SK),这其中有两个文件夹,u ...…

查看全部问答∨

有偿寻找RF 放大方案

有偿寻找RF信号 放大方案,具体要求如下: RF 频率:912MHz 带宽为:902.2 MHz ~ 927.8 MHz。 放大前信号为:10dBm 要求放大后信号半径为:600M. 本人在深圳,欢迎有方案的个人或者公司联系洽谈。 QQ:516236905…

查看全部问答∨

AD9852没有输出

用DSP28335控制AD9852时发现一直没有输出,但是板子发热倒是挺烫的说明是有工作的,现在找不到原因了,求问各位大侠怎么解决?…

查看全部问答∨

关于STM32的TIM1产生两对带死区互补堆成的PWM波

想产生两对带死区互补对称的PWM控制逆变全桥的四个管子,TIM1_CH1(PA8)和TIM1_CH1N(PB13),TIM1_CH2(PA9)和TIM1_CH2N(PB14),请问初始化设置用库的结构体定义一个变量还是两个变量呢? 例如: 1、        GPIO_InitTypeDef G ...…

查看全部问答∨

求购一块ST M0 DISCO ,030.051,072的都可以

本帖最后由 ddllxxrr 于 2016-1-7 16:12 编辑 求购一块ST M0 DISCO ,030. 051,072的都可以 有意者加QQ 2948530632 …

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved