Перейти к содержанию

Как избавиться от лишних операторов IF-ELSE?


-=FISHER=-

Рекомендуемые сообщения

Добрый день!

Уже не в первый раз сталкиваюсь с такой ситуацией, начинаю писать программу для Attiny13 и вот уже почти дописал, отладил, как упираюсь в объем Flash памяти МК, да он тут самый маленький в классе, всего 1 Кб, но дело в том, что есть множество аналогичных устройств с ещё большим количеством функций, и код которых вполне уместился в этот 1 Кб. И я начинаю задумываться, что я слишком "размашисто" пишу свой код. Не так давно на этом форуме человек под ником @ARV научил меня такому замечательному приёму как "Конечный автомат состояний", в итоге применив его у меня получилось в несколько раз сократить размер кода для своего "Удлинителя поворотника", @ARV, если Вы это читаете, то знайте я дописал ту программу и моё устройство прекрасно работает. Спасибо Вам большое :)

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

Техническое задание:

1. Включение ДХО только при заведённом двигателе (завод двигателя контролируем через контроль напряжения бортсети, напряжение стало выше условно 14В - включить ДХО, ниже - выключить) (чтобы сберечь светодиоды от бросков напряжения при заводе двигателя);

2. Притухание ДХО, на заранее запрограммированный уровень яркости (0-100%) при включении габаритов (чтобы не слепить ночью);

3. Отключение ДХО при поднятом ручнике (чтобы можно было стоять в темноте с заведенным двигателем без единого включенного светового прибора);

4. Раздельная настройка яркости для обычного режима и режима с габаритами с помощью одной кнопки.

 

Выкладываю схему устройства в Протеусе:

Скрытый текст

LEDDRL1.1.thumb.jpg.59a2586d6bea5a65ce7b168102cb5d8a.jpg

Далее выкладываю свой код, среда разработки Atmel Studio, функции связанные с ШИМ и АЦП в отдельных библиотеках их выкладывать не стал, так как сам их не писал, а вязл готовые. Основной объём всё равно занимает главный модуль LEDDRL.c

main.h

Скрытый текст

#ifndef MAIN_H_
#define MAIN_H_

#include "LEDDRL.h"
#include "pwm.h"
#include "adc.h"

#define F_CPU 8000000UL //задаем тактовую частоту

#define PIN_DRL 0 //порт для подключения ДХО
#define PIN_LIGHTS _BV(PB1) //порт для габаритов
#define PIN_BTN _BV(PB2) //(1<<PB2) порт для кнопки настройки
#define PIN_BRAKE _BV(PB3) //порт для ручника

#define DRL_ON 0x4 //команда включения ДХО
#define LIGHTS_ON 0x6 //команда включения габаритов
#define BRAKE_ON 0xC //команда включения ручника в режиме ДХО
#define LIGHTS_BRAKE_ON 0xE //команда включения ручника в режиме габаритов
#define BUTTON_DRL 0x0
#define BUTTON_LIGHTS 0x2

#define ANY_K (PIN_LIGHTS|PIN_BTN|PIN_BRAKE)

#define ERROR 10

#define BRIGHT_FULL 255
#define BRIGHT_LIGHTS 128

#include <avr/io.h> //подключаем библиотеку для работы с портами ввода/вывода
#include <util/delay.h> //подключаем библиотеку для работы с задержками
#include <avr/interrupt.h> //подключаем библиотеку для работы с прерываниями
#include <avr/eeprom.h> //подключаем библиотеку для работы с EEPROM

#endif /* MAIN_H_ */

 

 

LEDDRL.c

Скрытый текст

#include "main.h"

unsigned int adc_value, code, command, allow_drl, drl_turn_on, bright_DRL=128, bright_Light=50, delay=300, direction=0, trig, trig1;
float n;

typedef enum //список возможных состояний
{
	DRL,
	LIGHTS
} state_t;

