7. STM32. Програмування STM32F103. ADC


22.08.2016

ADC (Analog-to-Dogital Converter) - Аналого-цифровий перетворювач (далі АЦП). АЦП конвертує аналоговий сигнал у цифровий код. Такий собі вольтметр, який ми сьогодні заставимо працювати у декількох режимах, у тому числі із застосуванням DMA. Мікроконтролери можуть мати декілька АЦП. Конкретно STM32F103C8 має 2 АЦП. АЦП може обробляти декілька каналів (до 18). Канал - це зовнішній сигнал, який може бути заведений на одну з ніг мікроконтроллера, або внутрішній канал, наприклад вбудований термометр. Аналоговий сигнал можна подавати на ноги, які мають маркування ADC12_INn. Де n - номер каналу. Наприклад, ADC12_IN1.

На нашій платі доступні 10 зовнішніх каналів, позначені ADC12_IN0 ... ADC12_IN9, та два внутрішніх - термометр і опорна напруга.

STM32F103C8

Живлення АЦП

Почнемо зі схеми включення самого АЦП. Аналого-цифровий перетворювач має окремий вхід живлення (пін Vdda). Для чіткого перетворення треба, щоб живлення АЦП було стабільним. Живлення самого мікроконтролера досить зашумлене, тому краще подавати живлення на Vssa - Vdda окремо. Бажано використовувати два паралельно включених фільтруючих конденсатора - електролітичний ємністю 1мкФ, та керамічний 10нФ. На схемі вони позначені одним як 1μF //10 nF.

STM32 ADC Supply

У 100-ногих корпусах є виводи для подачі опорної напруги Vref- Vref+. На них треба подавати опорну напругу як рекомендовано у документації.

STM32 ADC Vref

Для вирішення виняткових задач на Vref- Vref+ може подаватися опорна напруга, відмінна від напруги живлення АЦП. Та вона не може бути вища напруги живлення АЦП (Vdda). А Vdda, у свою чергу, не може бути вища за 3.6В.

У нашому випадку STM32F103C8T6 не має таких виводів. Взагалі, на нашій тестовій платі нам нічого не доведеться добавляти. Вся обв’язка вже встановлена. Але, коли Вам доведеться розробляти власну плату, Ви згадаєте, що для використання АЦП треба зазирнути у документацію і виконати рекомендації до конкретного мікроконтролера.

Параметри ADC

Перш ніж перейти до прикладу, коротенько розглянемо можливості аналогово-цифрових конверторів мікроконтролерів STM32. Наведена нижче інформація загальна для усіх STM32F10x.

У нашому мікроконтролері STM32F103C8:

  • 2 шт. 12-бітних ADC. Кожен з них може обслуговувати декілька каналів до 16 зовнішніх (фізично доступні 10) і 2 внутрішніх.
  • різні режими перетворення:
    • одноразове
    • безперервне
    • по тригеру
    • за таймером
  • вирівнювання бітів результату (праворуч/ліворуч)
  • генерування переривань і сигналів для DMA
  • швидкість оцифровки - до 0.9 MSPS
  • автокалібровка
  • режим сканування входів за списком
  • аналоговий сторож (watchdog)
