EEPROM запись в цикле.

Обсуждаем контроллеры компании Atmel.
Ответить
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

но даже не знаю какой стандарт поддерживает avr-gcc в atmelstudio ведь не так давно вышел очередной стандарт с++17
Про это можете забыть сразу. :)))

Вообще про плюсы на контроллере, да еще и Гарвардской архитектуры, вспоминать грешно.

Чтобы использовать C++ с комфортом, необходимо как минимум динамическое выделение памяти, а для микроконтроллера это непозволительная роскошь - здесь нормальный менеджер памяти сам почти всю доступную память и скушает. :))) Отдельный поворот делу придает то, что AVR, например, не может выполнять код из RAM (Гарвардская архитектура, да).

Конечно, отдельные эстеты умудряются писать на плюсах и под мелкие МК, но к этому нельзя относиться всерьез.

Так что только ANSI C, причем, желательно, с учетом правил MISRA. :)
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18678
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

:facepalm: какой там С++17, когда с перебором массива структур проблемы...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Прорезались зубы
Аватара пользователя
Сообщения: 225
Зарегистрирован: Сб янв 14, 2012 22:53:50

Сообщение philosoraptor »

[uquote="YS",url="/forum/viewtopic.php?p=3211609#p3211609"]Вообще про плюсы на контроллере, да еще и Гарвардской архитектуры, вспоминать грешно.[/uquote]

Ну, это вы слегка погорячились. Практический пример преимущества использования плюсов под мк.

Так что при хорошем владении плас ничем не уступает Си, а местами даже дает преимущества.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Переделал немного код, добавил плавное включение. (осталось в момент плавного включения добавить обработку индикации).

Код:
Спойлер

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

#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//-------------------------------------------///init_pwm///-----------------------------------------------------------------//
void init_pwm()
{
	TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
	TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);
	OCR0A=0x00;
	OCR0B=0x00;
	
	TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
	TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
	OCR1AL=0x00;
	OCR1BL=0x00;

	TCCR2A=(1<<COM2A1) | (0<<COM2A0) | (1<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (1<<WGM20);
	TCCR2B=(0<<WGM22) | (0<<CS22) | (1<<CS21) | (0<<CS20);
	OCR2A=0x00;
	OCR2B=0x00;
}
//--------------------------------------------------------------------------------------------------------------------------//
void init_int0()
{
	//настраиваем на срабатывание INT0 по переднему фронту
	EICRA |= (1<<ISC01)|(0<<ISC00);
	//разрешаем внешнее прерывание INT0
	EIMSK |= (1<<INT0);
}
//--------------------------------------------------------------------------------------------------------------------------//
void init_io()
{
	DDRB=(0<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(0<<PB5)|(0<<PB6)|(0<<PB7);
	PORTB=0x00;
	DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4)|(1<<PC5)|(1<<PC6);
	PORTC=0x00;
	DDRD=(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
	PORTD=0x00;
}
//--------------------------------------------------------------------------------------------------------------------------//
//настройка параметров работы функций
#define BTN_LOCK_TIME		30					/*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME		1000				/*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
//настройки портов
/*порт чтения кнопок*/
#define BTN_PORT			PORTB
#define BTN_DDR				DDRB
#define BTN_PIN				PINB
/*пины чтения кнопок*/
#define BTN_LINE_UP		(1<<7)
#define BTN_LINE_DN		(1<<6)
#define BTN_LINE_POWER	(1<<5)
#define BTN_LINE_SW		(1<<0)
//глобальные переменные
volatile uint8_t BtnFlags;					//байт флагов нажатия кнопки
#define BTN_SHRT_UP			(1<<0)			/*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN			(1<<1)			/*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER		(1<<2)			/*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW			(1<<3)			/*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP			(1<<4)			/*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN			(1<<5)			/*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW			(1<<6)			/*бит короткого нажатия кнопки SW*/
//-------------------------------------------------------------------------------------------------------------------
//Функция настройки библиотеки работы с кнопками
void BtnInit (void)
{
	BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//на ввод
	BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//подтяжка вкл
}
//--------------------------------------------------------------------------------------------------------------------
//Функция чтения данных о нажатии кнопок
char BtnGet (void)
{
	cli();
	char temp = BtnFlags;
	BtnFlags = 0;
	sei();
	return temp;
}
//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
	static unsigned char BtnLockBit;				//защелка (защита от дребезга)
	static unsigned char BtnLockCoun;			//счетчик защелки (защита от дребезга)
	static unsigned char BtnLongCoun;			//счетчик длинного нажатия
	static unsigned char BtnLastState;			//последнее состояние кнопок перед отпусканием

	char mask = 0;
	if (! (BTN_PIN & BTN_LINE_UP))		mask = BTN_SHRT_UP;
	if (! (BTN_PIN & BTN_LINE_DN))		mask = BTN_SHRT_DN;
	if (! (BTN_PIN & BTN_LINE_POWER))	mask = BTN_SHRT_POWER;
	if (! (BTN_PIN & BTN_LINE_SW))		mask = BTN_SHRT_SW;

	if (mask){									//опрос состояния кнопки
		if (BtnLockCoun < (BTN_LOCK_TIME/10)){	//клавиша нажата
			BtnLockCoun++;
			return;								//защелка еще не дощитала - возврат
		}
		BtnLastState = mask;
		BtnLockBit =1;							//нажатие зафиксировано
		if (BtnLongCoun >= (BTN_LONG_TIME/10))
		return;								//возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
		if (++BtnLongCoun >= (BTN_LONG_TIME/10))
		BtnFlags |= (BtnLastState<<4);			//счетчик досчитал до максимума - устанавливаем биты длинного нажатия
	}
	else{										//клавиша отжата
		if (BtnLockCoun){
			BtnLockCoun --;
			return;								//защелка еще не обнулилась - возврат
		}
		if (! BtnLockBit)						//СТАТИЧЕСКИЙ ВОЗВРАТ
		return;
		BtnLockBit =0;							//отжатие зафиксировано
		if (BtnLongCoun < (BTN_LONG_TIME/10))
		BtnFlags |= BtnLastState;			//установка бита короткого нажатия
		BtnLongCoun = 0;					//сброс счетчика длительности нажатия
	}
}
//------------------------------------------------------****7SEG****----------------------------------------------------------
#define SEGA 6
#define SEGB 5
#define SEGC 1
#define SEGD 2
#define SEGE 3
#define SEGF 4
#define SEGG 0

