Умный дом на микроконтроллере ATMEGA1280 часть-2.

Итак в этой статье рассмотрим такие пункты:

1.Подключение модулей ICPCON к контроллеру.
2.Опрос модулей по протоклу DCON.

В следующих статьях:
3.Подключение панелей Weintek.
4.Опрос панелей по MODBUS.
5.Подключение модема.
6.Управление модемом.

Итак пункт 1:
Подключение модулей ICPCON к контроллеру.

В моем самопальном контроллере все порты заточены под RS232 интерфейс. А у модулей ICPCON шина данных работает по RS485. Поэтому я использовал модуль преобразователь ICPCON7520. Преобразует шину RS232 в RS485 и наоборот. Кстати есть разные преобразователи.

Вот они.


А вот их потроха.








Есть еще ETHERNET преобразователь в RS485, RS232. Вообще крутая вещь. Задаешь этому модулю IP адрес. Подключаешь к сети где нибудь у бабки Пеллагеи. А комп твой находится дома в городе. На комп ставишь спец утилиту и ура, через этот модуль по Ethernet можно работать удаленно с линией RS485.

Ладно хватит бла-бла-бла про преобразователи. Вот фото подключения модулей к контроллеру.



Как видим там ничего особенного нет. Контроллер подключается к преобразователю, а к преобразователю уже модули. Вот и все. Сам преобразователь имеет внутри себя гальваническую развязку между интерфейсами RS485 и RS232. Она равна 3000 вольт. Нехило. Часто линия RS485 лежит рядом с силовыми кабелями в лотках и вообще где угодно. На линию RS485 часто бывают наводки от силовsх линий, от молний и т.д. Но благодаря гальванической развязке в модуле, центральный управляющий контроллер если что, останется живой. Не сгорит. И не потащит за собой в могилу другое оборудование которое подключено на его другие порты. Модуль сам подстраивается под скорость передачи данных. И сам переключается на прием или передачу данных. Это очень удобно при написании кода. Но все же нельзя приняв последний байт из линии, тут же в ответ стрелять в линию другим байтом. Скорость переключения модуля на ответ/передачу, высокая, но все же микроконтроллер еще быстрее. Когда я писал код, то этот эффект имел место. Первый байт отправляемый в линию то проскакивал, то нет. Пришлось ввести небольшую задержку на отправку данных в линию. Проблема сразу пропала.

Два провода питания плюс и минус идут шлейфом от модуля к модулю. Так же и шина RS485 идет двумя проводами от модуля к модулю. Обычно это должна быть витая пара и экранированная. Есть спец кабель именно для RS485. Но он нехило дорогой. Поэтому часто RS485 делают их соплей. Тобишь из огрызков обычной витой пары. В моем случае я заказал обычную витую пару 5й категории, но экранированную. Работает отлично на 115200 кбт/сек. Это максимальная скорость для ICPCON. Правда длинна линии не большая, около 100 метров.

Это можно пропустить.
Как то раз я участвовал в проекте по подключению устройств к RS485 шине. Длина линии была очень даже приличная. Может километр и будет. Устройств на линии было тоже не мало. Что то около 25 штук. И знаете из какого провода была шина? Какой то четырех жильный кабель который используют для прокладки под землей. Не экранированный и жилы в кабеле не свиты между собой. Скорость как сейчас помню в линии 38400 кбт/сек. И что вы думаете, работает система уже как лет 5 до сих пор.

Это тоже можно пропустить.
Пару слов об преобразователе интерфейсов USB в RS232, RS485,RS422. На фото выше он есть. Удобная вещь просто зверски. Во первых все три интерфейса в одном флаконе. Во вторых есть гальваническая развязка от USB порта. Это значит можно смело экспериментировать, ваш любимый ноутбук не сгорит. В третьих сам модуль питается от порта USB. Просто втыкаешь модуль к USB и все. И у тебя сразу есть рабочая промышленная точка подключения оборудования к RS485. Твой ноут сразу превращается одним движением руки из штанов в шорты, в головную станцию управлением чего нибудь.

Итак пункт 2:
Опрос модулей по протоклу DCON.
Как было сказано в предыдущей статье модули работают по протоколу DCON. Этот протокол ASCII символьный. Как я уразумел, этот протокол был разработан именно для этих модулей. То есть он узко заточенный именно под эти модули. Как то и где то его еще применить, кажется никак. Что мне понравилось в нем, так это то что, команды запроса и ответа от модулей очень короткие. И это вещь. Любой модуль ICPCON поддерживает около 30 команд этого протокола. Часть этих команд служит для настройки модуля и нужны в общем то единожды. Часть команд нужны для настройки сторожевого таймера. Можно считать версию прошивки и т.д. Все настройки хранятся в EEPROM модуля. Но реально в линии RS485 нужны всего 1 или 2 команды и все. Если в нашем модуле есть только входы, тогда нужна только одна команда считывания состояния входов. Если в модуле есть и входы и выходы, тогда нужна еще одна команда записи новых значений в выходы. Итак представим себе что к нашему контроллеру подключен пока только один модуль 7050D.

Вот он.



Видим что у него есть 7 цифровых входов и 8 цифровых выхода. К входам можем подключить только контакты или замкнутые или разомкнутые. И это все. Больше ничего. К выходам например реле. Контакты могут размыкаться и замыкаться когда захотят, на контакты могут нажимать люди если допустим это включатели света. Или это могут быть контакты какого нибудь оборудования, например термореле, концевики, да что угодно. В любой момент времени мы можем считать состояние контактов и сохранить это значение в озу микроконтроллера. Потом посмотреть на это значение и воскликнуть. О… давно пора опустить графитовые стержни в атомный реактор!!! Следующей командой уже воздействуем на реле этого же модуля. За раз одной командой можем часть реле включить, а часть выключить.

Итак, как считываем состояние входов? А вот такой командой:
@02 — в общем то это все. Получив такую команду модуль понимает что к нему поступил запрос на считывание всех его цифровых каналов, к цифровым каналам относится состояние и входов и выходов. Эту команду примут все модули на линии, ведь они же все подключены параллельно на шину. Но ответит на команду только тот модуль которому адресована эта команда. Итак что мы имеем в команде:
@ — это просто символ разделитель он нужен перед командой, означает начало команды.
02 — это адрес модуля которому предназначена команда.
Вот как бы и все, но не все. Мы забыли, что это команда ASCII символьная. Лезем в таблицу ASCII символов и видим что @ это 0x40, 0 это 0x30, 2 это 0x32. Значит в порт USART надо выдать 4 байта со значениями:
40 30 32 0D — вот теперь точно все. Последний байт 0x0D это конец команды. Он нужен всегда. Модуль приняв этот байт знает что передача данных ему завершена.
Как только модуль принял команду, он формирует ответ. например такой. >0F00
> — это просто символ разделитель он нужен перед командой, означает начало команды.
0F — состояние релейных выходов. В данном случае это значение такое 00001111. Ну значит реле с DO0 по DO3 включены, а DO4 по DO7 выключены. Этот байт может принимать любое значение от 0 до FF.
00 — состояние входов. На данный момент все входы зажаты на минусовой провод питания. Если все контакты разжать, то значение было бы 7F ну или 01111111. Входов то у нас всего 7. Самый старший бит — пустышка. Не забываем что это ASCII символьный ответ. Поэтому если каждый символ превевести в hex, то наша Atmega на самом деле примет такие символы:
3E 30 46 30 30 0D