Канали АЦП мікроконтролерів STM32 поділяються на дві групи: регулярні канали (regular) і інжектовані (injected). Кількість регулярних каналів для одного АЦП дорівнює 18, серед них 16 зовнішніх і два внутрішніх (опорна напруга і температурний датчик). Результати вимірювань регулярних каналів зберігаються у одному регістрі і потребують збереження результатів у пам`яті мікроконтролера, а інжектовані канали (до 4) мають власні регістри для зберігання результату.

Існує можливість налаштовувати роботу каналів АЦП у довільному порядку, кілька разів поспіль обробляти обрані канали, використовувати зовнішні та програмні події для старту конвертації.

АПЦ можуть працювати як в одиночному, так і в парному режимі. Два АЦП з різними конфігураціями регулярних і інжектованих каналів. У загальному випадку можливі два варіанти: незалежна і парна робота. Для парної роботи у мікроконтролері мають бути 2 або більше АЦП. АЦП також можна налаштувати на роботу у якості аналогового сторожа (watchdog), тобто задаються верхній і нижній пороги вхідного сигналу. АЦП відстежує рівень сигналу і, коли той виходить за означені межі, генерується переривання. Налаштовується час перетворення для кожного каналу. Всього можна задати вісім значень часу для кожного каналу в діапазоні 1,5 ... 239,5 циклів тактування модуля АЦП.

Структура АЦП

STM32 ADC

Незалежна робота АЦП

Мається на увазі незалежна робота кожного з АЦП. Тобто коли АЦП працює незалежно від іншого. АЦП можуть бути налаштовані на різні режими.

1. Single-channel (Одноканальний) АЦП виконує одне перетворення одного каналу, записує отримане значення у вихідний регістр і зупиняється.

2. Single continuous (Одноканальний тривалий) Цей режим аналогічний першому, але АЦП не зупиняється, а продовжує роботу з обраним каналом. При цьому результат постійно перезаписується у вихідному регістрі.

3. Scan (Багатоканальний) У цьому режимі можливо конфігурувати АЦП для виконання послідовних перетворень декількох каналів у заданій послідовності. Час перетворення також налаштовується окремо для кожного каналу. Після обробки зазначеного числа каналів АЦП зупиняється.

4. Scan continuous (Багатоканальний тривалий) Те ж саме що і Scan, тільки АЦП не зупиняється після опитування всіх каналів, а знову починає обробку каналів. Як і у режимі Single continuous, усі результати складаються в один регістр і треба вчасно забирати дані, поки вони не затерті даними з обробки наступного каналу. Найкращий метод - задіяти для цього DMA. Саме цей спосіб ми розглянемо в одному з прикладів.

5. Discontinuous (Переривистий) Особливість цього режиму в тому, що будуть скановані не всі канали за раз, а лише деякі, заздалегідь встановлені. Наступного разу при настанні події, що запускає перетворення, буде просканована наступна група каналів і так далі.

Парна робота

Парна робота можлива коли у мікроконтролері є два або більше АЦП. Парна робота використовується перш за все для підвищення швидкості перетворень.

1. Regular / Injected simultaneous (Одночасний для Regular і Injected каналів) Перший АЦП буде сканувати канали починаючи з 0 до 15 для регулярних каналів, або з 0 до 3 для injected каналів. Другий - у зворотному напрямку, з 15 до 0 або з 3 до 0 для injected каналів. Таким чином, кожен канал за той же період часу буде оброблений двічі.

2. Fast interleaved (Швидкий поперемінний) Доступний тільки для одного обраного регулярного каналу. При запуску перетворення АЦП2 стартує перший, а АЦП1 - із затримкою в сім тактів. Якщо встановлений режим continuous, тоді така послідовність буде повторюватися далі, що дає можливість подвоїти частоту виміру вхідного сигналу.

3. Slow interleaved (Повільний поперемінний) Все як в попередньому режимі, тільки АЦП1 стартує з затримкою в 14 тактів, а після закінчення першого перетворення АЦП2 теж витримає затримку 14 тактів перед повторним стартом.

4. Alternate trigger (Альтернативний по тригеру) Доступний тільки для інжектованих каналів. АЦП1 починає послідовно конвертувати всі інжектовані канали. Коли стається наступна подія що запускає конвертацію, АЦП2 починає робити те ж саме. Якщо встановлений режим discontinuous, то буде перетворений тільки один канал на кожну подію.

5. Combined regular / injected simultaneous mode (Комбінований одночасний режим для регулярних / інжектованих каналів) Відбувається перетворення обох груп каналів. Але injected група каналів може перервати обробку групи регулярних каналів. Тобто, якщо станеться подія, що запускає конвертування injected каналів, а в цей час АЦП займається обробкою regular каналів, то АЦП переключиться на обробку injected каналів.

6. Combined regular simultaneous + alternate trigger mode Регулярна група каналів може бути перервана почерговим запуском інжектованих каналів. При завершенні перетворення групи зупиняються всі інші перетворення обох груп, і результати обробки зберігаються в регістрах даних кожного АЦП.

7. Combined regular simultaneous + alternate trigger mode Перетворення регулярних каналів здійснюється зі зміщенням по часу і відбувається одночасне перетворення двох груп injected каналів

Насправді, я сам тільки у загальних формах розумію парну роботу АЦП. Розберемося пізніше, коли буде потреба. А зараз перейдемо до прикладів з одним АЦП.

Приклад №1 ADC режим Scan continuous

Для першого знайомства ми використаємо режим Single continuous. АЦП буде постійно виміряти напругу на одному з каналів. А ми будемо забирати дані коли нам заманеться.

Підключимо до плати потенціометр, як вказано на схемі. Повертаючи ручку на потенціометрі, будемо змінювати напругу на вхідному каналі і спостерігати за показаннями через термінал по порту USART. У цьому прикладі опитуємо канал 1 (ADC12_IN1) (пін A1). Дані перетворення відправляються по USART1.STM32F103C8_ADC


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "misc.h"
 
volatile char buffer[50] = {'\0'};
 
void usart_init(void)
{
    /* Enable USART1 and GPIOA clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
    /* NVIC Configuration */
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    /* Configure the GPIOs */
    //GPIO_Configuration();
    GPIO_InitTypeDef GPIO_InitStructure;
 
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure the USART1 */
    //USART_Configuration();
    USART_InitTypeDef USART_InitStructure;
 
    /* USART1 configuration ------------------------------------------------------*/
    /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
     */
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
 
    /* Enable USART1 */
    USART_Cmd(USART1, ENABLE);
 
    /* Enable the USART1 Receive interrupt: this interrupt is generated when the
        USART1 receive data register is not empty */
    //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
 
}
 
void USARTSend(const unsigned char *pucBuffer, unsigned long ulCount)
{
    //
    // Loop while there are more characters to send.
    //
    while(ulCount--)
    {
        USART_SendData(USART1, *pucBuffer++);// Last Version USART_SendData(USART1,(uint16_t) *pucBuffer++);
        /* Loop until the end of transmission */
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
int main(void)
{
    const unsigned char mytext[] = " Hello World!\r\n";
    int adc_value;
 
    SetSysClockTo72();
 
    //USART1
    usart_init();
    USART_SendData(USART1, '\r');
    USARTSend(mytext, sizeof(mytext));
 
    //ADC
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef  GPIO_InitStructure;
    // input of ADC (it doesn't seem to be needed, as default GPIO state is floating input)
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_1 ;        // that's ADC1 (PA1 on STM32)
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    //clock for ADC (max 14MHz --> 72/6=12MHz)
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);
    // enable ADC system clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
 
    // define ADC config
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // we work in continuous sampling mode
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
 
    ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 1,ADC_SampleTime_28Cycles5); // define regular conversion config
    ADC_Init ( ADC1, &ADC_InitStructure);   //set config of ADC1
 
    // enable ADC
    ADC_Cmd (ADC1,ENABLE);  //enable ADC1
 
    //  ADC calibration (optional, but recommended at power on)
    ADC_ResetCalibration(ADC1); // Reset previous calibration
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1); // Start new calibration (ADC must be off at that time)
    while(ADC_GetCalibrationStatus(ADC1));
 
    // start conversion
    ADC_Cmd (ADC1,ENABLE);  //enable ADC1
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // start conversion (will be endless as we are in continuous mode)
 
    while (1)
    {
        adc_value = ADC_GetConversionValue(ADC1);
        sprintf(buffer, "%d\r\n", adc_value);
        USARTSend(buffer, sizeof(buffer));
    }
}

Приклад №2 ADC & DMA

Якщо нам треба вимірювати напругу на декількох каналах, ми можемо налаштувати АЦП на режим Scan continuous. Після кожного перетворення АЦП буде складати дані у той самий регістр і нам слід встигнути забрати дані і перекласти у пам`ять, поки вони не затруться даними наступного каналу. Звісно, можна налаштувати переривання, яке буде виникати коли АЦП закінчив перетворення. Але STM32 має DMA. Докладніше контролер DMA ми розглянемо у наступній статті. Зараз ми його просто використаємо, щоб збагнути наскільки це крута штука. DMA - це контролер прямого доступу до пам`яті. Ми налаштуємо DMA і АЦП таким чином, щоб дані з 4 каналів АЦП записувались у виділений нами буфер. При цьому ніякі переривання нам не потрібні. DMA буде сам перекладати дані у пам`ять на апаратному рівні. Нам залишається тільки читати дані з буфера, коли нам потрібно.


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_tim.h"
#include "misc.h"
 
