STM8L-Аналого-Цифровой Преобразователь (АЦП)

30 Января 2011 К комментариям

Сегодня кратенько о модуле АЦП. Плюс полный проект с примером.

001

В отличии от STM32, в микроконтроллерах серии STM8L модуль АЦП более простой, но в то же время более “продвинутый”, чем например AVR-ках.

Модуль АЦП Кратенько рассмотрим что же “может” АЦП:

  • разрешение 12, 10, 8, 6-бит
  • режимы одиночного и циклического преобразования
  • возможность подключения к встроенному ИОН и датчику температуры
  • время преобразования до 1 мкс, при тактовой частоте МК 16 МГц
  • возможность установки времени преобразования
  • аналоговый сторожевой триггер (не уверен в правильности интерпретирования “analog watchdog”)
  • возможность работы с контроллером DMA
  • и это ещё не все, читайте в документации RM0031.

Функциональная схема модуля АЦП:

image

Для начала работы с АЦП необходимо:

  • разрешить тактирование модуля
  • сконфигурировать необходимый режим работы

Максимальная частота для модуля АЦП составляет 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) для соответствующей линии, в моем случае это нулевой вход мультиплексора:

image

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