Еще возможные варианты ответа:
?02 — неверная команда.
— нет ответа.
Итак, получив верный ответ от модуля, его надо сохранить в ОЗУ. Но у нас ответ то ASCII символьный. То есть значение входов пришло в виде двух байтов 30 30. А нам бы запихнуть это значение в один байт, чтоб можно было анализировать. Можно так сделать:

        uint8_t b1, b2, result;
	b1=buff_1[0];
	b2=buff_1[1];
	result = ASCII_TO_HEX(b1) << 4;
	result |= ASCII_TO_HEX(b2);
	out_I_7050D_02=result;
	b1=buff_1[2];
	b2=buff_1[3];
	result = ASCII_TO_HEX(b1) << 4;
	result |= ASCII_TO_HEX(b2);
	in_I_7050D_02=result;


А эта сама функция ASCII_TO_HEX.

uint8_t ASCII_TO_HEX(uint8_t hex)//из  ASCII в HEX
{
	return hex > '9' ? (hex - 'A' + 10) : (hex - '0');
}



В итоге в однобайтной переменной out_I_7050D_02 находится значение выходов (модуля с адресом 2), а в переменной in_I_7050D_02 (модуля с адресом 2)находится значение входов.
Кстати функцию ASCII_TO_HEX и все что с ней связано предложил мне товарищ neiver
Вот теперь можно анализировать в программе состояние входов модуля, кто чего нажал или нажалось. Это примерно делается так.


if (BitIsSet(in_I_7050D_02,di01))     //Охранная зона №2
{
    SetBit(I7050D_02,SIREN_2_FL);     //Включили сирену 2го этажа
}
else
{
    ClearBit(I7050D_02,SIREN_2_FL);   //Выключили сирену 2го этажа
}



Итак с принятием данных разобрались. Сейчас второй этап. Надо послать команду включить какое нибудь реле в модуле.
Команда для этого такая: @02AA
@ — просто символ разделитель он нужен перед командой, озн. начало команды
02 — адрес модуля к которому делается запрос.(от 00 до FF)
AA — выходы выставим в такое значение 10101010. Выходы DO1,DO3,DO5,DO7 включаем, остальные выключаем. Этот байт может иметь значение от 0 до FF. Значение зависит от того какие выходы мы хотим включить.

Возможные ответы модуля:
> — если команда принята.
? АА — если команда неверная.
! АА — проигнорированная команда.
— ничего не ответил.

В итоге после преобразования команды в ASCII имволы в порт USART вывалится это: 40 30 32 41 41 0D

Преобразование в ASCII символы можно сделать так:

//преобразуем наши данные OUT в формат ASCII чтоб отправить 
//в модули и уст. выходы в нужное значение
uint8_t hex;
hex=I7050D_02;
buff_2[25] =  HEX_TO_ASCII(hex & 0x0f);//out_02_1
buff_2[24] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_02_2

Сама функция HEX_TO_ASCII.

uint8_t HEX_TO_ASCII(uint8_t bin)//из HEX в ASCII
{
	return bin > 9 ? (bin + 'A' - 10) : (bin + '0');
}


Теперь в переменных buff_2[25] и buff_2[24] лежат наши значения в ASCII символах, для того чтоб установить значения реле в нужное состояние.

Причем это касается только байтов 4 и 5. Остальные байты данной команды:
(40 30 32 x x 0D) заранее забиваем во флеш в ASCII виде. Их просто извлекаем когда надо и все.

В итоге на 18 модулей получилась такая последовательность команд выдаваемых в USART:
Команды идут в USART в том порядке в котором указаны ниже.

Чтение входов      адрес модуля  модель модуля
модулей
@110D			17        I7050D
@100D			16        I7050D
@0D0D			13        I7050D
@0C0D			12        I7050D
@0B0D			11        I7050D
@0A0D			10        I7050D
@080D			8         I7050D
@070D			7         I7050D
@060D			6         I7050D
@050D			5         I7050D
@040D			4         I7050D
@030D			3         I7050D
@020D			2         I7050D
@120D			18        I7051D
@090D			9         I7051D
@0F0D			15        I7051D
@0E0D			14        I7051D
#0100D			1         I7033

Запись выходов
модулей
@11000D 		17        I7050D
@10000D			16        I7050D
@0D000D			13        I7050D
@0C000D			12        I7050D
@0B400D			11        I7050D
@0A800D			10        I7050D
@08000D			8         I7050D
@07400D			7         I7050D
@06000D			6         I7050D
@05000D			5         I7050D
@04080D			4         I7050D
@03030D			3         I7050D
@02000D			2         I7050D


Не обязательно опрашивать, модули прям таки по порядку, можно и в разнобой. Это не существенно.
Кстати есть такой термин как скорость передачи данных и период опроса всех модулей на линии. Ну и за сколько же времени Atmega успевает пройти весь цикл опроса модулей? Я просто сделал эксперимент. Попробовал включать/выключать выход одного из модулей каждые 100 мсек. Не пошло. Иногда то больше времени горит, то меньше. В итоге при попытке включать/выключать выход каждые 250 мсек, стало работать стабильно. Получается что период опроса 18 модулей с опросом входов и обновлением выходов происходит примерно +/- за 250 мсек. Не плохо.

В итоге получилось вот что. Сперва я объявил глобальные переменные для хранения данных от модулей и к модулям.

uint8_t buff_0[7]; //+020.64 храним ответ о температуре в ASCII формате от I_7033
int8_t Temperatyra;//тут храним показания температуры на улице в hex формате. Обновляем раз в 25 сек (предел от -99 до 99)

uint8_t buff_1[5]; //временные данные от модулей в ASCII формате.
uint8_t buff_2[26];//сюда забрасываем данные для упр. выходами модулей. ICPCON

uint8_t in_I_7050D_02; //считанные значения входов
uint8_t out_I_7050D_02;//считанные значения выходов
uint8_t I7050D_02;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_03; //считанные значения входов
uint8_t out_I_7050D_03;//считанные значения выходов
uint8_t I7050D_03;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_04; //считанные значения входов
uint8_t out_I_7050D_04;//считанные значения выходов
uint8_t I7050D_04;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_05; //считанные значения входов
uint8_t out_I_7050D_05;//считанные значения выходов
uint8_t I7050D_05;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_06; //считанные значения входов
uint8_t out_I_7050D_06;//считанные значения выходов
uint8_t I7050D_06;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_07; //считанные значения входов
uint8_t out_I_7050D_07;//считанные значения выходов
uint8_t I7050D_07;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_08; //считанные значения входов
uint8_t out_I_7050D_08;//считанные значения выходов
uint8_t I7050D_08;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in0_I_7051D_09;//считанные значения входов
uint8_t in1_I_7051D_09;//считанные значения входов

uint8_t in_I_7050D_A_10; //считанные значения входов
uint8_t out_I_7050D_A_10;//считанные значения выходов
uint8_t I7050D_A_10;	 //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_B_11; //считанные значения входов
uint8_t out_I_7050D_B_11;//считанные значения выходов
uint8_t I7050D_B_11;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_C_12; //считанные значения входов
uint8_t out_I_7050D_C_12;//считанные значения выходов
uint8_t I7050D_C_12;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_D_13; //считанные значения входов
uint8_t out_I_7050D_D_13;//считанные значения выходов
uint8_t I7050D_D_13;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in0_I_7051D_E_14;//считанные значения входов
uint8_t in1_I_7051D_E_14;//считанные значения входов