#define ANOD1 4
#define ANOD2 7
#define ANOD3 4
//-------------------------------------------------------------------------------------------------------------------------
void segchar (unsigned char seg)
{
	switch (seg)
	{
		case 0:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break;
		case 1:
		PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
		case 2:
		PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
		case 3:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
		case 4:
		PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 5:
		PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 6:
		PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 7:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
		case 8:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 9:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 99: //OFF Все сегменты
		PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
	}
}

#define CONFIG_AMOUNT 6
typedef struct
{
	char FlagPower;
	char ValuePWM;
}ConfigurationLamp;

EEMEM ConfigurationLamp E_ConfigLamp[CONFIG_AMOUNT];
ConfigurationLamp ConfigLamp[CONFIG_AMOUNT];
//-----------------------------------------------------------------------------------------------------

void LoadingEEPROM()
{
	eeprom_read_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}

void SaveEEPROM()
{
	eeprom_update_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}
void WriteValue(unsigned char Zona,unsigned char ValuePWM)
{
	switch (Zona)
	{
		case 0:OCR0A=ValuePWM*2.56;break;
		case 1:OCR0B=ValuePWM*2.56;break;
		case 2:OCR1AL=ValuePWM*2.56;break;
		case 3:OCR1BL=ValuePWM*2.56;break;
		case 4:OCR2A=ValuePWM*2.56;break;
		case 5:OCR2B=ValuePWM*2.56;break;
	}
}
char ReadValue(unsigned char Zona)
{
	static unsigned char ValuePWM=0;
	switch (Zona)
	{
		case 0:ValuePWM=OCR0A;break;
		case 1:ValuePWM=OCR0B;break;
		case 2:ValuePWM=OCR1AL;break;
		case 3:ValuePWM=OCR1BL;break;
		case 4:ValuePWM=OCR2A;break;
		case 5:ValuePWM=OCR2B;break;
	}
	return(ValuePWM/2.56);
}
unsigned char TempValuePWM=0;
void UpdateValue()
{
	for (unsigned char ZonaCount = 0; ZonaCount < CONFIG_AMOUNT; ZonaCount++)
	{
		TempValuePWM=ReadValue(ZonaCount);
		if (ConfigLamp[ZonaCount].FlagPower==1)
		{
			if (TempValuePWM==0)
			{
				while(TempValuePWM<ConfigLamp[ZonaCount].ValuePWM)
				{
					TempValuePWM++;
					WriteValue(ZonaCount,TempValuePWM);
					_delay_ms(50);
				}
			}
			WriteValue(ZonaCount,ConfigLamp[ZonaCount].ValuePWM);
		}
		else
		{
			WriteValue(ZonaCount,0);
		}
		
	}
}
unsigned char ZoneNumber=0;
unsigned char count = 0;
void WriteSeg(unsigned char Number)
{
	unsigned char data1=ConfigLamp[Number].ValuePWM%10;
	unsigned char data2=ConfigLamp[Number].ValuePWM/10;
	PORTB |=(1<<ANOD1);
	PORTD |=(1<<ANOD2);
	PORTD |=(1<<ANOD3);
	segchar(99);
	if(ConfigLamp[Number].FlagPower==1)
	{
		count++;
		if (count==1){
			PORTB &= ~(1<<ANOD1);
			segchar(data1);
			PORTD |=(1<<ANOD2);
			PORTD |=(1<<ANOD3);
		}
		if (count==2){
			PORTD &= ~(1<<ANOD2);
			segchar(data2);
			PORTB |=(1<<ANOD1);
			PORTD |=(1<<ANOD3);
		}
		if (count==3){
			PORTD &= ~(1<<ANOD3);
			segchar(Number+1);
			PORTB |=(1<<ANOD1);
			PORTD|=(1<<ANOD2);
		}
		if (count==3){count=0;}
	}
}
char Stop=1;
ISR(INT0_vect)
{
	WriteSeg(ZoneNumber);
	UpdateValue();
	BtnExe();
}
void BtnUpdate(void)
{
	char BtnMask = BtnGet ();
	if (BtnMask == BTN_SHRT_POWER)
	{
		ConfigLamp[ZoneNumber].FlagPower++;
		if (ConfigLamp[ZoneNumber].FlagPower>1)
		{
			ConfigLamp[ZoneNumber].FlagPower=0;
		}
		SaveEEPROM();
	}
	if ((BtnMask == BTN_SHRT_SW))
	{
		ZoneNumber++;
		if (ZoneNumber==6)
		{
			ZoneNumber=0;
		}
	}
	//одиночное нажатие +
	if ((BtnMask == BTN_SHRT_UP)& (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		ConfigLamp[ZoneNumber].ValuePWM++;
		SaveEEPROM();
	}
	//одиночное нажатие -
	if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		ConfigLamp[ZoneNumber].ValuePWM--;
		SaveEEPROM();
	}
	//Удержание +
	if ((BtnMask == BTN_LONG_UP) & (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))
		{
			ConfigLamp[ZoneNumber].ValuePWM++;
			_delay_ms(50);
		}
		SaveEEPROM();
	}
	//Удержание -
	if ((BtnMask == BTN_LONG_DN) & (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		while ((!(PINB&0b01000000))& (ConfigLamp[ZoneNumber].ValuePWM > 0))
		{
			ConfigLamp[ZoneNumber].ValuePWM--;
			_delay_ms(50);
		}
		SaveEEPROM();
	}
}