volatile char buffer[80] = {'\0'};
 
volatile short FLAG_ECHO = 0;
 
void TIM4_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        FLAG_ECHO = 1;
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    }
}
 
void usart_init(void)
{
        /* Enable USART1 and GPIOA clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
        /* NVIC Configuration */
        NVIC_InitTypeDef NVIC_InitStructure;
        /* Enable the USARTx Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
 
 
        /* Configure the GPIOs */
        GPIO_InitTypeDef GPIO_InitStructure;
 
        /* Configure USART1 Tx (PA.09) as alternate function push-pull */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure USART1 Rx (PA.10) as input floating */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure the USART1 */
        USART_InitTypeDef USART_InitStructure;
 
      /* USART1 configuration ------------------------------------------------------*/
        /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
        */
        USART_InitStructure.USART_BaudRate = 115200;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
        USART_Init(USART1, &USART_InitStructure);
 
        /* Enable USART1 */
        USART_Cmd(USART1, ENABLE);
 
        /* Enable the USART1 Receive interrupt: this interrupt is generated when the
             USART1 receive data register is not empty */
        //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
 
void USARTSend(const unsigned char *pucBuffer)
{
    while (*pucBuffer)
    {
        USART_SendData(USART1, *pucBuffer++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
//=================================================================================
volatile uint16_t ADCBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA};
 
void ADC_DMA_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
 
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    /* Enable ADC1 and GPIOA clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE );
 
    DMA_InitStructure.DMA_BufferSize = 4;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCBuffer;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1 , ENABLE ) ;
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_NbrOfChannel = 4;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_7Cycles5);
    ADC_Cmd(ADC1 , ENABLE ) ;
    ADC_DMACmd(ADC1 , ENABLE ) ;
    ADC_ResetCalibration(ADC1);
 
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
 
    while(ADC_GetCalibrationStatus(ADC1));
    ADC_SoftwareStartConvCmd ( ADC1 , ENABLE ) ;
}
//=================================================================================
 
int main(void)
{
    SetSysClockTo72();
 
    const unsigned char mytext[] = " Hello World!\r\n";
 
    //USART
    usart_init();
    USARTSend(mytext);
 
    //ADC
    ADC_DMA_init();
 
    // TIMER4
    TIM_TimeBaseInitTypeDef TIMER_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
 
    TIM_TimeBaseStructInit(&TIMER_InitStructure);
    TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIMER_InitStructure.TIM_Prescaler = 7200;
    TIMER_InitStructure.TIM_Period = 5000;
    TIM_TimeBaseInit(TIM4, &TIMER_InitStructure);
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
 
    /* NVIC Configuration */
    /* Enable the TIM4_IRQn Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    while (1)
    {
        if (FLAG_ECHO == 1) {
            sprintf(buffer, "\r\n%d : %d : %d : %d\r\n", ADCBuffer[0], ADCBuffer[1], ADCBuffer[2], ADCBuffer[3]);
            USARTSend(buffer);
            FLAG_ECHO = 0;
        }
    }
}

Приклад №3 ADC Injected канали

Ми можемо налаштувати 4 Injected канали і дані перетворення будуть складатися не в один вихідний регістр а в окремі регістри для кожного з 4-х Injected каналів. Injected каналів може бути не більше 4, а у випадку з DMA ми можемо використати хоч усі канали.


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "misc.h"
 
volatile char buffer[80] = {'\0'};
 
void usart_init(void)
{
        /* Enable USART1 and GPIOA clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
        /* Configure the GPIOs */
        GPIO_InitTypeDef GPIO_InitStructure;
 
        /* Configure USART1 Tx (PA.09) as alternate function push-pull */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure USART1 Rx (PA.10) as input floating */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure the USART1 */
        USART_InitTypeDef USART_InitStructure;
 
      /* USART1 configuration ------------------------------------------------------*/
        /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
        */
        USART_InitStructure.USART_BaudRate = 115200;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
        USART_Init(USART1, &USART_InitStructure);
 
        /* Enable USART1 */
        USART_Cmd(USART1, ENABLE);
 
        /* Enable the USART1 Receive interrupt: this interrupt is generated when the
             USART1 receive data register is not empty */
        //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
 
void USARTSend(const unsigned char *pucBuffer)
{
    while (*pucBuffer)
    {
        USART_SendData(USART1, *pucBuffer++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
//=================================================================================
void ADC_Injected_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
 
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);
    /* Enable ADC1 and GPIOA clock */
    RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 ,   ENABLE ) ;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_NbrOfChannel = 2;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_Init(ADC1, &ADC_InitStructure);
 
    ADC_InjectedSequencerLengthConfig(ADC1, 2);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_7Cycles5);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_7Cycles5);
 
    ADC_ExternalTrigInjectedConvConfig( ADC1, ADC_ExternalTrigInjecConv_None );
 
    ADC_Cmd ( ADC1 , ENABLE ) ;
 
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
 
    ADC_AutoInjectedConvCmd( ADC1, ENABLE );
    ADC_SoftwareStartInjectedConvCmd ( ADC1 , ENABLE ) ;
}
//=================================================================================
 
int main(void)
{
    uint16_t ADC_value0, ADC_value1;
    SetSysClockTo72();
 
    //USART
    usart_init();
 
    //ADC
    ADC_Injected_init();
 
    while (1)
    {
        ADC_value0 = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
        ADC_value1 = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
        sprintf(buffer, "%d : %d\r\n", ADC_value0, ADC_value1);
        USARTSend(buffer);
    }
}

Приклад №4 ADC Внутрішній термометр

STM32 має вбудований термометр. Це один з внутрішніх каналів АЦП. Далі наведено приклад, як його використовувати.


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "misc.h"
 
volatile char buffer[50] = {'\0'};
 
void usart_init(void)
{
    /* Enable USART1 and GPIOA clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
    /* NVIC Configuration */
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    /* Configure the GPIOs */
    //GPIO_Configuration();
    GPIO_InitTypeDef GPIO_InitStructure;
 
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure the USART1 */
    //USART_Configuration();
    USART_InitTypeDef USART_InitStructure;
 
    /* USART1 configuration ------------------------------------------------------*/
    /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
     */
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
 
    /* Enable USART1 */
    USART_Cmd(USART1, ENABLE);
 
    /* Enable the USART1 Receive interrupt: this interrupt is generated when the
        USART1 receive data register is not empty */
    //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
 
}
 
void USARTSend(const unsigned char *pucBuffer)
{
    while (*pucBuffer)
    {
        USART_SendData(USART1, *pucBuffer++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
int main(void)
{
    const unsigned char mytext[] = " Hello World!\r\n";
    int adc_value;
    int temperature;
 
    const uint16_t V25 = 1750;// when V25=1.41V at ref 3.3V
    const uint16_t Avg_Slope = 5; //when avg_slope=4.3mV/C at ref 3.3V
 
    SetSysClockTo72();
 
    //USART1
    usart_init();
 
    //clock for ADC (max 14MHz --> 72/6=12MHz)
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);
    // enable ADC system clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
 
    //ADC
    ADC_InitTypeDef ADC_InitStructure;
    // define ADC config
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // we work in continuous sampling mode
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1, ADC_SampleTime_239Cycles5); // define regular conversion config
    ADC_Init (ADC1, &ADC_InitStructure);    //set config of ADC1
 
    // Enable Temperature sensor
    ADC_TempSensorVrefintCmd(ENABLE);
 
    // Enable ADC
    ADC_Cmd (ADC1, ENABLE); //enable ADC1
 
    //  ADC calibration (optional, but recommended at power on)
    ADC_ResetCalibration(ADC1); // Reset previous calibration
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1); // Start new calibration (ADC must be off at that time)
    while(ADC_GetCalibrationStatus(ADC1));
 
    // start conversion
    ADC_Cmd (ADC1,ENABLE);  //enable ADC1
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // start conversion (will be endless as we are in continuous mode)
 
    while (1)
    {
        adc_value = ADC_GetConversionValue(ADC1);
        temperature = (uint16_t)((V25-adc_value)/Avg_Slope+25);
        sprintf(buffer, "ADC=%d, T=%d C\r\n", adc_value, temperature);
        USARTSend(buffer);
    }
}

Приклад №5 ADC Аналоговий watchdog

Якщо нам потрібно моніторити який-небудь аналоговий сигнал і виконувати дії, лише коли він виходить за визначені межі, тоді можна налаштувати АЦП на роботу у якості аналогового watchdog. АЦП буде постійно вимірювати напругу на зазначеному каналі і, коли рівень сигналу виходить за межі, - викликає переривання.


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "misc.h"
 
volatile char buffer[50] = {'\0'};
 
volatile char ADC_IT_AWD_FLAG = 0;
 
void usart_init(void)
{
    /* Enable USART1 and GPIOA clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
    /* NVIC Configuration */
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    /* Configure the GPIOs */
    //GPIO_Configuration();
    GPIO_InitTypeDef GPIO_InitStructure;
 
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure the USART1 */
    //USART_Configuration();
    USART_InitTypeDef USART_InitStructure;
 
    /* USART1 configuration ------------------------------------------------------*/
    /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
     */
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
 
    /* Enable USART1 */
    USART_Cmd(USART1, ENABLE);
 
    /* Enable the USART1 Receive interrupt: this interrupt is generated when the
        USART1 receive data register is not empty */
    //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
 
}
 
void USARTSend(const unsigned char *pucBuffer)
{
    while (*pucBuffer)
    {
        USART_SendData(USART1, *pucBuffer++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
void ADC1_2_IRQHandler(void)
{
    if(ADC_GetITStatus(ADC1,ADC_IT_AWD))
    {
        ADC_IT_AWD_FLAG = 1;
        ADC_ClearITPendingBit(ADC1,ADC_IT_AWD);
    }
}
 
 
int main(void)
{
    int adc_value;
 
    SetSysClockTo72();
 
    //USART1
    usart_init();
 
    //clock for ADC (max 14MHz --> 72/6=12MHz)
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);
    // enable ADC system clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
 
    GPIO_InitTypeDef  GPIO_InitStructure;
    // input of ADC (it doesn't seem to be needed, as default GPIO state is floating input)
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 ;        // that's ADC0 (PA1 on STM32)
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    //NVIC
    /* NVIC Configuration */
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    //ADC
    ADC_InitTypeDef ADC_InitStructure;
    // define ADC config
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // we work in continuous sampling mode
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init (ADC1, &ADC_InitStructure);    //set config of ADC1
 
    /* ADC1 regular channel configuration */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5); // define regular conversion config
 
    /* Configure high and low analog watchdog thresholds */
    ADC_AnalogWatchdogThresholdsConfig(ADC1, 3000, 1000);
    /* Configure channel14 as the single analog watchdog guarded channel */
    ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_0);
    /* Enable analog watchdog on one regular channel */
    ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable);
 
    /* Enable AWD interrupt */
    ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);
 
    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);
 
    // Enable ADC
    ADC_Cmd (ADC1, ENABLE); //enable ADC1
 
    //  ADC calibration (optional, but recommended at power on)
    ADC_ResetCalibration(ADC1); // Reset previous calibration
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1); // Start new calibration (ADC must be off at that time)
    while(ADC_GetCalibrationStatus(ADC1));
 
    // start conversion
    ADC_Cmd (ADC1,ENABLE);  //enable ADC1
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // start conversion (will be endless as we are in continuous mode)
 
    while (1)
    {
        if (ADC_IT_AWD_FLAG == 1) {
            adc_value = ADC_GetConversionValue(ADC1);
            sprintf(buffer, "ADC=%d\r\n", adc_value);
            USARTSend(buffer);
            ADC_IT_AWD_FLAG = 0;
        }
    }
}

