STM8S+SDCC+SPL: использование АЦП в режиме однократного замера - SINGLE MODE

разделы: STM8 , дата: 7 августа 2016г.

В STM8S АЦП может быть двух типов: ADC1 и ADC2. Первый является полноценным АЦП, тогда как второй содержащийся в "скоростной" линейке чипов s207/s208/s007, имеет только два режима работы: разового и непрерывного замера.

STM8S может иметь до 16-и входных каналов, котрые выбираются битами CH[3:0] регистра ADC_CSR. Количество реальных каналов АЦП зависит от конкретной модели чипа.

Частота дискретизации АПЦ может быть установлена c fCPU/2 до fCPU/18.

Модуль АЦП первого типа имеет пять режимов работы: однократный замер - SINGLE MODE, непрерывное измерение - CONTINUOUS MODE, непрерывное измерение с записью результата в буфферные регистры - BUFFERED CONTINUOUS MODE, однократное сканирование - SINGLE SCAN MODE, непрерывное сканирование CONTINUOUS SCAN MODE.

Еще интересной возможность явяется возможность запуска АЦП от триггера таймера TIM1 или внешнего прерывания.

В режимах однократного или непрерывного замера возможно использование аналогового сторожа, когда понижение или повышение уровня сигнала ниже или выше заданного уровня, приводит установке флага AWD в регистре ADC_SCR.

Cейчас мне хотелось бы рассмотреть простейший вариант использования АЦП, режим однократного замера, который можно использовать, когда нужно считать выход с потенциометра или с какого либо аналогового датчика.

Пример программы для чтения второго канала АЦП(PC4) и вывода результата через UART:

#include "stm8s.h"
#include "stm8s_gpio.h"
#include "stm8s_clk.h"
#include "stm8s_adc1.h"
#include "stm8s_tim4.h"
#include "stm8s_uart1.h"
#include "stdio.h"

#define LED_PORT GPIOB
#define LED GPIO_PIN_5

#define TIM4_PERIOD       124

#define PUTCHAR_PROTOTYPE void putchar (char c)

ErrorStatus status = FALSE;
volatile uint16_t count;
volatile uint16_t adc2;

INTERRUPT_HANDLER(IRQ_Handler_TIM4, 23)
{
    if (count)
        count--;

    TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}

INTERRUPT_HANDLER(IRQ_Handled_ADC1,22)
{
    adc2=ADC1_GetConversionValue();

    ADC1_ClearITPendingBit(ADC1_IT_EOC);
}

void delay_ms(uint16_t ms)
{
    TIM4_Cmd(DISABLE);       // stop
    TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD);
    TIM4_ClearFlag(TIM4_FLAG_UPDATE);
    TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);

    count = ms;

    TIM4_Cmd(ENABLE);       // let's go

    while(count);
}


int main( void )
{

    // ----------- GPIO CONFIG -------------------
    GPIO_DeInit(LED_PORT);
    GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();

    CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
    CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // set 16 MHz for CPU

    TIM4_DeInit();
    // uncomment if use HSE on Quartz
    //status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE,
    //                CLK_CURRENTCLOCKSTATE_DISABLE);

    // ------------ ADC1 -------------------
    GPIO_Init(GPIOC,GPIO_PIN_4,GPIO_MODE_IN_FL_NO_IT);
    ADC1_DeInit();
    ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_2, ADC1_PRESSEL_FCPU_D8,
            ADC1_EXTTRIG_TIM,DISABLE, ADC1_ALIGN_RIGHT,  ADC1_SCHMITTTRIG_CHANNEL2, DISABLE);
    ADC1_ITConfig(ADC1_IT_EOCIE ,ENABLE);

    // ------------ UART1 -------------------
    UART1_DeInit();

    // 8N1
    UART1_Init((uint32_t)9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO,
              UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);


    adc2=0;
    enableInterrupts();

    for(;;)
    {
        GPIO_WriteReverse(LED_PORT, LED);
        ADC1_StartConversion();
        delay_ms(500);
        printf("value: %u\n",adc2);
        delay_ms(500);
    }
}


PUTCHAR_PROTOTYPE
{
    // Write a character to the UART1
    UART1_SendData8(c);
    // Loop until the end of transmission 
    while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);
 }

Здесь, вместо использования преывания ADC, можно было ждать в пустом цикле установки флага EOC(конец преобразования или End Of Conversion) в регистре ADC_SCR, но такой цикл не засунешь в прерывание по таймеру, из которого удобно периодически опрашивать аналоговую периферию.