int main(void)
{
	cli();
	LoadingEEPROM();
	init_io();
	BtnInit();
	init_int0();
	init_pwm();
	sei();
    while(1)
    {
		BtnUpdate();
	}
	return 0;
}
andrei23061996@gmail.com
.................................................................................................................
Реклама
Эиком - электронные компоненты и радиодетали
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

philosoraptor, я читал и эту статью, и ей подобные. И тут есть несколько моментов.

1. Автор рассматривает исключительно задачу работы с периферией, причем исключительно с портами. Выше же шла речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?

2. Автор использует исключительно статические классы и ни словом не заикнулся (логично) про создание их экземпляров. И все равно, несмотря на все изгибание C++ для применимости в эмбеде, не обошлось без ограничений:

- "От динамической конфигурации линий ввода-вывода сразу отказываемся из-за необходимости доступа к портам через указатель со всеми вытекающими последствиями."

- "Мы не можем прочитать состояние выходных линий регистра – он всегда работает на выход, поэтому функцию чтения состояния не реализуем и не объявляем. Попытка прочитать состояние такого пора вызовет ошибку компиляции."

А динамическая конфигурация бывает нужна. Например, чтобы имитировать открытый коллектор на AVR.

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

3. И несмотря на все ограничения код получился тяжеловесным и сложным для понимания. Даже сам автор в некоторый момент устал: "Обобщая доступ к отдельным битам в списке линий приходим к концепции среза. Не буду вдаваться в подробности их реализации".

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

