以前に書いた「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 件のコメント:
コメントを投稿