OSDev

для всех
Текущее время: 28 мар 2024, 12:40

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 13 ]  На страницу 1, 2  След.
Автор Сообщение
СообщениеДобавлено: 01 мар 2015, 15:39 

Зарегистрирован: 10 авг 2007, 15:37
Сообщения: 60
Добрый день всем. Решил попробовать сформулировать вопрос, с которым у меня вечно какие-то проблемы. собственно вот в чем дело...
Пусть есть устройство, с микроконтроллером. У устройства есть некоторое количество интерфейсов (для примера штуки четыре UART-ов, пара CAN и Ethernet до кучи). И есть несколько протоколов, по которым устройство должно общаться (возьмем ModBus RTU и TCP, МЭК 60870-5-103 ну и еще какие-нибудь наверняка появятся). Причем необходимо, что бы была возможность пользователю на объекте сделать связку протокол - интерфейс.
А проблема вот в чем, до сих пор была четкая привязка и модуль, обслуживавший передачу по какому-либо интерфейсу, знал о протоколе и делал некоторую предобработку (например в МодБасе неизвестно заранее какой длины пакет придет, вот обработчик UART-а это и отслеживал). В этом же случае они должны быть связаны через какой-то унифицированный интерфейс, но при этом вся эта куча должна быть достаточно шустрой, т.к. все-же мы в микроконтроллере делаем, а не на Core i7. Вот такая вот петрушка. Собственно задал вопрос тут, т.к. мне кажется эта задача близка людям, занимающимся разработкой ОСей и может быть кто-то сможет дать пинок в нужную сторону. Может в какой-то системе есть неплохая реализация и можно поучиться у неё.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 17:00 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
Я таки не понял в чём проблема?
Какой язык ассемблер или Си или Си++?

Описываешь общий интерфейс для аппаратуры.
Пишешь код протоколов который использует этот интерфейс.
Потом делаешь функцию, которая увязывает интерфейс аппаратуры и протокол.

Проще всего через объекты и ООП сделать.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 18:47 

Зарегистрирован: 10 авг 2007, 15:37
Сообщения: 60
Язык Си. Можно и С++ использовать, но пока не думал об этом. А проблема в том, что Модули протокола и обслуживания интерфейса оказываются слишком сильно связаны и дальнейшая модификация затрудняется. приведу пример. Возьмем UART, очень хотелось бы использовать DMA для приема, что бы на больших скоростях не дергать проц по приходу каждого байта, да вот проблема, DMA надо обязательно знать размер посылки, а его может сообщить только модуль, обслуживающий протокол, который в данный момент завязан на данный интерфейс. Сейчас так и делается - есть предобработчик, который анализирует первые полученные байты и сообщает сколько еще надо принять, а дальше уж DMA + Таймаут приема все разруливают. Но при этом, как я уже сказал, эти две части системы оказываются через чур сильно связаны... вот в этом и проблема. Просто мне кажется такая архитектура не очень хорошей, особенно в плане дальнейшей модификации.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 20:02 

Зарегистрирован: 21 фев 2015, 10:39
Сообщения: 50
зачем дма ? используй прерывания
все равно, если правильно написано, мк будет в слипе все время
передачу можно и через дма, но по прерываниям все равно проще

через дма проще гнать данные если интерфейс скоростной (спи или езернет)
а усарт не больше 56к
вот если 10 мегабит пустить, то да надо

даже кан без дма (хотя в dsPIC только через дма, но это гавнапроц) не сильно мк из слипа выводит


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 20:27 

Зарегистрирован: 10 авг 2007, 15:37
Сообщения: 60
Цитата:
а усарт не больше 56к

почему? у меня 115200.
Ну и потом поддежка протоколов связи это только малая часть того, что делает МК. так что спать ему некогда будет. поэтому и не хочу на прерывания время тратить, он же каждый раз кучу регистров сохраняет/восстанавливает (Cortex-M4 + FPU).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 21:00 

Зарегистрирован: 21 фев 2015, 10:39
Сообщения: 50
шутишь ?
он все регистры не сохраняет

я не знаю чем загрузить кортекс что б он по прерываниям не смог 12 кбайт/сек обработать ???
ты фурье там крутишь ?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 21:11 

Зарегистрирован: 10 авг 2007, 15:37
Сообщения: 60
Цитата:
ты фурье там крутишь ?

