STM32 Часть 9–Аналого-цифровой преобразователь (АЦП)

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

Сегодня по плану АЦП.

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

Модуль АЦП

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

  • разрешение 12-бит
  • минимальное время преобразования 1,17 мкс (при частоте системной шины 24 МГц)
  • встроенный ИОН и датчик температры
  • встроенный механизм самокалибровки
  • режимы однократного и циклического преобразования
  • режим “сканирования” для последовательного преобразования нескольких каналов
  • время преобразования устанавливается индивидуально для каждого канала
  • инициировать преобразования возможно от внешних или внутренних сигналов
  • и  это ещё не всё ;) читать подробнее в документции к МК

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

image

И так приступим.

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

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

Максимальная тактовая частота для модуля АЦП составляет 12 МГц, при частоте системной шины 24 МГц, на данном этапе выжимать по полной нет необходимости, поэтому продолжим работать от встроенного RC генератора частотой 8 МГц, при этом максимальная частота модуля может быть 4 Мгц, так как предварительный делитель имеет минимальный коэффициент деления двойку:

image

Установим предварительный делитель и разрешим тактирование модуля:

RCC->CFGR &= ~RCC_CFGR_ADCPRE;
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

Примечание: ранее я не сделал акцент на одном моменте, как только вы разрешаете тактирование того или иного блока микроконтроллера, путем установки соответствующего бита в блоке управлением сбросом и тактированием RCC, то все регистры выбранного блока автоматически инициализируются значениями по умолчанию  (если не ошибаюсь это касается всех модулей МК).

Из всех режимов работы наиболее простой режим однократного преобразования одного канала, с программным запуском. Вначале необходимо выбрать канал АЦП, мне понравился десятый канал и сконфигурировать линию ввода-вывода (вывод PC0):

#define ADC_INPUT10		C, 0, HIGH, ANALOG,

PIN_CONFIGURATION(ADC_INPUT10);

Модуль позволяет производить последовательное преобразование до 16 каналов, порядок каналов может быть любой, нам необходимо установить преобразование одного десятого канала. Для этого устанавливаем длину последовательности преобразований равной единице (значение после сброса АЦП) и десятый номер канала в первую ячейку (не уверен в правильности терминологии, так как английский знаю на три с минусом):

ADC1->SQR3 = ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_3;

На каждый канал выделено пяти битное поле, соответственно для 16 каналов необходимо три регистра (SQR1-SQR3), ячейка для первого преобразования занимает с 0 по 4 бит в регистре SQR3, соответственно для десятого канала необходимо установить 1 и 3 бит (ADC_SQR3_SQ1_1, ADC_SQR3_SQ1_3).

Далее необходимо выбрать источник запуска, нам нужен программный запуск:

ADC1->CR2 = ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTTRIG;

Ну вот теперь можно вывести АЦП из режима пониженного энергопотребления:

ADC1->CR2 |= ADC_CR2_ADON;

произвести калибровку АЦП:

ADC1->CR2 |= ADC_CR2_RSTCAL;
while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_RSTCAL)
{
}

ADC1->CR2 |= ADC_CR2_CAL;
while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL)
{
}

и всё ;) можно пользоваться.

Функция целиком:

void mcu_adc_init(void)
{
	RCC->CFGR &= ~RCC_CFGR_ADCPRE;
	RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2;
	RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

	ADC1->SQR3 = ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_3;
	ADC1->CR2 = ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2
			| ADC_CR2_EXTTRIG;
	ADC1->CR2 |= ADC_CR2_ADON;

	ADC1->CR2 |= ADC_CR2_RSTCAL;
	while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_RSTCAL)
	{
	}

	ADC1->CR2 |= ADC_CR2_CAL;
	while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL)
	{
	}
}

Для примера я использовал прерывания от таймера 17 (описан ранее), точнее прописал в обработчике: запуск – ожидание - считывание результата:

void mcu_adc_channel_10_convert(void)
{
	ADC1->CR2 |= ADC_CR2_SWSTART;
	while ((ADC1->SR & ADC_SR_EOC) != ADC_SR_EOC)
	{

	}
	mcu_adc_channel_10_value = ADC1->DR;
}

Результат преобразования вывожу на ЖКИ индикатор, ну и для тех у кого нет индикатора меняю частоту “мигания” зеленого светодиода в зависимости от результата преобразования:

void handler_tim1_trigger_and_communication_and_tim17(void)
{
	if (TIM17->SR & TIM_SR_UIF)
	{
		TIM17->SR &= ~TIM_SR_UIF;

		mcu_adc_channel_10_convert();

		if (test_count == 0)
		{
			PIN_ON(LED_GREEN);
		}
		else if (test_count == (1 + (mcu_adc_channel_10_value >> 4)))
		{
			PIN_OFF(LED_GREEN);
		}

		if (++test_count >= ((1 + (mcu_adc_channel_10_value >> 4)) * 2))
			test_count = 0;
	}
}

Напряжение на вход АЦП подаю используя обычный делитель напряжения выполненный на переменном резисторе:

image

Резистор R2 необходим для защиты линии ввода-вывода в случае неправильной конфигурации.

Небольшое видео, сори за тряску ;)

Всю необходимую информацию вы можете найти на офф стайте:

STM32F100RB - STM32 Value Line 32-bit Microcontrollers

Проект с примером:

[download id=”30”]



comments powered by Disqus