uint8_t in0_I_7051D_F_15;//считанные значения входов
uint8_t in1_I_7051D_F_15;//считанные значения входов

uint8_t in_I_7050D_10_16; //считанные значения входов
uint8_t out_I_7050D_10_16;//считанные значения выходов
uint8_t I7050D_10_16;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in_I_7050D_11_17; //считанные значения входов
uint8_t out_I_7050D_11_17;//считанные значения выходов
uint8_t I7050D_11_17;     //сюда забрасываем данные для упр. вых. модулей

uint8_t in0_I_7051D_12_18;//считанные значения входов
uint8_t in1_I_7051D_12_18;//считанные значения входов


В обработчике прерываний на отправку данных такой:

SetBit(PORTF, TXD_0);	//Зажгли LED отправки байта
	
//здесь отправляем байты  модулям ICPCON.		

if (f_2==0)//считываем значения входов и выходов
{
	UDR0=pgm_read_byte(UKAZATEL_1);
	d_0=pgm_read_byte(UKAZATEL_1);
	UKAZATEL_1++;
	d_10++;
	switch (d_10)                             
		{
			case 3:US0_CNT=17;break;	//опрос модуля I_7050D (17)
			case 7:US0_CNT=16;break;	//опрос модуля I_7050D (16)
			case 11:US0_CNT=13;break;	//опрос модуля I_7050D (13)
			case 15:US0_CNT=12;break;	//опрос модуля I_7050D (12)
			case 19:US0_CNT=11;break;	//опрос модуля I_7050D (11)
			case 23:US0_CNT=10;break;	//опрос модуля I_7050D (10)
			case 27:US0_CNT=8;break;	//опрос модуля I_7050D (8)
			case 31:US0_CNT=7;break;	//опрос модуля I_7050D (7)
			case 35:US0_CNT=6;break;	//опрос модуля I_7050D (6)
			case 39:US0_CNT=5;break;	//опрос модуля I_7050D (5)
			case 43:US0_CNT=4;break;	//опрос модуля I_7050D (4)
			case 47:US0_CNT=3;break;	//опрос модуля I_7050D (3)
			case 51:US0_CNT=2;break;	//опрос модуля I_7050D (2)
			case 55:US0_CNT=18;break;	//опрос модуля I_7051D (18)
			case 59:US0_CNT=9;break;	//опрос модуля I_7051D (9)
			case 63:US0_CNT=15;break;	//опрос модуля I_7051D (15)
			case 67:US0_CNT=14;break;	//опрос модуля I_7051D (14)
			case 72:US0_CNT=1;break;	//опрос модуля I_7033 (1)
			
			default:break;
		}
		
	if (d_0==0x0D)
	{
		UDRIE0_OFF();//выкл. опрос модулей
		f_0=1;       //Запуск таймера опроса модулей.
		d_2++;
	}

	if (d_2==18)
	{
		UKAZATEL_1=I_7050D_11;//на флеш
		UKAZATEL_2=&buff_2[0];//на озу
		d_10=0;
		d_2=0;
		f_2=1;
	}


Код преобразования из hex в ASCII.

//преобразуем наши данные для OUT в формат ASCII чтоб отправить 
//в модули и установить выходы в нужное значение
uint8_t hex;
hex=I7050D_02;
buff_2[25] =  HEX_TO_ASCII(hex & 0x0f);//out_02_1
buff_2[24] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_02_2

hex=I7050D_03;
buff_2[23] =  HEX_TO_ASCII(hex & 0x0f);//out_03_1
buff_2[22] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_03_2

hex=I7050D_04;
buff_2[21] =  HEX_TO_ASCII(hex & 0x0f);//out_04_1
buff_2[20] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_04_2

hex=I7050D_05;
buff_2[19] =  HEX_TO_ASCII(hex & 0x0f);//out_05_1
buff_2[18] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_05_2

hex=I7050D_06;
buff_2[17] =  HEX_TO_ASCII(hex & 0x0f);//out_06_1
buff_2[16] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_06_2

hex=I7050D_07;
buff_2[15] =  HEX_TO_ASCII(hex & 0x0f);//out_07_1
buff_2[14] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_07_2

hex=I7050D_08;
buff_2[13] =  HEX_TO_ASCII(hex & 0x0f);//out_08_1
buff_2[12] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_08_2

hex=I7050D_A_10;
buff_2[11] =  HEX_TO_ASCII(hex & 0x0f);//out_A_1
buff_2[10] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_A_2

hex=I7050D_B_11;
buff_2[9] =  HEX_TO_ASCII(hex & 0x0f);//out_B_1
buff_2[8] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_B_2

hex=I7050D_C_12;
buff_2[7] =  HEX_TO_ASCII(hex & 0x0f);//out_C_1
buff_2[6] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_C_2

hex=I7050D_D_13;
buff_2[5] =  HEX_TO_ASCII(hex & 0x0f);//out_D_1
buff_2[4] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_D_2

hex=I7050D_10_16;
buff_2[3] =  HEX_TO_ASCII(hex & 0x0f);//out_16_1
buff_2[2] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_16_2

hex=I7050D_11_17;
buff_2[1] =  HEX_TO_ASCII(hex & 0x0f);//out_17_1
buff_2[0] =  HEX_TO_ASCII((hex >> 4) & 0x0f);//out_17_2


Вот здесь во влеш забиты команды запроса на считывание входов/выходов.

/* команды опроса модулей ICPCON*/										
const uint8_t  I_7033_01[5] PROGMEM  = {'#','0','1','0',0x0D};//опрос модуля I_7033 (1)				
//ответ >+019.79 или 3E	2B 30 31 39 2E 37 39 0D	
//ошибка ?01     или 3F 30 31 0D
//нет ответа
const uint8_t I_7051D_0E[4] PROGMEM  = {'@','0','E',0x0D};//опрос модуля I_7051D (14)
const uint8_t I_7051D_0F[4] PROGMEM  = {'@','0','F',0x0D};//опрос модуля I_7051D (15)
const uint8_t I_7051D_09[4] PROGMEM  = {'@','0','9',0x0D};//опрос модуля I_7051D (9)
const uint8_t I_7051D_12[4] PROGMEM  = {'@','1','2',0x0D};//опрос модуля I_7051D (18)	
//ответ	>0000 или 3E 30 30 30 30 0D, >8040 это 0b10000000 	и 0b01000000
//ошибка ?09 0x0D  или 3F 30 39 0D
//нет ответа	
const uint8_t I_7050D_02[4] PROGMEM  = {'@','0','2',0x0D};//опрос модуля I_7050D (2)
const uint8_t I_7050D_03[4] PROGMEM  = {'@','0','3',0x0D};//опрос модуля I_7050D (3)	
const uint8_t I_7050D_04[4] PROGMEM  = {'@','0','4',0x0D};//опрос модуля I_7050D (4)	
const uint8_t I_7050D_05[4] PROGMEM  = {'@','0','5',0x0D};//опрос модуля I_7050D (5)
const uint8_t I_7050D_06[4] PROGMEM  = {'@','0','6',0x0D};//опрос модуля I_7050D (6)
const uint8_t I_7050D_07[4] PROGMEM  = {'@','0','7',0x0D};//опрос модуля I_7050D (7)
const uint8_t I_7050D_08[4] PROGMEM  = {'@','0','8',0x0D};//опрос модуля I_7050D (8)	
const uint8_t I_7050D_0A[4] PROGMEM  = {'@','0','A',0x0D};//опрос модуля I_7050D (10)
const uint8_t I_7050D_0B[4] PROGMEM  = {'@','0','B',0x0D};//опрос модуля I_7050D (11)
const uint8_t I_7050D_0C[4] PROGMEM  = {'@','0','C',0x0D};//опрос модуля I_7050D (12)
const uint8_t I_7050D_0D[4] PROGMEM  = {'@','0','D',0x0D};//опрос модуля I_7050D (13)
const uint8_t I_7050D_10[4] PROGMEM  = {'@','1','0',0x0D};//опрос модуля I_7050D (16)	
const uint8_t I_7050D_11[4] PROGMEM  = {'@','1','1',0x0D};//опрос модуля I_7050D (17)	
//	                 |out| | in|
//ответ	>007E или 3E 30 30 37 46 0D,   00 состояние выходов, а 7E состояние входов.
//ошибка команды       ?0D 0x0D  или 3F 0D
//Команда игнорирована !0D 0x0D
//нет ответа

Кстати забыл казать про систему общения между модулями и главным мозгом. DCON подразумевает что на линии есть только одно ведущее (master) устройство. В нашем случае это Atmega. Остальные все устройства ведомые (slave). В нашем случае это ICPCON. Только мастер имеет право выдавать команды ведомым. Ведомые без запросов от мастера никогда и ничего не выдают в линию. Если мастер молчит, значит молчит вся линия. И будет молчать всегда до тех пор, пока мастер сам не начнет опрос ведомых устройств.

Наверное заметили что я не слова не сказал про CRC контрольную сумму?
Что это и зачем?
Это делается для проверки правильности приема команды. Представим, что длина линии RS485 больше километра. И линия проложена в условиях электромагнитных помех. Пока наша команда летела по проводам, любой из битов мог измениться с 1 на 0, из-за наводки. Наше приемное устройство проглотит эту команду и все. У нас это Atmega. Но откуда Atmega знает, что тот бит должен быть 1, а не 0? А представим, что если бит равен 1, тогда надо опустить графитовые стержни в атомном реакторе, а если равен 0, тогда поднять стержни. Ну и вот, из-за наводки бит поменял свое значение на противоположное. Ну и понятно, что Atmega не опустит стержни, а поднимет, ну и привет Чернобыль номер 2. Поэтому и придумали CRC проверку команды. Перед тем как MASTER устройство выплюнет команду в линию, оно сперва возьмет все байты команды и по определенному алгоритму их между собой поделит, умножит, прибавит, отнимет – этих алгоритмов куча. Ну и получившийся результат приклеит к концу нашей команды и выплюнет уже все это в линию. Наше приемное устройство примет все это. И полученные байты подвергнет такой же процедуре. И если полученный результат равен результату принятого CRC, тогда все вери гуд. Команда дошла верно. Если нет, тогда PLC может выдать ответ типа ERROR, неправильная команда. Ну и наше MASTER устройство заново вышлет эту команду, пока она не примется без искажений.

По умолчанию в модулях ICPCON CRC всегда выключено. Существует специальная утилита для настройки модулей. С помощью этой утилиты можно перед установкой в сеть модуля, настроить его как надо. Задать скорость, адрес, CRC и др.

Кстати есть такая неплохая free терминальная программа для COM портов. Я ей частенько пользовался при работе с модулями ICPCON и панелями Weintek.


P.S.
Весь проект делался в Atmel Studio 6. Прошивка распухла до 54 кило флеша. Еще вагон места осталось. Но но мне пригодится когда буду прикручивать систему доступа.

Здесь находятся фото внутренностей модулей в хорошем качестве.

Комментарии (98)

RSS свернуть / развернуть
P.S. — это можно пропустить.
P.S. обычно в конце. А такие лирические отступления обычно в в виде вставки при помощи blockquote оформляют.
return hex > '9'? (hex — 'A' + 10): (hex — '0');
А если пришло 8J из-за помехи? Или 7f. Входные данные нужно проверять на принадлежность множеству [0-9,a-f,A-F] и отдельно преобразовывать каждый из диапазонов. К тому же, обычно она используется для создания функции, переводящей из HEX в byte[] строку произвольной длины. Аналогично в HEX_TO_ASCII нужно проверять значение на принадлежность [0..15].
Код в обработчике прерывания на принятие данных получился такой:
Код ужасен, мягко говоря. Начиная с того, что копипаста зло. Одним лишь вынесением функции преобразования hex-строки в число можно укоротить каждый case в среднем до 4 строк. С одновременной оптимизацией по размеру и по читабельности/поддерживаемости кода. А там можно подумать и над тем, что с самим кейсом можно (и нужно ли) сделать.
Вот здесь во влеш забиты команды запроса на считывание входов/выходов.
Ужос. В таких случаях делается функция отправки команды, получающая на вход адрес и собственно команду.
Прошивка распухла до 54 кило флеша.
При таком-то коде — неудивительно. Можно сократить раз в 5-10.
+3
  • avatar
  • Vga
  • 21 мая 2013, 15:05
На идеальность не претендую. Знаю что найдется немало спецов которые сделали бы это все лучше меня. Поэтому и выложил чтоб послушать полезную критику. Будет чему поучиться.
0
Одним лишь вынесением функции преобразования hex-строки в число можно укоротить каждый case в среднем до 4 строк.
Все правильно, конечно, но и вызов функций из обработчиков прерываний тоже не очень хорошо.

2 Papandopala
Нахрен вообще такую работу делать в прерывании? Это прям так сверхкртитично по времени, что с пом. флага не обработать никак??
0
Можно и в главном коде. Так даже грамотнее. Но несколько месяцев все прекрасно работает. Не вижу причин, что в будущем оно так же не будет работать.
0
Я бы посоветовал Вам для таких вещей использовать кольцевые буферы.
0
Как будто расход времени на один вызов в такой простыне сильно изменит ситуацию. Это все нужно из прерывания выкидывать, оставить в нем только прием байта и перекидывание его в буфер.
На крайний случай есть inline и макросы.
+1
Ну инлайн или макросы, если это все просто вихнуть в них в том же виде, не дадут ничего, окромя более красивой формы записи, ИМХО.
А при таком количестве однообразных кусков кода и правда лучше функцию, наверное. Только вот обычно жалко тратить время сохранение/восстановление всех регистров из-за одного вызова функции в прерывании.
0
Вообще, call convention в AVR-GCC странный. Разумнее регистры сохранять в функции, и только те, которые модифицируются.
Возможно, это связано с оптимизацией. Вроде если вызывающая и вызываемая функции в одном файле — компилер сохраняет только используемые регистры.
не дадут ничего, окромя более красивой формы записи, ИМХО.
Это уже весьма немало, на самом деле.
+1
Вообще, call convention в AVR-GCC странный.
А что в нем странного, можно подробнее?

Разумнее регистры сохранять в функции
И как это будет выглядить в GCC в виде кода на Си?
0
В первую очередь то, что регистры сохраняет вызывающая сторона, хотя вызываемая куда лучше знает, какие регистры нужно сохранять.
И как это будет выглядить в GCC в виде кода на Си?
Никак. Это вопрос трансляции C в машинный код.
0
Там где в статье фотка подключения модулей к преобразователю лучше вместо фотки вставить схему типа той что ниже. Она будет гораздо информативней чем пачка коробочек соединенных проводочками. Понятно, что сложного там ничего нет, но схема будет лучше.
0
Код в обработчике прерывания на принятие данных получился такой:
Нихрена себе обработчик прерывания… Как насчёт такой мысли, что прерывания должны быть максимально быстрыми?
+1
Он такой и есть. Ты думаешь что когда контроллер в обработчике, он это все исполняет? Там же SWICH стоит. За раз выполняется только одна секция из всех 18.
0
Ты думаешь что когда контроллер в обработчике, он это все исполняет? Там же SWICH стоит. За раз выполняется только одна секция из всех 18.
Вот только сначала нужно определить, каую из этих 18 нужно выполнить… Проверив от 1 до 18 (уж — как повезет!) — условий.

Посмотрите в ассемблерном листинге, во что превратится этот SWICH после компиляции… Листинг программы на С и то, что на самом деле шьется в контроллер — вещи очень даже разные.
+2
Листинг программы на С и то, что на самом деле шьется в контроллер — вещи очень даже разные.
Я в курсе этого. Несколько лет писал на асме. Какой предложишь вариант?
0
Тоже хотел написать о jump table оптимизации switch-а, но коллега dee меня опередил.

Позволю себе также немного покритиковать Ваш код (помимо уже высказанных замечаний). Имхо, Вы злоупотребляете volatile переменными. Вот зачем вы объявляете локальные переменные как volatile?
+1
Это верно, уже исправил.
0
Нормальные компиляторы из такого switch-а генерят табличку адресов всех веток + один переход сразу на case_addr[US0_CNT].

to Papandopala:
нафига такой жуткий копи-паст? у тебя там все ветки в этом switch как близнецы. пора отрефакторить это безобразие в отдельную ф-ию :-)
+2
А я бы не стал. Иначе компилятор наткнется на эту функцию и будет при входе в прерывание пихать в стек все регистры, а не только те, что используются.
0
inline же.
0
имеет внутри себя гальваническую развязку между интерфейсами RS485 и RS232. Она равна 5000 вольт. Нехило