Как догадались :D И не только его, там еще обработка этого фурье и логи и т.д.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 22:07 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
leonidpr писал(а):
Язык Си. Можно и С++ использовать, но пока не думал об этом. А проблема в том, что Модули протокола и обслуживания интерфейса оказываются слишком сильно связаны и дальнейшая модификация затрудняется. приведу пример. Возьмем UART, очень хотелось бы использовать DMA для приема, что бы на больших скоростях не дергать проц по приходу каждого байта, да вот проблема, DMA надо обязательно знать размер посылки, а его может сообщить только модуль, обслуживающий протокол, который в данный момент завязан на данный интерфейс. Сейчас так и делается - есть предобработчик, который анализирует первые полученные байты и сообщает сколько еще надо принять, а дальше уж DMA + Таймаут приема все разруливают. Но при этом, как я уже сказал, эти две части системы оказываются через чур сильно связаны... вот в этом и проблема. Просто мне кажется такая архитектура не очень хорошей, особенно в плане дальнейшей модификации.

Кажется я понял вашу проблему. Не умение проектировать. А мне лень цитировать учебники.
Да вы правильно заметили модули у вас тесно связаны.

Цитата:
(для примера штуки четыре UART-ов, пара CAN и Ethernet до кучи). И есть несколько протоколов, по которым устройство должно общаться (возьмем ModBus RTU и TCP, МЭК 60870-5-103 ну и еще какие-нибудь наверняка появятся).

Мы имеем 3 устройства на 3 транспортных протокола и того 9 комбинаций. И дальнейшее модификация затруднительна из-за того что число комбинаций растёт квадратично.

Вывод надо разделить на не зависимые части. Как это сделать?
Надо сокрыть информацию о реализации. Так же этот процесс известен как инкапсулирование или абстрогирование.

С чего начать? Думаю можно начать с построения схемы связей. Выделяете части программ рисуем их квадратиками. Также показываем на схеме программные интерфейсы.
И линиями связи. Далее когда вы нарисовали связи вам надо найти наименьший разрез графа. Т.е выделить такие связи которые присутствую во всех или почти во всех вариантах.
Это и будет основополагающим для вашего программного интерфейса.

Программный интерфейс это набор имён функции её переменные, описание того что должно быть на входе что есть на выходе и как функция работает. Далее сокращенно интерфейс. Реализация интерфейса может отличаться поэтому описание работы носит общий и размытый характер. Поэтому интерфейс в первую очередь определяет имена, функций. А описания может и отсутствовать.

Так как вы пишите на Си то объекты строит рассматривать в широком смысле слова. Как модуль или подсистему или составную часть программы. А не как конкретную грамматику языка.

У манипулирования объектами через интерфейс есть два преимущества:
□ клиенту не нужно иметь информации о конкретных типах объектов,
которыми он пользуется, при условии, что все они имеют ожидаемый клиентом
интерфейс;
□ клиенту необязательно «знать» о классах, с помощью которых реализованы
объекты. Клиенту известно только об абстрактном классе (или классах),
определяющих интерфейс.
Данные преимущества настолько существенно уменьшают число
зависимостей между подсистемами, что можно даже сформулировать принцип объектно-
ориентированного проектирования для повторного использования:
программируйте в соответствии с интерфейсом, а не с реализацией.
Не объявляйте переменные как экземпляры конкретных классов. Вместо
этого придерживайтесь интерфейса, определенного абстрактным классом. Это одна
из наших ключевых идей.

Инкапсуляция зависимостей от реализации
Введём класс Device для работы с устройствами или
структуры, состоящей из функций. Название ничего не говорилось о том, с каким устройством
работает этот объект, поскольку в действительности он вообще не
связан ни с одним устройством. Класс Device инкапсулирует понятие устройства для любого физического устройства:
□ Операции для отправка и приема сообщения;
□ Открытие и закрытие устройство;
□ Возможность определять обрыв связи и возобновлять;
□ Возможность назначать адрес получателя и отправителя;

Класс Device должен покрывать функциональность устройств, которая имеется
в различных физических устройствах. Рассмотрим два крайних подхода:
□ пересечение функциональности. Интерфейс класса Device предоставляет
только операции, общие для всех физических устройств. Однако в результате мы
получаем интерфейс не богаче, чем в самой слабой из рассматриваемых
устройств. Мы не можем воспользоваться более развитыми средствами, даже если
их поддерживает большинство устройство (но не все);
□ объединение функциональности. Создается интерфейс, который включает
возможности всех существующих устройств. Здесь нас подстерегает опасность
получить чрезмерно громоздкий и внутренне не согласованный интерфейс.
Кроме того, нам придется изменять его (а вместе с ним и всю прошивку) всякий раз,
как только производитель переработает интерфейс своего устройства. К примеру при смене микроконтроллёра.
Ни то, ни другое решение «в чистом виде» не годятся, поэтому мы выберем
компромиссное. Класс Device должен предоставлять удобный интерфейс,
поддерживающий наиболее популярные возможности устройств.

