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("%d\r\n", 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 );
    DMA_Config(DMA1_Channel6, (u32)&TIM3->ARR, (u32)send_buf, 100);
    TIM3->ARR = 599 ;   /* 由于最后一项是0,所以在最后的时刻ARR会被清零,导致下一次启动无效。*/
  DMA_InitStructure.DMA_BufferSize = DMA_CHx->CNDTR;
  DMA_Cmd(DMA_CHx, ENABLE);
}     

/*
 *进度反馈,返回剩下的数据量
 */
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_bufsize并不是要多大有多大的,比较STM32的RAM有限,没办法一下子给你分配那么多空间,如果你强行分配的话,那么在编译过程中一定会报错,我在测试的时候,分配到3W+的时候已经差不多是极限了,但是这仅仅只是一个demo程序,在实际应用的时候不可能把RAM都给send_buf,所以如果想要发送大量的脉冲的话,比如我想发409600个脉冲给步进驱动器,那么我需要分多次发送。这部分我后面会继续做,有空会分享给小伙伴们。

猜你喜欢

转载自blog.csdn.net/weixin_41995979/article/details/82495998