Что-то не очень верится мне в это:) Особенно учитывая то, что под оптопАрами нет и намёка на фрезеровку. Да еще и ноги оптронов отформованы под 7.62мм, что с учетом размера контактных площадок даст зазор между «первичной» и «вторичной» сторонами не более 7.12мм. Ну и использование «готовых» DC/DC тоже как-то не вяжется с цыфрой 5000В:)
0
Я наврал. 3000 вольт. Там на фото видно прямо на корпусе выгравировано.
0
Ну для 3кВ такого зазора, в принципе, достаточно. Требования по безопасности (не помню чьи, приведенные в аппнотах от power integrations) требуют 6мм зазора по поверхности при испытательном напряжении 3кВ RMS.
+1
ну так речь-то шла о 5кВ:)

требуют 6мм зазора по поверхности

у меня почему-то в башке цыфра 6,5мм. но спорить не буду, ибо я тоже не помню откуда она:) в любом случае — при формовке лап оптопары на 7,62мм и не сильно жЫрном «ободке» контактной площадки (+0,5мм) всё нормально для 3кВ получается
0
Возможно, и 6.5 — на самом деле это округление от 1/8".
+1
В моем самопальном контроллере все порты заточены под RS232 интерфейс. А у модулей ICPCON шина данных работает по RS485. Поэтому я использовал модуль преобразователь ICPCON7520. Преобразует шину RS232 в RS485 и наоборот.

