stm32: Rework register access on stm32f0_adc.c

Avoid read-modify-write operations where possible.  The register
values are in a known state so prefer absolute writes.

Improve handling of race conditions with hardware updates.

Remove the adc reference from "struct gpio_adc" as it is a constant.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2021-12-19 20:56:34 -05:00
parent 20ac48f680
commit 88325b6c93
1 changed files with 25 additions and 34 deletions

View File

@ -38,38 +38,23 @@ gpio_adc_setup(uint32_t pin)
break; break;
} }
// Determine which ADC block to use
ADC_TypeDef *adc = ADC1;
uint32_t adc_base = ADC1_BASE;
// Enable the ADC // Enable the ADC
if (!is_enabled_pclock(adc_base)) { if (!is_enabled_pclock(ADC1_BASE)) {
enable_pclock(adc_base); enable_pclock(ADC1_BASE);
ADC_TypeDef *adc = ADC1;
// 100: 41.5 ADC clock cycles // 100: 41.5 ADC clock cycles
adc->SMPR |= ~ADC_SMPR_SMP_Msk | ADC_SMPR_SMP_2; adc->SMPR = 4 << ADC_SMPR_SMP_Pos;
adc->CFGR2 |= ADC_CFGR2_CKMODE;
adc->CFGR1 &= ~ADC_CFGR1_AUTOFF;
adc->CFGR1 |= ADC_CFGR1_EXTSEL;
// do not enable ADC before calibration
adc->CR &= ~ADC_CR_ADEN;
while (adc->CR & ADC_CR_ADEN)
;
while (adc->CFGR1 & ADC_CFGR1_DMAEN)
;
// start calibration and wait for completion // start calibration and wait for completion
adc->CR |= ADC_CR_ADCAL; adc->CR = ADC_CR_ADCAL;
while (adc->CR & ADC_CR_ADCAL) while (adc->CR & ADC_CR_ADCAL)
; ;
// if not enabled adc->ISR = ADC_ISR_ADRDY;
if (!(adc->CR & ADC_CR_ADEN)) { adc->CR = ADC_CR_ADEN;
adc->ISR |= ADC_ISR_ADRDY; while (!(adc->ISR & ADC_ISR_ADRDY))
adc->CR |= ADC_CR_ADEN; ;
while (!(ADC1->ISR & ADC_ISR_ADRDY))
;
}
} }
if (pin == ADC_TEMPERATURE_PIN) if (pin == ADC_TEMPERATURE_PIN)
@ -77,7 +62,7 @@ gpio_adc_setup(uint32_t pin)
else else
gpio_peripheral(pin, GPIO_ANALOG, 0); gpio_peripheral(pin, GPIO_ANALOG, 0);
return (struct gpio_adc){ .adc = adc, .chan = 1 << chan }; return (struct gpio_adc){ .chan = 1 << chan };
} }
// Try to sample a value. Returns zero if sample ready, otherwise // Try to sample a value. Returns zero if sample ready, otherwise
@ -86,13 +71,16 @@ gpio_adc_setup(uint32_t pin)
uint32_t uint32_t
gpio_adc_sample(struct gpio_adc g) gpio_adc_sample(struct gpio_adc g)
{ {
ADC_TypeDef *adc = g.adc; ADC_TypeDef *adc = ADC1;
if (adc->ISR & ADC_ISR_EOC && adc->CHSELR == g.chan)
return 0;
if (adc->CR & ADC_CR_ADSTART) if (adc->CR & ADC_CR_ADSTART)
goto need_delay; goto need_delay;
if (adc->ISR & ADC_ISR_EOC) {
if (adc->CHSELR == g.chan)
return 0;
goto need_delay;
}
adc->CHSELR = g.chan; adc->CHSELR = g.chan;
adc->CR |= ADC_CR_ADSTART; adc->CR = ADC_CR_ADSTART;
need_delay: need_delay:
return timer_from_us(10); return timer_from_us(10);
@ -102,8 +90,7 @@ need_delay:
uint16_t uint16_t
gpio_adc_read(struct gpio_adc g) gpio_adc_read(struct gpio_adc g)
{ {
ADC_TypeDef *adc = g.adc; ADC_TypeDef *adc = ADC1;
adc->ISR &= ~ADC_ISR_EOSEQ;
return adc->DR; return adc->DR;
} }
@ -111,9 +98,13 @@ gpio_adc_read(struct gpio_adc g)
void void
gpio_adc_cancel_sample(struct gpio_adc g) gpio_adc_cancel_sample(struct gpio_adc g)
{ {
ADC_TypeDef *adc = g.adc; ADC_TypeDef *adc = ADC1;
irqstatus_t flag = irq_save(); irqstatus_t flag = irq_save();
if (!(adc->ISR & ADC_ISR_EOC) && adc->CHSELR == g.chan) if (adc->CHSELR == g.chan) {
adc->CR |= ADC_CR_ADSTP; if (adc->CR & ADC_CR_ADSTART)
adc->CR = ADC_CR_ADSTP;
if (adc->ISR & ADC_ISR_EOC)
gpio_adc_read(g);
}
irq_restore(flag); irq_restore(flag);
} }