Родственный пример (для AVR-libc) - функции задержки, например, _delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз. Здесь может произойти нечто подобное.

Кстати, рекурсия, используемая автором, запрещена стандартом MISRA.

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

struct MakePinList
{
private:
		// рекурсивно проходим все параметры
		// на следующей итерации Position увеличится на 1,
		// а T2 превратится в T1 и так далее
typedef typename MakePinList
<
		Position + 1,

...
5. В целом можно сказать, что фактически автор лишь использует механизм шаблонов C++ как продвинутый препроцессор. Впрочем, и без классического препроцессора не обходится:

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

#define MAKE_PORT(portName, className, ID) \
	class className{\
	public:\
		typedef uint8_t DataT;\
	public:\
...
Это вообще в некоторой степени противоречит идеологии C++.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Так в программирование неуместно употреблять один подход, и скорее даже не возможно написать код следуя всего лишь одной парадигме программирования т.к
даже на языке Си, который не является объектно-ориентированным, можно работать в соответствии с принципами объектно-ориентированного программирования, хотя это и сопряжено с определёнными сложностями, а функциональное программирование можно применять при работе на любом императивном языке, в котором имеются функции,
Я решил закончить данную прошивку в стиле функционального программирования, а уже следующие не мение интересную попробовать написать (не потеряв при этом быстродействие) сделав уклон на ООП.

Добавлено after 8 minutes 23 seconds:
А насчет MISRA C который являеться как бы ''стандартом разработки программного обеспечения на Си'" . предполгаю что на данный момент времени он не являеться актуальным т.к насколько мне известно они не удосужились внести С99 в описание своего стандарта, не говоря о уже о том что процесса сертификации MISRA не существует в принцепе.

Добавлено after 1 hour 7 minutes 32 seconds:
Я вот думаю как все таки более лаконично написать легко переносимую функцию? для работы с динамической индикацией семи сегментного индикатора.
А то segchar()+WriteSeg() выглядят как то не комильфо.
andrei23061996@gmail.com
.................................................................................................................
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

предполгаю что на данный момент времени он не являеться актуальным
Это всего лишь ваше предположение. :) Впрочем, некоторые говорят, что и Си не является актуальным. :)))

Процесса сертификации MISRA не существует по простой причине: MISRA - это, так сказать, набор хороших советов, которые применимы для создания кода, используемого в разных применениях в разных отраслях с разными требованиями и подходами к сертификации. Например, устройство в целом может быть сертифицировано на некоторый уровень SIL, при этом для достижения этой цели логично использовать правила MISRA при написании прошивки - это уберет много граблей. Можно, конечно, не заморачиваться и идти путем проб и ошибок, самостоятельно переоткрывая рекомендации MISRA. :wink:
Я вот думаю как все таки более лаконично написать легко переносимую функцию?
Я для динамической индикации использую что-то вроде фреймбуфера, в который складываю паттерны для символов на индикаторе. Этот буфер выводится в порт по прерыванию. Заполняет его отдельная функция, которая заодно преобразует числа/символы в паттерны.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Прорезались зубы
Аватара пользователя
Сообщения: 225
Зарегистрирован: Сб янв 14, 2012 22:53:50