Имхо, смысл было делать рс232 когда есть готовые микрухи юарт-рс485… Для гальванической развязки есть ADUM, получилось бы дешевле и красивее.
0
Это пиздец. Знаете, всёже ваш «открытый» проект лучше бы был закрытым, что бы следующая фирма обслуживания выкинула ЭТО и поставила ПЛК.
К замечаниям Vga добавлю:
— полное отсутствие защиты от саботажа, способное привести к «подъему стержней из реактора».
— полное отсутсвие защиты от внештатных ситуаций, ведущее к тем же последствиям.
С трудом вериться в выдерживании удара молнии, при том, что ваш код приема ни как не защищен от переполнения буффера.
Именование переменный просто грандиозное. Это не какие-то там нубские: i, j, ii, iii; а «осмысленные» имена с подчеркиванием в качестве разделителя!
Вообще после увиденного кода пропал весь конструктив в критике. Слов нет, одни эмоции.
+3
Это пиздец.
Честно говоря, когда я писал свой комментарий — именно такие формулировки в голове и крутились :)
+2
… насчет именования переменных, думаю автор просто замазал заменил все имена несущие какое-то смысловое значение, либо код генерированный какой-нибудь программой, по навороченному алгоритму, либо… вы правы :)
0
Если не умеешь уважительно выражать свои мысли, тогда вообще вали с этого сайта, чтоб духу твоего здесь не было. Была бы возможность, я тебя забанил бы отсюда в 3 секунды без суда и следствия. Контроллер питается от импульсного блока питания SIEMENS который стоит в соседнем шкафчике. В этом блоке уже стят предохранители, варисторы и пр. И который уже пережил за 15 лет не один десяток гроз и молний. Поэтому в данной ситуации я решил не устанавливать дополнительные элементы на плату. Это мое решение и я за него несу ответственность. А ты братушка научись сперва нормальному русскому языку, и не суйся туда, куда тебя вообще не просят.
-3
Бан не проблема, напишите хальту :)
Вопрос не в питании, вопрос в линии связи. Представьте: в 20 метрах от здания шандарахнула молния, или вообще в соседний щиток воткнули электросварку. Возникло достаточно мощное электромагнитное поле, что бы навести в линию связи помеху. И эта помеха может быть абсолютно чем угодно: стартовый бит на свободной линии (если длительность к тому же в 2 бита, то и CRC совпадет — байт 0x7F примется как достоверный) или искажением текущей передаваемой посылки. И точно так же «чудесным образом» исказиться может байт 0x0D (тут даже CRC не обязательно совпадать, просто испортить его). Куда пойдет ваш код при приеме всех последующих байт, не приняв 4-м байтом 0x0D (что код считает безоговорочно-достоверным фактом).
Любой человек, случайно или умышленно, подключившийся к вашей системе, и отправив строку >более4символов нагнет вашу систему. И совершенно неизвестны последситвия данного вмешательства. И ваша система очевидно не сможет после него восстановится.
Или скажем модуль вышел из строя и его заменили на другой такой же. Но вот только адрес не тот установили, или просто CRC включили. И что бы вы думали — нагнулась вся система. В кабинете гендиректора включилась система пожаротушения и дверь его заблокировалась.
Все ваши предположения «тут я главный и все молчат в тряпочку, пока я не разрешу» и «всегда в ответе 2 байта» НЕдопустимы и приступны. Ваша система обязана быть готова к внештатным ситуациям.
Модуль ввода-вывода это именно то, с чего вам следует начать. Да, ошибки в нем несут аналогичные последствия, но ваш модуль можно тут же выбросить и поставить промышленное устройство аналогичного назначения, либо заблокировать его работу в ПЛК до выяснения обстоятельств.
К сожалению вы этого не осознаете. Вы не готовы к реализации ответственных проектов.
+2
Народ, да какие 3 кВ? Посмотрите на схему в первом посте — вообще защиты никакой, ни по питанию, ни по портам!
Это чудо отличается от самого поганого ПЛК полной невозможностью переконфигурации логики работы — только перепрошивка. Что делать если перевесим местами 2 лампочки? Правильно, звать «мастера» с STK200 и ловить потом «веселые» баги на живой системе.
Почему бы не сделать массив из состояний входов? Почему вместо 1 RS485 стоят дорогие RS232Ю да еще по микрухе на порт? Автор не знал по какому протоколу будут его модули работать, или планировал тащить 232 на сотни метров?
Короче, пилить это еще и пилить, чтобы через год получить кривой ПЛК по цене самого навороченного симатика
+3
Не нравится — отойди в сторонку. Все настроение испортил
+1
Еще про порты добавлю.

