Стек для W5200 без циклов задержек + STM32F103

Чипы корейской фирмы WIZnet весьма широко известны и популярны. Так же полно где можно скачать драйверы для этих микросхем. Последняя реализация выполнена на W5500 здесь
Однако все драйверы построены по принципу вызова функций, которые весьма надолго стопорят основной цикл программы, мучительно и многократно ожидая события от внешней системы. Особенно «умиляет» ожидание в функции отправки по TCP и выход из нее по Timeout. А ведь это может растянуться не на одну секунду, и даже не 10! (При стандартных настройках — 28 сек). В некоторых случаях, если программа заточена полностью на Ethernet — это не критично, но не в моем случае. Да и вообще, инструкции вида
while(!Внешнее событие);
меня вымораживает напрочь, так как устройство полностью оказывается неработоспособным длительное время.
Мною были написаны несколько модулей, в которых я реализовал стек для W5200 без задержек.
Не спрашивайте, почему именно для W5200, и почему бы не использовать lwip + Stm32fxx7 и подобные. Ответ простой: Такова данность.

Итак, для работы стека нужно вызывать из основного цикла всего одну функцию
W5200();

Со стороны железа имеются другие функции, которые требуется описать:
void Init_W5200Driver(void);
Eth_Reset(uint8_t Level);
uint8_t Eth_GetInterrupt(void); //Ножка прерывания nINT
Eth_RW_SPI(uint8_t * Buffer, uint16_t Length);
WIZ_RW_Status_Typedef Eth_Busy(void);
где Eth_Init5200Driver — инициализация железа,
Eth_Reset — устанавливается уровень ножки nReset
Eth_GetInterrupt — читаем состояние ножки nINT
Eth_RW_SPI — это вызов функции передачи буфера данных
Eth_Busy — читаем состояние занятости SPI

Все эти функции у меня реализованы в модуле w5200driver.c

Чтобы начать работать, необходимо сделать следующее:
1. Инициализировать Системный таймер (Использовал вот этот)
2. Инициализировать стек вызовом функции:
Init_W5200();

3. Инициализировать сокеты вызовом функции:
uint8_t Socket_Init(SOCKET s, Socket_Callback_Typedef Callback);

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

После этих незамысловатых операций стек в порядке очереди начнет вызывать Callback функции инициализированных сокетов. Вызывает он их только тогда, когда:
1. В данный момент не выполняется никакого обмена по SPI
и
2. В данный момент произошло событие (прерывание) по сокету и требуется обработка.
Задача состоит лишь в том, чтобы написать конечный автомат для вызываемых функций. На примере такой автомат имеется для DHCP и HTTP

Важное замечание: Стек имеет одно ограничение: Все общение с W5200 нужно производить ТОЛЬКО из вызываемых функций Callback. Только так гарантируется, что в данный момент чип не занят.

И еще: Прежде чем запускать бездумно на своей железке — стоит убедиться в том, каким образом настроены ножки. Обратите внимание на файл periph.c. Думаю, его следует удалить и сделать инициализацию периферии заново «под себя»

Файл W5200driver.c имеет описание, каким образом софт контактирует с железкой. Его тоже скорее всего понадобится править.

Расписывать подробно работу всех функций — очень долго, думаю, народ здесь грамотный и быстро разберется что к чему.
Основной посыл — нет задержек.
  • +5
  • 26 декабря 2015, 22:52
  • Mihail
  • 1
Файлы в топике: W5200+STM32_without_delay.zip

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

RSS свернуть / развернуть
Кстати, забыл сказать. Поскольку здесь используется DMA — скорость обмена возросла где-то в 5 раз.
Как ни странно, нор этот стек работает заметно быстрее, чем стандартный
0
Добрый день начал разбираюсь в вашем проекте появились некоторые вопросы.
В частности в функции Interrupt_Callback, в самом конце есть такой кусок.
if((Sn_Ir & Sn_IR_SEND_OK) && (Sn_TX_WR != Sn_TX_RD) && Socket[Socket_Interrupt].Protocol == Sn_MR_TCP)	//errata
        W5200_Add_To_Buff_Byte_Const(Sn_CR(Socket_Interrupt), Sn_CR_SEND);
    else
        if(Socket_CallBack[Socket_Interrupt])
        {
            Socket_CallBack[Socket_Interrupt](0, 0, &Socket[Socket_Interrupt].Param);
            *(uint8_t*)&Socket[Socket_Interrupt].Param.Event &= 0xE0;
        }
