Skip to content

Releases: Strus05/MPSU

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

Choose a tag to compare

@Strus05 Strus05 released this 10 Dec 15:44
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)

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

Choose a tag to compare

@Strus05 Strus05 released this 10 Dec 15:30
a4a7725

Система прерываний. Изучение режимов работы системы прерываний

  • Цель работы: изучить режимы работы системы прерываний.

Задачи:

  1. Настроить систему прерываний на срабатывание INT0 по переднему фронту;
  2. Написать код в Atmel, который при нажатии на кнопку активирует прерывание, выводя случайную цифру на семисегментный индикатор;
  3. Построить схему в Proteus, проверив в нём работоспособность программы.

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

#include <avr/io.h>
#include <avr/interrupt.h>
volatile char n=0;
//Создаём массив, индекс элемента которого равен отображаемому на индикаторе числу, а значение элемента равен сигналу на выводе PB (выводы PB0-PB6 соответствуют ABCDEFG индикатора)
uint8_t digit_codes[] = {
	0b00111111, // 0
	0b00000110, // 1
	0b01011011, // 2
	0b01001111, // 3
	0b01100110, // 4
	0b01101101, // 5
	0b01111101, // 6
	0b00000111, // 7
	0b01111111, // 8
	0b01101111  // 9
};
// При срабатывании прерывания (передний фронт на PD2) port B выдаёт сигнал на индикатор
ISR(INT0_vect)
{
	PORTB=digit_codes[n];

}

int main(void)
{
	DDRB=0xff;		// Порты B будут работать на выводы индикатора
	EICRA|=(1<<ISC00) | (1<<ISC01); //прерывание INT0 будет происходить по переднему фронту
	EIMSK|=(1<<INT0); //разрешение прерывания INT0
	unsigned char i;
	sei(); //разрешение прерываний
	while(1)
	{
		for(i=0;i<10;i++)
		{
			n = i;
		}
	}
}

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

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

В схеме при срабатывании прерывания по переднему фронту INT0 port B подаёт сигналы на семисегментный индикатор.

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

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

Choose a tag to compare

@Strus05 Strus05 released this 13 Nov 03:37
07030d1

Работа с таймерами

  • Цель работы: Изучить режимы работы таймера ATMEGA328P

Задачи:

  1. Напишем программу, в которой при нажатии на кнопку, микроконтроллер формирует выдержку времени и зажигает зеленый светодиод с заданным периодом (0,1с и 1с)
  2. Напишем программу, в которой вывод PB5 при инициализации таймера 0 запрограммирован для выдачи сигнала с заданной частотой (1Гц)
  3. Напишем программу, в которой порт ввода PC0 подключен к потенциометру который регулирует напряжение на нём от 0 до +5В. Сигнал с порта PC0 обрабатывается с помощью АЦП и преобразуется в скважность ШИМ сигнала для порта вывода PB1 которым управляем частотой вращения двигателя.

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

#include <avr/io.h>
#include <avr/interrupt.h>
int scet = 0;
char otschet1s = 0;
char otschet1Gz = 0;

void timer2_init() {
	TCCR2B |= (1 << CS02) | (1 << CS01) | (1 << CS00);		//Задаём прескейлинг 1024. sT = 0,001024 c
	OCR2A = 98;		// OCR2A*sT = период работы диода. Здесь 0,1с
	OCR2B = 244;	// 0,25с
}

void timer0_init() {
	TCCR0A |= (1 << WGM01);    // Режим CTC
	OCR0A = 98;    // Значение при котором сбрасывается таймер (при 1МГц чипе, для 1с OCR1A = 977)
	TCCR0B |= (1 << CS02) | (0 << CS01) | (1 << CS00);		//Задаём прескейлинг 1024
}

void pwm_init_1() {
	TCCR1B |= (1 << WGM12);						// Режим FastPWM 10-bit
	TCCR1A |= (1 << WGM11) | (1 << WGM10);
	TCCR1A |= (1 << COM1A1);	// Clear OC1A/OC1B on compare match
	TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);		//Задаём прескейлинг 1024
}
void setup_adc() {
	ADMUX |= (1 << REFS0); // Voltage Reference - AVCC
	ADCSRA |= (1 << ADEN); // ADC Enable
	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // ADC Prescaler Selections 128
}
uint16_t read_adc() {
	ADMUX &= 0xF0; // Выбрать канал ADC0
	ADCSRA |= (1 << ADSC); // начало преобразования
	while (ADCSRA & (1 << ADSC)); // When the conversion is complete, ADSC returns to zero
	return ADC;			//Результат работы АЦП
}


