配置PWM
第一步,开启RCC时钟,把TIM外设和GPIO的外设时钟打开
第二步,配置时基单元,包括时钟源选择
第三步,配置输出比较单元,包括CCR的值、输出比较模式、极性选择、输出使能这些参数,库函数里也是统一用结构体来配置的
第四步,配置GPIO,把PWM对应的GPIO口,初始化复用推挽输出的配置
第五步,运行控制,启动计数器,这样才能输出PWM
初始化GPIO口输出PWM波形
初始化时基单元
初始化输出比较单元
设置输出比较的模式
设置输出使能
设置CCR
TIM_InternalClockConfig(TIM2);// 初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;///PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//初始化输出比较单元,参考stm32f10x_tim.h
/*
有4个初始化函数,对应4个输出比较单元(输出比较通道)
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的
本例中,使用PA0口,对应的是第一个输出比较通道。
*/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
/*
#define TIM_OCMode_Timing 冻结模式
#define TIM_OCMode_Active 相等时置有效电平
#define TIM_OCMode_Inactive 相等时置无效电平
#define TIM_OCMode_Toggle 相等时电平反转
#define TIM_OCMode_PWM1 PWM模式一
#define TIM_OCMode_PWM2 PWM模式二
TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式
*/
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
/*
#define TIM_OCPolarity_High 高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
#define TIM_OCPolarity_Low 低极性,有效电平为低电平,就是REF电平取反
*/
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
/*
#define TIM_OutputState_Disable 失能
#define TIM_OutputState_Enable 使能
*/
TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
//TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
//实现呼吸灯的效果,就是不断更改CCR的值即可
/*
72M/(PSC+1)/(ARR+1) = 1000
CRR/(ARR+1) = 50%
1/(ARR+1) = 1%
解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
现在就是频率为1KHz,占空比为50%的PWM波形了
*/
TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
TIM_Cmd(TIM2, ENABLE);
端口复用
参考引脚定义表中默认复用功能,就是片上外设的端口和GPIO的连接关系,在这里可以看到,有TIM2_CH1_ETR(外部触发输入)它是在这个PA0这一行的。这就说明,TIM2的ETR引脚和通道1的引脚,都是借用了PA0这个引脚的位置的,换句话说就是TM2的引脚复用在了PA0引脚上,所以说如果我们要使用TIM2的OC1也就是CH1通道,输出PWM,那它就只能在PA0的脚上输出一而不能任意选择引脚输出。同样,如果使用TIM2的CH2,那就只能在PA1端口输出,TIM2的CH3就只能是PA2,CH4就只能是PA3。不过,但是虽然它是定死的,STM32还是给了我们一次更改的机会的,这就是重定义,或者叫重映射。比如如果你既要用USART2的TX引脚,又要用TIM2的CH3通道,它俩冲突了,没办法同时用那我们就可以在这个重映射的列表里找一下。比如这里我们找到了TIM2的CH3,那TIM2的CH3就可以从原来的引脚,换到这里的引脚PB10,这样就避免了两个外设引脚的冲突。如果这个重映射的列表里找不到,那外设复用的GPIO就不能挪位置,配置重映射是用AFIO来完成的。如果使用重映射,它可以人PA0挪到这个PA15的引脚上,再其他的引脚,就没有机会作为这个通道的输出引脚了。
表43 TIM2复用功能重映像的表。如果把PA0改到PA15,就可以选择这个部分重映射方式1,或者完全重映射。 GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //引脚重映射配置 手册8.3节。8.3.5节 充映射的方式和引脚更改的关系。
@arg GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping
@arg GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping
@arg GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping
引脚定义表中, 这个PA15,是没给加粗的,因为它上电后已经默认复用为了调试端口JTDI,所以如果想让他作为普通的GPIO或者复用定时器的通道,那还需要先关闭调试端口的复用,也是用这个GPIO PinRemapConfig
函数 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
@arg GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
//解除JTRST引脚的复用 就是这个NJTRST也就是PB4 如果使用这个参数,那么这个PB4就变为正常的GPIO口了
@arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled
//解除JTAG调试端口的复用,在引脚定义里就是,PA15、PB3、PB4这三个端口变回GPIO,PA13和PA14,仍为SWD的调试端口
@arg GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)
//这个参数就是把SWD和JTAG的调试端口全部解除 PB3 PB4 PA13 PA14 PA15 这5个引脚全部变成普通的GPIO,没有调试功能了。
不要随便调用,一但你调用这个参数并且下载程序之后,那么你的调试端口就都没有了,再使用STLINK就下载不进去程序了。 这时就只能使用串口下载,下载一个新的没有解除调试端的程序(不小心用了这个函数可以试试,断电,拉低BOOT0,下载程序,上电,拉高BooT0,下载程序就可以恢复正常了)
表35 调试端口映像 在这里如果我们需要用PA15、PB3、PB4这三个引脚,那通常就是解除JTAG的复用,保留SWD的复用。所以这里,参数我们就选GPIO_Remap_SWJ_JTAGDisable,这三个参数,就是用来解除调试端口的复用的
总结一下,如果你想让PA15、PB3、PB4这二个引脚当做GPIO来使用的话 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
先打开AFIO时钟,在用AFIO将JTAG解除掉,即可。 如果想重映射定时器或者其他外设的复用引脚, RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
先打开AFIO时钟,再用AFIO重映射外设复用的引脚,即可。 如果你重映射的引脚又正好是调试端口, RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
打开AFIO时钟,重映射引脚,解除调试端口。
初始化时基单元
TIM_InternalClockConfig(TIM2);// 初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;///PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
初始化输出比较单元
初始化输出比较单元,参考stm32f10x_tim.h 有4个初始化函数,对应4个输出比较单元(输出比较通道)
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的。本例中,使用PA0口,对应的是第一个输出比较通道。
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
/*
#define TIM_OCMode_Timing 冻结模式
#define TIM_OCMode_Active 相等时置有效电平
#define TIM_OCMode_Inactive 相等时置无效电平
#define TIM_OCMode_Toggle 相等时电平反转
#define TIM_OCMode_PWM1 PWM模式一
#define TIM_OCMode_PWM2 PWM模式二
TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式*/
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
/*
#define TIM_OCPolarity_High 高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
#define TIM_OCPolarity_Low 低极性,有效电平为低电平,就是REF电平取反 */
设置输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
/*
#define TIM_OutputState_Disable 失能
#define TIM_OutputState_Enable 使能
*/
TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
//TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
//实现呼吸灯的效果,就是不断更改CCR的值即可
/*
72M/(PSC+1)/(ARR+1) = 1000
CRR/(ARR+1) = 50%
1/(ARR+1) = 1%
解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
现在就是频率为1KHz,占空比为50%的PWM波形了
*/
TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
TIM_Cmd(TIM2, ENABLE);
配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //AFIO重映射后的代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //打开时钟、选择内部时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟, 通过端口复用把TIM2的CH1从PA0挪到PA15引脚上 APB2的设备
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //引脚重映射配置 手册8.3节。8.3.5节 充映射的方式和引脚更改的关系。
//表43 TIM2复用功能重映像的表。如果把PA0改到PA15,就可以选择这个部分重映射方式1,或者完全重映射。
/**
@arg GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping
@arg GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping
@arg GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping
*/
/*
引脚定义表中, 这个PA15,是没给加粗的,因为它上电后已经默认复用为了调试端口JTDI,
所以如果想让他作为普通的GPIO或者复用定时器的通道,那还需要先关闭调试端口的复用,也是用这个GPIO PinRemapConfig函数
* @arg GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
解除JTRST引脚的复用 就是这个NJTRST也就是PB4 如果使用这个参数,那么这个PB4就变为正常的GPIO口了
* @arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled
解除JTAG调试端口的复用,在引脚定义里就是,PA15、PB3、PB4这三个端口变回GPIO,PA13和PA14,仍为SWD的调试端口
* @arg GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)
这个参数就是把SWD和JTAG的调试端口全部解除 PB3 PB4 PA13 PA14 PA15 这5个引脚全部变成普通的GPIO,没有调试功能了。
不要随便调用,一但你调用这个参数并且下载程序之后,那么你的调试端口就都没有了,再使用STLINK就下载不进去程序了。
这时就只能使用串口下载,下载一个新的没有解除调试端的程序(不小心用了这个函数可以试试,断电,拉低BOOT0,下载程序,上电,拉高BooT0,下载程序就可以恢复正常了)
表35 调试端口映像
在这里如果我们需要用PA15、PB3、PB4这三个引脚,那通常就是解除JTAG的复用,保留SWD的复用。所以这里,参数我们就选SWJ JTAGDisable,
这三个参数,就是用来解除调试端口的复用的
*/
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //正常使用PA15引脚。
/*
总结一下,如果你想让PA15、PB3、PB4这二个引脚当做GPIO来使用的话
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
先打开AFIO时钟,在用AFIO将JTAG解除掉,即可。
如果想重映射定时器或者其他外设的复用引脚,
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
先打开AFIO时钟,再用AFIO重映射外设复用的引脚,即可。
如果你重映射的引脚又正好是调试端口,
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
打开AFIO时钟,重映射引脚,解除调试端口。
*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
/*
*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //AFIO重映射后的代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);// 初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;///PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//初始化输出比较单元,参考stm32f10x_tim.h
/*
有4个初始化函数,对应4个输出比较单元(输出比较通道)
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的
本例中,使用PA0口,对应的是第一个输出比较通道。
*/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
/*
#define TIM_OCMode_Timing 冻结模式
#define TIM_OCMode_Active 相等时置有效电平
#define TIM_OCMode_Inactive 相等时置无效电平
#define TIM_OCMode_Toggle 相等时电平反转
#define TIM_OCMode_PWM1 PWM模式一
#define TIM_OCMode_PWM2 PWM模式二
TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式
*/
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
/*
#define TIM_OCPolarity_High 高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
#define TIM_OCPolarity_Low 低极性,有效电平为低电平,就是REF电平取反
*/
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
/*
#define TIM_OutputState_Disable 失能
#define TIM_OutputState_Enable 使能
*/
TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
//TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
//实现呼吸灯的效果,就是不断更改CCR的值即可
/*
72M/(PSC+1)/(ARR+1) = 1000
CRR/(ARR+1) = 50%
1/(ARR+1) = 1%
解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
现在就是频率为1KHz,占空比为50%的PWM波形了
*/
TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare); //用来单独修改CCR的值的函数
}
点击数:3