Бажаю успіхів!

Дивись також:

STM32
Коментарі:
muhittin kaplan говорить:
20.11.2016 15:41
Thanks for all

Vit говорить:
12.01.2017 22:26
Спасибо за Ваши статьи, очень полезные для начинающих в STM32.
        temperature = (uint16_t)((V25-adc_value)/Avg_Slope+25);//-тут не хватает скобки, правильнее так:
 temperature = (uint16_t)((V25-adc_value)/Avg_Slope)+25);

        sprintf(buffer, "ADC=%d, T=%d C\
\
", adc_value, temperature);//-тут некорректно  выводятся отрицательные  значения  температуры, правильнее так:
 sprintf(buffer, "ADC=%d, T=%hhi C\
\
", adc_value, temperature);
С уважением vit.

andre говорить:
13.01.2017 13:05
Уважаемый Vit, по поводу скобок, проверьте еще раз, Вы предлагаете добавить одну лишнюю закрывающую скобку. По поводу отрицательных значений, их не предполагалось в этом коде ибо temperature = (uint16_t)...
Если нужны отрицательные значения исправьте на temperature = ((V25-adc_value)/Avg_Slope+25);
Вы предлагаете заменить %d на %hhi в sprintf. Компилятор не понимет что такое %hhi.

Vit говорить:
13.01.2017 21:24
Спасибо за ответ, с удовольствием читаю Ваши статьи.Насчет скобок я неправ- это же "С".
По поводу форматированного вывода (я использую IAR Embedded Workbench for ARM) отвечает Dlib C++,
компилятор настраивается соответствующим образом и тогда не ругается , в IAR есть Help по использованию.
Вот попалась статья : http://microsin.net/programming/arm/iar-printf-dlib.html .
Спасибо за Ваши труды.

BIKTOP говорить:
07.02.2017 19:05
Велика подяка за вашу цiнну роботу для початкiвця!

Михайло говорить:
30.05.2021 06:27
Доброго дня, andre.
Хочу використати ADC_Channel_Vrefint.
Чи потрібно вмикати це джерело ? (Vrefint). В тому розумінні, як ADC_TempSensorVrefintCmd(ENABLE); 
Чи просто читаємо 17 канал?

Додати коментар
Code
* - обов'язкові поля

Архіви