Результат работы программы:

Предлагаю разобраться с тем, что скрывают за собой функции SPL.

Регистры АЦП описываются следующей структурой:

typedef struct ADC1_struct
 {
  __IO uint8_t DB0RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB0RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB1RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB1RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB2RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB2RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB3RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB3RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB4RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB4RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB5RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB5RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB6RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB6RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB7RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB7RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB8RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB8RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB9RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB9RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  uint8_t RESERVED[12];       /*!< Reserved byte */
  __IO uint8_t CSR;           /*!< ADC1 control status register */
  __IO uint8_t CR1;           /*!< ADC1 configuration register 1 */
  __IO uint8_t CR2;           /*!< ADC1 configuration register 2 */
  __IO uint8_t CR3;           /*!< ADC1 configuration register 3  */
  __IO uint8_t DRH;           /*!< ADC1 Data high */
  __IO uint8_t DRL;           /*!< ADC1 Data low */
  __IO uint8_t TDRH;          /*!< ADC1 Schmitt trigger disable register high */
  __IO uint8_t TDRL;          /*!< ADC1 Schmitt trigger disable register low */
  __IO uint8_t HTRH;          /*!< ADC1 high threshold register High*/
  __IO uint8_t HTRL;          /*!< ADC1 high threshold register Low*/
  __IO uint8_t LTRH;          /*!< ADC1 low threshold register high */
  __IO uint8_t LTRL;          /*!< ADC1 low threshold register low */
  __IO uint8_t AWSRH;         /*!< ADC1 watchdog status register high */
  __IO uint8_t AWSRL;         /*!< ADC1 watchdog status register low */
  __IO uint8_t AWCRH;         /*!< ADC1 watchdog control register high */
  __IO uint8_t AWCRL;         /*!< ADC1 watchdog control register low */
 }
 ADC1_TypeDef;

Регистров много, но они разбиты на несколько простых групп. Первая группа - это десять 16-разрядных буфферных регистров. СRx и CSR - это управляющие регистры. DRx - регистр данных. Сюда заносится результат АЦ-преобразования. TDRx - регистры для отключения или включения триггеров Шмитта. Обычно их следует отключать. Остальные регистры относятся к "аналоговой собаке", т.е. к аналоговому сторожу. HTRx - задает верхний порог срабатывания, LTRx - задает нижний порог срабатывания. AWSRx - флаговые регистры сторожа, а AWCRx - управляющие регистры .

При инициализации в эти регистры записываются следующие значения:

 #define  ADC1_CSR_RESET_VALUE    ((uint8_t)0x00)
 #define  ADC1_CR1_RESET_VALUE    ((uint8_t)0x00)
 #define  ADC1_CR2_RESET_VALUE    ((uint8_t)0x00)
 #define  ADC1_CR3_RESET_VALUE    ((uint8_t)0x00)
 #define  ADC1_TDRL_RESET_VALUE   ((uint8_t)0x00)
 #define  ADC1_TDRH_RESET_VALUE   ((uint8_t)0x00)
 #define  ADC1_HTRL_RESET_VALUE   ((uint8_t)0x03)
 #define  ADC1_HTRH_RESET_VALUE   ((uint8_t)0xFF)
 #define  ADC1_LTRH_RESET_VALUE   ((uint8_t)0x00)
 #define  ADC1_LTRL_RESET_VALUE   ((uint8_t)0x00)
 #define  ADC1_AWCRH_RESET_VALUE  ((uint8_t)0x00)
 #define  ADC1_AWCRL_RESET_VALUE  ((uint8_t)0x00) 

API модуля ADC1 в SPL задается следующим набором функций:

void ADC1_DeInit(void);
void ADC1_Init(ADC1_ConvMode_TypeDef ADC1_ConversionMode,
               ADC1_Channel_TypeDef ADC1_Channel,
               ADC1_PresSel_TypeDef ADC1_PrescalerSelection,
               ADC1_ExtTrig_TypeDef ADC1_ExtTrigger,
               FunctionalState ADC1_ExtTriggerState, ADC1_Align_TypeDef ADC1_Align,
               ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel,
               FunctionalState ADC1_SchmittTriggerState);
