ATmega8, динамическая индикация, switch case и десятичная то

Обсуждаем контроллеры компании Atmel.
Denis-1307
Родился
Сообщения: 12
Зарегистрирован: Сб сен 28, 2024 16:19:04

ATmega8, динамическая индикация, switch case и десятичная то

Сообщение Denis-1307 »

Доброго всем времени суток.
Начал недавно изучать программирование AVR на Си. Поднакопил немного знаний и решил сделать свое первое устройство, нагрузку для разряда АКБ.
Набросал в протеусе вот такую схемку
[img][url=https://img.radiokot.ru/files/158040/medium/3kcp23hfco.jpg][/img]
Написал в AtmelStudio вот такой код
Спойлер

Код: Выделить всё

#define F_CPU 8000000UL //частота МК
#include <avr/io.h>
#include <util/delay.h> //библиотека задержек
#include <avr/interrupt.h> //библиотека прерываний

//-----------------------переменные динамической индикации------------------------------
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 Timer0_setup(void) //функция настройки нулевого таймер-счетчика
{
	//включение таймер-счетчика с делителем на 64
	TCCR0 |= (1<<CS01) | (1<<CS00);
	TCCR0 &= ~(1<<CS02);
	//настройка прерывания по переполнению счетого регистра
	TIMSK |= (1<<TOIE0);
	//обнуление счетного регистра
	TCNT0 = 0;
}

void show_volt(unsigned int znach_volt)
{
	r1_1000 = znach_volt / 1000; //определение значения десятков вольт
	r2_100 = znach_volt % 1000 / 100; //определение значения единиц вольт
	r3_10 = znach_volt % 100 / 10; //определение значения десятоых долей вольта
	r4_1 = 12; //знак напряжения
}

void show_temp(unsigned int znach_temp)
{
	r1_1000 = znach_temp / 10; //
	r2_100 = znach_temp % 10; //
	r3_10 = 10;
	r4_1 = 11;
}

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; //определение значения единиц минут
}

void digits(unsigned char n) //функция отображения цифр
{
	//выключаем сегменты
	PORTB &= ~((1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0)); //низкий потенциал на пинах
	PORTD &= ~((1<<7) | (1<<6) | (1<<5)); //низкий потенциал на пинах
	switch(n)
	{
		case 0: PORTB |= (1<<3) | (1<<1) | (1<<0);
				PORTD |= (1<<7) | (1<<6) | (1<<5);
				break;
		case 1: PORTB |= (1<<3) | (1<<1);
				break;
		case 2: PORTB |= (1<<4) | (1<<1) | (1<<0);
				PORTD |= (1<<6) | (1<<5);
				break;
		case 3: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
				PORTD |= (1<<6);
				break;
		case 4: PORTB |= (1<<4) | (1<<3) | (1<<1);
				PORTD |= (1<<7);
				break;
		case 5: PORTB |= (1<<4) | (1<<3) | (1<<0);
				PORTD |= (1<<7) | (1<<6);
				break;
		case 6: PORTB |= (1<<4) | (1<<3) | (1<<0);
				PORTD |= (1<<7) | (1<<6) | (1<<5);
				break;
		case 7: PORTB |= (1<<3) | (1<<1) | (1<<0);
				break;
		case 8: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
				PORTD |= (1<<7) | (1<<6) | (1<<5);
				break;
		case 9: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
				PORTD |= (1<<7) | (1<<6);
				break;
		case 10: PORTB |= (1<<4) | (1<<1) | (1<<0); //знак градуса
				 PORTD |= (1<<7);
				 break;
		case 11: PORTB |= (1<<0);					//знак цельсия
				 PORTD |= (1<<7) | (1<<6) | (1<<5);
				 break;
		case 12: PORTB |= (1<<3) | (1<<1);			//знак напряжения
				 PORTD |= (1<<7) | (1<<6) | (1<<5);
				 break;
	}
}

ISR(TIMER0_OVF_vect) //макрос обработки прерывания нулевого таймер-счтечика
{
	if(++n > 4) n = 1;
	if (n == 1) //отображение в первом разряде
	{
		PORTC |= (1<<3) | (1<<2) | (1<<1);
		PORTC &= ~(1<<0);
		digits(r1_1000);
	}
	else if (n == 2) //отображение во втором разряде
	{
		PORTC |= (1<<3) | (1<<2) | (1<<0);
		PORTC &= ~(1<<1);
		digits(r2_100);
	}
	else if (n == 3) //отображение в третьем разряде
	{
		PORTC |= (1<<3) | (1<<1) | (1<<0);
		PORTC &= ~(1<<2);
		digits(r3_10);
	}
	else if (n == 4) //отображение в четвертом разряде
	{
		PORTC |= (1<<2) | (1<<1) | (1<<0);
		PORTC &= ~(1<<3);
		digits(r4_1);
	}
}
//--------------------------------------------------------------------------------------

