2014年7月25日金曜日

STM32F1でPWMのキャプチャ

STM32F1のTIMでPWMをキャプチャする方法
今回はTIM2を使いました

日本語で調べた感じあんまり解説が無かったので備忘録兼ね

    {
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;
        TIM_TimeBaseStructInit(&TIM_InitStructure);
        TIM_InitStructure.TIM_Prescaler        = 72 - 1;
        TIM_InitStructure.TIM_CounterMode    = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_Period        = 0xFFFF;
        TIM_InitStructure.TIM_ClockDivision    = TIM_CKD_DIV1;
        //TIM_InitStructure.TIM_RepetitionCounter    = ;
        TIM_DeInit(TIM2);
        TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    }
    
    {
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFFFC) | (( 0b01 <<  0) & 0x0003);
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFF0F) | ((    6 <<  4) & 0x00F0);
        TIM2->CCER  = (TIM2->CCER  & 0xFFFD) | ((    0 <<  1) & 0x0002);
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFFF3) | ((    0 <<  2) & 0x000C);
    }
    {
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFCFF) | (( 0b10 <<  8) & 0x0300);
        TIM2->CCER  = (TIM2->CCER  & 0xFFDF) | ((    1 <<  5) & 0x0020);
        TIM2->SMCR  = (TIM2->SMCR  & 0xFF8F) | ((0b101 <<  4) & 0x0070);
        TIM2->SMCR  = (TIM2->SMCR  & 0xFFF8) | ((0b100 <<  0) & 0x0007);
    }
    TIM2->CCER  = (TIM2->CCER  & 0xFFFE) | ((    1 <<  0) & 0x0001);
    TIM2->CCER  = (TIM2->CCER  & 0xFFEF) | ((    1 <<  4) & 0x0010);
    
    TIM_Cmd(TIM2, ENABLE);

最初にGPIOを初期化 TIM2 Ch1が接続されているPA0をIN PullDownで初期化します

次にタイマモジュールの初期化
今回は1マイクロ秒単位で計測したかったのでプリスケーラに72を入れています
Periodは正直何の設定かわからないです
おそらく計測できる最大の周期を設定するんではないかと
そしてキャプチャモジュールの初期化
本来はStdPeriphLibを使うべきですがRM0008を読みながら実装したため、直接ビット操作しています
1個目のブロックで立ち上がりエッジを 2個目のブロックで立ち下がりエッジを検出するための設定をしています
立ち下がりエッジの方はフィルタの設定等していないです 一応動いてますが
そしてキャプチャ2個をイネーブルにしてからTIM自体をイネーブルにしています
コレで計測ができます

値を読み出すには
    if (TIM_GetFlagStatus(TIM2, TIM_IT_CC1)) {
        uint16_t val = TIM2->CCR1;
        xprintf("CCR1:%d\n", val);
    }
    if (TIM_GetFlagStatus(TIM2, TIM_IT_CC2)) {
        uint16_t val = TIM2->CCR2;
        xprintf("CCR2:%d\n", val);

    }
みたいな感じでできます

今回は5msec周期で25%のパルスを入れてみました
その結果CCR1が5000 CCR2が1250 という値が読み出されました
分解能1usecで初期化しているため、5msecは5000として読み出され、パルス幅はその25%なので1250と、正常に動作しているようです

1chのPWMを読む場合、CCRはあと2本余っているので、ココでもう1本のPWMを読むとか、PWMを2本出力できるかは不明です

まずはPeriphLibで書き換えてから、他の機能を試してみようと思います
しかしSTMのTIMってレジスタ多すぎ 80バイトって…


追記:2014/07/26
StdPeriphLibを使って初期化する方法
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
 
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;
        TIM_TimeBaseStructInit(&TIM_InitStructure);
        TIM_InitStructure.TIM_Prescaler     = 72 - 1;
        TIM_InitStructure.TIM_CounterMode   = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_Period        = 0xFFFF;
        TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        //TIM_InitStructure.TIM_RepetitionCounter = ; /* TIM1 or TIM8 */
        TIM_DeInit(TIM2);
        TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    }

    {
        TIM_ICInitTypeDef TIM_ICInitStructure;
        TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
        TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
        TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
        TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
        TIM_ICInitStructure.TIM_ICFilter    = 5;
        TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
    }

    TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);

    TIM_Cmd(TIM2, ENABLE);

やはりStdPeriphLibはわかりやすいです
ビット単位で設定とか何やってるかわからないですし
次は残りのChannelをどうするか考えてみます


追記:2014/07/26(2回目)
PWM入力とPWM出力を同時に行うことはできないようです
タイミングチャートを見ると
パルスの立ち上がりでタイマのカウンタがクリアされるようです
そのため他にタイミングソースを提供することができません
という理由によりPWM入力は1本だけ、PWM出力も不可 ということになるようです

パルス幅を1個計測するのにTIMモジュールが1本必要なので、比較的高コストです
外部のセンサでUART接続よりパルス出力のほうが低コストかな と思ってましたが 全然そんなことなかったです

他の方法だとADCは3本しか無いですが、順次切り替えて10本以上測定できるので、こっちのほうが低コストかもしれません

0 件のコメント:

コメントを投稿