state_t state=DRL;

void flash(void) //функция для мигания
{
	TCCR0A^=(1<<COM0A1);
	_delay_ms(10);
}

void pause(void) //функция паузы
{
	while(delay>0){delay--;_delay_ms(10);}
	delay=300;
}

uint8_t get_button(void)
{
	return PINB & ANY_K;
}

uint8_t get_code(void) //опрос портов
{
	code=get_button();
	_delay_ms(10);
	if(code!=get_button()){code=ERROR;}
	return code;
}

void port_ini(void)
{
	PORTB|=(1<<2)/*|(1<<PIN_DRL)*/; //подключаем подтягивающий резистор к порту для кнопки
	DDRB|=(1<<PIN_DRL); //включаем порт для ДХО на выход
}

void Check_Engine(void) //функция проверки заведен ли двигатель
{
	adc_value=ADC_convert(); //измеряем напряжение борт сети
	if(adc_value<700){allow_drl=0;} //если напряжение бортсети меньше 13,8 В, отключаем ДХО (3,37 В после делителя),
																							//а значит при коэффециенте 205 (1024 разрешающая способность
																							//делим на опорное напряжение, в нашем случае напряжение питания МК 5 В, 1024/5 = 205)
																							//показания АПЦ будут равны 3,37 В * коэфф. 205 = 690, округляем получается 700
	else{allow_drl=1;} //если напряжение бортсети выше 13,8 В, включаем ДХО с задержкой 3 секунды
}

int main(void)
{
	port_ini();
	pwm_ini();
	ADC_Init();
    while(1)
    {
		Check_Engine(); //проверяем заведен ли двигатель
		command=get_code(); //считываем состояние входов
		switch(state) //конечный автомат состояний
		{
			case DRL: //режим ДХО
			{		
				if(command==DRL_ON){if(allow_drl){if(drl_turn_on==0){pause(); TCCR0A|=(1<<COM0A1); drl_turn_on=1;}else{if(trig){trig=0; bright_DRL=OCR0A;} TCCR0A|=(1<<COM0A1);   }}else{TCCR0A&=~(1<<COM0A1);drl_turn_on=0;}} //включаем ДХО
				if(command==LIGHTS_ON){while(OCR0A>bright_Light){OCR0A--; _delay_ms(10);} state=LIGHTS;} //притухание при включение габаритов
				if(command==BRAKE_ON){TCCR0A&=~(1<<COM0A1); drl_turn_on=0;} //включен ручник
				if(command==BUTTON_DRL){if(trig==0){trig=1; if(direction){direction=0;}else{direction=1;}}if(direction){if(OCR0A!=255){OCR0A++;_delay_ms(10);}else{flash();}}else{if(OCR0A!=0){OCR0A--;_delay_ms(10);}else{flash();}}} //нажата кнопка настройки
			break;
			}
			
			case LIGHTS: //режим габаритов
			{
				if(command==LIGHTS_ON){if(allow_drl){if(drl_turn_on==0){pause(); TCCR0A|=(1<<COM0A1); drl_turn_on=1;}else{if(trig1){trig1=0; bright_Light=OCR0A;}}}else{TCCR0A&=~(1<<COM0A1);drl_turn_on=0;}}
				if(command==DRL_ON){while(OCR0A<bright_DRL){OCR0A++; _delay_ms(10);} state=DRL;}
				if(command==BUTTON_LIGHTS){if(trig1==0){trig1=1; if(direction){direction=0;}else{direction=1;}}if(direction){if(OCR0A!=255){OCR0A++;_delay_ms(10);}else{flash();}}else{if(OCR0A!=0){OCR0A--;_delay_ms(10);}else{flash();}}}
			break;
			}
		}
    }
}

 

Спасибо всем за внимание!

Изменено пользователем -=FISHER=-

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

Сперва надо бы отформатировать код в удобочитаемом виде.

То есть преобразуй строки типа этой:

