Skip to content

Лабораторная работа 4

Latest

Choose a tag to compare

@Strus05 Strus05 released this 10 Dec 15:44
· 3 commits to main since this release
a4a7725

Аналого-цифровой преобразователь. Изучение режимов работы аналого-цифрового преобразователя

  • Цель работы: изучить режимы работы аналого-цифрового преобразователя

Задачи:

  1. Настройка АЦП: включение; выбор канала АЦП; выбор источника и уровня опорного напряжения; выбор делителя АЦП.
  2. Написать код в Atmel, позволяющий изменять тип выравнивания (левостороннее и правостороннее) и коэффициент усиления;
  3. Вывести на семисегментный индикатор напряжение;
  4. Построить схему в Proteus, проверив в нём работоспособность программы.

Код программы в Atmel Studio

#include <avr/io.h>
volatile float voltage;
volatile int ADC_result;
volatile int koef_usil;
volatile int voltXdot0;
volatile int volt0dotX;

void setup_timer0()	// Задаём таймер для переключения индикаторов
{
	TCCR0B |= (1 << CS00) | (1 << CS01) | (0 << CS02);	// Прескейлер таймера 64. Период таймера 16,3 мс
}

uint8_t digit_codes[] = 
{
	0b00111111, // 0
	0b00000110, // 1
	0b01011011, // 2
	0b01001111, // 3
	0b01100110, // 4
	0b01101101, // 5
	0b01111101, // 6
	0b00000111, // 7
	0b01111111, // 8
	0b01101111  // 9
};

void adc_setup()
{
	ADMUX |= (1<<REFS0);	// Выбор источника опроного напряжения
	ADMUX &= 0xF0;			// Выбор канала ADC0
	ADCSRA |= (1<<ADEN);	// Включение АЦП
	ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);		// Прескейлер АЦП, коэф. деления 128
}

uint16_t adc_read()
{
	ADCSRA |= (1 << ADSC);	// Начало преобразования
	ADCSRA |= (1 << ADATE);	// АЦП автоматически запускает следующее преобразование, после завершение предыдущего
	return ADC;				// adc_read() возвращает результат работы АЦП
}

void change_ADLAR()
{
	if (PIND & (1 << PD7))	// Порт PD7 управляет режимом выравнивания АЦП
	{
		ADMUX &= ~(1 << ADLAR);	// Правостороннее выравнивание
		ADC_result = adc_read();
	}
	else
	{
		ADMUX |= (1 << ADLAR);	// Левостороннее выравнивание
		ADC_result = (adc_read() >> 6);	// Смещаем резуальтат  на 6 разрядов вправо
	}
}

int change_Koef()
{
	if (PIND & (1 << PD6))	// Порт PD6 устанавливает коэффициент усиления
	{
		return koef_usil = 10;
	}
	else
	{
		return koef_usil = 5;
	}
}

int main(void)
{
	adc_setup();		// Устанавливаем параметры АЦП
	setup_timer0();		// Устанавливаем параметры таймера
	DDRB |= 0xFF;		// Порт B - вывод сигнала на индикатор
	DDRD |= (1 << PD1) | (1 << PD2);	// Порт D - выбор индикатора (инвертированный, при 0 на PDX выбран X индикатор)
	while(1)
	{
		change_ADLAR();
		change_Koef();
		voltage = ADC_result * koef_usil / 1023.0;	// Масштабирование сигнала
		voltXdot0 = (int)voltage;				// Единицы вольт
		volt0dotX = (int)(voltage * 10.0) % 10;	// Десятые вольт
		if (TCNT0 < 0x80)		// Половину периода таймера горит 1 индикатор
		{
			PORTD = (1 << PD2);	// Выключаем 2 индикатор
			// Записываем преобразованное для индикатора значение единиц вольт в порт B и добавляем точку
			PORTB = digit_codes[voltXdot0] + (1 << PB7);
		}
		else
		{
			PORTD = (1 << PD1);
			PORTB = digit_codes[volt0dotX];
		}
	}
}

Демонстрация работы схемы в Proteus

Видео без названия1 — сделано в Clipchamp

Порты PB0-PB7 формируют сигналы на семисегментном индикаторе. За выбор активного индикатора отвечают порты PD1 и PD2, подающие сигнал высокого или низкого уровня на порты индикатора 1 и 2. Переключение происходит с частотой 122,7 Гц, что позволяет не замечать мерцание человеческому глазу.

На порт PC0 поступает напряжение с потенциометра RV1, оно преобразуется в 10-битное число, хранимое в 16-разрядном регистре. Формат записи – левосторонняя или правосторонняя, задаётся переключателем DSW (3-2). Коэффициент усиления задаётся переключателем DSW (4-1)