STM8 ADC#

STM8的ADC功能较弱,没有DMA,对多通道AD采集支持不友好。

1. 扫描模式#

STM8的扫描模式很简陋,只能从AIN0 到 AINn,假如你需要扫描AIN2和AIN6,那么STM8只能做到从AIN0扫描到AIN6:AIN0 -> AIN1 -> AIN2 -> AIN3 -> AIN4 -> AIN5 -> AIN6(不像STM32可以设置扫描顺序,这样就能指定扫描的通道了) 。

2. 多通道ADC#

STM8的ADC没有DMA(STM8提供了一个数据Buffer),并且其数据寄存器是8位的,ADC是10 bit的,所以需要两个寄存器来存储ADC转换结果。

STM8提供了一个数据Buffer,用来存储多通道ADC采样的转换结果,但是这个Buffer也是8位的,所以从ADC的数据寄存器到Buffer需要Copy两次,并且STM8不保证该数据Copy的原子性。

所以直接读取buffer(ADC1_GetBufferValue())的话是不安全的,容易发生字节错位。

安全的做法是 单次扫描


/* Signal Scan */
void adc_init()
{
  /* Input floating, no external interrupt */
  GPIOD->DDR &= (uint8_t)(~(GPIO_PIN_2));
  GPIOD->CR1 &= (uint8_t)(~(GPIO_PIN_2));
  GPIOD->CR2 &= (uint8_t)(~(GPIO_PIN_2));

  GPIOC->DDR &= (uint8_t)(~(GPIO_PIN_4));
  GPIOC->CR1 &= (uint8_t)(~(GPIO_PIN_4));
  GPIOC->CR2 &= (uint8_t)(~(GPIO_PIN_4));

  /* Clear the align bit */
  ADC1->CR2 &= (uint8_t)(~ADC1_CR2_ALIGN);
  /* Configure the data alignment */
  ADC1->CR2 |= (uint8_t)(ADC1_ALIGN_RIGHT);

  /* Clear the ADC1 channels */
  ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
  /* Select the ADC1 channel */
  ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_3); // from AIN0 ~ AIN3

  /* Clear the SPSEL bits */
  ADC1->CR1 &= (uint8_t)(~ADC1_CR1_SPSEL);
  /* Select the prescaler division factor according to ADC1_PrescalerSelection values */
  ADC1->CR1 |= (uint8_t)(ADC1_PRESSEL_FCPU_D2);

  // DISABLE
  ADC1->TDRL |= (uint8_t)((uint8_t)0x01 << (uint8_t)ADC1_SCHMITTTRIG_CHANNEL2);
  ADC1->TDRL |= (uint8_t)((uint8_t)0x01 << (uint8_t)ADC1_SCHMITTTRIG_CHANNEL3);

   /* Enable the ADC1 peripheral */
  ADC1->CR1 |= ADC1_CR1_ADON;

  /* Enables the ADC1 scan mode */
  ADC1->CR2 |= ADC1_CR2_SCAN;

  /* Enables the ADC1 data store into the Data Buffer registers rather than in the Data Register */
  ADC1->CR3 |= ADC1_CR3_DBUF;
}

uint16_t get_adc_value()
{
    /* Start ADC1 conversion */
    ADC1->CR1 |= ADC1_CR1_ADON;

    /* Wait for conversion finish */
    while((ADC1->CSR & ADC1_FLAG_EOC)==0);
    /* clear the flag */
    ADC1->CSR &= (uint8_t)~(ADC1_FLAG_EOC);

    // read current ADC1_CH3
    raw_current_adc = (uint16_t)(*((uint8_t*)0x53E7) | (uint16_t)(*((uint8_t*)0x53E6) << (uint8_t)8));

    // read position ADC1_CH2
    return (uint16_t)(*((uint8_t*)0x53E5) | (uint16_t)(*((uint8_t*)0x53E4) << (uint8_t)8));
}