最近在学习PWM+DMA配合生成可改变占空比的PWM波形。找了很多很多资料但是感觉对初学者不是很友善,只是提供了很多原理。这边使用的代码是固件库版的,也是学习STM32最基础的固件库代码了吧!
预分频器(TIMx_PSC)
自动重装载寄存器(TIMx_ARR)
捕获/比较寄存器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(都要使能)文章来源:https://www.toymoban.com/news/detail-417652.html
四、代码
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各八组的数据组测量的,可以明显看到下述测量波形占空比的变化文章来源地址https://www.toymoban.com/news/detail-417652.html
六、后续补充、、、
到了这里,关于STM32F103C6T6之PWM+DMA篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!