最近在学习PWM+DMA配合生成可改变占空比的PWM波形。找了很多很多资料但是感觉对初学者不是很友善,只是提供了很多原理。这边使用的代码是固件库版的,也是学习STM32最基础的固件库代码了吧!

  1. 预分频器(TIMx_PSC)
  1. 自动重装载寄存器(TIMx_ARR)
  1. 捕获/比较寄存器x(TIMx_CCRx)

一、原理

当PWM计数到CCR寄存器的设定值后触发对应DMA请求,将下次CCR值装入就是了。

二、函数(结构体)

PWM:时基初始化结构体、输出比较寄存器结构体

DMA:DMA初始化结构体

三、踩到的坑(真的是绝)

1、确定定时器

高级控制定时器(TIM1、TIM8)、通用定时器(TIMx)、基本定时器(TIM6、TIM7)—我选用的是TIM2

2、确定引脚(查用户手册)

我选择的是:TIM2-CH4 PA3

3、确定DMA以及通道(查询参考手册)

根据上述选择觉得DMA1还是DMA2,这边是DMA1 通道为 Channel7

4、确定APBx(查询参考手册)

决定外设和GPIO的时钟使能

5、总结

选择TIM2 – CH4(PA3) – DMA1和Channel7 – APB1和APB2(都要使能)

四、代码

1、PWM.c文件(涉及定时器PWM初始化、GPIO初始化化)(其实这边中断都不需要要)

#include "pwm.h"

static void TIM2_PWM_GPIO_Init(void);
static void TIM2_PWM_Mode(void);
static void TIM2_NVIC_Config(void);

static void TIM2_NVIC_Config(void)
{
#include "stm32f10x.h"                  // Device header
    NVIC_InitTypeDef NVIC_InitStruct;
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQ;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    
    NVIC_Init(&NVIC_InitStruct);
}

static void TIM2_PWM_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //开启GPIO时钟
    PWM_TIMx_APBxClk(PWM_TIMx_Clkx, ENABLE);
    
    GPIO_InitStruct.GPIO_Pin = PWM_TIMx_GPIOx;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PWM_TIMx_PORPx, &GPIO_InitStruct);
}

static void TIM2_PWM_Mode(void)
{
//-------------------结构体------------------------------------//
    TIM_TimeBaseInitTypeDef      TIM_TimeBase_InitSturct;
    TIM_OCInitTypeDef                    TIM_OC_InitSturct;
    
//----------------------开启定时器时钟---------------------//
    ADVANCE_TIMx_APBxClk(ADVANCE_TIMx_Clk, ENABLE);

//-------------------时基初始化结构体-------------------------//
    //信号周期
    TIM_TimeBase_InitSturct.TIM_Period = TIM2_Period;
    //分频周期,记一次的时间
    TIM_TimeBase_InitSturct.TIM_Prescaler    =    (9);
    //计数模式
    TIM_TimeBase_InitSturct.TIM_CounterMode    =    TIM_CounterMode_Up;
    //死区(普通,通用定时器不需要配置)
    //TIM_TimeBase_InitSturct.TIM_ClockDivision    =    TIM_CKD_DIV1;                //CR1
    //重复寄存器,没用到不要管
    TIM_TimeBase_InitSturct.TIM_RepetitionCounter = 0;                                    //RCR
    TIM_TimeBaseInit(ADVANCE_TIMx, &TIM_TimeBase_InitSturct);
    
//--------------------输出比较寄存器结构体-----------------//
    //模式选择
    TIM_OC_InitSturct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OC_InitSturct.TIM_OutputState = TIM_OutputState_Enable;
    //
    TIM_OC_InitSturct.TIM_Pulse = TIM2_Pulse;    //占空比
    TIM_OC_InitSturct.TIM_OCPolarity = TIM_OCPolarity_High;
    //
    TIM_OC_InitSturct.TIM_OCIdleState = TIM_OCIdleState_Set;
    //
    TIM_OC4Init(ADVANCE_TIMx, &TIM_OC_InitSturct);
    TIM_OC4PreloadConfig(ADVANCE_TIMx, TIM_OCPreload_Enable);
    
//-----------------------------------------------------------//
    TIM_Cmd(ADVANCE_TIMx, ENABLE);
    TIM_CtrlPWMOutputs(ADVANCE_TIMx, ENABLE);
}