Сообщение philosoraptor »

YS писал(а):_delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз.
На эти грабли, пожалуй, все новички наступают.
YS писал(а): В синтетических примерах автора результат дизассемблирования выглядит неплохо, но где гарантия, что в каких-то реальных условиях что-то не пойдет не так, и компилятор не сгенерирует код для того, что по задумке должен вычислять во время компиляции?
Гарантии нет в любом случае, даже если строго следовать всем стандартам. Остается уповать лишь на здравый смысл кодера и на способность препроцессора отловить совсем уж явные несуразности.
YS писал(а):речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?
Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код. В чем профит, спросите вы? Ну, к примеру, есть множество ардуинообразных библиотек на С++, которые можно использовать и без ардуины. Да, их можно легко портировать и на Си, но это лишние телодвижения.
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Гарантии нет в любом случае, даже если строго следовать всем стандартам.
Просто не надо полагаться на оптимизации вроде вычисления выражений на этапе компиляции (если только они не состоят исключительно из констант-дефайнов) и, тем более, замену компилятором участка кода на результат его выполнения. Это очень рискованно само по себе, а заодно усложняет портирование кода.

Я считаю, что на оптимизацию вообще полагаться не надо. Надо стараться, чтобы написанный код был как можно более прозрачен.
Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код.
В этом случае обычно пишут библиотеку на чистом C, а на C++ делают только обертку для желающих. Такой подход гораздо лучше потому, что обеспечивает совместимость не только на уровне исходников, но и на бинарном уровне - скажем, DLL на чистом Си гораздо универсальнее. То же самое справедливо и для скомпилированных статических библиотек.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Ну вот и готов финишный код.
Конечно реализация плавного запуска желает желать лучшего, но она все же справляется со своими задачами.

Код:
Спойлер

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

