Заголовок сообщения: ATmega8, динамическая индикация, switch case и десятичная то
Добавлено: Сб сен 28, 2024 16:49:42
Родился
Зарегистрирован: Сб сен 28, 2024 16:19:04 Сообщений: 12
Рейтинг сообщения:0
Доброго всем времени суток. Начал недавно изучать программирование AVR на Си. Поднакопил немного знаний и решил сделать свое первое устройство, нагрузку для разряда АКБ. Набросал в протеусе вот такую схемку [img][url=https://img.radiokot.ru/files/158040/medium/3kcp23hfco.jpg][/img] Написал в AtmelStudio вот такой код Спойлер
//---------- динамической индикации------------------------------ unsigned char n = 0; //переменная количества вхождений в прерывание unsigned char r1_1000 = 0; //переменная значения разряда тысяч unsigned char r2_100 = 0; //переменная значения разряда сотен unsigned char r3_10 = 0; //переменная значения разряда десятков unsigned char r4_1 = 0; //переменная значения разряда единиц //----------
//---------- времени--------------------------------------------- unsigned char sec = 0; //переменная количества секунд unsigned char min = 0; //переменная количества минут unsigned char hour = 0; //переменная количества часов //----------
//---------- кнопок (внешние прерывания)---------- unsigned char button_1 = 0; //переменная количества секунд unsigned char button_2 = 0; //переменная количества минут //----------
unsigned int temp = 0; //переменная хранения значения температуры float volt = 0; //переменная хранения значения напряжения АКБ unsigned char n_ADC = 0; //переменная количества преобразований АЦП
//---------- индикация и отображение информации---------------- void LED_DISP_pin_setup(void) //функция настройки пинов LED индикатора { //настройка сегментов A, B, Dp, C, G DDRB |= (1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0); //режим работы - выход PORTB &= ~((1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0)); //низкий потенциал на пинах //настройка сегментов E, D, F DDRD |= (1<<7) | (1<<6) | (1<<5); //режим работы - выход PORTD &= ~((1<<7) | (1<<6) | (1<<5)); //низкий потенциал на пинах //настройка разрядов 1-4 DDRC |= (1<<3) | (1<<2) | (1<<1) | (1<<0); //режим работы - выход PORTC |= (1<<3) | (1<<2) | (1<<1) | (1<<0); //высокий потенциал на пинах }
void show_min_sec(unsigned int znach_min, unsigned char znach_sec) { r1_1000 = znach_min / 10; //определение значения десятков минут r2_100 = znach_min % 10; //определение значения единиц минут r3_10 = znach_sec / 10; //определение значения десятков секунд r4_1 = znach_sec % 10; //определение значения единиц секунд }
void show_hour_min(unsigned int znach_hour, unsigned char znach_min) { r1_1000 = znach_hour / 10; //определение значения десятков часов r2_100 = znach_hour % 10; //определение значения единиц часов r3_10 = znach_min / 10; //определение значения десятков минут r4_1 = znach_min % 10; //определение значения единиц минут }
ISR(INT0_vect) //макрос обработки прерывания, изменение отображаемой информации { if (++button_1 > 3) button_1 = 1; }
ISR(INT1_vect) {
} //----------
//---------- void ADC_pin_setup(void) { DDRC &= ~((1<<5) | (1<<4)); //пин PD4 и PD5 в режим работы вход PORTC &= ~((1<<5) | (1<<4)); //высокое входное сопротивление на пинах PD4 и PD5 }
void ADC_setup() { //разрешаем работу АЦП ADCSRA |= (1<<ADEN); //непрерывный режим работы АЦП ADCSRA |= (1<<ADFR); //частота дискретизации 125 кГц (частота МК 8 МГЦ, делитель 64) ADCSRA |= (1<<ADPS2) | (1<<ADPS1); ADCSRA &= ~(1<<ADPS0); //подключаем внешний ИОН - 5В //ADMUX |= (1<<REFS0); //ADMUX &= ~(1<<REFS1); //подключаем внутренний ИОН - 2,56В ADMUX |= (1<<REFS1) | (1<<REFS0); //выравнивание результата преобразования в регистре ADC по правой стороне ADMUX &= ~(1<<ADLAR); //настраиваем 5-й канал АЦП ADMUX |= (1<<MUX2) | (1<<MUX0); ADMUX &= ~((1<<MUX3) | (1<<MUX1)); //разрешаем преобразование АЦП ADCSRA |= (1<<ADSC); //разрешаем прерывание от АЦП ADCSRA |= (1<<ADIE); }
void ADC_set_chanel(unsigned char chanel) //функция выбора канала АЦП { switch(chanel) { case 0: //4-й канал ADMUX |= (1<<MUX2); ADMUX &= ~((MUX3) | (MUX1) | (MUX0)); break; case 1: //5-й канал ADMUX |= (1<<MUX2) | (1<<MUX0); ADMUX &= ~((1<<MUX3) | (1<<MUX1)); } }
ISR(ADC_vect) { n_ADC++; //этотвариант работает if (n_ADC == 1) { temp = (ADC*2.56/1024)*100; } else if (n_ADC == 2) { volt = (ADC*2.56/1024.0*5.3)*100; n_ADC = 0; } ADC_set_chanel(n_ADC); } //----------
int main(void) { LED_DISP_pin_setup(); Timer0_setup(); Timer1_setup(); External_interrupts_pin_setup(); External_interrupts_setup(); ADC_pin_setup(); ADC_setup();
sei(); //глобальное разрешение прерываний while (1) { if (button_1 == 1) { show_min_sec(min, sec); } else if (button_1 == 2) { show_temp(temp); } else if (button_1 == 3) { show_volt(volt); } } }
После включения, при нажатии на кнопку (подключена к пину PD2) изменяется отображаемая информация на семисегментном индикаторе. Поле первого нажатия отображается время прошедшее с момента включения, после второго - температура, после третьего - напряжение на АКБ. И вот тут возник вопрос. А как включить десятичную точку во втором разряде при отображении напряжения?
Очень у вас хорошо читаемый код получился. Хотя могут, конечно, набежать и сказать, что не по феншую, и какие-то непонятные константы в тексте тут и там)
По мне так лучше держать сразу отображаемый код в переменных, а не цифру от 0 до 9. Хоть у вас и в двух разных регистрах сегменты, но все равно по номерам битов от 0 до 7 - простой char получается. Лучше переделать иначе с точкой будет слишком кучеряво.
а не поличилось, патамучто "GyverTM1637 — бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх" (орфография сохранена) и пака одна дурака макака совитует хирню, дргая дурака макака не мажит то же думот. да здраствуят адурина! с кучей приколюх
Вообще, это обычный путь начинающего - одиночные светодиоды, семисегментный индикатор в один разряд, и наконец - динамическая индикация на семисегментниках. Всё по классике, это же изучение. Затем должен быть двустрочный знакосинтезирующий ЖК-дисплей, желательно не на I2C, а на параллельной шине. А вот ассемблер нынче не нужен. Его можно знать, но писать на нем - смысла нет никакого.
А вот ассемблер нынче не нужен. Его можно знать, но писать на нем - смысла нет никакого.
То, что я пишу на асме, я бы и не стал здесь упоминать, это не тема данного обсуждения. Сказал лишь для того, что бы развеять "уверенность" мартиана что все сидят на ардуине и на библиотеках:
Martian писал(а):
а не поличилось, патамучто "GyverTM1637 — бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх" (орфография сохранена) и пака одна дурака макака совитует хирню, дргая дурака макака не мажит то же думот. да здраствуят адурина! с кучей приколюх
к чему эта реплика вообще?
По поводу есть смысл или нет, то это обычный холивар на тему асм/яву, гимп/фотошоп, линукс/виндовс... А писАть я могу хоть на тетрадке в клеточку, главное результат.
Ардуина существует уже больше 15 лет, поэтому немудрено, что многие её используют. Я вот например использую готовые платы от WeAct, китайцы научились ставить на них мощные МК.
По поводу затронутой темы с дин.идникацией. Я тоже в своих первых опытах писал нечто подобное Однако, затем переделал на таблицу. То есть, список зажигаемых сегментов можно представить в виде таблицы - индексированный одномерный массив на 10 элементов. То есть, примерно так:
Код:
/* Распиновка сегментов на одном порту (можно в любом порядке пинов) */ #define SEGA (1 << 0) #define SEGB (1 << 1) #define SEGC (1 << 2) #define SEGD (1 << 3) #define SEGE (1 << 4) #define SEGF (1 << 5) #define SEGG (1 << 6) #define SEGp (1 << 7)
/* Распиновка разрядов (можно в любом порядке пинов) */ #define D0 (1 << 0) #define D1 (1 << 1) #define D2 (1 << 2) #define D3 (1 << 3)
#define ALLDIGIT_OFF GPIOB = D0 | D1 | D2 | D3
/** * Получение списка включаемых пинов для сегментов символа * @param ch - отображаемая цифра (0 - 9) * @return Значение для записи в выходной порт */ uint8_t GetSegments(uint8_t ch) { const uint8_t digits[10] = {CH0, CH1, CH2, CH3, CH4, CH5, CH6, CH7, CH8, CH9}; return digits[ch]; }
/** * Фаза дин.индикации. Отображение цифры в указанном разряде * @param digit - номер разряда индикатора * @param ch - отображаемая цифра * @param dot - признак десятичной точки */ void Phase(uint8_t digit, uint8_t ch, bool dot) { ALLDIGIT_OFF; // выключение всех разрядов GPIOA = GetSegments(ch); // выходы сегментов
/* добавление в порт десятичной точки */ if(dot) GPIOA |= SEGp;
/* Выбор разряда */ switch(digit) { case 0: GPIOB &= ~D0; // включение разряда 0 break; case 1: GPIOB &= ~D1; // включение разряда 1 break; case 2: GPIOB &= ~D2; // включение разряда 2 break; case 3: GPIOB &= ~D3; // включение разряда 3 break; } }
(GPIOA и GPIOB написаны условно) И затем функцию Phase() вызывать через заданные промежутки времени в соответствии с порядком индикации (можно даже и в любом порядке разрядов). В функцию передаем номер выбираемого разряда, отображаемую цифру и признак включения десятичной точки в разряде. Да, в эту функцию можно еще добавить параметр - признак гашения цифры, и по этому признаку гасить все сегменты в разряде, то есть, не отображать его.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 20
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения