DMA+TIM方式输出PWM,无需CPU干预,所以想用DMA的方式直接输出到PWM
本次测试采用的是STM32F103C8T6 测试发现定时器的DMA通道与官方资料不符,实际情况与CUBEmx硬件配置也不符,刨了两天终于发现问题,遂做记录,后方道友谨慎这个坑...
DMA+TIM方式输出PWM配置大致过程:
1、使能GPIO、TIM、DMA对应的时钟
2、GPIO初始化:配置相关IO口
3、DMA初始化:配置数据源、目标,输出传输方向,数据长度、大小,DMA通道........
4、TIM初始化:配置装载值,分频、通道......
重要
1、STM32F103C8T6 共有四个 TIM1-TIM4 其中TIM1为高级定时器 输出PWM配置与其他配置不一致,最后需要加上这一句
TIM_CtrlPWMOutputs(TIM1, ENABLE); //高级定时器才有 必须打开
具体可以看我另外一篇文章-----高级定时器输出PWM
2、STM32F103C8T6 只有DMA1 通道1-7
3、TIM对应的DMA通道实测与官方资料不符,没有试过其他外设,使用的标准库versionV3.4.0
(按理说官方资料很严谨,应该不会有问题,也可能我的芯片或者代码哪里有问题,反正最后功能实现了,欢迎各位道友评论、讨论~~~)
各个通道的DMA1请求一览官方资料如下图,官方CUBE的里的TIM配置也和下图一致
实测情况是一个定时器对应一个DMA通道,如下:
TIM1:
PA8---TIM1-CH1 -------DMA1-CH5
PA9---TIM1-CH2 -------DMA1-CH5
PA10---TIM1-CH3 -------DMA1-CH5
PA11---TIM1-CH4 -------DMA1-CH5
TIM2:
PA0---TIM2-CH1 -------DMA1-CH2
PA1---TIM2-CH2 -------DMA1-CH2
PA2---TIM2-CH3 -------DMA1-CH2
PA3---TIM2-CH4 -------DMA1-CH2
TIM3:
PA6---TIM3-CH1 -------DMA1-CH3
PA7---TIM3-CH2 -------DMA1-CH3
PB0---TIM3-CH3 -------DMA1-CH3
PB1---TIM3-CH4 -------DMA1-CH3
TIM4:
PB6---TIM4-CH1 -------DMA1-CH7
PB7---TIM4-CH2 -------DMA1-CH7
PB8---TIM4-CH3 -------DMA1-CH7
PB9---TIM4-CH4 -------DMA1-CH7
STM32工程文件:
STM32TIM+DMA输出PWM工程源码-C/C++文档类资源-CSDN下载
源码:
其他芯片可能需要更改相关引脚配置
主函数调用:
#define MAX_TABLE 8
uint16_t Buffer_1[MAX_TABLE] =
{
36, 72, 108, 144, 180, 216, 252, 288
}
uint16_t Buffer_2[MAX_TABLE] =
{
1800, 1836, 1872, 1908, 1944, 1980, 2016, 2052
}
int main(void)
{
int i = 0;
SystemInit();
uart1_Init(115200 );
TIM_DMA_Configuration(TIM1, 1, (uint32_t*)Buffer_1, MAX_TABLE);
TIM_DMA_Configuration(TIM2, 1, (uint32_t*)Buffer_2, MAX_TABLE);
while (1)//断电打在此处即可查看DMA 和TIM 相关寄存器配置
{
}
}
DMA-TIM 输出PWM相关配置:
void TIM_DMA_Configuration(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t *pData, uint16_t Length)
{
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//GPIO配置 根据芯片更改引脚配置
if(TIMx == TIM1)
{
switch (Channel)
{
case 1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
break;
case 2:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//与串口1同引脚 不建议使用
break;
case 3:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//与串口1同引脚 不建议使用
break;
case 4:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
break;
default:
break;
}
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
else if(TIMx == TIM2)
{
switch (Channel)
{
case 1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
break;
case 2:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
break;
case 3:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
break;
case 4:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
break;
default:
break;
}
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
else if(TIMx == TIM3)
{
switch (Channel)
{
case 1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
break;
case 2:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
break;
case 3:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
break;
case 4:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
break;
default:
break;
}
}
else if(TIMx == TIM4)
{
switch (Channel)
{
case 1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
break;
case 2:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
break;
case 3:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
break;
case 4:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
break;
default:
break;
}
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//GPIO配置 根据芯片更改引脚配置
//DMA配置 //DMA配置 //DMA配置
switch (Channel)
{
case 1:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR1;//DMA外设基地址 TIMx CH1
break;
case 2:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR2;//DMA外设基地址 TIMx CH2
break;
case 3:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR3;//DMA外设基地址 TIMx CH3
break;
case 4:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR4;//DMA外设基地址 TIMx CH4
break;
default:
break;
}
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pData;//DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//数据方向 内存到外设
DMA_InitStructure.DMA_BufferSize = Length;//数据长度
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_Circular;//DMA_Mode_Circular--循环模式 DMA_Mode_Normal--工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;// DMA通道 x拥有中优先级 */
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;/// 禁用内存到内存传输
if(TIMx == TIM1)
{
DMA_Init(DMA1_Channel5, &DMA_InitStructure);//配置DMA通道
DMA_Cmd(DMA1_Channel5, ENABLE);//DMA使能
}
else if(TIMx == TIM2)
{
DMA_Init(DMA1_Channel2, &DMA_InitStructure);//配置DMA通道
DMA_Cmd(DMA1_Channel2, ENABLE);//DMA使能
}
else if(TIMx == TIM3)
{
DMA_Init(DMA1_Channel3, &DMA_InitStructure);//配置DMA通道
DMA_Cmd(DMA1_Channel3, ENABLE);//DMA使能
}
else if(TIMx == TIM4)
{
DMA_Init(DMA1_Channel7, &DMA_InitStructure);//配置DMA通道
DMA_Cmd(DMA1_Channel7, ENABLE);//DMA使能
}
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 3600 - 1; //
TIM_TimeBaseStructure.TIM_ClockDivision = 0;//分频值
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 50;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
switch (Channel)
{
case 1:
TIM_OC1Init(TIMx, &TIM_OCInitStructure);//TIMx-CH1
break;
case 2:
TIM_OC2Init(TIMx, &TIM_OCInitStructure);//TIMx-CH2
break;
case 3:
TIM_OC3Init(TIMx, &TIM_OCInitStructure);//TIMx-CH3
break;
case 4:
TIM_OC4Init(TIMx, &TIM_OCInitStructure);//TIMx-CH4
break;
default:
break;
}
TIM_DMACmd(TIMx, TIM_DMA_Update, ENABLE);
TIM_Cmd(TIMx, ENABLE);
if(TIMx == TIM1)
{
TIM_CtrlPWMOutputs(TIM1, ENABLE);//高级定时器必须使能
}
}