目次
1: TIM出力比較機能
1: 再開
OC (Output Compare) 出力比較
出力比較は、CNT と CCR レジスタの値の関係を比較することにより、出力レベルを 1 に設定、0 に設定、または反転することができ、特定の周波数とデューティ サイクルの PWM 波形を出力するために使用されます。
各アドバンスト タイマーと一般タイマーには 4 つの出力比較チャネルがあります。
アドバンストタイマーの最初の 3 チャネルには、デッドゾーン生成と相補出力の機能が追加されています。
2:PWM波形
PWM(パルス幅変調)パルス幅変調
慣性のあるシステムでは、一連のパルスの幅を変調することで必要なアナログパラメータを等価的に取得でき、モーター速度制御などの分野でよく使用されます。
PWM パラメータ: 周波数 = 1 / TS デューティ サイクル = TON / TS 分解能 = デューティ サイクル変更ステップ
3: 出力比較モード
比較ユニットを構成する場合はTIM_OCXInit()を使用します
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM1 のモードを選択
4:パラメータ計算
5:PWM基本構造
6: 出力比較機能の実用化
A:PWM駆動のLEDブリージングライト
1: 接続図
2: ステップ
1: RCC がクロックをオンにします (TIM ペリフェラル --- RCC_APB1PeriphClockCmd および GPIO ペリフェラル -- RCC_APB2PeriphClockCmd クロックがオンになります)
2: GPIO の構成 ----GPIO_Init (2 番目のステップを最後に書き込みます)
3: クロック選択----TIM_InternalClockConfig (内部)
4: タイムベースユニットの設定-----TIM_TimeBaseInit()
5: 出力比較ユニットの構成---TIM_OC1Init()
6: タイマー開始-----TIM_Cmd
3: 機能紹介
stm32f10x tim.h ファイル内の関数 ----- 出力比較ユニット関数を構成します
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);
TIM_OCXInit : 出力比較ユニットの設定
stm32f10x tim.h ファイル内の関数
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
TIM_OCStructInit:出力比較構造体にデフォルト値を割り当てます。
stm32f10x tim.h ファイル内の関数-----デューティ サイクル関数の変更
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
TIM_SetCompareX: CCR レジスタの値を個別に変更する関数。デューティ サイクルを変更できます。
4: コード
ブリージングライトの機能を実現するために呼び出されるたびにデューティサイクルを変更します。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
void PWM_init(){
//第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步,配置GPIO
GPIO_InitTypeDef GPIO_INITStruct;
GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_INITStruct.GPIO_Pin=GPIO_Pin_0 ;
GPIO_INITStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_INITStruct);
//第三步,时钟源选择(内部时钟)
TIM_InternalClockConfig(TIM2);
//第四步,配置时基单元
TIM_TimeBaseInitTypeDef TIM_INITStruct;
TIM_INITStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_INITStruct.TIM_CounterMode=TIM_CounterMode_Up; 向上计数
TIM_INITStruct.TIM_Period=100-1; ///自动重装载寄存器ARR
TIM_INITStruct.TIM_Prescaler=720-1; // //预分频器PSC
TIM_INITStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_INITStruct);
//第五步,配置输出比较单元
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给输出比较结构体赋一个默认的值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择PWM1的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //有效电频为低电频
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//第六步,启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
uint8_t i;
int main(void)
{
OLED_Init();
PWM_init();
while (1)
{
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}
TIM_OC1Init を選ぶ理由
ここでわかるように、PAO 行に TM2-CH1_ETR があります。これは、TIM2 の ETR ピンとチャネル 1 のピンが PAO ピンの位置を借用していることを意味します。 ; PAO ピンに多重化されるため、CH1 チャネルである TIM2 の OC1 を使用して PWM を出力したい場合、PA0 ピンには 1 つしか出力できず、ピン出力を任意に選択することはできません。; PA0 は PWM 波形を出力します
TIM_SetCompare1 を選択する理由
TIM_OC1Init CH1 チャネルが使用されるため
TIM_SetCompareX: CCR レジスタの値を個別に変更する関数。デューティ サイクルを変更できます。
ピンの再定義
まず、AFIO を使用するには、AFIO クロックをオンにする必要があります。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
ピンの再マッピング
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//部分再マッピング 1
PA0をPA15に変更します
PA15 は、電源投入後、デバッグ用に TDIT を返すためにデフォルトで多重化されているため、通常の GPIO または多重化されたタイマー チャネルとして使用したい場合は、最初にデバッグ ポートの多重化をオフにする必要があります (慎重に)
//PA15、PB3、PB4这三个引脚当做GPIO来使用的话 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//解除JTAG复用(解除重映射端口) //想重映射定时器或者其他外设的复用引脚 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//GPIO_PartialRemap1_TIM2部分重映射1 //如果重新映射的使调式端口使用 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//GPIO_PartialRemap1_TIM2部分重映射1 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);解除JTAG复用(解除重映射端口)
GPIO デバッグ選択多重化プッシュプル出力
GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP; //多重プッシュプル出力、
出力制御はオンチップ周辺機器に転送され、ピンを介して PWM 波形を出力できます
計算する
PWM 周波数: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM デューティ サイクル: デューティ = CCR / (ARR + 1)
PWM分解能: Reso = 1 / (ARR + 1)
CK_PSC=72MHZ PSC=TIM_INITStruct.TIM_Prescaler=; // //プリスケーラ PSC
ARR= TIM_INITStruct.TIM_Period=; ///自動リロードレジスタ ARR
CRR= TIM_OCInitStructure.TIM_Pulse = ; //CCR
分解能 = デューティサイクル変更ステップ
周波数 1KHz、デューティ サイクル 50%、分解能 1% の PWM 波形
B:PWM駆動ステアリングギア
1: 接続図
赤-----USE5Vに接続(3vは使用できません)
橙色-----信号線 茶色-----アース線
2: ステアリングギアの紹介
ステアリングギアは、入力された PWM 信号のデューティサイクルに応じて出力角度を制御するデバイスです。
入力PWM信号要件:周期20ms、ハイレベル幅0.5ms~2.5ms
ハイレベル幅 = 特定の電気周波数の持続時間
CCR=高周波
ステアリングギアの制御には一般に20msのタイムベースパルス(周期)が必要で、そのパルスのハイレベル部分は一般的に0.5ms~2.5msの範囲の角度制御パルス部分となります。180 度角度サーボを例にとると、対応する制御関係は次のようになります:
0.5ms-----0 度;
1.0ms---------- -45 度;
1.5 ミリ秒----------90 度;
2.0 ミリ秒-----------135 度;
2.5 ミリ秒----------- 180度;
3: ステップ
1: RCC がクロックをオンにします (TIM ペリフェラル --- RCC_APB1PeriphClockCmd および GPIO ペリフェラル -- RCC_APB2PeriphClockCmd クロックがオンになります)
2: GPIO の構成 ----GPIO_Init (2 番目のステップを最後に書き込みます)
3: クロック選択----TIM_InternalClockConfig (内部)
4: タイムベースユニットの設定-----TIM_TimeBaseInit()
5: 出力比較ユニットの構成 ----TIM_OC2Init()
6: タイマー開始-----TIM_Cmd
4: コード
これは CH2 チャンネルを使用し、上の LED は CH2 チャンネルを使用します。
使用法: TIM_SetCompareX および TIM_OCXInit 関数は、対応する X 値に変更されます。
X は 2 である必要があります。以下のピン定義表を参照してください。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
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 = 20000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
void Servo_Init(void)
{
PWM_Init();
}
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
Delay_ms(20);
KeyNum = 1;
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
uint8_t KeyNum;
float Angle;
int main(void)
{
OLED_Init();
Servo_Init();
Key_Init();
OLED_ShowString(1, 1, "Angle:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
Angle += 30;
if (Angle > 180)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1, 7, Angle, 3);
}
}
計算する
TIM_SetCompareX: CCR レジスタの値を個別に変更する関数。デューティ サイクルを変更できます。
ピン定義テーブル
サーボの信号線は PA1 に接続されているため、CH2 チャンネル 2 が使用され、X は 2 となり、PA1 は PWM 波形を出力します。
C:PWM駆動DCモーター
1: 接続図
2: モーターと駆動回路
DC モーターは電気エネルギーを機械エネルギーに変換する装置で、2 つの電極があり、電極がプラスに接続されるとモーターは正回転します。
電極が逆になると、モーターが逆回転します。DC モーターは高出力デバイスなので、GPIO ポートで直接駆動することはできません。モーター駆動回路で動作させる必要があります。TB6612 は、デュアル H ブリッジ タイプの DC モーター ドライバーです
。 2 つの DC モーターを駆動し、その速度と方向を制御できるチップ
モータ駆動の詳細は「51:モータ(ULN2003D)」をご参照ください。
3: コード
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "PWM.h"
#include "motor.h"
#include "Key.h"
void PWM_init(){
//第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步,配置GPIO
GPIO_InitTypeDef GPIO_INITStruct;
GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_INITStruct.GPIO_Pin=GPIO_Pin_2 ;
GPIO_INITStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_INITStruct);
//第三步,时钟源选择(内部时钟)
TIM_InternalClockConfig(TIM2);
//第四步,配置时基单元
TIM_TimeBaseInitTypeDef TIM_INITStruct;
TIM_INITStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_INITStruct.TIM_CounterMode=TIM_CounterMode_Up; 向上计数
TIM_INITStruct.TIM_Period=100-1;
TIM_INITStruct.TIM_Prescaler=720-1;
TIM_INITStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_INITStruct);
//第五步,配置输出比较单元
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给输出比较结构体赋一个默认的值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择PWM1的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //有效电频为低电频
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
//第六步,启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare);
}
void motor_init(){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_INITstruct;
GPIO_INITstruct.GPIO_Mode=GPIO_Mode_IPU;//GPIO_Mode_Out_PP //GPIO_Mode_IPU
GPIO_INITstruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
GPIO_INITstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_INITstruct);
PWM_init();
}
void motor_serspeed(int8_t speed){
if(speed >=0){
GPIO_SetBits(GPIOA,GPIO_Pin_5); //高电频
GPIO_ResetBits(GPIOA,GPIO_Pin_4); //低电频
PWM_SetCompare3(speed);
}
else
{
GPIO_SetBits(GPIOA,GPIO_Pin_4); //高电频
GPIO_ResetBits(GPIOA,GPIO_Pin_5); //低电频
//传穿过来的speed为负数,要变为正,所以要加一个负号;
PWM_SetCompare3(-speed);
}
}
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
Delay_ms(20);
KeyNum = 1;
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
uint8_t key_num;
int8_t speed; //速度必须是有符号的
int main(void)
{
Key_Init();
OLED_Init();
motor_init();
OLED_ShowString(1,1,"speed:");
while (1)
{
key_num=Key_GetNum();
if (key_num==1){
speed+=20;
if(speed>100){
speed=-100;
}
}
motor_serspeed(speed);
OLED_ShowSignedNum(1, 7, speed, 3);
}
}