2017年2月8日水曜日

STM32F1でADCをTIMで駆動してDMAを駆動する

 やりたいこと:ADCで連続変換、タイミングはTIMで作ってDMA転送
 つかうもの:STM32F1

 今回はTIM4でタイミングを作り、ADC1を駆動、PB0のchを変換してDMA転送を行う。

 ADC1をTIM4で駆動する場合、TIM4 OC4のエッジで変換が開始されるが、注意点として、OCのOutputStateはEnableにしなければいけない。
 例えばTIMをトリガにDMA転送を行う場合、内部的にOCのエッジがあればいいので、OutputStateはDisableでも問題ない。しかし、ADCのトリガとして使うにはOutputStateはEnableにしなければ動かなかった。
 たとえOutputStateをEnableにしても、GPIOをAFで初期化しなければOCが出力されることはない。GPIOをソフトウェアで入出力に使う分にも問題はないが、ペリフェラルで使うことはできなくなる。TIM4 OC4はPB9に接続されており、このピンはI2C1 SDAのリマップと、CAN TXのリマップに接続されている。特にCANは標準がUSBと同居しており、STBeeMiniで使う際はPB8/PB9にリマップする必要があるから、TIM4 OC4とは競合してしまう。もしかしたらTIM4 OC4をリマップしてPD15に移動すれば問題ないかもしれないが、そこまでは確認していない。


 TIMでADCを駆動してDMA転送すれば、バッファが埋まるまではCPUからはノータッチで処理できるので、他の処理に専念することができる。YES DMA! NO タッチ!





// ADCの初期化 
{
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
    // GPIOの初期化 
    {
        GPIO_InitTypeDef GPIO_InitStructure;
            
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
    }

    ADC_DeInit(ADC1);

    // ADC1の初期化 
    {
        ADC_InitTypeDef ADC_InitStructure;

        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
        ADC_InitStructure.ADC_ScanConvMode = DISABLE;
        ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
        ADC_InitStructure.ADC_NbrOfChannel = 1;
        ADC_Init(ADC1, &ADC_InitStructure);
    }

    ADC_ExternalTrigConvCmd(ADC1, ENABLE);

    ADC_Cmd(ADC1, ENABLE);

    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));

    ADC_RegularChannelConfig(ADC1, ADC_Channel_8 /*PB0*/, 1, ADC_SampleTime_7Cycles5);
}

// TIMの初期化 
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

    // タイマの初期化 72MHzで48kHz
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;

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

        TIM_TimeBaseInit(TIM4, &TIM_InitStructure);
    }

    // OC4の初期化 OutputState=Enableに注意 
    {
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        TIM_OCInitStructure.TIM_Pulse = 1;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

        TIM_OC4Init(TIM4, &TIM_OCInitStructure);
    }

    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM4, ENABLE);
}

// DMAの初期化 
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel1);

    // DMAの初期化 
    {
        DMA_InitTypeDef DMA_InitStructure;

        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&ADC1->DR);
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(ADCbuff);
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
        DMA_InitStructure.DMA_BufferSize = sizeof(ADCbuff) / sizeof(ADCbuff[0]);
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

        DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    }

    // NVICの初期化 
    {
        NVIC_InitTypeDef NVIC_InitStructure;

        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

        NVIC_Init(&NVIC_InitStructure);

        DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE);
        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    }

    DMA_Cmd(DMA1_Channel1, ENABLE);
}


void DMA1_Channel1_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_HT1)) {

        DMA_ClearITPendingBit(DMA1_IT_HT1);
    }
    if (DMA_GetITStatus(DMA1_IT_TC1)) {

        DMA_ClearITPendingBit(DMA1_IT_TC1);
    }
}

 追記:2017/02/14
 重要な"ADC_DMACmd(ADC1, ENABLE);"を書くのを忘れていた。DMA_Cmdの前辺りに書いておくように。

0 件のコメント:

コメントを投稿