void ADC1_Cmd(FunctionalState NewState);
void ADC1_ScanModeCmd(FunctionalState NewState);
void ADC1_DataBufferCmd(FunctionalState NewState);
void ADC1_ITConfig(ADC1_IT_TypeDef ADC1_IT, FunctionalState NewState);
void ADC1_PrescalerConfig(ADC1_PresSel_TypeDef ADC1_Prescaler);
void ADC1_SchmittTriggerConfig(ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel,
                              FunctionalState NewState);
void ADC1_ConversionConfig(ADC1_ConvMode_TypeDef ADC1_ConversionMode,
                           ADC1_Channel_TypeDef ADC1_Channel,
                           ADC1_Align_TypeDef ADC1_Align);
void ADC1_ExternalTriggerConfig(ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, FunctionalState NewState);
void ADC1_AWDChannelConfig(ADC1_Channel_TypeDef Channel, FunctionalState NewState);
void ADC1_StartConversion(void);
uint16_t ADC1_GetConversionValue(void);
void ADC1_SetHighThreshold(uint16_t Threshold);
void ADC1_SetLowThreshold(uint16_t Threshold);
uint16_t ADC1_GetBufferValue(uint8_t Buffer);
FlagStatus ADC1_GetAWDChannelStatus(ADC1_Channel_TypeDef Channel);
FlagStatus ADC1_GetFlagStatus(ADC1_Flag_TypeDef Flag);
void ADC1_ClearFlag(ADC1_Flag_TypeDef Flag);
ITStatus ADC1_GetITStatus(ADC1_IT_TypeDef ITPendingBit);
void ADC1_ClearITPendingBit(ADC1_IT_TypeDef ITPendingBit);

Нас больше всего интересует ADC1_Init(), которая на проверку оказывается не более чем агрегатором:

void ADC1_Init(ADC1_ConvMode_TypeDef ADC1_ConversionMode, ADC1_Channel_TypeDef ADC1_Channel, ADC1_PresSel_TypeDef ADC1_PrescalerSelection, ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, FunctionalState ADC1_ExtTriggerState, ADC1_Align_TypeDef ADC1_Align, ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState ADC1_SchmittTriggerState)
{
  /*-----------------CR1 & CSR configuration --------------------*/
  /* Configure the conversion mode and the channel to convert
    respectively according to ADC1_ConversionMode & ADC1_Channel values  &  ADC1_Align values */
  ADC1_ConversionConfig(ADC1_ConversionMode, ADC1_Channel, ADC1_Align);
  /* Select the prescaler division factor according to ADC1_PrescalerSelection values */
  ADC1_PrescalerConfig(ADC1_PrescalerSelection);

  /*-----------------CR2 configuration --------------------*/
  /* Configure the external trigger state and event respectively
    according to NewState, ADC1_ExtTrigger */
  ADC1_ExternalTriggerConfig(ADC1_ExtTrigger, ADC1_ExtTriggerState);

  /*------------------TDR configuration ---------------------------*/
  /* Configure the schmitt trigger channel and state respectively
    according to ADC1_SchmittTriggerChannel & ADC1_SchmittTriggerNewState  values */
  ADC1_SchmittTriggerConfig(ADC1_SchmittTriggerChannel, ADC1_SchmittTriggerState);

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

С помощью ADC1_ConversionConfig(ADC1_ConversionMode, ADC1_Channel, ADC1_Align) выбираем канал, режим и вариант выравнивания:

void ADC1_ConversionConfig(ADC1_ConvMode_TypeDef ADC1_ConversionMode, ADC1_Channel_TypeDef ADC1_Channel, ADC1_Align_TypeDef ADC1_Align)
{
  /* Clear the align bit */
  ADC1->CR2 &= (uint8_t)(~ADC1_CR2_ALIGN);
  /* Configure the data alignment */
  ADC1->CR2 |= (uint8_t)(ADC1_Align);

  if (ADC1_ConversionMode == ADC1_CONVERSIONMODE_CONTINUOUS)
  {
    /* Set the continuous conversion mode */
    ADC1->CR1 |= ADC1_CR1_CONT;
  }
  else /* ADC1_ConversionMode == ADC1_CONVERSIONMODE_SINGLE */
  {
    /* Set the single conversion mode */
    ADC1->CR1 &= (uint8_t)(~ADC1_CR1_CONT);
  }

  /* Clear the ADC1 channels */
  ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
  /* Select the ADC1 channel */
  ADC1->CSR |= (uint8_t)(ADC1_Channel);
}

режимов на выбор всего два, а не пять как можно было бы подумать:

typedef enum
{
  ADC1_CONVERSIONMODE_SINGLE     = (uint8_t)0x00, /**< Single conversion mode */
  ADC1_CONVERSIONMODE_CONTINUOUS = (uint8_t)0x01  /**< Continuous conversion mode */
} ADC1_ConvMode_TypeDef;

Имеющиеся каналы:

typedef enum
{
  ADC1_CHANNEL_0  = (uint8_t)0x00, /**< Analog channel 0 */
  ADC1_CHANNEL_1  = (uint8_t)0x01, /**< Analog channel 1 */
  ADC1_CHANNEL_2  = (uint8_t)0x02, /**< Analog channel 2 */
  ADC1_CHANNEL_3  = (uint8_t)0x03, /**< Analog channel 3 */
  ADC1_CHANNEL_4  = (uint8_t)0x04, /**< Analog channel 4 */
  ADC1_CHANNEL_5  = (uint8_t)0x05, /**< Analog channel 5 */
  ADC1_CHANNEL_6  = (uint8_t)0x06, /**< Analog channel 6 */
  ADC1_CHANNEL_7  = (uint8_t)0x07, /**< Analog channel 7 */
  ADC1_CHANNEL_8  = (uint8_t)0x08, /**< Analog channel 8 */
  ADC1_CHANNEL_9  = (uint8_t)0x09, /**< Analog channel 9 */
  ADC1_CHANNEL_12 = (uint8_t)0x0C /**< Analog channel 12 */
                 /* refer to product datasheet for channel 12 availability */
} 

Варианты выравнивания:

typedef enum
{
  ADC1_ALIGN_LEFT  = (uint8_t)0x00, /**< Data alignment left */
  ADC1_ALIGN_RIGHT = (uint8_t)0x08  /**< Data alignment right */
} ADC1_Align_TypeDef;

Рассмотрим регистры которые затрагиваются в ADC1_ConversionConfig():

Здесь CH[3:0] отвечает за выбор канала. Как видно, выбрать можно только один канал из шестнадцати. EOCIE - бит разрешения прерывания по EOC - завершению преробразования. AWDIE - бит разрешения прерывания при срабатывании аналогового сторожа. AWD - флаг срабатывания аналогового сторожа, EOC флаг завершения АЦ-преобразования.

Прерывание у ADC одно, и если оно разрешено от EOC и AWD, то вначале прерывания по флагам следует определить, какое именно событие его вызвало.

Здесь через SPSEL[2:0] устанавливается частота дискретизации АПЦ. Через бит CONT устанавливается или сбрасывается непрерывный режим работы АЦП. ADON включает или выключает АЦП.

Регистр ADC_CR2:

Здесь через EXTTRIG включается старт АЦП от внешнего триггера, через EXTSEL выбирается внешний триггер, через ALIGN устанавливается режим выравнивания, SCAN включает режим сканирования.

Функция ADC1_PrescalerConfig(ADC1_PresSel_TypeDef ADC1_Prescaler) устанавливает частоту дискретизации:

void ADC1_PrescalerConfig(ADC1_PresSel_TypeDef ADC1_Prescaler)
{
  /* 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_Prescaler);
}

Имеющиеся варианты:

typedef enum
{
  ADC1_PRESSEL_FCPU_D2  = (uint8_t)0x00, /**< Prescaler selection fADC1 = fcpu/2 */
  ADC1_PRESSEL_FCPU_D3  = (uint8_t)0x10, /**< Prescaler selection fADC1 = fcpu/3 */
  ADC1_PRESSEL_FCPU_D4  = (uint8_t)0x20, /**< Prescaler selection fADC1 = fcpu/4 */
  ADC1_PRESSEL_FCPU_D6  = (uint8_t)0x30, /**< Prescaler selection fADC1 = fcpu/6 */
  ADC1_PRESSEL_FCPU_D8  = (uint8_t)0x40, /**< Prescaler selection fADC1 = fcpu/8 */
  ADC1_PRESSEL_FCPU_D10 = (uint8_t)0x50, /**< Prescaler selection fADC1 = fcpu/10 */
  ADC1_PRESSEL_FCPU_D12 = (uint8_t)0x60, /**< Prescaler selection fADC1 = fcpu/12 */
  ADC1_PRESSEL_FCPU_D18 = (uint8_t)0x70  /**< Prescaler selection fADC1 = fcpu/18 */
} ADC1_PresSel_TypeDef;

Функция ADC1_ExternalTriggerConfig(ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, FunctionalState NewState) управляет внешними триггерами. В нашем случае он выключен.

void ADC1_ExternalTriggerConfig(ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, FunctionalState NewState)
{
  /* Clear the external trigger selection bits */
  ADC1->CR2 &= (uint8_t)(~ADC1_CR2_EXTSEL);

  if (NewState != DISABLE)
  {
    /* Enable the selected external Trigger */
    ADC1->CR2 |= (uint8_t)(ADC1_CR2_EXTTRIG);
  }
  else /* NewState == DISABLE */
  {
    /* Disable the selected external trigger */
    ADC1->CR2 &= (uint8_t)(~ADC1_CR2_EXTTRIG);
  }

  /* Set the selected external trigger */
  ADC1->CR2 |= (uint8_t)(ADC1_ExtTrigger);
}

Функция ADC1_SchmittTriggerConfig(ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState NewState) отключает триггеры Шмитта(они полезны когда канал работает как цифровой вход и мешают когда канал переключают на аналоговый режим). В нашем случае они тоже отключаются:

void ADC1_SchmittTriggerConfig(ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState NewState)
{
  if (ADC1_SchmittTriggerChannel == ADC1_SCHMITTTRIG_ALL)
  {
    if (NewState != DISABLE)
    {
      ADC1->TDRL &= (uint8_t)0x0;
      ADC1->TDRH &= (uint8_t)0x0;
    }
    else /* NewState == DISABLE */
    {
      ADC1->TDRL |= (uint8_t)0xFF;
      ADC1->TDRH |= (uint8_t)0xFF;
    }
  }
  else if (ADC1_SchmittTriggerChannel < ADC1_SCHMITTTRIG_CHANNEL8)
  {
    if (NewState != DISABLE)
    {
      ADC1->TDRL &= (uint8_t)(~(uint8_t)((uint8_t)0x01 << (uint8_t)ADC1_SchmittTriggerChannel));
    }
    else /* NewState == DISABLE */
    {
      ADC1->TDRL |= (uint8_t)((uint8_t)0x01 << (uint8_t)ADC1_SchmittTriggerChannel);
    }
  }
  else /* ADC1_SchmittTriggerChannel >= ADC1_SCHMITTTRIG_CHANNEL8 */
  {
    if (NewState != DISABLE)
    {
      ADC1->TDRH &= (uint8_t)(~(uint8_t)((uint8_t)0x01 << ((uint8_t)ADC1_SchmittTriggerChannel - (uint8_t)8)));
    }
    else /* NewState == DISABLE */
    {
      ADC1->TDRH |= (uint8_t)((uint8_t)0x01 << ((uint8_t)ADC1_SchmittTriggerChannel - (uint8_t)8));
    }
  }
}

Варианты:

typedef enum
{
  ADC1_SCHMITTTRIG_CHANNEL0  = (uint8_t)0x00, /**< Schmitt trigger disable on AIN0 */
  ADC1_SCHMITTTRIG_CHANNEL1  = (uint8_t)0x01, /**< Schmitt trigger disable on AIN1 */
  ADC1_SCHMITTTRIG_CHANNEL2  = (uint8_t)0x02, /**< Schmitt trigger disable on AIN2 */
  ADC1_SCHMITTTRIG_CHANNEL3  = (uint8_t)0x03, /**< Schmitt trigger disable on AIN3 */
  ADC1_SCHMITTTRIG_CHANNEL4  = (uint8_t)0x04, /**< Schmitt trigger disable on AIN4 */
  ADC1_SCHMITTTRIG_CHANNEL5  = (uint8_t)0x05, /**< Schmitt trigger disable on AIN5 */
  ADC1_SCHMITTTRIG_CHANNEL6  = (uint8_t)0x06, /**< Schmitt trigger disable on AIN6 */
  ADC1_SCHMITTTRIG_CHANNEL7  = (uint8_t)0x07, /**< Schmitt trigger disable on AIN7 */
  ADC1_SCHMITTTRIG_CHANNEL8  = (uint8_t)0x08, /**< Schmitt trigger disable on AIN8 */
  ADC1_SCHMITTTRIG_CHANNEL9  = (uint8_t)0x09, /**< Schmitt trigger disable on AIN9 */
  ADC1_SCHMITTTRIG_CHANNEL12 = (uint8_t)0x0C, /**< Schmitt trigger disable on AIN12 */
                                              /* refer to product datasheet for channel 12 availability */
  ADC1_SCHMITTTRIG_ALL       = (uint8_t)0xFF /**< Schmitt trigger disable on All channels */
} ADC1_SchmittTrigg_TypeDef;

С остальными функциями все гораздо проще. Функция ADC1_GetConversionValue() считывает значение DRx регистров в переменную типа uint16_t:

uint16_t ADC1_GetConversionValue(void)
{
  uint16_t temph = 0;
  uint8_t templ = 0;

  if ((ADC1->CR2 & ADC1_CR2_ALIGN) != 0) /* Right alignment */
  {
    /* Read LSB first */
    templ = ADC1->DRL;
    /* Then read MSB */
    temph = ADC1->DRH;

    temph = (uint16_t)(templ | (uint16_t)(temph << (uint8_t)8));
  }
  else /* Left alignment */
  {
    /* Read MSB first*/
    temph = ADC1->DRH;
    /* Then read LSB */
    templ = ADC1->DRL;

    temph = (uint16_t)((uint16_t)((uint16_t)templ << 6) | (uint16_t)((uint16_t)temph << 8));
  }

  return ((uint16_t)temph);
}

Функция ADC1_ITConfig(ADC1_IT_EOCIE ,ENABLE) разрешает или запрещает прерывание. Она может также сбасывать флаги в ADC_CSR:

void ADC1_ITConfig(ADC1_IT_TypeDef ADC1_IT, FunctionalState NewState)
{
  if (NewState != DISABLE)
  {
    /* Enable the ADC1 interrupts */
    ADC1->CSR |= (uint8_t)ADC1_IT;
  }
  else  /* NewState == DISABLE */
  {
    /* Disable the ADC1 interrupts */
    ADC1->CSR &= (uint8_t)((uint16_t)~(uint16_t)ADC1_IT);
  }
}

варианты:

typedef enum
{
  ADC1_IT_AWDIE = (uint16_t)0x010, /**< Analog WDG interrupt enable */
  ADC1_IT_EOCIE = (uint16_t)0x020, /**< EOC interrupt enable */
  ADC1_IT_AWD   = (uint16_t)0x140, /**< Analog WDG status */
  ADC1_IT_AWS0  = (uint16_t)0x110, /**< Analog channel 0 status */
  ADC1_IT_AWS1  = (uint16_t)0x111, /**< Analog channel 1 status */
  ADC1_IT_AWS2  = (uint16_t)0x112, /**< Analog channel 2 status */
  ADC1_IT_AWS3  = (uint16_t)0x113, /**< Analog channel 3 status */
  ADC1_IT_AWS4  = (uint16_t)0x114, /**< Analog channel 4 status */
  ADC1_IT_AWS5  = (uint16_t)0x115, /**< Analog channel 5 status */
  ADC1_IT_AWS6  = (uint16_t)0x116, /**< Analog channel 6 status */
  ADC1_IT_AWS7  = (uint16_t)0x117, /**< Analog channel 7 status */
  ADC1_IT_AWS8  = (uint16_t)0x118, /**< Analog channel 8 status */
  ADC1_IT_AWS9  = (uint16_t)0x119, /**< Analog channel 9 status */
  ADC1_IT_AWS12 = (uint16_t)0x11C, /**< Analog channel 12 status */
                                   /* refer to product datasheet for channel 12 availability */
  ADC1_IT_EOC   = (uint16_t)0x080  /**< EOC pending bit */

} ADC1_IT_TypeDef;

В конце обработчика прерывания следует очищать бит события вызвавшего прерывания функцией ADC1_ClearITPendingBit(ADC1_IT_TypeDef ITPendingBit):

void ADC1_ClearITPendingBit(ADC1_IT_TypeDef ITPendingBit)
{
  uint8_t temp = 0;

  /* Check the parameters */
  assert_param(IS_ADC1_ITPENDINGBIT_OK(ITPendingBit));

  if (((uint16_t)ITPendingBit & 0xF0) == 0x10)
  {
    /* Clear analog watchdog channel status */
    temp = (uint8_t)((uint16_t)ITPendingBit & 0x0F);
    if (temp < 8)
    {
      ADC1->AWSRL &= (uint8_t)~(uint8_t)((uint8_t)1 << temp);
    }
    else
    {
      ADC1->AWSRH &= (uint8_t)~(uint8_t)((uint8_t)1 << (temp - 8));
    }
  }
  else  /* Clear EOC | AWD flag status */
  {
    ADC1->CSR &= (uint8_t)((uint16_t)~(uint16_t)ITPendingBit);
  }
}

Последняя функция ADC1_StartConversion() запускает АЦП:

void ADC1_StartConversion(void)
{
  ADC1->CR1 |= ADC1_CR1_ADON;
}

Это все сложности. Последнее, что хочется заметить, если нужно будет замерить внешний сигнал т.е. сигнал с другого устройства, например напряжение NiMH аккамулятора, то следут "землю" устройства(в случае с аккамулятором "минус") соеденить с "землей" микросхемы, а сигнальную линию(в случае с аккамулятором "плюс") подключить на аналоговый ход. Первый попавшийся мне под руку аккамулятор выдал значение 440. Т.к. напряжение питания чипа 3.3 вольта то напряжение аккамулятора 440*3.3/1024 ~ 1.4 Вольт, что примерно соответствует напряжению холостого хода заряженного аккамулятора. Правда мультиметр мне показал значение 1.3 Вольта. Не следует забывать следить, чтобы входное напряжение сигнала не превышало напряжение питания чипа/АЦП.

Использование АЦП в чипе STM8L051F3 (добавлено 16 сентября 2016г.)

В L-серии микроконтроллеров STM8 АЦП имеет 12 разрядов. Там нет буферных регистров и режимов работы связаных с их использованием, зато есть DMA который делает тоже самое только еще лучше. DMA Пока трогать не буду, предлагаю взглянуть на общую структуру ADC модуля SPL для L-серии.

typedef struct ADC_struct
{
  __IO uint8_t   CR1;      /*!< Control register 1    */
  __IO uint8_t   CR2;      /*!< Control register 2    */
  __IO uint8_t   CR3;      /*!<  Control register 3    */
  __IO uint8_t   SR;       /*!< Status register    */
  __IO uint8_t   DRH;      /*!< Data register MSB    */
  __IO uint8_t   DRL;      /*!< Data register LSB    */
  __IO uint8_t   HTRH;     /*!< High voltage reference register MSB    */
  __IO uint8_t   HTRL;     /*!< High voltage reference register LSB    */
  __IO uint8_t   LTRH;     /*!< Low voltage reference register MSB    */
  __IO uint8_t   LTRL;     /*!< Low voltage reference register LSB    */
  __IO uint8_t   SQR[4];   /*!< Channel select scan registers    */
  __IO uint8_t   TRIGR[4]; /*!< Trigger disable  registers  */
}
ADC_TypeDef;

Интерфейс ADC модуля реализуется следующими функциями:

/*  Function used to set the ADC configuration to the default reset state *****/
void ADC_DeInit(ADC_TypeDef* ADCx);

/* Initialization and Configuration functions *********************************/
void ADC_Init(ADC_TypeDef* ADCx,
              ADC_ConversionMode_TypeDef ADC_ConversionMode,
              ADC_Resolution_TypeDef ADC_Resolution,
              ADC_Prescaler_TypeDef ADC_Prescaler);
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartConv(ADC_TypeDef* ADCx);
void ADC_ExternalTrigConfig(ADC_TypeDef* ADCx,
                            ADC_ExtEventSelection_TypeDef ADC_ExtEventSelection,
                            ADC_ExtTRGSensitivity_TypeDef ADC_ExtTRGSensitivity);

/* Analog Watchdog configuration functions ************************************/
void ADC_AnalogWatchdogChannelSelect(ADC_TypeDef* ADCx,
                                     ADC_AnalogWatchdogSelection_TypeDef ADC_AnalogWatchdogSelection);
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold,
                                        uint16_t LowThreshold);