//----------------отсчет одной секунды--------------------------------------------------
void Timer1_setup(void)
{
	//включение таймер-счетчика с делителем на 256
	TCCR1B |= (1<<CS12);
	TCCR1B &= ~((1<<CS11) | (1<<CS10));
	//разрешаем прерывание по совпадению
	TIMSK |= (1<<OCIE1A);
	//разрешаем автоматическое обнуление счетного регистра при совпадении
	TCCR1B |= (1<<WGM12);
	//обнуляем счетный регистр
	TCNT1 = 0;
	//записываем в счетный регистр значение кличества тактов равное одной секунде
	OCR1A = 31250;
}

ISR(TIMER1_COMPA_vect)
{
	sec++;
	if (sec == 60)
	{
		sec = 0;
		min++;
	} 
	else if (min == 60)
	{
		min = 0;
		hour++;
	}
}
//--------------------------------------------------------------------------------------

//----------------внешние прерывания----------------------------------------------------
void External_interrupts_pin_setup(void)
{
	DDRD &= ~((1<<3) | (1<<2));
	PORTD |= (1<<3) | (1<<2);
}

void External_interrupts_setup(void)
{
	//настройка прерывания INT0 по спаду
	MCUCR |= (1<<ISC01);
	MCUCR &= ~(1<<ISC00);
	//настройка прерывания INT1 по спаду
	MCUCR |= (1<<ISC11);
	MCUCR &= ~(1<<ISC10);
	//разрешаем внешние прерывания INT0 и INT1
	GICR |= (1<<INT1) | (1<<INT0);
}

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) изменяется отображаемая информация на семисегментном индикаторе. Поле первого нажатия отображается время прошедшее с момента включения, после второго - температура, после третьего - напряжение на АКБ.
И вот тут возник вопрос. А как включить десятичную точку во втором разряде при отображении напряжения?
Аватара пользователя
Starichok51
Модератор
Сообщения: 19039
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Starichok51 »

так добавь точку к цифре по ИЛИ.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Аватара пользователя
ks0
Прорезались зубы
Сообщения: 231
Зарегистрирован: Чт фев 28, 2013 14:16:10

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение ks0 »

Очень у вас хорошо читаемый код получился. Хотя могут, конечно, набежать и сказать, что не по феншую, и какие-то непонятные константы в тексте тут и там)

По мне так лучше держать сразу отображаемый код в переменных, а не цифру от 0 до 9. Хоть у вас и в двух разных регистрах сегменты, но все равно по номерам битов от 0 до 7 - простой char получается. Лучше переделать иначе с точкой будет слишком кучеряво.

Я последний раз точку делал так:

Код: Выделить всё

char get_code(char c, char pt)
{
	if(pt) pt=0b10000000;	// добавить точку
	switch(c)
	{
		case 0: return 0b00111111 | pt;
Martian
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32
Контактная информация:

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Martian »

Если занести всё в массив, то вместо кучи хорошо читаемых строчек
switch(c)
{
case 0...

была бы одна плохо читаемая PORTB |= MyArray[c];
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение shonty »

народ до сих пор динамикой занимается..
есть копеечные готовые модули типа MAX7219, TM1637 и др..
Аватара пользователя
Thinnnfor
Вымогатель припоя
Сообщения: 595
Зарегистрирован: Пн июн 03, 2024 22:29:24

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Thinnnfor »

MAX7219-это круто , это просто ! TM1637 это какое то дерьмище вообще не получилось ничего ...
Последний раз редактировалось Thinnnfor Сб сен 28, 2024 22:03:18, всего редактировалось 1 раз.
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение shonty »

Thinnnfor писал(а):TM1637 это какое то дерьмище вообще не поличилось ничего ...
если не получилось - не значит, что дермище))
вручную интерфейс мутить нужно
Martian
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32
Контактная информация:

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Martian »

а не поличилось, патамучто "GyverTM1637 — бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх" (орфография сохранена)
и пака одна дурака макака совитует хирню, дргая дурака макака не мажит то же думот. да здраствуят адурина! с кучей приколюх
Аватара пользователя
Thinnnfor
Вымогатель припоя
Сообщения: 595
Зарегистрирован: Пн июн 03, 2024 22:29:24

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Thinnnfor »

Я не пользуюсь библиотеками , тем более ардуино ! Ардуино не пользуюсь из принципа ! У меня CV AVR !

Добавлено after 5 minutes 3 seconds:
Re: ATmega8, динамическая индикация, switch case и десятичная то
CV AVR для авр, для микрочипов PIC CCS
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение shonty »

Martian, ты увенный и сам себе веришь)
если что я асме пишу)
Martian
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32
Контактная информация:

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Martian »

