有关STM32的复用功能重映射(以STM32F103C8T6生成16路PWM波实战)

正点原子STM32F103学习笔记(七)——程序调试,端口复用与重映射,NVIC中断优先级管理 程序调试方法 PB3,PB4不能输出他所需要的电平状态原因 JTAG/SWD模式设置库函数(在文件stm32f10x_gpio.c中): void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) /* GPIO_Remap有效性 #define GPIO_Remap_SWJ_NoJTRST ((uint32_t)0x00300100) /*!< Full SWJ Enabled (JTAG-DP + 阅读详情

引言

近日在整理一些常用模块的资料,顺便复习一下各芯片的基础知识,发现自己是真的菜 在整理PWM这块知识点在GPIO引脚复用这块死掉了┭┮﹏┭┮,经过查阅了一些资料发现基础的问题是使用GPIO引脚的复用功能使用引脚作为PWM的输出IO口。


GPIO的复用功能

正常来说,我们在使用GPIO口的时候,一般分为两种情况:

  1. 作为正常的IO口使用,模拟输入、上拉、下拉、推挽等等功能,可以实现常用的LED、按键等功能,需要正常打开GPIO口的时钟。例如RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  2. 做为复用功能,使用IO口的开漏复用和推挽复用功能,作为芯片的ADC、DAC、USART、定时器等功能的引脚使用,需要打开复用时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

复用功能重映射

使用引脚的复用功能的时候,有三种情况:无重映射、部分重映射和完全重映射,而每种重映射的引脚资源又是不同的。我们拿STM32F1芯片的PWM输出举例,下面是一段TIM3输出PWM的代码:

void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO, ENABLE);  //使能        
    GPIO外设和AFIO复用功能模块时钟使能

	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射映射

    //设置该引脚为复用输出功能,输出TIM3 CH1、2的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM3_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //TIM3_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	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_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化TIM
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	
	TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
 
}

 这是一段非常常规的库函数初始化定时器输出PWM波代码,我们重点看复用功能重映射部分,在代码中也就是这一行:

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射映射

这段代码将GPIO的引脚重映像设置为了部分重映射,对应的通道和引脚就是

CH1->PB4,CH2->PB5

需要注意的是,GPIO的复用引脚都是芯片规定好的,不可以人为修改,所规定好的引脚在数据手册中都有标注(STM32F1XXX数据手册113-121页)

这里我将截图放出来,方便大家理解

在我的代码里使用的是部分重映像,所以TIM3的PWM输出通道1、2就默认为PB4,PB5,这个时候我们只需要初始化PB4、PB5为复用推挽输出就可以实现输出PWM的功能了

同理,如果我们使用无重映像的引脚,也就是PA6,PA7,这个时候不需要在GPIO_PinRemapConfig函数中配置,直接将配置这行注释掉就可以。

若使用完全重映像则是:

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);

这时候使用的引脚则是最右边那一列。


上面这部分讲的是IO口的复用功能重映射,我觉得可以解决大部分同学在输出PWM波时配置引脚的问题,还有一个重要的步骤是配置OC通道

在刚刚的代码中有这么一段:

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器

最后两行OC2Init,简明大意就是初始化TIM3CH2的PWM通道,如果要使能CH1,CH3,CH4,大家懂得都懂,使能四个通道就可以了。

TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器

实战代码

以上就是基复用功能重映射的基本实例了,下面我以STM32F103C8T6核心板生成16路PWM波做一个实战,并给出代码,方便大家更好理解

timer.c

#include "timer.h"

//定时器3PWM初始化
void TIM3_PWM_Init(u16 arr,u16 psc)
{
	//结构体初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	//时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO, ENABLE);

	//重映射
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射映射

   //设置该引脚为复用输出功能,输出TIM3 CH1-4的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //TIM3_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM3_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM3_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM3_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//定时器配置
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	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的时间基数单位
	
	//PWM配置
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	
	TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
 
}


//TIM1 CH1 PWM输出设置 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
//CH1 PA8;CH2 PA9;CH3 PA10;CH4 PA11
void TIM1_PWM_Init(u16 arr,u16 psc)
{		 					 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能TIMx外设
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA外设时钟使能

  //设置该引脚为复用输出功能,输出TIM1的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM1_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用功能输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TIM1_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用功能输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //TIM1_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用功能输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //TIM1_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用功能输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
 
	TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载周期值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值 不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
	
	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //CH1 PWM2模式	
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //OC1 低电平有效 
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIMx
	TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Disable);
	TIM_OC2Init(TIM1, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIMx
	TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Disable);
	TIM_OC3Init(TIM1, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIMx
	TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Disable);
	TIM_OC4Init(TIM1, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIMx

	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1 预装载使能
	
	TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
	
	TIM_CtrlPWMOutputs(TIM1,ENABLE);	//MOE 主输出使能,高级定时器必须开启这个 
	
	TIM_Cmd(TIM1, ENABLE);  //使能TIMx
	   										  
}

void TIM2_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能

//	GPIO_PinRemapConfig(GPIO_N, ENABLE); //无重映像

   //设置该引脚为复用输出功能,输出TIM3 CH的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM3_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM3_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //TIM3_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //TIM3_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	
	TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIMx在ARR上的预装载寄存器

	TIM_Cmd(TIM2, ENABLE);  //使能TIMx外设
 
}

void TIM4_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能

//	GPIO_PinRemapConfig(GPIO_N, ENABLE); //无重映像

   //设置该引脚为复用输出功能,输出TIM3 CH的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM3_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM3_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM3_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TIM3_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM4, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC1Init(TIM4, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC3Init(TIM4, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	TIM_OC4Init(TIM4, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
	
	TIM_ARRPreloadConfig(TIM4, ENABLE); //使能TIMx在ARR上的预装载寄存器

	TIM_Cmd(TIM4, ENABLE);  //使能TIMx外设
 
}

timer.h

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM3_PWM_Init(u16 arr,u16 psc);
void TIM1_PWM_Init(u16 arr,u16 psc);
void TIM4_PWM_Init(u16 arr,u16 psc);
void TIM2_PWM_Init(u16 arr,u16 psc);
#endif

main.c

#include "sys.h"
#include "delay.h"
#include "timer.h"

int main()
{
	delay_init();
	TIM3_PWM_Init(1999,719);//这里生成的是周期为20ms的PWM波,方便使用SG90舵机做测试
	TIM1_PWM_Init(1999,719);
	TIM2_PWM_Init(1999,719);
	TIM4_PWM_Init(1999,719);
	while(1)
	{
        TIM_SetCompare1(TIM1,50);
		TIM_SetCompare2(TIM1,100);
		TIM_SetCompare3(TIM1,150);
		TIM_SetCompare4(TIM1,200);
		TIM_SetCompare1(TIM2,50);
		TIM_SetCompare2(TIM2,100);
		TIM_SetCompare3(TIM2,150);
		TIM_SetCompare4(TIM2,200);
		TIM_SetCompare1(TIM3,50);
		TIM_SetCompare2(TIM3,100);
		TIM_SetCompare3(TIM3,150);
		TIM_SetCompare4(TIM3,200);
		TIM_SetCompare1(TIM4,50);
		TIM_SetCompare2(TIM4,100);
		TIM_SetCompare3(TIM4,150);
		TIM_SetCompare4(TIM4,200);
        //在TIM1——TIM4的CH1——CH4通道分别设置了0.5ms——2ms的占空比,使用SG90舵机轻触可测试PWM波的生成
	}
}

总结

在复用时,DAC和ADC使用的是GPIO的模拟通道,其他是正常的复用通道。最重要的是对齐数据手册。如果我的文章中有哪些错误的地方欢迎大家在评论区指出或者私信我!

正点原子STM32F103(精英版)------端口复用和重映射 一、端口复用 STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。 例如串口1的发送接收引脚是PA9、PA10,当我们把PA9、PA10不用做GPIO,而用作复用功能串口1的发送接收引脚的时候,叫端口复用。 1、端口复用配置过程(以PA9、PA10配置为串口1为例) GPIO端口时钟使能 RCC_APB2PeriphClockCmd(RCC_A... 阅读详情

相关推荐

STM32F103定时器引脚重定义功能问题处理

在一个项目中,使用STM32F103RCT6单片机,定时器引脚使用的PC6,PC6默认复用定时器为TIM8_CH1,但是由于其他原因,不能使用TIM8_CH1,因此需要使用其重定义功能TIM3_CH1,本文分享了本人在该项目中定时器TIM3引脚复用功能重定义的一些问题及解决过程,希望对大家能有帮助。

m0_53284507的博客 6046

stm32f103在使用定时器1重映射引脚输出PWM

在使用stm32f103ZGT6单片机输出PWM时候,各种配置都不能正确输出,后来查看手册,PE11该引脚对应的确实是定时器1的通道2,但是属于该引脚的重映射功能,遇到这种情况,需要多加一条语句。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 ,ENABLE); //TIM1时钟使能 RCC_APB2PeriphClockCm...

shizhibuyi1234的博客 6250

深入解析STM32F103C8T6复用重映射机制:从理论到16路PWM实战配置

本文深入解析STM32F103C8T6的复用功能重映射机制,从核心概念到实战配置,详细讲解如何通过重映射解决引脚冲突并实现16路PWM输出。涵盖GPIO复用、AFIO时钟配置、定时器PWM原理及TIM1-TIM4的重映射实战,为嵌入式开发提供完整解决方案和常见问题排查指南。

earth的博客 648

关于stm32中引脚的复用,重映射问题

文章目录前言一、问题提出二、问题解决1.资料查阅2.问题解决总结 前言 本次实验平台为stm32f103rbt6,使用标准库 一、问题提出 最近在使用stm32f03的板子做一些实验的时候,PB4引脚接蜂鸣器,配置完了GPIO后发现无论怎么写入值都无法改变引脚的电平。通过查阅资料发现该引脚在stm32f103下是属于JTAG下载调试相关的端口,需要进行一些另外的配置才可以当做普通IO使用。 二、问题解决 1.资料查阅 查阅stm32f103的数据手册 可以看出,PB4的主功能是NJTRST,NJTRS

weixin_47042449的博客 7838

STM32F103C8T6 PWM配置全解析:从原理到电机驱动实战

脉冲宽度调制(PWM)是一种通过调节数字信号占空比来模拟模拟量输出的基础技术,其核心原理是利用定时器生成固定频率的方波,并通过改变高电平时间比例控制等效平均电压。在嵌入式系统中,PWM技术因其高效的数字-模拟转换能力,广泛应用于电机调速、舵机控制、LED调光等场景。STM32系列微控制器通过其内置的高级/通用定时器硬件模块,提供了灵活高效的PWM生成方案。本文以STM32F103C8T6的定时器资源为例,深入剖析其PWM生成机制,涵盖**引脚重映射**配置要点与**死区时间**等高级功能,并结合**舵机控制

weixin_42520780的博客 324

STM32F103C8T6扩展16路PWM:PCA9685舵机控制实战

在嵌入式开发中,I2C总线是一种广泛使用的串行通信协议,仅需两根信号线即可连接多个外设,而PWM(脉冲宽度调制)则是控制舵机、LED亮度等设备的常用手段。当主控芯片如STM32F103C8T6的定时器资源不足以驱动多个舵机时,通过I2C扩展PWM通道成为高效且稳定的解决方案。PCA9685正是这样一颗芯片,它内置16路12位PWM输出,能够通过I2C接口接收指令,在解放主控定时器的同时提供精确的脉宽控制。本文从I2C通信原理与寄存器配置出发,深入讲解PCA9685的初始化时序、PWM频率计算以及舵机角度换算

weixin_26802933的博客 271

STM32F103C8T6复用功能重映射实战:16路PWM波生成与舵机控制

本文详细介绍了STM32F103C8T6的复用功能重映射技术,通过配置定时器实现16路PWM波生成,并应用于舵机控制。内容涵盖硬件准备、定时器原理、完整代码实现及调试技巧,帮助开发者高效利用有限资源完成多路舵机控制项目。

blue的专栏 1003

从模拟到实战:移远EC800M单片机MQTT接入OneNet全流程解析

本文详细解析了移远EC800M单片机通过MQTT协议接入OneNet平台的全流程,涵盖环境准备、模拟设备搭建、硬件迁移、数据交互调试及性能优化等关键步骤。针对常见问题如AT指令缓冲区溢出、心跳间隔设置等提供实战解决方案,帮助开发者高效实现物联网设备稳定接入。

weixin_29215815的博客 432

舵机控制(STM32F103C8T6)

前言 本文是以STM32F103C8T6作为主控芯片,通过PB6端口输出PWM,实现控制180°舵机。 一、舵机控制原理 (一)概述 舵机是一种位置伺服驱动器器,是一种带有输出轴的小装置。当我们向伺服器发送一个控制信号时,输出轴就可以转到特定的位置。只在控制信号持续不变,伺服机构就会保持相对的角度位置不变。如果控制信号发生变化,输出轴的位置也会相应发生变化。舵机的控制大部分都是通过PWM信号控制的。 注:舵机按角度分为90°、180°、270°、36...

Me-Space的博客 5万+

TortoiseGit提示No supported authentication methods available错误

一、问题 在使用TortoiseGit与远程仓库进行同步代码的时候提示如下错误,即:没有支持认证的方法可用。而GitBash却可以正常使用,不受影响。 通过调查,发现应该是TortoiseGit远程仓库的公匙无法和本地的密匙进行匹配认证造成的(主要是TortoiseGit缺少本地密匙)。由于TortoiseGit的默认网络SSH client是TortoiseGitPlink.exe,如下图所示。因此主要有两种方式来解决该问题: 不修改TortoiseGit的默认网络SSH client,此时需要为To

Jeffxu_lib的博客 2万+

Latex参考文献中大写字母编译后自动变成了小写,如何保持原字母大写形式

bib文件参考文献中大写自动变为小写,如何保持原大写问题。Latex: 保持参考文献大小写,overleaf文献编译后,原大写字母自动变成了小写字母,如何保持原字母大写

我的个人博客 5805

STM32F103C8T6芯片介绍

系列:STM32F1内核:ARM Cortex-M3主频:72MHzFlash:128KB(MD系列,用于存储程序代码和数据)RAM:20KB(SRAM,静态随机存储器,即内存大小)ROM(只读存储器):64KB供电:2.0~3.6V(标准3.3V)封装:LQFP48标橙色:电源相关引脚;标蓝色:最小系统相关的引脚;标绿色:IO口、功能口引脚S:电源;I:输入;O:输出;I/O:输入输出I/O口电平:I/O口所能容忍的电压(FT:5V;无FT:3.3V)主功能:上电后默认功能。

guguhen的博客 3万+

士兰微SC7A20H三轴加速度计在智能穿戴设备中的运动检测实践

本文详细介绍了士兰微SC7A20H三轴加速度计在智能穿戴设备中的运动检测实践,包括其工作原理、硬件设计避坑指南、运动检测算法优化、低功耗设计及校准测试技巧。SC7A20H凭借±16g超宽量程和0.004g高精度,成为智能穿戴设备的运动感知核心,适用于计步、6D方向检测等场景,显著提升用户体验。

weixin_29168153的博客 739

STM32 定时器2 CH1 CH2 PWM输出 产生呼吸灯效果

      以STM32F103C8T6为例,从其数据手册中可以看到,PA0、PA1的复用功能(Default alternate functions)分别是定时器2的CH1和CH2。    由图2可以看到,其中PA0的复用功能中TIM2_CH1_ETR表示,PA0既可以复位为TIM2_CH1,也可以复用为TIM2_ETR功能。图1 TIM2 CH1、CH2引脚(图来自数据手册)       由下...

正直善良的小伟的博客 2万+

Telegram Bot Api使用教程

其中token为你的机器人token,method为telegram给定的方法,在获取群组chat_id那一步就使用了telegram的其中一个方法(getUpdates),其他方法后面会介绍。telegram发送消息的方式类似与钉钉机器人,都是向一个api发送http请求,而且对于同一个APItelegram支持GET和POST两种请求方式.请求的api格式如下。通常来说我们都需要让机器人在一个群组里工作,所以首先我们需要将机器人添加到我们指定的群组,在群组里发送随意消息并@这个机器人,比如...

豫迷糊的码上生活 1万+

利用Matlab高效处理Comsol仿真数据的可视化技巧

本文详细介绍了如何将Comsol仿真数据高效导入Matlab并进行专业可视化与后处理。核心内容包括正确的数据导出格式设置、三维矩阵重构方法、二维截面与三维体渲染技巧,以及基于Matlab的定量分析与批处理自动化流程,帮助工程师和科研人员深度挖掘仿真数据价值,提升工作效率。

weixin_29065791的博客 362

使用STM32C103C8T6的注意点

基于STM32F103C8T6制作平衡小车过程中的问题 文章目录基于STM32F103C8T6制作平衡小车过程中的问题前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 本文主要讲了在用STM32F103C8T6制作平衡小车时遇见的一下问题,对这些问题的解决方案或者是理解,而不是教大家如何调平衡车,那样的文章在优快云上已经很多很完善了。因为是两个月前做的平衡车,很多问题已经忘记了,但两个月后还能记住的问题应该是更加普遍更加重要的问题,所以记录在此。 提示:以下是本篇文章正文内容

qq_45977463的博客 4822

YOLOv11低照度增强主干网络PE-YOLO:原理与完整实现教程

编码器部分# 解码器部分self.dec_conv2 = nn.Conv2d(512, 128, 3, padding=1) # 跳跃连接self.dec_conv3 = nn.Conv2d(256, 64, 3, padding=1) # 跳跃连接self.dec_conv4 = nn.Conv2d(128, 32, 3, padding=1) # 跳跃连接# 编码路径# 解码路径(带跳跃连接)

FJN110的博客 725

第五章-PWM控制电机 开源stm32循迹小车详细制作过程(附加完全版代码)-openmv视觉循迹、红外循迹、避障跟随、超声波跟随、蓝牙遥控 m32f103c8t6、stm32cubemx

第五章-STM32智能小车 循迹 避障 跟随 遥控

qq_46187594的博客 6806

Vue3 + uniapp 踩坑:iOS 真机调试一直报错Maximum call stack size exceeded,和(in promise)MiniProgramError err的解决方法

不是你写错代码,是微信开发工具和 iOS 真机调试的兼容问题。开个空项目都报错,勾个设置就没问题,不是你,是工具的锅。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1267

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

STM32F103C8T6芯片GPIO功能详细描述+通用模板+关键代码参数修改解析

STM32F103C8T6的每个GPIO(通用输入输出)引脚均可独立配置为多种工作模式,支持数字信号输入/输出、模拟信号采集、复用功能(连接内部外设)等。芯片共有37个GPIO引脚(PA0-PA15, PB0-PB15, PC13-PC15, PD0-PD1),部分引脚与电源或调试接口复用。

小蜗牛的博客 3669

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【含源代码】STM32F1控制L298N直流电机驱动,PWM调速、电机调速、STM32F103C8T6智能小车教程、控制电机正反转

L298N、STM32F103C8T6、PWM调速、智能小车

m0_60997683的博客 1万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

基于stm32F103c8t6的标准外设库的pwm驱动控制电机

通用定时器TIM2~TIM5挂在APB1总线上,时钟源频率最高为72MHZ,预分频器是由TIMx_PSC这样一个16位的寄存器来对定时器的时钟源进行分频实现,可设置预分频器的分频系数来对72Mhz进行频率的降低,所以可以选择1~65535之间的任意数值对时钟源CK_PSC的时钟频率进行分频,输出的CK_CNT脉冲提供计数器计数。计数器溢出中断是定时器最基本的功能,属于软件中断,即定时器计数到预先设定的数值后再重新计数,并同时触发定时器更新溢出中断,从而执行相应的定时器中断服务程序。

weixin_55741047的博客 1340

stm32————重映射基础知识点(PWM呼吸灯实验)

例如,要将USART1的TX和RX重映射到PB6和PB7,你需要使能GPIOB和AFIO的时钟,然后配置PB6和PB7为复用推挽输出模式,并在AFIO的MAPR寄存器中设置相应的位来启用重映射。函数允许用户为定时器TIMx的第三个捕获/比较寄存器(CCR3)设置一个新的值。CCR3是定时器的一个通道寄存器,用于捕获模式下捕获输入信号的值,或在输出比较模式下设置输出比较事件。宏用于确保传入的TIMx是有效的定时器地址,即TIMx是TIM1, TIM2, TIM3, TIM4, TIM5或TIM8中的一个。

2401_82978699的博客 1840

STM32F103C8T6 控制板载任意一个支持PWM输出的引脚输出不同脉宽的PWM脉冲

STM32F103C8T6 脉冲输出 官方例程的升级版,用板载LED引脚外的其他引脚输出递增的PWM脉冲。 本来是用407ZGT6做的,但ADC一时疏忽接到了6.6V然后烧了(心疼死了)踩了一些坑,给大家分享一下。 文章目录STM32F103C8T6 脉冲输出前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 三个步骤, 在手册中查找那些定时器支持PWM输出。 找到原理图引脚支持的复用功能有没有类似TIMx_CHx的字眼。 参照官方例程初始化定时器和引脚。 在主函数中进行初始化,

lzsm_的博客 3354
上一篇: 基于Openmv(MicroPython)控制串行总线舵机
下一篇: AC-DC模拟电路直流电源的设计方案(一-直流电源的组成和前置知识)
神奇淼淼
博客等级 码龄4年 39粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值