void ADC_AnalogWatchdogConfig(ADC_TypeDef* ADCx,
                              ADC_AnalogWatchdogSelection_TypeDef ADC_AnalogWatchdogSelection,
                              uint16_t HighThreshold,
                              uint16_t LowThreshold);

/* Temperature Sensor & Vrefint (Voltage Reference internal) management functions */
void ADC_TempSensorCmd(FunctionalState NewState);
void ADC_VrefintCmd(FunctionalState NewState);

/* Channels Configuration functions *******************************************/
void ADC_ChannelCmd(ADC_TypeDef* ADCx, ADC_Channel_TypeDef ADC_Channels,
                    FunctionalState NewState);

void ADC_SamplingTimeConfig(ADC_TypeDef* ADCx, ADC_Group_TypeDef ADC_GroupChannels,
                            ADC_SamplingTime_TypeDef ADC_SamplingTime);
void ADC_SchmittTriggerConfig(ADC_TypeDef* ADCx, ADC_Channel_TypeDef ADC_Channels,
                              FunctionalState NewState);
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);



/* Channels DMA Configuration function ****************************************/
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

/* Interrupts and flags management functions **********************************/
void ADC_ITConfig(ADC_TypeDef* ADCx,
                  ADC_IT_TypeDef ADC_IT,
                  FunctionalState NewState);
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, ADC_FLAG_TypeDef ADC_FLAG);
void ADC_ClearFlag(ADC_TypeDef* ADCx, ADC_FLAG_TypeDef ADC_FLAG);
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, ADC_IT_TypeDef ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, ADC_IT_TypeDef ADC_IT);

