tugoboss
Интересующийся
Offline
|
|
« : 12-03-2009 16:36 » |
|
Пишу программу на С для микроконтроллера. Одновременно выполняется несколько задач: прием-отправка данных медленному самодельному интерфейсу, прием-отправка данных по MODBUS, АЦП, несколько временно-зависимых задач. Задачи выполняются одновремено и они взаимосвязаны и число взаимосвязей увеличивается по мере написания программы. Возможно добавится новый функционал, а соотв. и взаимосвязи. Уже начинаю путаться, забывать что-откуда-куда. Как формализовать написание программы (в идеале - сколь угодно сложной), какими инструментами для этого пользоваться? Хочется построить программу стройно и красиво, чтобы наращивая функционал, структура не менялась, только добавлялись модули.
Должны помочь старые-добрые блок схемы и графы состояний? Что-то у меня не получается описать задачу графами.
Посоветуйте литературу по этому вопросу.
Спасибо.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #1 : 12-03-2009 18:55 » |
|
tugoboss, так в чем сложность то? В согласовании основной программы с асинхронными прерываниями?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Sla
|
|
« Ответ #2 : 12-03-2009 19:04 » |
|
Это элементарно, tugoboss, Start: Call Init Call Run end:
init: Call check_hard Call init_interrupt Call init_hard Call init_soft Ret end_init:
Run: while true do { ... ... } Ret end_run: ;блок описания прерываний .... .... ;блок функций обработки данных ... ... и тд
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #3 : 12-03-2009 20:48 » |
|
По-подробнее опишу устройство. По медленному самодельному интерфейсу (2wire) подключены 32 термодатчика. Каждый термодатчик опрашивается с собственным периодом (в общем случае у каждого датчика период опроса свой). Напрашивается некая очередь. Период опроса каждого датчика может быть изменен, очередь соотв. нужно перестроить (или заполнять уже по новым данным). Может поступить команда по Modbus - быстро опросить группу датчиков - в эту очередь нужно вставить команды их опроса. Каким образом это организовать - мне пока не понятно. Интерфейс 2wire медленный - опрос датчика - 1,5 секунды, поэтому в паузах между битами занимаемся другими задачами. Может все еще не достаточно полно описано. Sla ну в общих чертах у меня программа так и выглядит 14кб кода написал. Для МК это не много, но уже и не мало.
|
|
« Последнее редактирование: 12-03-2009 21:05 от tugoboss »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #4 : 12-03-2009 21:02 » |
|
Я бы предложил использовать ООП для проектирования - выделить классы и объекты, расписать логику каждого объекта (состояния и переходы) - всё это без наследований и полиморфизма. Дальше преобразовать этот проект в алгоритм на C.
Это и есть способ проектирования таких программ, которые делают много чего, причём одновременно: представить их как множество отдельных "контроллеров", каждый из которых решает одну единственную задачу.
Опиши подробнее контроллер.
Значит имеется:
- 1 интерфейс, разделяемый для 32 одинаковых датчиков, так что в 1 момент времени опрашивается 1 датчик; - по ту сторону есть 32 датчика (наверно, идентифицируемых по номерам или другим кодам); - внутри программы должны быть 32 экземпляра модели датчика, модель включает в себя периодичность опроса значений датчика; - наверно, есть протокол общения датчика и контроллера;
что ещё?
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Sla
|
|
« Ответ #5 : 12-03-2009 21:22 » |
|
мало исходных данных
каждый датчик - необходимо описать (приблизетельно так) sensor1 net_addr db ... name dw ... registr_start db ... registr end db ... ... Т.е. нужно определить необходимые параметры. Кроме того, такое описание не обязательно должно быть в коде. Описание датчиков может вводиться с пульта, соответственно должна быть функция, выделена память, как в ОЗУ, так и в ППЗУ Если это Modbus, то подразумевается запрос-ответ. Можно извратиться с задержками приема/передачи. Но это только после того как отлажена работа последовательного перебора устройств. Кроме того. Внешние датчики, могут быть медленными устройствами (время чтоб проснуться, время чтоб померять, время чтоб подготовить данные). Это все к чему? Не спеши писать программу. Опиши внешнее устройство. Алгоритм работы с устройством. Используй объектный подход к вопросу. Именно подход, но не реализацию. Свойства объекта. Методы работы с объектом.
Я б не назвал I2C медленным интерфейсом (до 400кбод). То ты медленных шин не видел (320 бод на 128 датчиков)
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Sla
|
|
« Ответ #6 : 12-03-2009 21:23 » |
|
dimka, вот-вот и я о том же
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #7 : 12-03-2009 21:26 » |
|
Значит имеется:
- 1 интерфейс, разделяемый для 32 одинаковых датчиков, так что в 1 момент времени опрашивается 1 датчик; - по ту сторону есть 32 датчика (наверно, идентифицируемых по номерам или другим кодам); - внутри программы должны быть 32 экземпляра модели датчика, модель включает в себя периодичность опроса значений датчика; - наверно, есть протокол общения датчика и контроллера;
Да, все так. Протокол общения датчика и контоллера. Запрос: - адрес (1 байт) - команда (1 байт, 8 разных) - данные (от 0 до 6 байт) - контр. сумма (1 байт) Ответ: - данные (0 - 6 байт) - контр. сумма (1 байт) Кодирование от контроллера к датчикам - длительность паузы между сменой полярности двух проводной линии. От датчика к контроллерам - подсадкой этой линии. Подсадку в линии измеряем АЦП. 32 экземпляра модели датчика включает в себя: - идентификатор - периодичность опроса - температура - состояние - калибровочное значение - и т.д. Общение с верхней системой по Modbus. Этим занят UART и связанный с ним обработчик прерывания. В основном цикле только разбираю пакет и готовлю пакет к отправке. Собственно задача Модбас не знает ни о какой 2wire, он просто забирает из структур запрашиваемые данные и отправляет наверх. 2wire тоже знает только структуру данных, куда нужно что-то сложить. Но иногда нужно датчики быстро опросить и размеренный порядок нарушается. Задача Модбас обращается к задаче 2wire (это мое видение, тут может я не прав и можно сделать по другому).
|
|
|
Записан
|
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #8 : 12-03-2009 21:28 » |
|
Тот самый медленный интерфейс я условно назвал 2wire, он самопридуманный, аналогов я не встречал. "0" - 10мс, "1" - 20мс. В предыдущем сообщении, я описал логику его работы в общих чертах.
|
|
« Последнее редактирование: 12-03-2009 21:30 от tugoboss »
|
Записан
|
|
|
|
Sla
|
|
« Ответ #9 : 12-03-2009 21:30 » |
|
tugoboss, Стоп Не вали все в кучу...
1. Твой девайс - буфер для сбора/опроса датчиков. О датчиках, твой девайс видимо ничего не знает. Знает"верхняя" система.
2. Верхняя система не знает о датчиках, она знает о регистрах твоего девайса. А вот твой девайс уже знает о датчиках
Одно из двух.
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #10 : 12-03-2009 21:32 » |
|
2 вариант. Бестолково же я пишу
|
|
|
Записан
|
|
|
|
Sla
|
|
« Ответ #11 : 12-03-2009 21:32 » |
|
tugoboss, а зачем ты придумал такой интерфейс. Какой МК? Какие возможности? Ты сам будешь формировать временную диаграмму?
Еще раз. Не хватайся за все сразу. Проблемы нужно разгребать постепенно.
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #12 : 12-03-2009 21:34 » |
|
Как контроллер узнает, что нужно немедленно опросить датчики: происходит чтение определенной области структуры данных. Контроллер понимает, что нужно опросить те датчики, данные по которым запрошены.
|
|
|
Записан
|
|
|
|
Sla
|
|
« Ответ #13 : 12-03-2009 21:40 » |
|
Ты не ответил на вопросы. tugoboss, Стоп Не вали все в кучу...
1. Твой девайс - буфер для сбора/опроса датчиков. О датчиках, твой девайс видимо ничего не знает. Знает"верхняя" система.
2. Верхняя система не знает о датчиках, она знает о регистрах твоего девайса. А вот твой девайс уже знает о датчиках
Одно из двух.
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #14 : 12-03-2009 21:46 » |
|
Под контроллером я подразумеваю устройство с установленным микроконтроллером LPC2103 (архитектура ARM7, у него на борту АЦП, UART), микросхема-интерфейс RS-485, интерфейс на 2wire, источник питания.
Этот интерфейс придумал не я, устройство уже готово. Его история давно тянется, нужно его доделать. Этот интерфейс хорош тем, что по 2 проводам передается и информация и питаются термодатчики.
Насчет временной диаграммы не понял, что это такое?
В ТЗ на систему полно белых пятен, поэтому пишу пока макетный вариант. Он задышит - буду шлифовать.
Спасибо за помощь!
|
|
|
Записан
|
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #15 : 12-03-2009 21:54 » |
|
Sla На какие? Стараюсь отвечать. Если вопрос по цитируемому. Верхняя система не знает о датчиках, она знает только о регистрах. В какие-то записывает данные, из каких-то читает. Чтение членов структуры realTimeTemperature: typedef struct { float realTimeTemperature[32]; }RealTime;
будет означать, что нужно сейчас же опросить эти датчики. Опрашиваются датчики, данные кладуться в эту структуру и отправляются наверх. Запись данных в структуру typedef struct { float CalibrTemperature[32]; }Calibrate;
будет означать немедленную калибровку датчиков.
|
|
« Последнее редактирование: 12-03-2009 21:56 от tugoboss »
|
Записан
|
|
|
|
Sla
|
|
« Ответ #16 : 12-03-2009 22:01 » |
|
Бомба! Multiple serial interfaces including two UARTs (16C550), two Fast I2C-buses (400 kbit/s), SPI and SSP with buffering and variable data length capabilities.
Какой интерфейс медленный? Какой из интерфейсов смотрит во вне? Какой смотрит на датчики? Где у тебя в задаче используется The 10-bit ADC provides eight analog inputs, with conversion times as low as 2.44 ms per channel and dedicated result registers to minimize interrupt overhead.
Еще раз
Разберись где сено, а где солома. Не вали все в кучу.
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #17 : 12-03-2009 22:01 » |
|
В ТЗ на систему полно белых пятен, поэтому пишу пока макетный вариант. Он задышит - буду шлифовать. Не знаю, как программируется LPC2103, но если бы это был такой контроллер, который прошивается один раз и на всю жизнь, тебя бы с таким подходом к разработке его прошивки уволили бы. (IMHO) Что касается шлифовки... - почитай на досуге вот это: http://ru.wikipedia.org/wiki/Therac-25Ты вообще представляешь, что баги, вызванные разными накладками в программной системе с асинхронно работающими частями, отладкой ловятся очень плохо? И что решить проблему качества такой программной системы можно только адекватным, тщательным и аккуратным проектированием.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Sla
|
|
« Ответ #18 : 12-03-2009 22:14 » |
|
tugoboss, Еще раз... Протокол Модбас подразумевает следующее
сетевой адрес функция данные црц
Функция (например) 3 (чтение) 4 (запись) и т.д.
Данные номера регистров для опроса номера регистров, данные для записи.
Ты знаком с протоколом?
Где используется Modbus? Подозреваю - верхний уровень, твой девайс.
Внешние датчики по I2C или по SPI или по SPS общаются с твоим девайсом. Причем датчики прекрасно понимают что от них хотят читать или писать. Датчики, скорей всего, имеют свой "сетевой" адрес (выданный производителем)
Я только догадываюсь.
Еще раз Нарисуй структурную схему с указанием шин, например: Верхний уровень <-----485----->девайс===spi====датчики
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Sla
|
|
« Ответ #19 : 12-03-2009 22:16 » |
|
dimka, ISP/IAP via on-chip bootloader software. Single flash sector or full chip erase in 100 ms and programming of 256 bytes in 1 ms. Сейчас других не делают
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #20 : 12-03-2009 22:26 » |
|
Сейчас других не делают Потому и программисты мельчают - ни ответственности, ни адреналина
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Sla
|
|
« Ответ #21 : 12-03-2009 22:29 » |
|
dimka, 100% их бы на ультрафиолет(эрэфки), да без отладчика...
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
RXL
|
|
« Ответ #22 : 13-03-2009 09:01 » |
|
Sla, ну без отладчика - это ты загнул. Я использовал симулятор 8051 - пошагово проходил спорные моменты. А в реальном - именно так: стер, записал, запустил, не работает, потыкался в предполагаемых местах симулятором, поправил код и все заново.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #23 : 13-03-2009 09:16 » |
|
Расскажите, пожалуйста, подробнее про объектный подход. Если можно с примером простым. Языка С++ я не знаю, нужно ли его изучать? Чтобы почитать для понимания объектного подхода. Ты знаком с протоколом?
Да. Где используется Modbus? Подозреваю - верхний уровень, твой девайс.
Да. Внешние датчики по I2C или по SPI или по SPS общаются с твоим девайсом.
Наверное это не важно, но НЕ по I2C или SPI. Это самодельный интерфейс - по 2 проводам передается и информация и питаются датчики. Кодирование от контроллера к датчикам осуществляется шириной импульсов. "1" - 10мс, "0" - 20мс.3. Кодирование от датчиков к контроллеру осуществляется подсадкой тока в линии. "1" – в линию включено дополнительное сопротивление, "0" – не включено. Процессор LPC2103 измеряет падение напряжения на токоизмерительном резисторе, таким образом обнаруживая включение доп. сопротивления в линию. Причем датчики прекрасно понимают что от них хотят читать или писать. Датчики, скорей всего, имеют свой "сетевой" адрес (выданный производителем)
Да. Рисую структурную схему: Верхний уровень <-----485----->девайс===2wire====датчик====датчик===...===датчик 2wire - это мое обозначение "медленного самодельного интерфейса"(tm). А процессор... Он уже старый, всего то 60МГц тактовая. Сейчас уже есть под 1ГГц (для embedded применений конечно
|
|
|
Записан
|
|
|
|
Sla
|
|
« Ответ #24 : 13-03-2009 09:19 » |
|
я начинал программить микроконтроллеры с 8049 (это старший брат 51-й серии) еще на ДВК, там тогда о симмуляторах речи вообще не было. Отладка проходила втемную. Потом уже появились симмуляторы на писишках. Но, потом...
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Sla
|
|
« Ответ #25 : 13-03-2009 09:27 » |
|
С++ тебе не нужен Я тебе приблизительно описал как должно выглядеть описание объекта
1. Блок параметров датчика сетевой адрес частота опроса имя датчика 2. Блок описания методов работы с датчиком.
Вот и весь "объектный подход". Т.е. струтурное программирование с использованием "объектного" подхода. Как сказал Димка - никакого полиморфизма, инкапсуляций, наследований. Расскажи что у тебя есть. Что реализовано.
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
tugoboss
Интересующийся
Offline
|
|
« Ответ #26 : 03-04-2009 15:07 » |
|
Здравствуйте! Спасибо за направление – использовать объектный подход. Есть даже такая книга: Object Oriented Programming with ANSI C. Оцените, нормально ли получилось? Описал класс Sensor: typedef struct _TSensor { unsigned char idx; // индекс датчика в массиве unsigned char call; // вызов датчика: 0 - не вызываем, 1 - вызываем unsigned char task; // задача: 0 - чтение, 1 - калибровка unsigned char *addr; // сетевой адрес датчика unsigned char *state; // состояние: 0 - выключен, 1 - включен unsigned short *reqPeriod; // период опроса в секундах unsigned short callTimer; // таймер вызова, в секундах. При вызове - он обнуляется, и начинает считать снова, до совпадения с периодом опроса // методы класса, в качестве параметра передается указатель на объект этого же класса void (*readID) (struct _TSensor *pthis); // чтение идентификатора датчика void (*startMeasure)(struct _TSensor *pthis); // запуск измерения температуры void (*readTemp) (struct _TSensor *pthis); // считывание измеренного значения температуры void (*writeCalib) (struct _TSensor *pthis); // записываем калибровочное значение struct _TSensor *next; // для однонаправленного списка - указатель на следующий элемент }TSensor;
TSensor sensor[MAX_TSENSOR_CNT]; // создал массив объектов TSensor *psens;
Замыкаю список датчиков в кольцо и инициализирую объекты __arm void Init_Sensor (void) { unsigned char i; psens = &sensor[0]; for (i = 0; i < 32; i++) // инициализация Датчиков { psens->idx = i; psens->state = &pramRW->m_SensorParam.m_Param[i].m_State; psens->call = 0; psens->task = 0; psens->addr = &pramRW->m_SensorTable.m_Addr[i]; psens->reqPeriod = &pramRW->m_SensorTable.m_ReqPeriod[i]; psens->callTimer = 0;
psens->readID = ReadID; psens->startMeasure = StartMeasure; psens->readTemp = ReadTemp; psens->writeCalib = WriteCalib; if (i == 31) { psens->next = &sensor[0]; psens = &sensor[0]; } else { psens->next = &sensor[i+1]; // создаем цепочку указателей psens++; } } }
Реализация методов класса: __arm void ReadID(TSensor *ps) { CreateBufWW(*ps->addr, 0x04, 0, 0); // создание буфера на оптправку по 2wire SetFlagJobWW(ENTRY); }
__arm void StartMeasure(TSensor *ps) { CreateBufWW(*ps->addr, 0x05, 0, 0); SetFlagJobWW(ENTRY); }
__arm void ReadTemp(TSensor *ps) { CreateBufWW(*ps->addr, 0x06, 0, 0); SetFlagJobWW(ENTRY); }
__arm void WriteCalib(TSensor *ps) { signed short int etalonTemp; etalonTemp = (signed short int)((pramRW->m_CalibrEtalon.m_Etalon[ps->idx])*100); // эталонная Т (калибровки) в формате T*100 CreateBufWW(*ps->addr, 0x07, (unsigned char *)(&etalonTemp), 2); SetFlagTaskWW (START_MEASURE); SetFlagJobWW(ENTRY); }
Процедура опроса датчиков по очереди стала очень проста: проход по кольцу датчиков в поисках флага вызова "call" case READ: switch (flag.taskWW.stage) // стадии чтения датчика - запуск измерения, таймаут, чтение, сохранение результатов { case START_MEASURE: if ( *psens->state && psens->call) // если датчик включен и требует вызова { psens->startMeasure(psens); // запускаем измерение SetFlagTaskWW(TIMEOUT); SetFlagJobWW(BUSY); } else psens = psens->next; break; case TIMEOUT: // запуск таймаута, достаточного для измерения температуры SetFlagTaskWW(READ_TEMP); break; case READ_TEMP: psens->readTemp(psens); // читаем температуру датчика SetFlagTaskWW(SAVE_RESULTS); SetFlagJobWW(BUSY); break; case SAVE_RESULTS: // раскидываем полученные данные по структурам CalculateTemp(psens->idx); // вычисление температуры SetFlagTaskWW(START_MEASURE); psens->call = 0; // вызов датчика снимается psens = psens->next; // следующий датчик break; } break;
case CALIBRATE: if (*psens->state && psens->task) // если датчик включен и нужна калибровка { psens->writeCalib(psens); // запускаем калибровку SetFlagJobWW(BUSY); } psens->task = 0; psens = psens->next; break; } break;
Очень легко ложится на эту модель - вызов и калибровка определенных датчиков, периодический опрос с индивидуальным для каждого датчика периодом опроса - каждый получит свой вызов. Буду благодарен любым конструктивным замечаниям.
|
|
|
Записан
|
|
|
|
Sla
|
|
« Ответ #27 : 03-04-2009 15:23 » |
|
если ты создаешь массив датчиков, то зачем создаешь также список? Ты используешь микроконтроллер. Контролировать кучу, намного сложнее чем работать со статическими данными. В случае неосвобождения памяти - можешь поиметь проблему. Методы нужно вынести за описание структуры. Тебе советовали использовать объектный подход к построению необъектного кода зы. я в С, С++ не силен (совсем), но приведенный код - начало ООП, на мой неискушенный взгляд. Я, например вижу это так структура ( описание данных ) методы ( описание функций ) Инициализация данных
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Вад
|
|
« Ответ #28 : 03-04-2009 17:34 » |
|
Я бы сделал инициализацию методов в виде метода "конструктора", а то в цикле смотрится нехорошо: ведь функции для всех сенсоров будут одни и те же? Кстати, если так - есть ли смысл хранить их указатели в структуре? Потом, список лучше отделить от собственно структур датчиков - в этом и суть ООП, чтобы одна сущность выполняла одну задачу. Одновременно быть "сенсором" и "элементом списка" - не очень чисто, имхо.
|
|
|
Записан
|
|
|
|
barracudo
Гость
|
|
« Ответ #29 : 03-04-2009 18:24 » |
|
tugoboss, МК не выполняет команды парралельно. Он выполняет последовательно. Команда за командой. Это значит, что одновременно, несколько действий, выполняться не может. Если тебе нужна мультизадачность, то надо задействовать ПЛИС.
Все датчики аналоговые приборы. МК совершает действия только с цифрой. Если тебе нужна обработка аналогового сигнала, то нужно сначала задействовать АЦП (аналогово-цифровой преобразователь). Ты знаешь что такое строба? Строба это способ, как на 1 веревке повесить 32 пирата. Соедени все 32 датчика с АЦП. Каждый датчик подсоедини к одному и тому же АЦП своими контактами, не вешай их все на одну шину (питание или (-) каждого датчика выведи с МК. Есть 4-ех портовые МК. Оставшийся вывод соедени с АЦП). Выводи поочередно в цикле сигнал на каждую ногу длительностью ровно столько, сколько нужно для опроса датчика.
Если тебе нужна мультизадачность на МК, то одним МК тебе не обойтись. Должна быть система. В минимуме, это будет миниPC.
|
|
|
Записан
|
|
|
|
|