Repository navigation
Releases: Strus05/MPSU
Release list
Лабораторная работа 4
Аналого-цифровой преобразователь. Изучение режимов работы аналого-цифрового преобразователя
- Цель работы: изучить режимы работы аналого-цифрового преобразователя
Задачи:
- Настройка АЦП: включение; выбор канала АЦП; выбор источника и уровня опорного напряжения; выбор делителя АЦП.
- Написать код в Atmel, позволяющий изменять тип выравнивания (левостороннее и правостороннее) и коэффициент усиления;
- Вывести на семисегментный индикатор напряжение;
- Построить схему в 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
Порты PB0-PB7 формируют сигналы на семисегментном индикаторе. За выбор активного индикатора отвечают порты PD1 и PD2, подающие сигнал высокого или низкого уровня на порты индикатора 1 и 2. Переключение происходит с частотой 122,7 Гц, что позволяет не замечать мерцание человеческому глазу.
На порт PC0 поступает напряжение с потенциометра RV1, оно преобразуется в 10-битное число, хранимое в 16-разрядном регистре. Формат записи – левосторонняя или правосторонняя, задаётся переключателем DSW (3-2). Коэффициент усиления задаётся переключателем DSW (4-1)
Лабораторная работа 3
Система прерываний. Изучение режимов работы системы прерываний
- Цель работы: изучить режимы работы системы прерываний.
Задачи:
- Настроить систему прерываний на срабатывание INT0 по переднему фронту;
- Написать код в Atmel, который при нажатии на кнопку активирует прерывание, выводя случайную цифру на семисегментный индикатор;
- Построить схему в 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
В схеме при срабатывании прерывания по переднему фронту INT0 port B подаёт сигналы на семисегментный индикатор.
Исходя из того, что в коде мы задали подачу сигнала высокого уровня на нужные светодиоды индикатора, целесообразно использовать катодный семисегментный индикатор.
Лабораторная работа 2
Работа с таймерами
- Цель работы: Изучить режимы работы таймера ATMEGA328P
Задачи:
- Напишем программу, в которой при нажатии на кнопку, микроконтроллер формирует выдержку времени и зажигает зеленый светодиод с заданным периодом (0,1с и 1с)
- Напишем программу, в которой вывод PB5 при инициализации таймера 0 запрограммирован для выдачи сигнала с заданной частотой (1Гц)
- Напишем программу, в которой порт ввода 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% скважность)
Таймером 2 (Нормальный режим) управляем Кнопками 1 и 2, подавая сигнал на PC1 и PC2. Полученный сигнал с PB3 подаётся на базу биполярного npn транзистора, светодиод подключен к коллектору.
Таймер 0 (Clear Timer on Compare ) задаёт частоту переключения порта PB5, сигнал с которого подаётся на базу биполярного npn транзистора. К эмиттеру подключено реле, которое подключает лампу к источнику 12В, когда по нему протекает ток.
Таймер 1 (Fast PWM) получает значение скважности (OCR1A) от АЦП порта PC0, сигнал на котором регулируется через потенциометр RV1. ШИМ сигнал, формируемый на выводе PB1, подаётся через оптопару на затвор mosfet, который управляет напряжением на двигателе.
Лабораторная работа 1
Авторы:
Астафьев Илья,
Никитин Даниил,
Ульянчик Руслан
Содержание
Работа с 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
- Создание модели:
- Добавляем на схему:
Микроконтроллер ATmega328P
3 кнопки
4 резистора
1 светодиод
- Соединяем кнопки с источником тока и подключаем каждую кнопку отдельно ко входам
PB0,PB1иPB2. - Светодиод подключаем анодом через резистор R1 к выходу
PC0и катодом к "земле"; Связываем кнопки через подтягивающие резисторы с "землей";
- Загружаем в микроконтроллер файл программы в расширении .hex
-
Анализ результатов:
- Реализуется алгоритм 3ИЛИ, в соотвествии с таблицей истинности: при вводе сигнала высокого уровня на любой из входов
PB0,PB1,PB2на выходеPC0генерируется сигнал высокого уровня
- Реализуется алгоритм 3ИЛИ, в соотвествии с таблицей истинности: при вводе сигнала высокого уровня на любой из входов
Работа с fritzink
Микроконтроллер ATmega328P используется на плате Arduino NANO, рассмотрим её распиновку:
Из распиновки видим, что под PC0 в Arduino NANO подразумевается вывод A0 (Analog Pin 0), а под PB0, PB1, PB2 выводы D8, D9, D10 (Digital Pin 8, 9, 10) соответственно.
- Собираем схему:














