前回(ADC編)と前々回(DAC編)で、決まった周期でDACあるいはADCを駆動する方法を紹介しました。また、ADC編の最後にはDACで出力した正弦波をADCで測定したグラフも紹介しました。ただ、その時はDACとADCの同期が取れていないため、ADCの結果は位相ずれが発生していました。
ということで、今回はDACとADCの同期を行いたいと思います。
CubeMXの設定
ADCに使っているTIM(今回はTIM3)のSlave ModeをTrigger Modeにします。
Trigger Sourceを適切な値に設定します。今回はマスタがTIM4でスレーブがTIM3なので、ITR3を選択します。詳しくは各MCUのリファレンスマニュアル(STM32F4:RM0090, STM32F1:RM0008 等)の"汎用タイマ→レジスタ→スレーブモード制御レジスタ(SMCR)"を参照してください。
CubeMXの設定はこれだけです。コードを生成してソースコードの変更に移ります。
ソースコード
constexpr size_t DAC_len(1000);
constexpr size_t ADC_len(1000);
static uint8_t DAC1_buff[DAC_len];
static uint8_t DAC2_buff[DAC_len];
static uint16_t ADC_buff[ADC_len];
for (size_t i(0); i < DAC_len; ++i)
{
const float s(sinf((float)i / DAC_len * pi * 2));
DAC1_buff[i] = (uint8_t)(+s * 110 + 128);
DAC2_buff[i] = (uint8_t)(-s * 110 + 128);
}
memset(ADC_buff, 0, sizeof(ADC_buff));
HAL_TIM_GenerateEvent(&htim4, TIM_EVENTSOURCE_UPDATE);
HAL_TIM_Base_Stop(&htim3);
HAL_TIM_GenerateEvent(&htim3, TIM_EVENTSOURCE_UPDATE);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)DAC1_buff, DAC_len, DAC_ALIGN_8B_R);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)DAC2_buff, DAC_len, DAC_ALIGN_8B_R);
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_buff, ADC_len);
HAL_TIM_Base_Start(&htim4);
while (__HAL_DMA_GET_COUNTER(hdac.DMA_Handle1) > 0 ||
__HAL_DMA_GET_COUNTER(hdac.DMA_Handle2) > 0 ||
__HAL_DMA_GET_COUNTER(hadc1.DMA_Handle) > 0)
{
osDelay(1);
}
HAL_TIM_Base_Stop(&htim4);
HAL_TIM_Base_Stop(&htim3);
HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1);
HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_2);
HAL_ADC_Stop_DMA(&hadc1);
for (size_t i(0); i < ADC_len; ++i)
{
printf("%u %hu\n", i, ADC_buff[i]);
}
DACのバッファ初期化やADCのバッファ出力等が近い場所にあるため、少し複雑です。が、処理としてはDAC単体、ADC単体のときとほとんど同じです。
唯一違うのは、TIM3(ADCトリガ)をソフトウェアで開始しない点と、TIM4/TIM3のカウンタクリアの順番に注意する必要がある点です。
TIM3はTIM4のアップデートで開始しますが、TIM4をクリアするためにソフトウェアでアップデートイベントを発生させます。この際にTIM3が開始してしまうので、先にTIM3をクリアしてしまっても意味がありません。そのため、先にマスタ側をクリアし、またスレーブ側をクリアする前にスレーブを停止させる必要があります。
結果
前回のグラフと比べて、確かに位相ずれが減っているのがわかります。厳密に位相ずれがゼロになるわけではありませんが、ジッターを大幅に減らすことが可能になります。
履歴
2018-06-19
- 転送終了待ちwhileの条件を修正