Рассмотрим на примерах.
1) POSIX часть Си++ библиотека <stdio.h> - стандартный файловый ввод вывод.
Ещё в DOS были блочные устройства, к примеру тот же UART который можно было открыть как файл COM1 и читать и писать в устройство.
Код:
int fclose(FILE *stream);  /* returns 0 if success, EOF if failure */
int fflush(FILE *stream);  /* returns 0 if success, EOF if failure */
FILE *fopen(const char *filename, const char *mode); /* returns NULL iff failure */
FILE *freopen(const char *filename, const char *mode, FILE *stream); /* returns NULL iff failure */
void setbuf(FILE *stream, char *buf); /* no return value */
int setvbuf(FILE *stream, char *buf, int mode, size_t size); /* returns 0 iff success */

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); /* returns number of elements read.  * A return of 0 is error unless NMEMB was 0 */
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); /* returns number of elements written. * A return < NMEMB is error. */

int fgetc(FILE *stream); /* returns EOF iff error or end-of-file (see feof, ferror) */
char *fgets(char *s, int n, FILE *stream); /* returns NULL iff failure */
int fputc(int c, FILE *stream); /* returns EOF iff write error, C  if success */
int fputs(const char *s, FILE *stream); /* returns EOF iff write error, else >=0 */
#define getc(_s_) fgetc(_s_) /* returns EOF iff error or end-of-file (see feof, ferror) */
int getchar(void); /* returns EOF iff error or end-of-file (see feof, ferror) */
char *gets(char *s); /* returns NULL iff failure */
#define putc(_c_,_s_) fputc(_c_,_s_) /* returns EOF iff write error, C  if success */
int putchar(int c); /* returns EOF iff write error, C  if success */
int puts(const char *s); /* returns EOF iff write error, else >=0 */
int ungetc(int c, FILE *stream); /* returns EOF iff failure */

Для вас тут много лишнего можно оставить только 6 функций. И ещё добавить 1 для определения состояния есть обрыв нет обрыва установлена связь или есть ошибки нет ошибок.

2) FreeRtos реализация сокетов Беркли
Код:
void lwip_socket_init(void);

int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen);
int lwip_shutdown(int s, int how);
int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen);
int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen);
int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen);
int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
int lwip_close(int s);
int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);
int lwip_listen(int s, int backlog);
int lwip_recv(int s, void *mem, size_t len, int flags);
int lwip_read(int s, void *mem, size_t len);
int lwip_recvfrom(int s, void *mem, size_t len, int flags,
      struct sockaddr *from, socklen_t *fromlen);
int lwip_send(int s, const void *dataptr, size_t size, int flags);
int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
    const struct sockaddr *to, socklen_t tolen);
int lwip_socket(int domain, int type, int protocol);
int lwip_write(int s, const void *dataptr, size_t size);
int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
                struct timeval *timeout);
int lwip_ioctl(int s, long cmd, void *argp);
int lwip_fcntl(int s, int cmd, int val);


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 22:30 

Зарегистрирован: 10 авг 2007, 15:37
Сообщения: 60
Спасибо за то что подробно описали процесс. вы правы, дело тут в неумении проектировать. Просто разработка под МК носит еще и свою специфику, как вы знаете, которая отражается и на процессе проектирования. я понимаю, что можно сделать модули, обслуживающие интерфейсы связи, а протоколы реализовать в виде задач, каждая из которых просто читает или пишет то, что модуль интерфейса накопил в буфере. Но мне кажется это неэффективным решением. ведь если оперировать терминами ОС, задача, обслуживающая протокол должна в таком случае периодически просыпаться, что бы посмотреть, пришло что-то или не пришло.
х-м-м, а вот тут идейка пришла. ведь можно сделать что-то вроде мьютекса, который будет взводиться обслуживающим интерфейс модулем, когда пришел блок данных. Соответственно задача, которой эти данные нужны будет спать, пока они не появятся. И плюс реализовать возможность сообщить модулю, обслуживающему интерфейс взводить мьютекс только по приходу N байт (ну или пакетов, не принципиально).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 мар 2015, 22:36 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
Про динамическое связывание интерфейсов в Си. Для связи адресного протокола и интерфейса устройства.
Можно использовать функциональный тип в структуре
http://www.gamedev.ru/flame/forum/?id=168742&page=2#m24
http://www.cyberforum.ru/post5020356.html

Тогда заменяя функции в струткуре будут вызываться разные реализации.
Так вот вам понадобиться строитель(шаблон фабрика) чтобы сделать нужную структуру с нужными реализациями. Строитель будет связывать протокол и устройство.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 13 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB