2015年6月5日金曜日

STM32F4のADC3をDMA転送02



以前に書いた「STM32のAD変換01」では、AD変換した結果は右詰されていた。
しかし、その後に書いた記事、「STM32のAD変換結果をDMA転送」では、AD変換の結果が右詰されないで左詰で変数に格納されていた。

DMA関連の何かが悪いのだと思ったので、じっくり眺めたけれど分からなかった。

そこで、AD変換の初期化まわりを眺めなおしたら、少し気がついた。ADC_InitTypeDef構造体でADCを初期化する前に、ADC_StructInit関数を使って構造体自体の初期化をしているサンプルがある。それを真似して、ADC_StructInit関数で初期化してから構造体に値をセットして、ADC_Init関数で初期化してみた。

うまくいった。

なるほどね、構造体の初期値が不定だから、自分で設定しなかったメンバーが悪さをしていたのかな。だけれど、それだと、最初の「STM32のAD変換01」で期待通りの右詰で値を取得できた理由が分からない。
まぁ、とりあえず今はいいか。


それと、各種の関数の実体をソースで呼んでいるときに、ADC_DeInit関数の中身をみたらADC1のリセットをしているだけだったので、ADC3のリセット処理に変えてみた。


一度に色々と変更したせいで、どの変更がどう反映されたか分からなくなってしまったが、望んだ結果が得られたから、今日のところは良しとしよう。

ソースは以下。





#include "stm32f4xx.h"
#include "uart1.h"
#include "stm32f4dis.h"


volatile uint16_t adc_dma_data[4];
void ADC3_DMA_Config(void);
void tim5_pwm_init(void);
void set_pulse(uint16_t pulse1,uint16_t pulse2,uint16_t pulse3,uint16_t pulse4);


/* --------------------------------------
  time : 約1uSec,1000000=1sec
   -------------------------------------- */
void delay(uint32_t time){
  volatile uint32_t t;
  volatile uint8_t i;
  
  for(i=0;i<24;i++){
    t = time;
    while(t--);
  }
} 

// -------------------------------------------------------------------
// systick 割込み(1msec毎に設定してある)
// stm32f4xx_it.c で呼び出している
// -------------------------------------------------------------------
volatile uint16_t time_sec=0;
volatile uint16_t time_msec=0;
volatile uint16_t sw_flag=0;
void systick_1msec(void){
  static uint16_t cnt=0;
  
  cnt++;
  time_msec++;
  if(cnt>1000){
    time_msec=0;
    cnt=0;
 time_sec++;
  }
  
  if(sw_flag){
    sw_flag = 0;
  }else{
  }
  
}

void Delay_ms(uint16_t msec){
  uint16_t cnt=0;
  
  cnt = time_msec;
  while(msec){
    if(cnt!=time_msec){
   cnt = time_msec;
   msec--;
 }
  }
}

void set_pulse(uint16_t pulse1,uint16_t pulse2,uint16_t pulse3,uint16_t pulse4){
  TIM5->CCR1 = pulse1;
  TIM5->CCR2 = pulse2;
  TIM5->CCR3 = pulse3;
  TIM5->CCR4 = pulse4;
}


// -------------------------------------------------------
// 
// -------------------------------------------------------
int main(void)
{
  SystemInit();
  stm32f4_init();       set_led(0x01);
  ADC3_DMA_Config();    delay(10000);
  tim5_pwm_init();      delay(10000);

  uart1_init(9600);

  // systick 設定(1msec)
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
  if(SysTick_Config(SystemCoreClock/1000)){
    while(1);                // error
  }
  
  while(1){
    // SW処理
    if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13)){
      //ボタンが押されて1になっていたら以下を実行
      GPIO_ResetBits(GPIOB,GPIO_Pin_7);     // 点灯
   while(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13));  // ボタンを放したら以下を実行
 set_pulse(100,100,100,100);
    }else{
      GPIO_SetBits(GPIOB,GPIO_Pin_7);       // 消灯
    }

#define _SEC_
#ifdef _SEC_
 // 1秒処理
  volatile static uint16_t time_tmp=0;
  volatile static uint16_t adc30_max=0;
  
  if(adc30_max<(adc_dma_data[0])){
    adc30_max=(adc_dma_data[0]);
  }

 if(time_tmp!=time_sec){
   set_led(0x04);
   time_tmp = time_sec;

        usart1_printf(\"%4d : AD ch1=%6d , ch2=%6d , ch3=0x%4X , ch4=%6d \\r\",
                 time_sec,adc30_max,0,adc_dma_data[2],0);
  adc30_max = 0;

   set_led(0x00);
 }
#endif
  }
  
  return 0;
}