Аналогом приведенной выше программы для stm8l051f3 будет такой:

#include "stm8l.h"
#include "stm8l_gpio.h"
#include "stm8l_clk.h"
#include "stm8l_tim4.h"
#include "stm8l_usart.h"
#include "stm8l_adc.h"
#include "stdio.h"

#define PORT GPIOB
#define LED  GPIO_Pin_1
#define TIM4_PERIOD 124

#define PUTCHAR_PROTOTYPE void putchar (char c)

volatile uint16_t count;
volatile uint16_t adc_val;
uint8_t i;

INTERRUPT_HANDLER(IRQ_Handled_ADC1,18)
{
        adc_val=ADC_GetConversionValue(ADC1);

        ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
}

INTERRUPT_HANDLER(IRQ_Handler_TIM4, 25)
{
        if (count)
          count--;

        TIM4_ClearITPendingBit(TIM4_IT_Update);
}

void delay_ms(uint16_t ms)
{
        TIM4_Cmd(DISABLE);       // stop

        TIM4_TimeBaseInit(TIM4_Prescaler_128, TIM4_PERIOD);
        TIM4_ClearFlag(TIM4_FLAG_Update);
        TIM4_ITConfig(TIM4_IT_Update, ENABLE);

        count = ms;

        TIM4_Cmd(ENABLE);       // let's go

        while(count);
}