Шкурный интерес еще есть:
1. у Arduino UNO — 1 USART, т.е. она подходит только для оконечного модуля ввода/вывода, ей только нужен шилд(или разные шилды) с трансивером RS-485 и изоляционной требухой защиты внешних дискретных/аналоговых входов/выходов + чип SPI EEPROM + чип watch_dog таймера, т.е. будет полный аналог этого ICPCON I-7000, только мощнее по мозгам(там C8051 RAM 256 15K Flash).

2. У Arduino Mega или Due — 4 порта USART, т.е. как бы специально для ПЛК. Тоже нужен похожий шилд, только изолированных RS-485 — 2,3 или 4, и нет внешних линий ввода/вывода, можно еще иметь Ethernet и USB.
-1
Эти платы в пластиковых корпусах? Есть крепление на дин рейку? Есть удобные быстросъемные клеммные колодки? Есть индикация состояния каждого ввода вывода? Они прошиты поддержкой DCON протокола? Знаю что ничего этого нету. Поэтому куплены ICPCON где это все есть.
0
Все то-же самое есть у важего центрального модуля? Тогда почему не был куплен ПЛК?
+1
Есть ли разница, сделать 18 плат вручную или только одну плату для центрального процессора? Из этих 18 плат одна должна работать с аналоговым датчиком Pt1000, 13 плат имеют по 7 входов и 8 выходов, остальные платы по 16 выходов. Уже получается 3 разных вида плат. Каждая модель модуля ICPCON работает со своими командами из протокола DCON. Значит фактически уже надо сделать три разные прошивки. А если придется подправлять прошивки, то предлагаешь к каждой платке с программатором и ноутбуком бегать? Сколько уйдет времени чтоб сделать такие платки аналогичные ICPCON модулям? Корпуса как предлагаешь делать для платок и крепление к дин рейке из картона? Так есть разница? А тебе какая разница, что я поставил PLC или свой процессор? Тебе от этого сильно голова болит? Когда будешь делать свой проект, покупай и ставь туда что хочешь. Какое тебе дело до этого?
-2
Значит фактически уже надо сделать три разные прошивки.
3 более простых и похожих прошивки. Сразу учимся «тиражированию» (18 плат), «универсализации» (все реализуют одинаковый протокол) и «индивидуализации» (некоторое различие команд/данных).
А если придется подправлять прошивки, то предлагаешь к каждой платке с программатором и ноутбуком бегать?
Осознаем, что чем «бегать к каждому глючному устройству» лучше зарание его протестировать и убедлиться в коррекности работы.
Корпуса как предлагаешь делать для платок и крепление к дин рейке из картона?
Про корпуса для РЭА и под DIN-рейки вы не слышали? Ах да, они же не такие гламурные.
А тебе какая разница, что я поставил PLC или свой процессор?… Какое тебе дело до этого?
Я переживаю за колег, тех кто работает в моей и смежной с моей деятельностью профессиях. Не дай бог им поддерживать ваш продукт. Сегодня подсунул ты им говно, а завтра они посмотрят — чё мы самые лысые что ли, дай ка и мы тоже «сэкономим» — и подсунут говно мне.
+2
3 более простых и похожих прошивки.
Точнее даже одну конфигурируемую. У самого ICPCON'а наверняка так и есть. Возможно даже конфигурируется в EEPROM'е или перемычками — это дешевле в производстве, чем зашивать разные прошивки.
0
а ты, быстро пишешь!:)
0
Это же Vga! :)
0
имхо, можно сделать одну «универсальную» и режим работы задавать, например, перемычками
0
Вы код из статьи видели? Лучше пусть будет 3 отдельных скопипасченных проекта. Потом прийдет просветление.
0
Это такое же зло, как код из статьи. Он тоже методом копипаста сделан.
0
Я переживаю за колег, тех кто работает в моей и смежной с моей деятельностью профессиях. Не дай бог им поддерживать ваш продукт. Сегодня подсунул ты им говно, а завтра они посмотрят — чё мы самые лысые что ли, дай ка и мы тоже «сэкономим» — и подсунут говно мне.
Братка («коллега», как культурно говорит emc2), ну эт ты что-то перегнул…

Я в 1-й части тоже наехал на него — думал какой-то менеджер или IT-шник из московского офиса понтуется — накупил модулей за $80 для таун-хауса. Но он оказывается весь в мозолях — наш, трудовой человек :D
0
Трудовой — не трудовой, а свинью подкладывает по той причине, что он так захотел. В той же первой части (комменты) он писал что выбор МК вместо ПЛК из его жадности и его желания сделать именно на МК. А из-за этого выбора будут париться те, кто будет поддерживать систему в будущем. Либо пытаться разобраться и переделать его код, либо тратить деньги и ставить нормальную систему. Сделал то он не свой дом умным, а некое промышленное здание (опять же из комментов).

Таким образом наезд в первой части в плане неправильного определения принадлежности статьи (уже исправлено — притензий нет) и выбора средств. Наезд во второй статье в плане неправильного выбора средств и ужасной реализации, из-за которой встрянет кто-то другой.
+1
Это не промышленное здание.
0
USART3 туда еще сядет в будущем контроллер доступа в здание, на 16 дверей. Тоже будет самопальный на такой же Atmega1280.
На самом деле эта система стоит в здании более чем на 2600 тыс. человек.
Это жилой дом с 16-ю дверями в котором проживает 2600 человек? Это больше похоже на торговый центр или прочие офисные здания.
Но даже жилой дом на столько людей — где гарантия что вы всегда в нем будите проживать и будите доступны в любое время? То вы на работе, то в отпуск уехали. А в это время систему наладить ни кто не сможет кроме вас. Добавили дверь под контроль, а она возьми, да заблокируйся. И сидят жильцы дома под дверью, ни войти — ни выйти.
P.S.: Гм, верно. Офисное != промышленное. Виноват, не чётко сформулировал предложение.
+1
Не парься, будь счастлив.
0
а чем ПЛК лучше?
0
а чем ПЛК лучше?
Именно. Будешь сидеть на крючке у барыг и башлять 10-ками тыр каждый раз. Барыги от пром.автом. тоже не лыком шиты — документация формальная, все скрывают, к тому же конкретное семейство ПЛК и ПО к нему — это сама по себе закрытая тема для потребителя.
0
Вот и у меня такие же мысли крутятся. Тут любой Си программист справится, а там свои языки, свои среды разработки,..)
0
Корпуса как предлагаешь делать для платок и крепление к дин рейке из картона?

ну это уж вообще не проблема:

chipnn.ru/82.php (второй раздел) — это просто в качестве примера
0
Ну это я написал для своих любительских целей, на работе конечно же надо покупать готовые модули ввода/вывода, иначе уйдет год упорной работы на проектирование и тестирование только 3-4 таких плат разного назначения и ПО для их MCU.

Пром.протокол этот DCON понравился очень — простой и дубовый, на порядок легче реализовать, чем MODBUS RTU :D

Papandopala, братка :D, а ты можешь сфоткать эти модули I-7000 в высоком разрешении и выложить куда-нибудь? Приблизительно там все понятно, но не видна маркировка на многих компонентах.
0
Ты имеешь ввиду внутренности модулей?
0
Да
0
Сделаем.
0
Ссылка на фото в конце статьи.
0
Thankh!
0
Там в первой части написано, что в архитектуре запроектировано 4 шлейфа RS-485:
1 — на 18 ICPCON-ов
2 — на 4 панельных компа (они могут одновременно работать на общей шине RS-485 ??? более привычно радиальное соединение звездой через RS-232/Ethernet)
3 — отдельно на модем
4 — еще на какую-то отд.подсистему
так что может это вполне разумно, а подробности(почему так) надо спросить у автора.

Мне лично близка более универсальная, но избыточная идея 2-х портовых контроллеров: один шлейф RS-485 для подключения к нему опрашиваемых модулей, 2-й шлейф RS-485(или порт Ethernet вместо него) для подключения к шине(сегменту сети Ethernet) для контроллеров и host-компов. Просто такой контроллер можно реализовать на более мелком medium density MCU чипе(LQFP48/64) не с 4-мя, а с 2-мя UART.

Впрочем это(1 + Ethernet,2 или 4 порта RS-485 в контроллере)- вопросы архитектуры сети у конкретной системы и выбранного семейства/производителя оборуд-ния.

Резюме: вполне может быть такое решение с 4-мя портами.

Про 3kV: какие 3kV, если устр-во будет стоять только в шкафу и не имеет шлейфов выходящих наружу. Видимо можно было бы вместо RS-232 поставить RS-485 трансиверы, но RS-232 в каком-то смысле более универсально.

P.S.
Почему бы не сделать массив из состояний входов?

Можно поподробнее(и желательно с кодом)? А то так умно крикнул что-то, а многим не понятно.
0
Грубо говоря все те отдельные переменные состояний отдельных модулей объеденить в один массив состояний всех модулей. В такой версии «стирается» граница между модулями. Есть один универсальный адрес сигнала, и в случае переноса лампочки с пина 3 модуля 5 на пин 7 модуля 10 (с коридора кнопку перенесли внутрь помещения — от шаловливых ручек спрятали) меняется только один адрес. Сейчас же требуется менять и переменную и маску пина.
Как пример:
onRecive(line, str) {
 // пришли данные по N-ой линии, заносим их в хранилище
 array[line]=HEX_VAL_TO_BINARY_DATA(str);
}
getPin(pinId)
{
 // Извлекаем бит по соответствующему адресу
 return ( array[ HIBYTE(pinId) ] & LOWBYTE(pinId) ) ? 1 : 0;
 //return ( ( array[ pinId >> 3 ] ) >> (pinId & 7) ) & 1
}

// было
//#define BTN_ID    0x0508 /* 05 - 5-й модуль, 08 - маска 3-го пина */
// стало
#define BTN_ID    0x0A80 /* 0A - 10-й модуль, 80 - маска 7-го пина */

// сам код при этом не изменился
btnState = getPin(BTN_ID);
0
Ну только все равно непонятно, почему если предполагается использование RS485 ставить RS232. По массиву уже ответили ниже. Хотя в данном случае придираться к массиву в глобальном контексте кода и устройства это как в России жаловаться исключительно на плохой городской транспорт.
0
В следующих статьях поймешь.
0
Хотя в данном случае придираться к массиву в глобальном контексте кода и устройства
Нет, он как раз лежит у корня проблем — неправильно спроектированной архитектуры программы.
+1
Это можно пропустить.
Дальше не читал )))
0
в чем сакральный смысл этого:
b1=buff_1[0];
        b2=buff_1[1];
        result = ASCII_TO_HEX(b1) << 4;
        result |= ASCII_TO_HEX(b2);
, если можно записать одной строкой без промежуточных переменных
+1
В такой записи некоторый смысл есть, а в некоторых случаях даже требуют именно такой формы. Суть в улучшении понимания. Каждая отдельная строка делает отдельное действие.
простой перенос из массива в 2 переменные — явное выделение исходных данных для задачи
отдельные строки аски-ту-хекс — явное получение отдельных частей результата
при этом результатов должно быть 2 временные, которые потом помещаються в итог через | (ни каких |= не допускается).
Естественно использование volatile в данном случае для временных переменных не допустим, форматирование добавить, ну и отрефакторить хорошенько.
А оптимизацию делает компилятор, избавляясь от лишних временных переменных, оптимизируя код. Получается то же что и при записи одной строкой.
0
Сейчас пример не приведу, но заметил, что при использовании промежуточных локальных переменных в теле функции, иногда ИАР лучше оптимизирует, чем если все в строку пытаться уместить.
0
Да, у IAR'а на STM8 такое наблюдал. У них ещё слабоват оптимизатор, многих возможностей ББ не реализует. В моем случае было обусловлено порядком вызова функций и тем, что компилятор всё равно создавал временные переменные. Когда же указал явные переменные, оптимизатор смог их переставить и избавился от лишних перемещений.
Естественно можно было самому переставить части одной большой строки, не отрицаю :)
0
Суть в улучшении понимания. Каждая отдельная строка делает отдельное действие.
Так-то оно так, но здась это выглядит как полировка болтиков на проржавевшем разваливающемся автомобиле — читабельность куда лучше убивается неудачными именами переменных и функциями в 10 экранов длиной. И запись вида result = HexToInt(&buf[0], 2) была бы куда понятнее.
0
Ну про свое мнение о коде я уже написал первым комментом :)
Просто привел вариант когда разбиение имеет право быть. И ту же функцию HexToInt (теоритически возникшую после рефакторинга) иммеет смысл так же и расписать, хотя конечно можно и одной строкой сразу.
Вообщем по этому поводу (оформление кода) можно развести очередной холливар :)
0
Интересно
Мне одному кажется что начиная вот с этого места можно оптимизировать все до 20-30 строк вместо тех пару сотен что написаны?
switch (US0_CNT)
{
case 1: buff_0[d_4]=d_1;//I_7033_01
d_4++;
ClearBit(F1,0);
ClearBit(mod[1],2);//Сообщаем панели, что cвязь с модулем 01 востановлена.
break;
case 2: buff_1[d_4]=d_1;//I_7050D_02
d_4++;
if (d_4==4)
{

Над кодом еще работать и работать.
Такое впечатление что автор максимум пару лет пишет на С.
0
Мне одному кажется
Не одному. И тут надо не только оптимизировать, но и отделить мух от котлет — сейчас там в одну кучу смешаны и раскопипащены прием, декодирование, разбор данных и реакция на них.
Такое впечатление что автор максимум пару лет пишет на С.
Скорее, что он так и не научился программировать. Типичный индусский код. Я бы даже сказал, сферический в вакууме.
+2
Это мой первый проект на си. До этого писал на асме. Я думаю что твой первый проект на си выглядел бы так же, а может и хуже.
0
Это мой первый проект на си. До этого писал на асме.

Вы уж меня извините, но такие отмазки не канают в данном случае. Я не помню как выглядел мой первый проект на С, подозреваю что хуже чем код который выложили Вы. Но есть существенная разница – мой первый проект это был какой-то сферический «хелло ворлд» в вакууме, он ре управлял боевой промышленной системой.

Ели вы профессионально разрабатываете софт для боевого применения (тем более, что область применения у Вас весьма специфическая) – то заявления типа «я первый раз пишу на этом ЯП», «я первый раз работаю с этой чипой» никого не интересуют. И дело даже не в С. Проверять входные параметры на допустимые значения нужно независимо от того на каком языке Вы пишете. Копипаст тоже зло независимо от ЯП, и т. д.
+2
Да но учти система прекрасно работает уже не один месяц. Не вижу причин что она не будет работать и дальше. Да мог код не самый оптимальный, замечания данные выше я принял. Я сделал код на таком уровне какой есть у меня сейчас. Через 5 лет будет уровень другой. И код будет другим. Да в прерывании есть лишние строки, которые можно и нужно оптимизировать, оптимизируем, руки еще не дошли, так как проект не закончен. Но не смотря на это процессор прекрасно справляется с поставленной задачей. Голодающих прерывание нет, все вовремя обрабатывается. Что еще надо?
Цель достигнута, система работает. Никто претензий не предъявляет.
0
Да но учти система прекрасно работает уже не один месяц. Не вижу причин что она не будет работать и дальше.

А я вижу. Выше уже писали – помеха при передачи данных по UART мы не проверяя диапазон значений подаем полученный мусор в ASCII_TO_HEX() и дальше по тексту…

Заметьте, что помеха при передаче данных по UART – это вполне себе обычное явление, это никак не высосанная из пальца проблемная ситуация которая никогда не наступит.
+3
Для решения этой проблемы CRC есть.
0
Дак нету же её у вас.
0
Я в целом говорю.
0
Вынужден тебя разочаровать — даже мой первый проект на бейсике был лучше. Без предварительного опыта программирования вообще.
И да, отмазки насчет языка не канают. Умение программировать от языка не зависит и подобный код на ассемблере — такое же говно.
+2
Теперь мне стала понятна причина падений спутников и не выхода на орбиту носителей. Видимо раньше правила проектирования лучше изучали и применяли. Сейчас подход проще и ситуация, думаю, будет только хуже.
+2
У меня такое чувство, что сюда пишут исключительно из-за плюсов к посту. Причем, чем ниже читаешь — тем изощреннее обсирают поделие автора. Ну раз сказали, ну два… Чего корыто помоями заново наполнять то? И так же понятно, что проект не самого лучшего пилотажа. Автор (надеюсь) исправится в будущем. Примет к сведению все замечания.
К чему повторять одни и те же слова, только под другим углом? Даже спутники приплели уже… тьфу! читать противно!
+1
Автор утверждает что у него крутая система «прекрасно работает уже не один месяц» и упортвует на этом ни смотря ни на что. Ему объясняют где он не прав, на собственных ошибках. В целом вы правы — не хочет учиться на чужих ошибках, пусть учиться на своих. Но все же «страдать-то в итоге будет не он один, а и невинные люди».
+2
Автор утверждает что у него крутая система
а ну ка покажи где я так написал?
0
первая статья, описание модулей и того, на сколько они круты (вотчдоги и пр)
первая статья, описание опторазвязок
комменты обоих статей, система работает Н месяцев, и грозы выдерживает.
комменты первой статьи, интерпретация цены/качества/удобства каждого примененного решения по отдельности, где что подходит там то и только то и упоминается.
коммент первой статьи, «Порой самодельные вещи работают лучше покупных.» Как расценивать этот отказ от применения ПЛК?
коммент к этой статье, «Но несколько месяцев все прекрасно работает. Не вижу причин, что в будущем оно так же не будет работать.» как это расценивать?

За всё время, на сколько помню, вы явно согласились только с избытком volatile, а все остальные замечание заворачиваете/игнорируете. Ни как иначе я это расценить не могу.
0
Если тебе так все не нравится и так все ужасно плохо, чего ты вообще сюда сунешься? Зачем ты пишешь все эти комментарии? Зачем читаешь все это? Ты имеешь право иметь свое какое то мнение по этому материалу. Ну так и имей его. Я как бы что, обязан перед тобой отчитываться или что то тебе доказывать?
0
Он (и остальные) имеет право это мнение не только иметь, но и высказать.
+1
Есть такое золотое правило. Как ты относишься к другим, так и к тебе будут относиться. Поэтому высказывать свое мнение можно и даже нужно. Но надо еще научиться это делать правильно. Если этого не умеешь, то лучше оставь его при себе. Больше пользы будет для окружающих.
0
Золотое правило. Но последовать ему следовало бы тебе. angel5a вполне корректен, а термин «пиздец» на этом ресурсе запрещенным не является.
+1
Даже спутники приплели уже… тьфу! читать противно!
Это ничего, про 3kV, молнии, зловещие опускающиеся «стержни», спутники уже написали. BANG!BANG! Надо еще про atom bomb EMI упомянуть
0
Ставлю плюсик за протокол DCON. Теперь знаю, что буду у себя в «умном доме» использовать. А детали реализации мне не интересны. Я сюда за идеями и умными мыслями прихожу.
0
А чем именно он тебе понравился? Та часть, что описана — на редкость очевидна, такой протокол самому придумать дело 5 минут. Или ты изучил полную спецификацию и там нашлись более интересные моменты?
0
у меня для выбора протокола были такие критерии
1) стандартный и описанный хоть где-нибудь (DCON оказывается даже под gpl)
2) достаточно простой (модбас слишком избыточен для моих задач. может ошибаюсь?)
3) транспортный уровень желательно rs485

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

есть другие варианты? с удовольствием ознакомлюсь.
0
Да, пункт 1 — веский.
0
Ты съел(узнал) инфу, которой совершенно не знал, но все оказалось очень просто на самом деле(как обычно), и ты сейчас ковыряясь в зубах плюешься и громко провозглашаешь: обед — дерьмо! я такой и сам сварю!

P.S. DCON, в отличии от твоего самопала поддерживается массой популярных контроллеров и модулей ввода-вывода(например Advantech ADAM) и самое главное — системами SCADA, например OpenSCADA.
0
У меня такой вопрос. У 7000x есть собственый ПЛК с аналогом DOS-системы. Почему для управления периферийными модулями не использовался собственный серийный ПЛК?
0
  • avatar
  • uni
  • 22 мая 2013, 21:04
Да это хороший вопрос. Я о PLC много читал, но буквально с ними не разу не сталкивался. Опыта работы с ними нету. На данный момент проще было сделать на ATmega1280. А второе, из спортивного интереса хотелось прикрутить всю эту массу железа к 8ми битному AVR.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.