if(command==DRL_ON){if(allow_drl){if(drl_turn_on==0){pause(); TCCR0A|=(1<<COM0A1); drl_turn_on=1;}else{if(trig){trig=0; bright_DRL=OCR0A;} TCCR0A|=(1<<COM0A1); }}else{TCCR0A&=~(1<<COM0A1);drl_turn_on=0;}} //включаем ДХО

В такой вид

if(command==DRL_ON)
{
    if(allow_drl)
    {
        if(drl_turn_on==0)
        {
            pause();
            TCCR0A|=(1<<COM0A1);
            drl_turn_on=1;
        }
        else
        {
            if(trig)
            {
                trig=0;
                bright_DRL=OCR0A;
            }
            TCCR0A|=(1<<COM0A1);
        }
    }
    else
    {
        TCCR0A&=~(1<<COM0A1);
        drl_turn_on=0;
    }

} //включаем ДХО

 

Изменено пользователем Vascom
Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

13 минуты назад, Vascom сказал:

То есть преобразуй строки типа этой:

Готово.

Скрытый текст



#include "main.h"

unsigned int adc_value, code, command, allow_drl, drl_turn_on, bright_DRL=128, bright_Light=50, delay=300, direction=0, trig, trig1;
float n;

typedef enum //список возможных состояний
{
	DRL,
	LIGHTS
} state_t;

state_t state=DRL;

void flash(void)
{
	TCCR0A^=(1<<COM0A1);
	_delay_ms(10);
}

void pause(void)
{
	while(delay>0){delay--;_delay_ms(10);}
	delay=300;
}

uint8_t get_button(void)
{
	return PINB & ANY_K;
}

uint8_t get_code(void) //опрос портов
{
	code=get_button();
	_delay_ms(10);
	if(code!=get_button()){code=ERROR;}
	return code;
}

void port_ini(void)
{
	PORTB|=(1<<2)/*|(1<<PIN_DRL)*/; //подключаем подтягивающий резистор к порту для кнопки
	DDRB|=(1<<PIN_DRL); //включаем порт для ДХО на выход
}

void Check_Engine(void)
{
	adc_value=ADC_convert(); //измеряем напряжение борт сети
	if(adc_value<700){allow_drl=0;} //если напряжение бортсети меньше 13,8 В, отключаем ДХО (3,37 В после делителя),
																							//а значит при коэффециенте 205 (1024 разрешающая способность
																							//делим на опорное напряжение, в нашем случае напряжение питания МК 5 В, 1024/5 = 205)
																							//показания АПЦ будут равны 3,37 В * коэфф. 205 = 690, округляем получается 700
	else{allow_drl=1;} //если напряжение бортсети выше 13,8 В, включаем ДХО с задержкой 3 секунды
}

