2016年9月24日土曜日

TLC5940を使いたい

TLC5940を使いたいので、一番面倒そうなパルス出力部分を作ってみることにします。
結論から言うとTIMのPWM出力をちょこちょこ動かすわけですが。


TIM2とTIM3を使用します。
TIM2でGSCLKを作り、TIM3でBLANKを作ります。

TIM2は1MHzのPWMを出力します。
STM32のPWMはPWM1とPWM2の2種類があるわけですが、今回はPWM2を使用します。PWM1と2はパルスの極性が反転します。極性はPolarity_HighやPolarity_Lowで設定できますが、今回はPWM2で設定しました。
PWM1の場合、カウンタが0だとHが出力されるため、あとでカウンタをクリアした際にその時点でHが出力されてしまうためです。

TIM3はいくつかの機能を持たせていますが、一番大きな機能はTIM2のゲートとしての機能です。
STM32のTIMはTIMマスタのPWMがHのときだけTIMスレーブを走らせる、ということが可能なので、正確な時間だけTIMを動かしたいという場合には便利です。今回はTIM2で4096個のパルスを送りたいので使用しました。BLANKパルスを送った後にGSCLKを送信したいので、PWM2でTIMの後半に送信するようにします。
前述の通りPWM以外にPolarityで極性を設定できますが、ゲートとして使うにはPWM1/2で極性を設定する必要があります。

ゲートはCH1を使いますが、他にCH3とCH4を初期化しています。CH3とCH4ではOCで割り込みを発生させており、この割り込みハンドラの中でGPIOをトグルしてBLANK信号を出力しています。本当はハードウェアで処理したかったのですが、方法がわからなかったのでソフトウェアに逃げました。


TIM2は1MHzで分解能は1usec、TIM3は20msecで1サイクルとなります。まぁサーボモータ向けの設定ですね。サーボモータを使うなら2.5msecもあれば十分ですからTIM2はもう少し高速でもいいですが、1usec単位のほうが扱いやすいのでこの設定です。

TIM3の更新割り込みなどを使えばGSCLKを送った後にハンドラを起動できるので、そこでデータの転送を開始させるなどの設定も可能です。

とりあえず必要なパルスは送れるようになったので、あとはデータを送ってやれば使えるようになるはずです。それはまた次回。


// TIM2 init 
{
    {
        GPIO_InitTypeDef GPIO_InitStructure;

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

        TIM_InitStructure.TIM_Period = 72 - 1;
        TIM_InitStructure.TIM_Prescaler = 0;
        TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_RepetitionCounter = 0;

        TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    }
    {
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        TIM_OCInitStructure.TIM_Pulse = 36 - 1;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

        TIM_OC4Init(TIM2, &TIM_OCInitStructure);

        TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
    }

    TIM_ARRPreloadConfig(TIM2, ENABLE);
}

// TIM3 init 
{
    const uint16_t Period = 20 * 1000;

    {
        GPIO_InitTypeDef GPIO_InitStructure;

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

        TIM_InitStructure.TIM_Period = Period - 1;
        TIM_InitStructure.TIM_Prescaler = 72 - 1;
        TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_RepetitionCounter = 0;

        TIM_TimeBaseInit(TIM3, &TIM_InitStructure);
    }
    {
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
        TIM_OCInitStructure.TIM_Pulse = Period - 4096;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

        TIM_OC1Init(TIM3, &TIM_OCInitStructure);

        TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    }
    {
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
        TIM_OCInitStructure.TIM_Pulse = Period - 6000;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

        TIM_OC3Init(TIM3, &TIM_OCInitStructure);

        TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
    }
    {
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
        TIM_OCInitStructure.TIM_Pulse = Period - 5000;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

        TIM_OC4Init(TIM3, &TIM_OCInitStructure);

        TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
    }

    {
        NVIC_InitTypeDef NVIC_InitStructure;

        NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    }

    TIM_ARRPreloadConfig(TIM3, ENABLE);
}

TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_OC1Ref);
TIM_SelectInputTrigger(TIM2, TIM_TS_ITR2);
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Gated);

TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE);

TIM_Cmd(TIM2, ENABLE);
TIM_Cmd(TIM3, ENABLE);

***

void TIM3_IRQHandler(void) {
    if (TIM_GetITStatus(TIM3, TIM_IT_CC3))
    {
        GPIO_SetBits(GPIOA, GPIO_Pin_4);

        TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
        return;
    }
    if (TIM_GetITStatus(TIM3, TIM_IT_CC4))
    {
        GPIO_ResetBits(GPIOA, GPIO_Pin_4);
        TIM_GenerateEvent(TIM2, TIM_EventSource_Update);

        TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
        return;
    }
}

0 件のコメント:

コメントを投稿