У-ух! (почесал себе зад)
OKF
Это не хвост, это антенна
Сообщения: 1385
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение OKF »

Не перевелись ведь люди в русских селеньях!
Аватара пользователя
Thinnnfor
Вымогатель припоя
Сообщения: 595
Зарегистрирован: Пн июн 03, 2024 22:29:24

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Thinnnfor »

shonty писал(а):если что я асме пишу)
Ну ....респект и уважуха !!!
OKF
Это не хвост, это антенна
Сообщения: 1385
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение OKF »

Дурак дураком. Соболезную.
Аватара пользователя
Thinnnfor
Вымогатель припоя
Сообщения: 595
Зарегистрирован: Пн июн 03, 2024 22:29:24

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Thinnnfor »

Ассемблер - это круто !

Добавлено after 56 seconds:
Re: ATmega8, динамическая индикация, switch case и десятичная то
Когда то под z80 тоже писал , давно это было !
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение shonty »

Martian писал(а):У-ух! (почесал себе зад)
OKF писал(а):Дурак дураком. Соболезную.
а что вы такие все взволнованные? :shock: :) :)
Аватара пользователя
Thinnnfor
Вымогатель припоя
Сообщения: 595
Зарегистрирован: Пн июн 03, 2024 22:29:24

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Thinnnfor »

Всё норм ! динамическая индикация , индицирует динамически ! Всё хорошо !!
Базилюк
Нашел транзистор. Понюхал.
Сообщения: 159
Зарегистрирован: Чт сен 19, 2024 19:18:28

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Базилюк »

Вообще, это обычный путь начинающего - одиночные светодиоды, семисегментный индикатор в один разряд, и наконец - динамическая индикация на семисегментниках. Всё по классике, это же изучение. Затем должен быть двустрочный знакосинтезирующий ЖК-дисплей, желательно не на I2C, а на параллельной шине.
А вот ассемблер нынче не нужен. Его можно знать, но писать на нем - смысла нет никакого.
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение shonty »

Базилюк писал(а):А вот ассемблер нынче не нужен. Его можно знать, но писать на нем - смысла нет никакого.
То, что я пишу на асме, я бы и не стал здесь упоминать, это не тема данного обсуждения.
Сказал лишь для того, что бы развеять "уверенность" мартиана что все сидят на ардуине и на библиотеках:
Martian писал(а):а не поличилось, патамучто "GyverTM1637 — бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх" (орфография сохранена)
и пака одна дурака макака совитует хирню, дргая дурака макака не мажит то же думот. да здраствуят адурина! с кучей приколюх
к чему эта реплика вообще? :shock:

По поводу есть смысл или нет, то это обычный холивар на тему асм/яву, гимп/фотошоп, линукс/виндовс...
А писАть я могу хоть на тетрадке в клеточку, главное результат.

:beer:
Базилюк
Нашел транзистор. Понюхал.
Сообщения: 159
Зарегистрирован: Чт сен 19, 2024 19:18:28

Re: ATmega8, динамическая индикация, switch case и десятична

Сообщение Базилюк »

Ардуина существует уже больше 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 CH0		(SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG)
#define CH1		(SEGA | SEGB                                   )
#define CH2		(SEGA | SEGB |        SEGD | SEGE |        SEGG)
#define CH3		(SEGA | SEGB | SEGC | SEGD |               SEGG)
#define CH4		(       SEGB | SEGC |               SEGF | SEGG)
#define CH5		(SEGA |        SEGC | SEGD |        SEGF | SEGG)
#define CH6		(SEGA |        SEGC | SEGD | SEGE | SEGF | SEGG)
#define CH7		(SEGA | SEGB | SEGC                            )
#define CH8		(SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG)
#define CH9		(SEGA | SEGB | SEGC | SEGD |        SEGF | SEGG)
#define SEG_OFF	0

/* Распиновка разрядов (можно в любом порядке пинов) */
#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() вызывать через заданные промежутки времени в соответствии с порядком индикации (можно даже и в любом порядке разрядов). В функцию передаем номер выбираемого разряда, отображаемую цифру и признак включения десятичной точки в разряде.
Да, в эту функцию можно еще добавить параметр - признак гашения цифры, и по этому признаку гасить все сегменты в разряде, то есть, не отображать его.
Ответить

Вернуться в «AVR»