#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//-------------------------------------------///init_pwm///-----------------------------------------------------------------//
void init_pwm()
{
	TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
	TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);
	OCR0A=0x00;
	OCR0B=0x00;
	
	TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
	TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
	OCR1AL=0x00;
	OCR1BL=0x00;

	TCCR2A=(1<<COM2A1) | (0<<COM2A0) | (1<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (1<<WGM20);
	TCCR2B=(0<<WGM22) | (0<<CS22) | (1<<CS21) | (0<<CS20);
	OCR2A=0x00;
	OCR2B=0x00;
}
//--------------------------------------------------------------------------------------------------------------------------//
void init_int0()
{
	//настраиваем на срабатывание INT0 по переднему фронту
	EICRA |= (1<<ISC01)|(0<<ISC00);
	//разрешаем внешнее прерывание INT0
	EIMSK |= (1<<INT0);
}
//--------------------------------------------------------------------------------------------------------------------------//
void init_io()
{
	DDRB=(0<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(0<<PB5)|(0<<PB6)|(0<<PB7);
	PORTB=0x00;
	DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4)|(1<<PC5)|(1<<PC6);
	PORTC=0x00;
	DDRD=(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
	PORTD=0x00;
}
//--------------------------------------------------------------------------------------------------------------------------//
//настройка параметров работы функций
#define BTN_LOCK_TIME		30					/*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME		1000				/*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
//настройки портов
/*порт чтения кнопок*/
#define BTN_PORT			PORTB
#define BTN_DDR				DDRB
#define BTN_PIN				PINB
/*пины чтения кнопок*/
#define BTN_LINE_UP		(1<<7)
#define BTN_LINE_DN		(1<<6)
#define BTN_LINE_POWER	(1<<5)
#define BTN_LINE_SW		(1<<0)
//глобальные переменные
volatile uint8_t BtnFlags;					//байт флагов нажатия кнопки
#define BTN_SHRT_UP			(1<<0)			/*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN			(1<<1)			/*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER		(1<<2)			/*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW			(1<<3)			/*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP			(1<<4)			/*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN			(1<<5)			/*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW			(1<<6)			/*бит короткого нажатия кнопки SW*/
//-------------------------------------------------------------------------------------------------------------------
//Функция настройки библиотеки работы с кнопками
void BtnInit (void)
{
	BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//на ввод
	BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//подтяжка вкл
}
//--------------------------------------------------------------------------------------------------------------------
//Функция чтения данных о нажатии кнопок
char BtnGet (void)
{
	cli();
	char temp = BtnFlags;
	BtnFlags = 0;
	sei();
	return temp;
}
//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
	static unsigned char BtnLockBit;				//защелка (защита от дребезга)
	static unsigned char BtnLockCoun;			//счетчик защелки (защита от дребезга)
	static unsigned char BtnLongCoun;			//счетчик длинного нажатия
	static unsigned char BtnLastState;			//последнее состояние кнопок перед отпусканием

	char mask = 0;
	if (! (BTN_PIN & BTN_LINE_UP))		mask = BTN_SHRT_UP;
	if (! (BTN_PIN & BTN_LINE_DN))		mask = BTN_SHRT_DN;
	if (! (BTN_PIN & BTN_LINE_POWER))	mask = BTN_SHRT_POWER;
	if (! (BTN_PIN & BTN_LINE_SW))		mask = BTN_SHRT_SW;

	if (mask){									//опрос состояния кнопки
		if (BtnLockCoun < (BTN_LOCK_TIME/10)){	//клавиша нажата
			BtnLockCoun++;
			return;								//защелка еще не дощитала - возврат
		}
		BtnLastState = mask;
		BtnLockBit =1;							//нажатие зафиксировано
		if (BtnLongCoun >= (BTN_LONG_TIME/10))
		return;								//возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
		if (++BtnLongCoun >= (BTN_LONG_TIME/10))
		BtnFlags |= (BtnLastState<<4);			//счетчик досчитал до максимума - устанавливаем биты длинного нажатия
	}
	else{										//клавиша отжата
		if (BtnLockCoun){
			BtnLockCoun --;
			return;								//защелка еще не обнулилась - возврат
		}
		if (! BtnLockBit)						//СТАТИЧЕСКИЙ ВОЗВРАТ
		return;
		BtnLockBit =0;							//отжатие зафиксировано
		if (BtnLongCoun < (BTN_LONG_TIME/10))
		BtnFlags |= BtnLastState;			//установка бита короткого нажатия
		BtnLongCoun = 0;					//сброс счетчика длительности нажатия
	}
}
//------------------------------------------------------****7SEG****----------------------------------------------------------
#define SEGA 6
#define SEGB 5
#define SEGC 1
#define SEGD 2
#define SEGE 3
#define SEGF 4
#define SEGG 0

#define ANOD1 4
#define ANOD2 7
#define ANOD3 4
//-------------------------------------------------------------------------------------------------------------------------
void segchar (unsigned char seg)
{
	switch (seg)
	{
		case 0:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break;
		case 1:
		PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
		case 2:
		PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
		case 3:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
		case 4:
		PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 5:
		PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 6:
		PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 7:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
		case 8:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 9:
		PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
		case 99: //OFF Все сегменты
		PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
	}
}

#define CONFIG_AMOUNT 6
typedef struct
{
	char FlagPower;
	char ValuePWM;
}ConfigurationLamp;

EEMEM ConfigurationLamp E_ConfigLamp[CONFIG_AMOUNT];
ConfigurationLamp ConfigLamp[CONFIG_AMOUNT];
//-----------------------------------------------------------------------------------------------------

void LoadingEEPROM()
{
	eeprom_read_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}

void SaveEEPROM()
{
	eeprom_update_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}