#define PWM_PERIOD   1000
void tim5_pwm_init(void){

  // --------------------------------------------------
  // Port E 設定(PE9,PE10 PWM 出力) ここから
  
  // GPIOA クロックの有効化
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);

  // PA0,PA1,PA2,PA3 : 
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;       // この行がないとPWM出力されない
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA,&GPIO_InitStructure);
  
  //GPIOAのPIN0,PIN1,PIN2,PIN3をオルタネィテブファンクションのTIM5に割り当て
  GPIO_PinAFConfig(GPIOA , GPIO_PinSource0  , GPIO_AF_TIM5);
  GPIO_PinAFConfig(GPIOA , GPIO_PinSource1  , GPIO_AF_TIM5);
  GPIO_PinAFConfig(GPIOA , GPIO_PinSource2  , GPIO_AF_TIM5);
  GPIO_PinAFConfig(GPIOA , GPIO_PinSource3  , GPIO_AF_TIM5);
  //GPIOAのPIN0,PIN1,PIN2,PIN3をオルタネィテブファンクションのTIM5に割り当て
  // --------------------------------------------------
  

  // --------------------------------------------------
  // TimeBase 設定 ここから
  // TIM5 クロックの有効化
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

  //   TIM1 clock = SystemCoreClock / 2 = 168 MHz /2 = 84 MHz
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
  TIM_TimeBaseStructure.TIM_Prescaler     = 84;               // 84 MHz / 84 = 1,000 kHz
  TIM_TimeBaseStructure.TIM_Period        = PWM_PERIOD;              // 周期設定
  TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;  // カウントアップモード
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        // インプットキャプチャ用設定(未使用)
  TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
  // TimeBase 設定 ここまで
  // --------------------------------------------------
  
  
  // --------------------------------------------------
  // アウトプット・キャプチャ設定 ここから
  //PWM1 Modeの設定・チャネル1,2
  TIM_OCInitTypeDef TIM_OCInitStructure;
  TIM_OCStructInit(&TIM_OCInitStructure);
  
  TIM_OCInitStructure.TIM_OCMode      = TIM_OCMode_PWM1;         // PWM1 モード
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // タイマ出力を有効
  TIM_OCInitStructure.TIM_Pulse       = 0;                       // Duty 設定
  TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;      // アクティブ時 出力High
  // ch1
  TIM_OC1Init(TIM5, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);             // プリロード
  // ch2
  TIM_OC2Init(TIM5, &TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIM5, TIM_OCPreload_Enable);             // プリロード
  // ch3
  TIM_OC3Init(TIM5, &TIM_OCInitStructure);
  TIM_OC3PreloadConfig(TIM5, TIM_OCPreload_Enable);             // プリロード
  // ch4
  TIM_OC4Init(TIM5, &TIM_OCInitStructure);
  TIM_OC4PreloadConfig(TIM5, TIM_OCPreload_Enable);             // プリロード
  // アウトプット・キャプチャ設定 ここまで
  // --------------------------------------------------

//  TIM_CtrlPWMOutputs(TIM5,ENABLE);    // 高機能タイマ専用
  TIM_Cmd(TIM5,ENABLE);               // TIM5 有効
}


void ADC3_DMA_Config(void)
{
  ADC_InitTypeDef       ADC_InitStructure;
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  DMA_InitTypeDef       DMA_InitStructure;
  GPIO_InitTypeDef      GPIO_InitStructure;

  
  //必要なペリフェラルにクロック供給開始
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
  delay(10000);

  //DMAの設定
  DMA_InitStructure.DMA_Channel              = DMA_Channel_2;
  DMA_InitStructure.DMA_PeripheralBaseAddr   = (uint32_t)&ADC3->DR;
  DMA_InitStructure.DMA_Memory0BaseAddr      = (uint32_t)&adc_dma_data;
  DMA_InitStructure.DMA_DIR                  = DMA_DIR_PeripheralToMemory;
  // ↓ 4チャンネルの変換結果を送るからデータ数は4(チャネルを増やしたらここを変える)
  DMA_InitStructure.DMA_BufferSize           = 4; 
  DMA_InitStructure.DMA_PeripheralInc        = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc            = DMA_MemoryInc_Enable; //送り先アドレスをインクリメント
  DMA_InitStructure.DMA_PeripheralDataSize   = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize       = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode                 = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority             = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode             = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold        = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst          = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst      = DMA_PeripheralBurst_Single;
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);

  //GPIOCの指定の入力をアナログに設定
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //PC[3:0]を設定
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

//  ADC_DeInit();          // AD変換器のリセット
//  delay(10000);
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, ENABLE);
  delay(10000);
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, DISABLE);
  delay(10000);
  
  //ADC3の基本設定
  ADC_CommonStructInit(&ADC_CommonInitStructure);
  ADC_CommonInitStructure.ADC_Mode             = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler        = ADC_Prescaler_Div2;
  ADC_CommonInitStructure.ADC_DMAAccessMode    = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);

  //ADC3の変換モード設定。入力4本をスキャン変換モードで逐次変換
  ADC_StructInit( &ADC_InitStructure );
  ADC_InitStructure.ADC_Resolution           = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode         = ENABLE;    //スキャン変換モードに
  ADC_InitStructure.ADC_ContinuousConvMode   = ENABLE;
  ADC_InitStructure.ADC_DataAlign            = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion      = 4;        //入力を4本(チャネルを増やしたらここを変える)
  ADC_Init(ADC3, &ADC_InitStructure);
  delay(10000);

  //ADC3のアナログ入力を定義する
  ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
  ADC_RegularChannelConfig(ADC3, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
  ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);
  ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);

  //変換結果がDMA転送されるごとに、ADCは次の変換を開始するように設定
  ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

  //ADC3のDMAを使えるようにする
  DMA_Cmd(DMA2_Stream0, ENABLE);
  ADC_DMACmd(ADC3, ENABLE);

  //ADC3を使えるようにする
  ADC_Cmd(ADC3, ENABLE);            delay(10000);
  ADC_SoftwareStartConv( ADC3 );    delay(10000);  // AD変換開始

}


0 件のコメント:

コメントを投稿