int main(void)
{
	port_ini();
	pwm_ini();
	ADC_Init();
    while(1)
    {
		Check_Engine(); //проверяем заведен ли двигатель
		command=get_code(); //считываем состояние входов
		switch(state) //конечный автомат состояний
		{
			case DRL: //режим ДХО
			{		
				if(command==DRL_ON) //включаем ДХО
				{
					if(allow_drl)
					{
						if(drl_turn_on==0)
						{
							pause(); TCCR0A|=(1<<COM0A1); drl_turn_on=1;
						}
						else
						{
							if(trig){trig=0; bright_DRL=OCR0A;
						}
						TCCR0A|=(1<<COM0A1);
						}
					}
					else
					{
						TCCR0A&=~(1<<COM0A1);drl_turn_on=0;
					}
				} 
				if(command==LIGHTS_ON) //притухание при включение габаритов
				{
					while(OCR0A>bright_Light)
					{
						OCR0A--; _delay_ms(10);
					}
					state=LIGHTS;
				} 
				if(command==BRAKE_ON) //включен ручник
				{
					TCCR0A&=~(1<<COM0A1); drl_turn_on=0;
				} 
				if(command==BUTTON_DRL)
				{
					if(trig==0)
					{
						trig=1;
						if(direction)
						{
							direction=0;
						}
						else
						{
							direction=1;
						}
					}
					if(direction)
					{
						if(OCR0A!=255)
						{
							OCR0A++;_delay_ms(10);
						}
						else
						{
							flash();
						}
					}
					else
					{
						if(OCR0A!=0){OCR0A--;_delay_ms(10);}else{flash();
					}
				}
			} //нажата кнопка настройки
			break;
			}
			
			case LIGHTS: //режим габаритов
			{
				if(command==LIGHTS_ON)
				{
					if(allow_drl)
					{
						if(drl_turn_on==0)
						{
							pause(); TCCR0A|=(1<<COM0A1); drl_turn_on=1;
						}
						else
						{
							if(trig1)
							{
								trig1=0; bright_Light=OCR0A;
							}
						}
					}
					else
					{
						TCCR0A&=~(1<<COM0A1);drl_turn_on=0;
					}
				}
				if(command==DRL_ON)
				{
					while(OCR0A<bright_DRL)
					{
						OCR0A++; _delay_ms(10);
					}
					state=DRL;
				}
				if(command==BUTTON_LIGHTS)
				{
					if(trig1==0)
					{
						trig1=1;
						if(direction)
						{
							direction=0;
						}
						else
						{
							direction=1;
						}
					}
					if(direction)
					{
						if(OCR0A!=255)
						{
							OCR0A++;_delay_ms(10);
						}
						else
						{
							flash();
						}
					}
					else
					{
						if(OCR0A!=0)
						{
							OCR0A--;_delay_ms(10);
						}
						else
						{
							flash();
						}
					}
				}
			break;
			}
		}
    }
}

 

 

Изменено пользователем -=FISHER=-

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Сделай case вместо

if(command==LIGHTS_ON)
if(command==DRL_ON)
...

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

Изменено пользователем Vascom
Ссылка на комментарий
Поделиться на другие сайты

8 минут назад, Vascom сказал:

Сделай case вместо

Сейчас попробую, но мне кажется у меня ещё смысловые ошибки что ли или как сказать...

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

9 минут назад, BARS_ сказал:

Кнопку настройки крайне желательно подтянуть внешним резистором.

Внутренней подтяжки думаете не хватит?

8 минут назад, Vascom сказал:

Беда.