int main(void)
{
	DDRB |= (1 << PB5);
	DDRB |= (1 << PB4);
    DDRB |= (1 << PB1);
    DDRB |= (1 << PB3);
	DDRC |= (0 << PC0);
	DDRC |= (0 << PC1) | (0 << PC2);
	setup_adc();
    pwm_init_1();
	timer0_init();
	timer2_init();
	while(1)
	{
		if((TIFR2 & (1 << OCF2A)) & (PINC & (1 << PC1)))		// OCF2A устанавливается 1 при достижении таймером OCR2A 
		{
			PORTB ^= (1 << PB3);
			TIFR2 |= (1 << OCF2A);	// Сбрасываем флаг OCF2A
			TCNT2 = 0;				// Сбрасываем таймер2
		}
		else if((TIFR2 & (1 << OCF2B)) & (PINC & (1 << PC2)))		// OCF2B устанавливается 1 при достижении таймером OCR2B 
		{
			TIFR2 |= (1 << OCF2B);
			TCNT2 = 0;				
			otschet1s++;			// Для достижения 1с отсчитываем 4 раза по 0,25с
			if(otschet1s == 4)
			{
				otschet1s = 0;
				PORTB ^= (1 << PB3);
			}
		}
		if(TIFR0 & (1 << OCF0A))	// OCF0A устанавливается 1 при достижении таймером OCR0A 
		{
			TIFR0 |= (1 << OCF0A);
			otschet1Gz++;
			if(otschet1Gz==10)		// Для достижения нужной частоты otschet1Gz == 1/(0.1f)
			{
				otschet1Gz = 0;
				PORTB ^= (1 << PB5);
			}
		}
        OCR1A = read_adc(); // Прочитать значение с ADC
	}
}

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

Видео

Сигнал ШИМ при выкрученным на 75% потенциометром (75% скважность)

image

Таймером 2 (Нормальный режим) управляем Кнопками 1 и 2, подавая сигнал на PC1 и PC2. Полученный сигнал с PB3 подаётся на базу биполярного npn транзистора, светодиод подключен к коллектору.

Таймер 0 (Clear Timer on Compare ) задаёт частоту переключения порта PB5, сигнал с которого подаётся на базу биполярного npn транзистора. К эмиттеру подключено реле, которое подключает лампу к источнику 12В, когда по нему протекает ток.

Таймер 1 (Fast PWM) получает значение скважности (OCR1A) от АЦП порта PC0, сигнал на котором регулируется через потенциометр RV1. ШИМ сигнал, формируемый на выводе PB1, подаётся через оптопару на затвор mosfet, который управляет напряжением на двигателе.

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

Choose a tag to compare

@Strus05 Strus05 released this 01 Oct 12:40
55c9235

Авторы:

Астафьев Илья,
Никитин Даниил,
Ульянчик Руслан

Содержание

Работа с Atmel Studio

  • Цель работы: Создать логический элемент 3ИЛИ
  • Программирование микроконтроллера: Реализуем функцию вывода сигнала на вывод PC0 порта PORTC микроконтроллера ATmega328P при наличии определённого состояния на входах PB0, PB1 и PB2 порта PINB.

Строка DDRC |= (1<<PC0);Устанавливает вывод PC0 в режим выхода.
Условие if ((PINB & (1<<PB0)) || (PINB & (1<<PB1)) || (PINB & (1<<PB2))) проверяет состояние входов PB0, PB1 и PB2, и если
хотя бы один из них установлен в 1, то выполняется код PORTC |= (1<<PC0);
Код PORTC |= (1<<PC0); Устанавливает вывод PC0 в 1, если соблюдается условие if. В противном случае PORTC &= ~(1<<PC0); устанавливает вывод в 0.

Таблица истинности 3ИЛИ:

PB5 PB6 PB7 PC7
0 0 0 0
0 0 1 1
0 1 0 1
1 0 0 1
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 1

Полный код выглядит так:

#include <avr/io.h>

int main(void)
{
   DDRC |= (1<<PC0);  
   while(1)
   {
   	if ((PINB & (1<<PB0)) || (PINB & (1<<PB1)) || (PINB & (1<<PB2))) // PINB || 0b00000111)
   	{
   		PORTC |= (1<<PC0);
   	}
   	else
   	{
   		PORTC &= ~(1<<PC0);
   	}
   }
}

Для компиляции программы нажимаем F7, либо Build > Build Solution, полученые после компиляции файлы будут находиться в папке LR1/Output Files нашего проекта

Моделирование в Proteus 8

  • Создание модели:
  1. Добавляем на схему:

Микроконтроллер ATmega328P

3 кнопки

4 резистора

1 светодиод

  1. Соединяем кнопки с источником тока и подключаем каждую кнопку отдельно ко входам PB0, PB1 и PB2.
  2. Светодиод подключаем анодом через резистор R1 к выходу PC0 и катодом к "земле"; Связываем кнопки через подтягивающие резисторы с "землей";

  1. Загружаем в микроконтроллер файл программы в расширении .hex

  • Симуляция:
    Запустим симуляцию:
    "Демонстрация работы"

  • Анализ результатов:

    • Реализуется алгоритм 3ИЛИ, в соотвествии с таблицей истинности: при вводе сигнала высокого уровня на любой из входов PB0, PB1, PB2 на выходе PC0 генерируется сигнал высокого уровня

Работа с fritzink

Микроконтроллер ATmega328P используется на плате Arduino NANO, рассмотрим её распиновку:

Из распиновки видим, что под PC0 в Arduino NANO подразумевается вывод A0 (Analog Pin 0), а под PB0, PB1, PB2 выводы D8, D9, D10 (Digital Pin 8, 9, 10) соответственно.

  • Собираем схему: