STM32F103单片机PB4配置PWM+DMA模式

今天一天都被自闭在配置PB4的PWM+DMA模式,重映射真的很难受,尤其是应用到高频率0.847MHZ的场景下,不过,通过今天配置,让我对DMA了解了很多,不敢独享,故拿出来分享,欢迎大家纠错。
有几点需要注意:
1. PB4重映射到TIM3-CH1:下面三句必不可少,且顺序不能改变。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);	//失能PB4——>JNTRST主要功能,这样子才能重映射到TIM3-CH1,注意顺序,先失能JNTRST再重映射
	
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH1->PB4

2. PWM1模式和PWM2模式:
PWM模式1:在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为有效电平,否则为无效电平;在 向下计数时,一旦TIMx_CNT > TIMx_CCR1时通道1为无效电平,否则为有效电平。

PWM模式2:在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平
有效电平的设置为:TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置输出有效极性

3. 外设基地址别设置错了
比如这里我需要把TIM3-CH1作为外设,故外设基地址就设置为:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&TIM3->CCR1);	//DMA外设TIM3-CH1对应的基地址,“&”符号别忘记了

4. TIM-DMA中断设置

TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);	//使能定时器3的TIM_DMA_Update更新中断

 这里一开始我并没有选择TIM_DMA_Update,而是选择的TIM_DMA_CC1,后面出来的PWM波形会出现异常现象,没有发PWM波时,会时而高电平时而低电平,很不稳定,故而选择TIM_DMA_Update,这里又可以牵扯出下面5和6两个需要注意点

5. TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);中间这个TIM_DMA_CC1如何选择?
 TIM_DMA_CC1是和TIM的通道口对应的,如:PB4为TIM3-CH1,对应通道1,则为TIM_DMA_CC1,若为TIM3-CH2,则为TIM_DMA_CC2

6. TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);这里我需要TIM_DMA_Update时,DMA通道口能不能随意设置?
 答案:不能!
       STM32F103单片机PB4配置PWM+DMA模式_第1张图片
 我一开始就是根据这个图将DMA通道口设置为通道6,而且只使能TIM_DMA_Update ,即:

TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);

后面Debug发现,会卡在

while(!DMA_GetFlagStatus(DMA1_FLAG_TC3));	//等待传输完成

这句话里,找了好久好久的Bug,从网上也找了很久的解决办法,最后解决了问题,因为假如我设置为DMA的通道6,那么就应该用

TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);

而不能用TIM_DMA_Update,否则就会卡死。而如果必须要用TIM_DMA_Update,就应该将DMA通道口修改为通道3,由上表可知,只有通道三才有TIM3的“TIM_UP”这个功能,这才解决了问题

7. DMA_GetFlagStatus(DMA1_FLAG_TC3),这里的DMA1_FLAG_TC3该根据什么设置?
 这个是根据你设的DMA通道口来设置的,比如我使用的是DMA通道3,那么就用DMA1_FLAG_TC3,类似于这种不知道各个参数的含义的,建议查看参考手册或者百度DMA配置参数解释。

最后,附上代码,仅供参考:
初始化配置函数:
void Timer3_PWM_DMA_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  	TIM_OCInitTypeDef  TIM_OCInitStructure;
 	GPIO_InitTypeDef GPIO_InitStructure;
 	DMA_InitTypeDef DMA_InitStructure;
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
  	GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);	//失能PB4——>JNTRST主要功能,这样子才能重映射到TIM3-CH1,注意顺序,先失能JNTRST再重映射
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH1->PB4    
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
	
	TIM_TimeBaseStructure.TIM_Period = 85-1;
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	//PWM1模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//设置输出有效极性
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);
		
		
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA1时钟	
	DMA_DeInit(DMA1_Channel3);	//使能DMA1的通道3
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&TIM3->CCR1);	//DMA外设TIM3-CH1对应的基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LED_BYTE_Buffer;		//内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;	//设置传输方向:从内存到外设的传输方向
	DMA_InitStructure.DMA_BufferSize = 24;	//内存缓存器buffer的大小
	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_Low;	 //DM通道拥有低优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;	//失能内存到内存的传输方式,即非内存到内存传输
	
	DMA_Init(DMA1_Channel3, &DMA_InitStructure);	//初始化DMA1通道3
	
	TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);	//使能定时器3的TIM_DMA_Update更新中断
}

发送函数里:
		DMA_SetCurrDataCounter(DMA1_Channel3, buffersize);	 //先设置传输数据长度,再有下一步使能
		DMA_Cmd(DMA1_Channel3, ENABLE);	//打开DMA通道3
		TIM_Cmd(TIM3, ENABLE);	//打开TIM3
		while(!DMA_GetFlagStatus(DMA1_FLAG_TC3));	//等待传输完成
		TIM_Cmd(TIM3, DISABLE);		//及时关闭
		DMA_Cmd(DMA1_Channel3, DISABLE);
		DMA_ClearFlag(DMA1_FLAG_TC3);	//清除传输完成标志位

你可能感兴趣的:(单片机)