Это Вы о чем конкретно? Сейчас поменял If-else на case, размер не изменился ни на сотую процента :-(

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

4 минуты назад, BARS_ сказал:

А можно еще так:


direction = !direction;

Это освободило 0,4 % процесс пошел. Наверняка ещё много подобных ошибок есть в моем коде, пожалуйста проверьте его :(

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

1 минуту назад, -=FISHER=- сказал:

думаете не хватит?

В условии внешних помех, как в машине, может не хватить.

 

1 минуту назад, -=FISHER=- сказал:

размер не изменился

Только что проверил, правда для 8051 МК.

if(a == 1){
	b = 2;
}else
if(a == 2){
	b = 3;
}

Занимает меньше флеша, чем 

switch(a){
case 0: b = 1; break;
case 1: b = 2; break;
}

 

Ссылка на комментарий
Поделиться на другие сайты

3 минуты назад, Vascom сказал:

В общем этот код пилить и пилить можно.

Он не такой уж большой, посмотрите пожалуйста где я ещё откровенно туплю:unsure:

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

Да, кстати, зачем вам столько переменных типа unsigned int? Они же целых два байта занимают. unsigned char для большинства из них более чем достаточно.

Переменная adc_value совсем не нужна,

	adc_value=ADC_convert(); //измеряем напряжение борт сети
	if(adc_value<700){allow_drl=0;}

можно сделать так

if(ADC_convert() < 700){//измеряем напряжение борт сети
   allow_drl = 0;
}

 

Ссылка на комментарий
Поделиться на другие сайты

3 минуты назад, BARS_ сказал:

Да, кстати, зачем вам столько переменных типа unsigned int?

 

7 минут назад, Vascom сказал:

тип unsigned int у тебя 8-битный?

Да вы правы!! 20% объёма как с куста после замены для всех переменных кроме adc_value тип на unsigned char! Забылся и всем присвоил unsigned int !

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, BARS_ сказал:

Так ее вообще можно убрать из кода

Спасибо, уберу.

4 часа назад, Vascom сказал:

однобитовые переменные вообще можно представлять как отдельные биты одной переменной.

Да, действительно, большинство переменных принимают значения только 0 и 1, так и сделаю.

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

16 часов назад, Vascom сказал:

эти твои однобитовые переменные вообще можно представлять как отдельные биты одной переменной.

Сделал вот так, создал одну переменную status, продефайнил её биты:

#define ALLOW_DRL 0
#define DRL_TURN_ON 1
#define DIRECTION 2
#define TRIG 3
#define TRIG1 4

и заменил все операции с переменной drl_turn_on на операции с битом DRL_TURN_ON переменной status:

drl_turn_on=0;      ======>      status&=~(1<<DRL_TURN_ON);

drl_turn_on=1;      ======>      status|=(1<<DRL_TURN_ON);

if(drl_turn_on==0)      ======>      if(!(status&(1<<DRL_TURN_ON)));

if(drl_turn_on)      ======>      if(status&(1<<DRL_TURN_ON));

В итоге получил только увеличение объёма кода на 3%... Думаю что это из-за увеличения арифметических операций, в первой строчке в левом столбце я просто присвоил 0 переменной, а в правом сначала идёт битовый сдвиг, потом операция инверсии, потом ещё и сложение.

Изменено пользователем -=FISHER=-

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

5 hours ago, -=FISHER=- said:

Сделал вот так, создал одну переменную status, продефайнил её биты:

Это не совсем то. Не знаю как в АтмелСтудио, а в CVAVR есть тип bit. Эти переменные помещаются компилятором в регистр. Код получается проще и компактней.

bit adc_ready   = 0;
bit u_akk_fault = 0;

...
  adc_ready   = 1;
...

Каждая команда заменяется одной ассемблерной инструкцией.

Ссылка на комментарий
Поделиться на другие сайты

5 часов назад, -=FISHER=- сказал:

Сделал вот так, создал одну переменную status, продефайнил её биты:

drl_turn_on=0;      ======>      status&=~(1<<DRL_TURN_ON);

drl_turn_on=1;      ======>      status|=(1<<DRL_TURN_ON);

if(drl_turn_on==0)      ======>      if(!(status&(1<<DRL_TURN_ON)));

if(drl_turn_on)      ======>      if(status&(1<<DRL_TURN_ON));

Ты пытаешься сделать универсально или оптимизировать?

Может надо отказаться от

status&=~(1<<DRL_TURN_ON);

и делать сразу

status &= 0xFD;

То есть разворачивай твои логические и арифметические операции.

Ссылка на комментарий
Поделиться на другие сайты

5 часов назад, -=FISHER=- сказал:

а в правом сначала идёт битовый сдвиг, потом операция инверсии, потом ещё и сложение.

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

это:

  status&=~(1<<DRL_TURN_ON);

эквивалентно

status&=0b11111110;

те регистр логически умножается на константу результат записывается в регистр.

7 минут назад, snn_krs сказал:

Каждая команда заменяется одной ассемблерной инструкцией.

какой? насколько помнитцо в аврах изменение битов напрямую доступно только для регистров портов ввода/вывода или нет?

Ссылка на комментарий
Поделиться на другие сайты

10 минут назад, Vascom сказал:

Ты пытаешься сделать универсально или оптимизировать?

нужен компромисс 

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...