void PWM_Init(void)
{
    TIM2_NVIC_Config();
    TIM2_PWM_GPIO_Init();
    TIM2_PWM_Mode();
    
    //TIM_DMAConfig(ADVANCE_TIMx, TIM_DMABase_CCR4, TIM_DMABurstLength_1Transfer);
    TIM_DMACmd(ADVANCE_TIMx, TIM_DMA_CC4, ENABLE);
}

2、DMA.c文件(涉及DMA结构初始化)RGB_Buff内存存储的缓存要发给PWM改变占空比的

#include "dma.h"

#define RGB_Size 25
//1码:0x06    0码:0x02
const uint16_t RGB_G_Buff[RGB_Size] = { 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x00 };
const uint16_t RGB_R_Buff[RGB_Size] = { 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x00 };
const uint16_t RGB_B_Buff[RGB_Size] = { 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
                                                                            0x00 };
const uint16_t RGB_0_Buff[RGB_Size] = { 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
                                                                            0x00 };

void Dma_Init(void)
{
    DMA_InitTypeDef DMA_InitStruct;
    
    TIM_DMA_APBxCLK(TIM_DMA_CLK ,ENABLE);    
    
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR4;
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)RGB_B_Buff;
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;//从存储器读
    DMA_InitStruct.DMA_BufferSize = RGB_Size;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
    
    DMA_Init(MTM_DMA_Channe, &DMA_InitStruct);
    DMA_Cmd(MTM_DMA_Channe, ENABLE);
    //DMA_ClearFlag(MTM_DMA_TCx);
}

3、PWM.h

#ifndef __PWM_H
#define __PWM_H

#include "stm32f10x.h"

//GPIO定义
#define PWM_TIMx_PORPx          GPIOA
#define PWM_TIMx_GPIOx          GPIO_Pin_3
#define PWM_TIMx_APBxClk        RCC_APB2PeriphClockCmd
#define PWM_TIMx_Clkx                RCC_APB2Periph_GPIOA
//TIM2定义
#define ADVANCE_TIMx                TIM2        //TIM2_CH4
#define ADVANCE_TIMx_APBxClk        RCC_APB1PeriphClockCmd
#define ADVANCE_TIMx_Clk            RCC_APB1Periph_TIM2
//中断定义
#define TIM2_IRQ                     TIM2_IRQn
#define TIM2_IRQandle                TIM2_IRQHandler
//PWM配置
//PWM时钟频率 = TIM_CLK/(ARR+1)(PSC+1)
#define TIM2_Period                (8-1)        // 周期
#define TIM2_Psc                    (9-1)        //分频因子
#define TIM2_Pulse                    0            //CCR占空比

void PWM_Init(void);

#endif /* __PWM_H */

4、DMA.h

#ifndef __DMA_H
#define __DMA_H

#include "stm32f10x.h"

#define MTM_DMA_Channe      DMA1_Channel7
#define MTM_DMA_TCx                DMA1_FLAG_TC7
#define    TIM_DMA_CLK                RCC_AHBPeriph_DMA1
#define    TIM_DMA_APBxCLK        RCC_AHBPeriphClockCmd

void Dma_Init(void);

#endif /* __DMA_H */

5、main.c

#include "stm32f10x.h"
#include "pwm.h"
#include "dma.h"

uint16_t time = 0;

int main()
{
    Dma_Init();
    PWM_Init();

    while(1)
    {
        
    }
}

五、测试波形

之前是使用0x06, 0x02,0x00各八组的数据组测量的,可以明显看到下述测量波形占空比的变化

六、后续补充、、、

物联沃分享整理
物联沃-IOTWORD物联网 » STM32F103C6T6之PWM+DMA篇

发表评论