W5200_Send_CallBack(0, 0);


Вот тут не понятно:
1)Зачем тут нужен вызов Socket_CallBack если он уже есть в «maine»
if(Eth_GetInterrupt()){
...
}else{
.....
    Socket[Socket_Current].Param.Event.Request = 1;
    Socket_CallBack[Socket_Current](&Header, RBuffer, &Socket[Socket_Current].Param);
    Socket[Socket_Current].Param.Event.Request = 0;
.....
}


2) Даже если она и запустила HTTP_Handler, и даже если там что-то надо сделать допустим Socket_Disconnect(Param->Index); то после выхода из обработчика(HTTP_Handler) следующая функция W5200_Send_CallBack(0, 0); напрочь перетрет вызванные функции в Socket_Disconnect.
0
  • avatar
  • pokk
  • 01 сентября 2016, 13:28
Здесь вызывается CallBack, чтобы сообщить о прерывании SEND_OK. Здесь именно передается флаг о завершении передачи данных. Причем если речь идет о TCP, то функция вызовется только если буфер передачи пустой. Иначе — будет вызвана передача нового блока данных.
0
Вызов функции позволяет оперативнее реагировать на событие.
0
Благодарю, за ответ. Возникло ещё пару вопросов:
1) как у вас происходит ожидание, освобождения буфера WIZNET? После установки флага Send_OK буфер должен быть пуст и TX_Free_Size=2048(размер буфера 2кб)?

Дело вот в чем переписываю вашу библиотеку на W5500, но тут данных на передачу гораздо больше чем размер буфера W5500 сделал дробление данных на пакеты по размеру буфера или по TX_Free_Size в обработчике Http ->Send_OK, но после отправки пары пакетов флаг Send_OK перестает выставятся и http виснет на статусе SOCK_ESTABLISHED, с одним флагом OPEN. Причем если установить точку остановки в обработчик Send_OK, то все нормально отправляется.

2) В w5500 в SPI Frame, отсутствует длина посылки(он её определяет по переключению CS), по этому если передавать
данные в режиме Variable Length Data Mode, то

Ok = W5200_Add_Write_Data(s, Source, Length);
Ok &= W5200_Add_To_Buff_Byte_Const(Sn_CR(s), Sn_CR_SEND);

команда Sn_CR_SEND в данном случае будет интерпретирована как данные и она не вызовется
На данный момент я просто сделал вызов Sn_CR_SEND внутри W5200_Add_Write_Dat, но всё же контролировать порядок записи 1-4 байтных данных и переменных данных(мак адрес), не сильно хорошо.
Если ли тут какие либо идеи как это решить?
0
  • avatar
  • pokk
  • 14 сентября 2016, 14:06
недовелось работать с W5500, не могу конкретно ничего подсказать.
Освобождение буфера происходит при аппаратном прерывании SEND_OK и условии, что регистры записи и чтения равны. (Буфер пуст).
0
… что регистры записи и чтения равны..
Вы имели ввиду регистры Sn_TX_WR,Sn_TX_RD? Даже если они равны и есть флаг SEND_OK, то Sn_TX_FSR выдает что буфер не пуст (Sn_TX_FSR!=Socket.TX_Size). Это как так?
0
нужно смотреть errata. Правда чип новый, поэтому может быть это не описано еще.
0
В 5500 все вроде хорошо работает — я публиковал простецкий прото-сервер, там тока одна ошибка длинна файла неправильно указана в моменте отправки пакетов. А по SPI там 2 вроде режима с фиксированной длинной и нет, но вроде как это трогать и необязательно.
На 5500 было сделано устройство — по сравнению с ENC28J60 — это супер супер
0
Решил сделать обработку cgi запросов, но с толкнулся с небольшой проблемой, если в обработчике cgi есть какие-то задержки не связанные с wiznet, как их выдержать?
Пока что придумал сделать примерно так, внутри wizneta сделать установку обработчика cgi, а выполнение запроса вызывать в главном цикле, и вот тут встал вопрос как обратно отправить ответ запросу? Единственное решение которое приходит в голову это как-то синхронизироваться с wiznet флагами когда он там закончил все передавать/принимать и в этот момент устанавливать отправку ответа, но я считаю это как-кто не правильно.
0
  • avatar
  • pokk
  • 10 ноября 2016, 13:29
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.