STM8L-Аналого-Цифровой Преобразователь (АЦП)
Сегодня кратенько о модуле АЦП. Плюс полный проект с примером.
В отличии от STM32, в микроконтроллерах серии STM8L модуль АЦП более простой, но в то же время более “продвинутый”, чем например AVR-ках.
Модуль АЦП Кратенько рассмотрим что же “может” АЦП:
- разрешение 12, 10, 8, 6-бит
- режимы одиночного и циклического преобразования
- возможность подключения к встроенному ИОН и датчику температуры
- время преобразования до 1 мкс, при тактовой частоте МК 16 МГц
- возможность установки времени преобразования
- аналоговый сторожевой триггер (не уверен в правильности интерпретирования “analog watchdog”)
- возможность работы с контроллером DMA
- и это ещё не все, читайте в документации RM0031.
Функциональная схема модуля АЦП:
Для начала работы с АЦП необходимо:
- разрешить тактирование модуля
- сконфигурировать необходимый режим работы
Максимальная частота для модуля АЦП составляет 16 МГц, при частоте системной шины 16 МГц. Я опять пропустил модуль тактирования, так как после сброса микроконтроллер работает от встроенного RC генератора частотой 16 МГц, с коэффициентом деления 8, т.е. на частоте 2 МГц, чего более чем достаточно на данном этапе.
Разрешим тактирование модуля:
CLK->PCKENR2 |= CLK_PCKENR2_ADC1;
Начать я думаю можно с простого режима однократного преобразования одного канала (точнее канал то у нас всегда один, так как физически у нас одно АЦП с мультиплексором).
Определившись с линией ввода-вывода необходимо её сконфигурировать, я выбрал линию PA6:
#define ADC_INPUT A, 6, HIGH, INPUT_FLOATING_WITHOUT_ITERRUPT
PIN_CONFIGURATION(ADC_INPUT);
Однако после этого необходимо отключить триггер Шмитта, в регистре ADC_TRIGR (ADC trigger disable) для соответствующей линии, в моем случае это нулевой вход мультиплексора:
ADC1->TRIGR[4-1] = BIT(0);
Вход выбран и настроен, можно перейти к настройке режима работы АЦП.
По умолчанию разрешена работа АЦП с блоком ПДП (DMA), пока он нам не нужен отключим его:
ADC1->SQR[1-1] = ADC_SQR1_DMAOFF;
Модуль АЦП может последовательно преобразовывать несколько каналов, нам пока нужен лишь один канал:
ADC1->SQR[4-1] = BIT(0);
Зададимся временем преобразования (48 тактов):
ADC1->CR2 = 4;
Осталось только выбрать разрешение (8-бит), режим однократного преобразования и включить АЦП:
ADC1->CR1 = ADC_CR1_ADON | ADC_CR1_RES_8BIT;
Для запуска преобразований достаточно программного запуска:
ADC1->CR1 |= ADC_CR1_START;
После подождать окончания преобразования:
while ((ADC1->SR & ADC_SR_EOC) == 0)
{
}
и считать полученный результат:
adc_result = ADC1->DRL;
Ну вот и всё ;)
Примечание. На форуме попадалось высказывание что АЦП не очень хорошее в связи со встроенным ИОН, и результат “пляшет” очень сильно. У меня результат такой при 6,8,10 битах иногда переключается последний разряд, при 12 битах +-2 единицы. Это без фильтрации 50 Гц, и длинных проводках до переменного резистора (номинал 50 кОм (импортный, в наш ряд не попадает)).
Тестовый проект
На основе рассмотренного выше примера работы с АЦП, я написал маленький пример. Напряжение на вход АЦП подаю с делителя напряжения (аналогично примеру с STM32), результат преобразования вывожу на два индикатора. Подключение индикаторов прописано в файле main.h:
#define LCD_NOKIA_1100_RST D, 3, LOW, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_NOKIA_1100_CS D, 2, LOW, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_NOKIA_1100_SDA D, 1, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_NOKIA_1100_SCLK D, 0, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_RS C, 5, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_E C, 4, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_DB4 C, 3, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_DB5 C, 2, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_DB6 C, 1, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
#define LCD_2X16_DB7 C, 0, HIGH, OUTPUT_PUSH_PULL_SPEED_LIMIT_10MHZ
Задержка реализована простым программным циклом:
void delay_ms(uint16_t time)
{
volatile uint32_t i;
while (time-- > 0)
{
i = 30;
while (i-- > 0)
{
}
}
}
Основной цикл:
int main(void)
{
PIN_CONFIGURATION(ADC_INPUT);
lcd_nokia1100_init();
lcd_nokia1100_set_position(7, 0);
lcd_nokia1100_print_string("ZiBlog.ru @ 2011");
lcd_nokia1100_set_position(1, 1);
lcd_nokia1100_print_string("ADC CH0:");
lcd_2x16_init();
lcd_2x16_set_position(0, 0);
lcd_2x16_print_string("ZiBlog.ru @ 2011");
lcd_2x16_set_position(1, 0);
lcd_2x16_print_string("ADC CH0:");
mcu_adc_init();
while (1)
{
ADC1->CR1 |= ADC_CR1_START;
while ((ADC1->SR & ADC_SR_EOC) == 0)
{
}
lcd_nokia1100_set_position(1, 10);
lcd_nokia1100_print_dec_xxx(ADC1->DRL);
lcd_2x16_set_position(1, 10);
lcd_2x16_print_dec_xxx(ADC1->DRL);
}
return 0;
}
[download id="32"]
comments powered by Disqus