//Функция записи значения PWM
void WriteValue(unsigned char Zona,unsigned char ValuePWM)
{
	switch (Zona)
	{
		case 0:OCR0A=ValuePWM*2.56;break;
		case 1:OCR0B=ValuePWM*2.56;break;
		case 2:OCR1AL=ValuePWM*2.56;break;
		case 3:OCR1BL=ValuePWM*2.56;break;
		case 4:OCR2A=ValuePWM*2.56;break;
		case 5:OCR2B=ValuePWM*2.56;break;
	}
}
//Функция считывания значения PWM
char ReadValue(unsigned char Zona)
{
	static unsigned char ValuePWM=0;
	switch (Zona)
	{
		case 0:ValuePWM=OCR0A;break;
		case 1:ValuePWM=OCR0B;break;
		case 2:ValuePWM=OCR1AL;break;
		case 3:ValuePWM=OCR1BL;break;
		case 4:ValuePWM=OCR2A;break;
		case 5:ValuePWM=OCR2B;break;
	}
	return(ValuePWM/2.56);
}
//Функция вывода значений на 7SEG индикатор. 
unsigned char ZoneNumber=0;
unsigned char count = 0;
void WriteSeg(unsigned char NumberZon,unsigned char ValuePWM)
{
	
	unsigned char data1=ValuePWM%10;
	unsigned char data2=ValuePWM/10;
	PORTB |=(1<<ANOD1);
	PORTD |=(1<<ANOD2);
	PORTD |=(1<<ANOD3);
	segchar(99);
	if(ConfigLamp[NumberZon].FlagPower==1)
	{
		count++;
		if (count==1){
			PORTB &= ~(1<<ANOD1);
			segchar(data1);
			PORTD |=(1<<ANOD2);
			PORTD |=(1<<ANOD3);
		}
		if (count==2){
			PORTD &= ~(1<<ANOD2);
			segchar(data2);
			PORTB |=(1<<ANOD1);
			PORTD |=(1<<ANOD3);
		}
		if (count==3){
			PORTD &= ~(1<<ANOD3);
			segchar(NumberZon+1);
			PORTB |=(1<<ANOD1);
			PORTD|=(1<<ANOD2);
		}
		if (count==3){count=0;}
	}
}

/*****************************************Обновление Значений PWM********************************************************/
float TempValuePWM=0;
void UpdateValue()
{
	for (unsigned char ZonaCount = 0; ZonaCount < CONFIG_AMOUNT; ZonaCount++)
	{
		TempValuePWM=ReadValue(ZonaCount);
		if (ConfigLamp[ZonaCount].FlagPower==1)
		{
			if (TempValuePWM==0)
			{
				//Плавный Запуск
				TempValuePWM=1;
				while(TempValuePWM<ConfigLamp[ZonaCount].ValuePWM)
				{
					TempValuePWM=TempValuePWM+TempValuePWM/40;
					WriteValue(ZonaCount,TempValuePWM);
					WriteSeg(ZonaCount,TempValuePWM);
					_delay_ms(13);
				}
			}
			WriteValue(ZonaCount,ConfigLamp[ZonaCount].ValuePWM);
		}
		else
		{
			WriteValue(ZonaCount,0);
		}
		
	}
}

