目次
ステップ1:GPIO / AFIO / TIMの周辺バスクロックを有効にする
PWM出力実験
PWMの動作原理(例としてカウントを取り上げます)
CCRXレジスタの値は、カウンタの値と比較するために使用されます。CNTカウンタの値がCCRXレジスタと等しい場合、一般的なタイマー出力信号レベルが反転します。最後に、カウンターがMAX(ARRの値)までカウントされると(「更新割り込み」もトリガーできます)、すぐに0に戻り、カウントアップを再開します。
PWMおよびAFIOピンの再マッピングの包括的な実験
ライブラリ機能の説明
ライブラリ関数名 |
特徴 |
RCC_APB1PeriphClockCmd()/ RCC_APB2PeriphClockCmd() |
APBバス周辺クロックの有効化 |
GPIO_Init() |
GPIO初期化機能 |
GPIO_PinRemapConfig() |
GPIOポート再マッピング機能 |
TIM_ARRPreloadConfig() |
ARRでのTIMxのプリロードレジスタを有効または無効にします
|
TIM_TimeBaseInit() |
タイマーを初期化する |
TIM_OCInit() |
比較パラメータを初期化します |
TIM_Cmd() |
タイマーの有効化 |
TIM_SetCompare2() |
比較値CCRxを絶えず変更して、さまざまなデューティサイクル効果を実現します |
PWM構成の基本的な知識ポイント
GPIOポート機能の再マッピング
まず、TIM関数を次のように再マップするピンを明確にする必要があります。
AFIOのGPIO多重化機能の再マッピングから、「TIM3部分部分再マッピング」機能を使用すると、TIM3のTIM3_CH3ピンをPB5ピンにマッピングし、それを使用してLED0を駆動してPWM機能を表示できることがわかりました。 。
このとき、次の点に注意してください。
①GPIO出力モードは「多重プッシュプル出力」として設定する必要があります。下図のように、多重プッシュ設定の場合のみ、他の非IO機能の信号をこのGPIOピンから出力できます。プッシュプルとして設定した場合のみ。 、外部レベルに依存せずにハイレベルとローレベルを出力でき、プッシュプル出力はTIM3機能の出力要件を満たしています。
②GPIOピンのリマッピング機能を設定します
重要性と機能:TIM3の機能はPB5に属していませんが、TIM3機能をPB5ピンに再マップすると、「TIM3は機能をPB5ピンに再マップできる」ことがわかります。有効にする必要があります。 AFIOクロックと構成再マッピングレジスタ。
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
クロック分割の意味
TIMを構成するときは、次の関数を呼び出します。TIM_TimeBaseInit();
その中で、その構造パラメーターは「TIM_ClockDivision」属性を構成します。
まず、TIMER3がPSCで除算されたクロックとして取得される方法を理解しましょう。
私たちの実験では、APB1バスからのクロックを使用します(AHBクロック周波数は72MHzです)。AHB-> APB1分周器-> APB1では、APB1プリスケーラーの分周係数が1の場合、TIMXCLKのクロック周波数は次のようになります。 TIMCLK = APB1 = AHB "、APB1プリスケーラの分割係数がN(Nは1ではない)の場合、TIMXCLKのクロック周波数は" TIMCLK = 2xAPB1 = 2xAHB / N "です。
ただし、STが提供する初期化クロックソースライブラリ関数を呼び出すと、APB1分周器のデフォルトの分周係数は2であるため、最終的なTIMXCLK = 2xAPB1 = 2xAHB / 2 = AHBになります。
ここでのクロック分割は、APB1のプリスケーラ係数を表します。
PWMの2つの比較モードは何ですか?
プリロードレジスタ(TIMx_CCMR1)の役割
動作中にARRの値を更新する場合、プリロードレジスタのOC1PEが1に設定されていると、次のサイクルでARR値がカウンタに読み込まれます。0に設定されていると、ARR値はすぐにカウンタに読み込まれます。
PWM出力の極性は何ですか?
PWM構成に関する考慮事項
PWM信号をPB5ピンに出力するので、通常のようにGPIOピン属性を構成し、PB5に特定のレベルを割り当てる必要はありません。PB5は別のIOピンではないため、その状態は受信したPWM出力レベルによって決まります。
デューティサイクルの設定方法は?
timer.cでは次の部分のみを定義しました。
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 配置有效电平——高电平有效
TIM_OC2Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性
このコードは、「TIMER3の2つのポートのPWM出力プロパティ(PWMモード、有効レベル、有効または無効)を設定する方法」のみを説明し、「比較値」を設定しなかったため、デフォルトのデューティサイクルは100%です。
タイマーのPWMを設定した後、CCRx比較値を設定する必要があります。コードは次のとおりです。
TIM_SetCompare2(TIM3, CompareValue); // 用于修改CCRx的值进而修改PWM占空比
ここでは、CCRx比較値を継続的に変更して、輝度が徐々に変化するLEDライト効果を生成するために使用されるmain関数で構成します。同様に、ARRの値を変更してPWM周期を調整することもできます。対応するライブラリ関数は次のとおりです。
TIM_SetAutoreload(TIM3, NEW_ARR); // 用于修改ARR的值进而修改PWM周期
ライブラリ関数の構成手順
ステップ1:GPIO / AFIO / TIMの周辺バスクロックを有効にする
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
ステップ2:ピン機能の再マッピングを構成する
GPIO_InitTypeDef GPIO_InitStructure;
PIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
ステップ3:タイマーの基本的なプロパティを構成する
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
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的时间基数单位
手順4:PWM出力モードを構成する
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 配置有效电平——高电平有效
TIM_OC3Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性
手順5:プリロードレジスタを構成する
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能比较值预加载寄存器
TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能预加载值寄存器
TIM_OC3PreloadConfig() |
「CCRx値のプリロードに使用される」レジスタを構成するために使用されます |
TIM_ARRPreloadConfig() |
「ARR値のプリロードに使用される」レジスタを構成するために使用されます |
ステップ6:TIM機能を正式に有効にする
登録ワークフローのブロック図を見ると、タイマーのすべてのプロパティを構成した後、TIM機能を正式に有効にできることがわかります。
TIM_Cmd(TIM3, ENABLE); //使能TIM3
包括的なコード例
Main.c
#include "led.h"
#include "sys.h"
#include "timer.h"
int main(void)
{
u16 CompareValue=0;
u8 Dir=1;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
LED_InitConfig(); //LED端口初始化
TIMER_InitConfig(899,0); //不分频。PWM频率=72000000/900=80Khz
while(1)
{
if(Dir == 1)
{
CompareValue++;
}
else
{
CompareValue--;
}
if(CompareValue >= 300)
{
Dir = 0;
}
else
{
Dir = 1;
}
TIM_SetCompare2(TIM3, CompareValue);
}
}
Led.c
#include "led.h"
#include "stm32f10x.h"
void LED_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能LED1的外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure); // 配置LED1的输出属性
GPIO_SetBits(GPIOE, GPIO_Pin_5); // 配置初始引脚输出电平为低电平
}
Led.h.
#ifndef _LED_H
#define _LED_H
#include "sys.h"
void LED_InitConfig();
#define LED1 PEout(5)
#endif
Timer.c
#include "timer.h"
#include "led.h"
#include "stm32f10x.h"
#include "delay.h"
void TIMER_InitConfig(u16 ARR, u16 PR)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOB的外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3的外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 一定要选择“复用推挽输出”
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置GPIOB的引脚属性
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); // 引脚重映射配置
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB到TIMxCLK之间没有分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInitStructure.TIM_Period = ARR; // 0x1388 = 5000
TIM_TimeBaseInitStructure.TIM_Prescaler = PR; // 0x1C20 = 7200
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); // 周期长度为1s
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 配置有效电平——高电平有效
TIM_OC2Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能比较值预加载寄存器
TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能预加载值寄存器
TIM_Cmd(TIM3, ENABLE); // TIM3使能
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级
TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE); // 定时器具体中断模式配置
}
void TIM3_IRQHandler()
{
delay_init();
if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET)
{
delay_ms(500);
LED1 = !LED1;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
}
Timer.h
#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"
void TIMER_InitConfig(u16 ARR, u16 PR);
#endif
運用結果
LEDxステータス |
ステータス変更位置 |
LED1が交互に点灯します |
PWM比較割り込み |
LED0の明るさはデューティサイクルによって異なります |
PWM出力レベル |
遅延としてdelay_ms(500)を使用したので、比較割り込み状態によりLED1が交互に変化することがはっきりとわかりますが、PWM比較値は+1ずつゆっくり変化するため、LED0の輝度変化を注意深く観察する時間が必要です。