STM32 研究ノート_9 タイマー割り込み: エンコーダー インターフェイス モード

TIMエンコーダインターフェース

ロータリーエンコーダを扱う前は、回転させると一度中断されてリソースを消費していました。

TIM のエンコーダ機能を利用して、ローテーターの値を一定間隔で取得して cnt++ または – を作成し、回転位置と計算速度を判断することができ、割り込みに比べてリソースを節約できます。これは、外部クロックを方向付きで接続することに相当します。

エンコーダは直交エンコーダ信号を受信して​​、cnt の自己インクリメントおよび自己デクリメントを制御できます。

画像-20230515201313919

一方の相の立ち上がり/立ち下がりを判断して、もう一方の相がハイかローかを判断し、回転方向を決定します。

画像-20230515201912210

画像-20230515202159695

エンコーダインターフェースの判定は正負で、cnt++ または – を制御します。arr は 65535 に設定されているため、cnt=0 の場合、デクリメントすると 65535 になり、符号付き数値に変換すると -1 になります。

画像-20230515202625535

後者は一般的に使用されており、精度が高いです。

ここでの内容はABフェーズに相当する判定です。例えば、図ではFP1がハイレベルからFP2に立ち上がり、1がA相、2がB相とすると、Aが立ち上がっているときはBがハイレベルとなり、反転に相当するのでカウントダウンします。

画像-20230515202857336

一方の信号が遷移すると、もう一方の信号の状態は最後の遷移と同じになり、グリッチを示します。カウント値が前後に振れてノイズを除去します。

コード: タイマーベースのタッチ可能なエンコーダー

ほとんどは依然として IC ベースのコードです。新しい関数: TIM_EncoderInterfaceConfig は、エンコーダ インターフェイスを構成します。

#include "stm32f10x.h"

void Encoder_Init(){
    
    
    //rcc, gpio, time base, ic(只配置滤波器和极性), TIM_Cmd 启动
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//滤波器采样,频率越低,采样点数越多,滤波效果越好。不过延迟也越大。采样频率就是内部时钟和这个分频参数共同作用的结果
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
    //比如我们想定1s中断一次,CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1),也就是72M / (PSC + 1) / (ARR + 1) = 1
    //所以两者赋值可以是10000-1和7200-1,只要两者都在65535以内就行,赋值不唯一
    TIM_TimeBaseInitStructure.TIM_Period=65536-1;
    TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;//预分频器值,不分频
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器值,高级计数器才用
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
    
    //PA6 7对应定时器3,oc channel1 channel2.
    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//PA6对应定时器3 ch1
    //TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//下面一行配置的极性会覆盖上面的配置,所以这里不用写
    TIM_ICInitStructure.TIM_ICFilter=0xF;//滤波消除毛刺,越大效果越好,看需求
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;//PA7对应定时器3 ch2
    //TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICFilter=0xF;//滤波消除毛刺,越大效果越好,看需求
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
    
    TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
    
    TIM_Cmd(TIM3, ENABLE);
}

uint16_t Encoder_Get(){
    
    
    return TIM_GetCounter(TIM3);
}

グリッドを 1 つ回すため、AB にはそれぞれ立ち下がりエッジと立ち上がりエッジがあるため、±4 になります。

速度を測定したい場合は、get 関数で取得した後に手動でクリアし、メインで取得するための遅延 (1000 ミリ秒ごとなど) を追加できます。

while(1){
        OLED_ShowNum(4,1,Encoder_Get(),5);
        Delay_ms(1000);
}

ただし、main 関数に遅延を記述するとプログラムの他の部分の動作に影響を与えるため、割り込み関数を記述するのが最善です。

void TIM2_IRQHandler(void){
    
    
    if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
    
    
        cnt=Encoder_Get();
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
}

おすすめ

転載: blog.csdn.net/jtwqwq/article/details/130699623