int main( void )
{
        // ---------   GPIO Config ---------------------------

        GPIO_DeInit(PORT);

        GPIO_Init(PORT, LED, GPIO_Mode_Out_PP_Low_Fast);

        // ---------- CLK Config -----------------------
        CLK_DeInit();
        CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);
        CLK_PeripheralClockConfig(CLK_Peripheral_TIM4, ENABLE);
        CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE);
        CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, ENABLE);
        // ---------- TIM4 Init -----------------------------
        TIM4_DeInit();

        // ---------- ADC1 Init -----------------------------
        ADC_DeInit(ADC1);
        ADC_Init(ADC1, ADC_ConversionMode_Single, ADC_Resolution_12Bit, ADC_Prescaler_2);
        ADC_Cmd(ADC1, ENABLE);
        ADC_ChannelCmd(ADC1, ADC_Channel_18, ENABLE);
        ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

        // ---------- USART Init ---------------------------
        USART_DeInit(USART1);

        USART_Init(USART1, (uint32_t)9600, USART_WordLength_8b, USART_StopBits_1,
                         USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Tx | USART_Mode_Rx));

        enableInterrupts();
        i=0;

        for(;;)
          {
              delay_ms(500);
              GPIO_ToggleBits(PORT, LED);
              adc_val=0;
              ADC_SoftwareStartConv(ADC1);
              delay_ms(500);
              printf("value: %u\n",adc_val);
          }

}

PUTCHAR_PROTOTYPE
{
        /* Write a character to the USART */
        USART_SendData8(USART1, c);
        /* Loop until the end of transmission */
          while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

      //    return (c);
}

Здесь на вход АЦП - B0 подключается выход потенциометра, на B1 - светодиод для индикации, на С5 - Rx канал TTL-конвертера.

Результат выглядит как-то так:

Скачать оригинальный архив с полными исходниками и Makefile'ом можно здесь: скачать

Скачать новый архив с полными исходниками и Makefile'ом можно здесь: скачать

поделиться: