stm32: Add support for additional ADC3 ports on stm32f4

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2019-08-15 22:48:07 -04:00
parent d3bd4f9622
commit 4ec6db7a87
2 changed files with 42 additions and 20 deletions

View File

@ -19,7 +19,13 @@ static const uint8_t adc_pins[] = {
GPIO('A', 0), GPIO('A', 1), GPIO('A', 2), GPIO('A', 3),
GPIO('A', 4), GPIO('A', 5), GPIO('A', 6), GPIO('A', 7),
GPIO('B', 0), GPIO('B', 1), GPIO('C', 0), GPIO('C', 1),
GPIO('C', 2), GPIO('C', 3), GPIO('C', 4), GPIO('C', 5)
GPIO('C', 2), GPIO('C', 3), GPIO('C', 4), GPIO('C', 5),
#if CONFIG_MACH_STM32F4
0x00, 0x00, 0x00, 0x00,
GPIO('F', 6), GPIO('F', 7), GPIO('F', 8), GPIO('F', 9),
GPIO('F', 10), GPIO('F', 3), 0x00, 0x00,
0x00, 0x00, GPIO('F', 4), GPIO('F', 5),
#endif
};
#if CONFIG_MACH_STM32F1
@ -40,30 +46,42 @@ gpio_adc_setup(uint32_t pin)
break;
}
// Determine which ADC block to use
ADC_TypeDef *adc = ADC1;
uint32_t adc_base = ADC1_BASE;
#if CONFIG_MACH_STM32F4
if (chan >= 16) {
// On the STM32F4, some ADC channels are only available from ADC3
adc = ADC3;
adc_base += 0x800;
chan -= 16;
}
#endif
// Enable the ADC
if (!is_enabled_pclock(ADC1_BASE)) {
enable_pclock(ADC1_BASE);
if (!is_enabled_pclock(adc_base)) {
enable_pclock(adc_base);
uint32_t aticks = 3; // 2.5-3.2us (depending on stm32 chip)
ADC1->SMPR1 = (aticks | (aticks << 3) | (aticks << 6) | (aticks << 9)
adc->SMPR1 = (aticks | (aticks << 3) | (aticks << 6) | (aticks << 9)
| (aticks << 12) | (aticks << 15) | (aticks << 18)
| (aticks << 21) | (aticks << 24));
ADC1->SMPR2 = (aticks | (aticks << 3) | (aticks << 6) | (aticks << 9)
adc->SMPR2 = (aticks | (aticks << 3) | (aticks << 6) | (aticks << 9)
| (aticks << 12) | (aticks << 15) | (aticks << 18)
| (aticks << 21) | (aticks << 24) | (aticks << 27));
ADC1->CR2 = CR2_FLAGS;
adc->CR2 = CR2_FLAGS;
#if CONFIG_MACH_STM32F1
// Perform calibration
udelay(timer_from_us(1));
ADC1->CR2 = ADC_CR2_CAL | CR2_FLAGS;
while (ADC1->CR2 & ADC_CR2_CAL)
adc->CR2 = ADC_CR2_CAL | CR2_FLAGS;
while (adc->CR2 & ADC_CR2_CAL)
;
#endif
}
gpio_peripheral(pin, GPIO_ANALOG, 0);
return (struct gpio_adc){ .chan = chan };
return (struct gpio_adc){ .adc = adc, .chan = chan };
}
// Try to sample a value. Returns zero if sample ready, otherwise
@ -72,17 +90,18 @@ gpio_adc_setup(uint32_t pin)
uint32_t
gpio_adc_sample(struct gpio_adc g)
{
uint32_t sr = ADC1->SR;
ADC_TypeDef *adc = g.adc;
uint32_t sr = adc->SR;
if (sr & ADC_SR_STRT) {
if (!(sr & ADC_SR_EOC) || ADC1->SQR3 != g.chan)
if (!(sr & ADC_SR_EOC) || adc->SQR3 != g.chan)
// Conversion still in progress or busy on another channel
goto need_delay;
// Conversion ready
return 0;
}
// Start sample
ADC1->SQR3 = g.chan;
ADC1->CR2 = ADC_CR2_SWSTART | CR2_FLAGS;
adc->SQR3 = g.chan;
adc->CR2 = ADC_CR2_SWSTART | CR2_FLAGS;
need_delay:
return timer_from_us(10);
@ -92,16 +111,18 @@ need_delay:
uint16_t
gpio_adc_read(struct gpio_adc g)
{
ADC1->SR = ~ADC_SR_STRT;
return ADC1->DR;
ADC_TypeDef *adc = g.adc;
adc->SR = ~ADC_SR_STRT;
return adc->DR;
}
// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
ADC_TypeDef *adc = g.adc;
irqstatus_t flag = irq_save();
if (ADC1->SR & ADC_SR_STRT && ADC1->SQR3 == g.chan)
if (adc->SR & ADC_SR_STRT && adc->SQR3 == g.chan)
gpio_adc_read(g);
irq_restore(flag);
}

View File

@ -22,6 +22,7 @@ void gpio_in_reset(struct gpio_in g, int32_t pull_up);
uint8_t gpio_in_read(struct gpio_in g);
struct gpio_adc {
void *adc;
uint32_t chan;
};
struct gpio_adc gpio_adc_setup(uint32_t pin);