定时器触发的STM32F767ZI双ADC模式
Posted
技术标签:
【中文标题】定时器触发的STM32F767ZI双ADC模式【英文标题】:STM32F767ZI dual ADC mode triggered by timer 【发布时间】:2021-10-22 03:18:25 【问题描述】:我正在开发一个电流控制器来同时控制两个线圈中的电流。 因此,我想同时测量两个模拟引脚并同步 w.r.t。 PWM 定时器。
对于 PWM,我使用定时器 TIM2。 PWM 运行正常。 此外,我将 ADC 配置为双注入同时模式。
现在我的问题是: 当我通过在定时器 IRQ-Handler 中设置寄存器 ADC1_CR1 的 JSWSTART 位来启动 ADC 时,将执行测量(软件触发 ADC 执行)。但是当我想使用定时器更新事件作为 ADC 触发时,不会进行测量。我做错了什么?
我只使用 HAL 库的低级函数。
这是我使用软件触发 ADC 时的代码。
void adcInit()
/* prepare ADC for synchronous measurement */
LL_GPIO_InitTypeDef GPIO_InitStruct;
LL_ADC_CommonInitTypeDef ADC_CommonInitStruct;
LL_ADC_InitTypeDef ADC_InitStruct;
LL_ADC_INJ_InitTypeDef ADC_INJ_InitStruct;
/* enable clocks */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC2);
/* configure input channels */
/* common to all pins */
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
/* internal current sensor coil C1 */
GPIO_InitStruct.Pin = ANALOG_CS_INT_C1_PIN;
LL_GPIO_Init(ANALOG_CS_INT_C1_PORT, &GPIO_InitStruct);
/* internal current sensor coil C2 */
GPIO_InitStruct.Pin = ANALOG_CS_INT_C2_PIN;
LL_GPIO_Init(ANALOG_CS_INT_C2_PORT, &GPIO_InitStruct);
/* external current sensor coil C1 */
GPIO_InitStruct.Pin = ANALOG_CS_EXT_C1_PIN;
LL_GPIO_Init(ANALOG_CS_EXT_C1_PORT, &GPIO_InitStruct);
/* external current sensor coil C2 */
GPIO_InitStruct.Pin = ANALOG_CS_EXT_C2_PIN;
LL_GPIO_Init(ANALOG_CS_EXT_C2_PORT, &GPIO_InitStruct);
/* initialize ADC register */
/* use ADC1 & ADC2 in dual combined injected mode */
ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_DUAL_INJ_SIMULT;
ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV4; // 27MHz
ADC_CommonInitStruct.MultiTwoSamplingDelay = LL_ADC_MULTI_TWOSMP_DELAY_5CYCLES;
ADC_CommonInitStruct.MultiDMATransfer = LL_ADC_MULTI_REG_DMA_EACH_ADC;
LL_ADC_CommonInit(ADC123_COMMON, &ADC_CommonInitStruct);
ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_DISABLE;
LL_ADC_Init(ADC1, &ADC_InitStruct);
LL_ADC_Init(ADC2, &ADC_InitStruct);
ADC_INJ_InitStruct.SequencerLength = LL_ADC_INJ_SEQ_SCAN_DISABLE;
ADC_INJ_InitStruct.SequencerDiscont = LL_ADC_INJ_SEQ_DISCONT_DISABLE;
ADC_INJ_InitStruct.TrigAuto = LL_ADC_INJ_TRIG_INDEPENDENT;
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_SOFTWARE;
LL_ADC_INJ_Init(ADC1, &ADC_INJ_InitStruct);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_SOFTWARE; // disable trigger of ADC2, triggered by ADC1
LL_ADC_INJ_Init(ADC2, &ADC_INJ_InitStruct);
/* select channels and set sampling time*/
LL_ADC_INJ_SetSequencerRanks(ADC1, LL_ADC_INJ_RANK_1, ANALOG_CS_C1_ADC_CH);
LL_ADC_INJ_SetSequencerRanks(ADC2, LL_ADC_INJ_RANK_1, ANALOG_CS_C2_ADC_CH);
LL_ADC_SetChannelSamplingTime(ADC1, ANALOG_CS_C1_ADC_CH, LL_ADC_SAMPLINGTIME_3CYCLES);
LL_ADC_SetChannelSamplingTime(ADC2, ANALOG_CS_C2_ADC_CH, LL_ADC_SAMPLINGTIME_3CYCLES);
/* enable interrupts */
NVIC_SetPriority(ADC_IRQn, NVIC_PRIORITY_ADC);
NVIC_EnableIRQ(ADC_IRQn);
LL_ADC_EnableIT_JEOS(ADC1);
// JEOC-interrupt for ADC1 is sufficient, because interrupt is generated when injected channels have all been converted (manual p. 458)
// LL_ADC_EnableIT_JEOS(ADC2);
/* enable ADCs */
LL_ADC_Enable(ADC1);
LL_ADC_Enable(ADC2);
void TIM2_IRQHandler(void)
static uint32_t ctrlExecCnt = PWM_TIMER_FREQ/CTRL_EXEC_FREQ;
if(timer2.timer->SR & TIM_SR_UIF_Msk)
timer2.timer->SR = ~(TIM_SR_UIF_Msk);
if(!(timer2.timer->CR1 & TIM_CR1_DIR_Msk))
// reached counter bottom value, now counting up
// update TIMx_CCRy value -> because of enabled preload, value will be applied when reaching timer top update event
LL_TIM_OC_SetCompareCH3(timer2.timer, ctrlState.outC1);
LL_TIM_OC_SetCompareCH4(timer2.timer, ctrlState.outC2);
if(!--ctrlExecCnt)
ctrlExecCnt = PWM_TIMER_FREQ/CTRL_EXEC_FREQ;
execControlLoop();
LL_ADC_INJ_StartConversionSWStart(ADC1);
else
// reaching counter top value -> OC pin is low, so now direction could be changed
if(ctrlState.dirC1 == POSITIVE)
LL_GPIO_ResetOutputPin(DIR_C1_PORT, DIR_C1_PIN);
else
LL_GPIO_SetOutputPin(DIR_C1_PORT, DIR_C1_PIN);
if(ctrlState.dirC2 == POSITIVE)
LL_GPIO_ResetOutputPin(DIR_C2_PORT, DIR_C2_PIN);
else
LL_GPIO_SetOutputPin(DIR_C2_PORT, DIR_C2_PIN);
void ADC_IRQHandler(void)
LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
// get interrupt source
// checking ADC1 JEOC-interrupt is sufficient, because interrupt will be generated when all injected channels have been converted (manual p. 458)
if(ADC1->SR & ADC_SR_JEOC_Msk)
// both ADC1 & ADC2 injected conversion finished
LL_ADC_ClearFlag_JEOS(ADC1);
//LL_ADC_ClearFlag_JEOS(ADC2);
// read in data
adcData[0] = ADC1->JDR1;
adcData[1] = ADC2->JDR1;
char tmp[20];
STM32_usartPrintf(&usart3, "ADC: ");
utoa(adcData[0],tmp,10);
STM32_usartPrintf(&usart3, tmp);
STM32_usartPrintf(&usart3, "\t");
utoa(adcData[1],tmp,10);
STM32_usartPrintf(&usart3, tmp);
STM32_usartPrintf(&usart3, "\r");
使用计时器触发器的更改: 选择更新事件作为定时器 TIM2 的触发输出,并选择 TIM2_TRGO 作为 ADC 触发源。 并且我还评论了在定时器 IRQ-Handler 中设置 JSWSTART-Bit 的功能,以防止意外启动 ADC2。
void adcInit()
...
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_UPDATE);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_EXT_TIM2_TRGO;
LL_ADC_INJ_Init(ADC1, &ADC_INJ_InitStruct);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_SOFTWARE; // disable trigger of ADC2, triggered by ADC1
LL_ADC_INJ_Init(ADC2, &ADC_INJ_InitStruct);
...
void TIM2_IRQHandler(void)
...
// LL_ADC_INJ_StartConversionSWStart(ADC1);
...
【问题讨论】:
【参考方案1】:解决方案很简单,但并不容易找到:D
当使用低级 HAL 功能LL_ADC_INJ_Init(...)
时,控制寄存器 CR2 中的 JEXTEN 位被清除。清除 JEXTEN 位意味着“没有触发评估”。此外,分配给ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_EXT_TIM2_TRGO;
的值被屏蔽,因此无法使用init 函数设置触发源和触发检测。
自己做,解决问题。所以“解决方案”是
void adcInit()
...
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_UPDATE);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_EXT_TIM2_TRGO;
LL_ADC_INJ_Init(ADC1, &ADC_INJ_InitStruct);
ADC1->CR2 |= (0x01 << ADC_CR2_JEXTEN_Pos);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_SOFTWARE; // disable trigger of ADC2, triggered by ADC1
LL_ADC_INJ_Init(ADC2, &ADC_INJ_InitStruct);
...
使用常规通道时应该会发生相同的行为,因为ADC_REG_Init(...)
也会清除 EXTEN 位。所以在这种情况下添加
ADC1->CR2 |= (0x01 << ADC_CR2_EXTEN_Pos);
【讨论】:
以上是关于定时器触发的STM32F767ZI双ADC模式的主要内容,如果未能解决你的问题,请参考以下文章