char Stop=1;
ISR(INT0_vect)
{
	WriteSeg(ZoneNumber,ConfigLamp[ZoneNumber].ValuePWM);
	UpdateValue();
	BtnExe();
}
void BtnUpdate(void)
{
	char BtnMask = BtnGet ();
	if (BtnMask == BTN_SHRT_POWER)
	{
		ConfigLamp[ZoneNumber].FlagPower++;
		if (ConfigLamp[ZoneNumber].FlagPower>1)
		{
			ConfigLamp[ZoneNumber].FlagPower=0;
		}
		SaveEEPROM();
	}
	if ((BtnMask == BTN_SHRT_SW))
	{
		ZoneNumber++;
		if (ZoneNumber==6)
		{
			ZoneNumber=0;
		}
	}
	//одиночное нажатие +
	if ((BtnMask == BTN_SHRT_UP)& (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		ConfigLamp[ZoneNumber].ValuePWM++;
		SaveEEPROM();
	}
	//одиночное нажатие -
	if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		ConfigLamp[ZoneNumber].ValuePWM--;
		SaveEEPROM();
	}
	//Удержание +
	if ((BtnMask == BTN_LONG_UP) & (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))
		{
			ConfigLamp[ZoneNumber].ValuePWM++;
			_delay_ms(50);
		}
		SaveEEPROM();
	}
	//Удержание -
	if ((BtnMask == BTN_LONG_DN) & (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
	{
		while ((!(PINB&0b01000000))& (ConfigLamp[ZoneNumber].ValuePWM > 0))
		{
			ConfigLamp[ZoneNumber].ValuePWM--;
			_delay_ms(50);
		}
		SaveEEPROM();
	}
}

int main(void)
{
	cli();
	LoadingEEPROM();
	init_io();
	BtnInit();
	init_int0();
	init_pwm();
	sei();
    while(1)
    {
		BtnUpdate();
	}
	return 0;
}
Если кому интересно для чего писался этот код , то ловите ссылочку.
http://radiokot.ru/forum/viewtopic.php? ... 7#p3213287
andrei23061996@gmail.com
.................................................................................................................
Грызет канифоль
Сообщения: 289
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Сообщение viiv »

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

void BtnUpdate(void)
{
..............
..............
      ZoneNumber++;
// !!!! здесь может вызваться обработчик INT0_vect??? Если да, то внимательно посмотрите, что произойдет!!!!
      if (ZoneNumber==6)
      {
         ZoneNumber=0;
      }
..........
..........
}
Последний раз редактировалось viiv Пн окт 23, 2017 17:07:05, всего редактировалось 3 раза.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Уважаемый viiv, я конечно еще не знаю всех тонкостей программирование мк. Но может все таки дадите более развернутый комментарий.
andrei23061996@gmail.com
.................................................................................................................
Грызет канифоль
Сообщения: 289
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Сообщение viiv »

Обработчик прерываний асинхронный? т.е. может прервать программу в любом месте? Тогда посмотрите внимательно, что будет, если обработчик вызовится там, где у меня комментарий (при вызове обработчика ZoneNumber равняется 6).

2)
unsigned char count = 0; // используется только в WriteSeg() - нет причин делать данную переменную глобальной; Ограничте видимость функцией WriteSeg().
Это не единственное, "видимость" чего желательно ограничить.

3)
[uquote="7seg",url="/forum/viewtopic.php?p=3213286#p3213286"]Конечно реализация плавного запуска желает желать лучшего, но она все же справляется со своими задачами.[/uquote]

Сколько времени Вы не выходите из обработчика? Вызов _delay_ms () в обработчике мне "глаза режет". :-)

4) float - применен неоправдано. Зачем нужен float?

5)
if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
Warning-ов нет??? '&' и '&&' - это разные вещи.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Насчет _delay_ms () знаю что не комильфо, но тот цикл используется всего лишь один раз при включении а более элегантного решения в голову не пришло (.
Насчет всего остального спасибо еще немного покопаюсь в этом коде.
А насчет 5того пункта компилятор не выдавал Warningов , хотя согласен логическое И правильней писать &&, странно что эти условия еще и работали.
andrei23061996@gmail.com
.................................................................................................................
Грызет канифоль
Сообщения: 289
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Сообщение viiv »

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

if (ZoneNumber==6)
У Вас же есть специальная константа для этого (CONFIG_AMOUNT). Нет причин ее здесь не использовать - потенциальная причина ошибок.

Таких мест не одно.

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

while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))
BTN_PIN и BTN_LINE_UP - вроде для того и определили, чтобы в данном случае не PINB и 0b10000000 написАть, а что-то более понятное.

Ну и я бы, написал

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

if (ZoneNumber >= CONFIG_AMOUNT)
Т.е. по тексту сразу видно, что проверяется выход за переделы диапазона. А сравнение с константой на равенство, на мой взгляд, менее информативно :-)

ЗЫ. Логику не смотрел вообще. Но например, логика опроса кнопок станная.

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

#define BTN_SHRT_UP         (1<<0)         /*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN         (1<<1)         /*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER      (1<<2)         /*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW         (1<<3)         /*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP         (1<<4)         /*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN         (1<<5)         /*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW         (1<<6)         /*бит короткого нажатия кнопки SW*/
Могут одновревенно стоять носколько бит? Если да, то (BtnMask == BTN_SHRT_UP) - неверно. Если нет, то и незачем определять, как маску - это сильно запутывет.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 303
Зарегистрирован: Ср май 03, 2017 03:22:26

Сообщение 7seg »

Спасибо за более конструктивную критику у себя внес некоторые изменения.
andrei23061996@gmail.com
.................................................................................................................
Ответить

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