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

Обработка прерывания wiznet 5300

Добрый день, пишу модуль UDP сервера для wiznet 5300 подключенный по параллельной шине.

Столкнулся с такой проблемой, при роботе с ним по выводу прерывании INT, процессор может потерять событие, принятие пакета сокетом.

У w5300 есть 8 сокетов и если сокет принимает пакет UDP то выставляется  логический уровень (1) на вывод прерывания (INT) и записывает событие в регистр прерываний сокета, так вот если во время прерывания wiznet примет пакет в другой сокет вывод INT так и будет находиться в единице,  без переключения  1/0/1 и тем самым процессор может пропустить этот сигнал если он работает по прерыванию вывода INT, хотя в регистре прерывания другого сокета будет стоять бит приема. Но все же для максимальной реакции (в будущем планирую принимать UDP поток данных) приема  UDP пакета, хочу заложить работу по прерыванию + периодический опрос размера данных в сокете.

Модуль сервера UDP хочу заложить таким образом:
1) Есть основная задача (FreeRTOS), которая периодически проверяет размер данных в сокетах, именно в нескольких (которые настроены в режиме UDP), и если в каком из сокетов появились данные, то запускаем отдельную задачу на обработку этого пакета. Таким образом обработка пакетов на разных сокетах происходит независимо друг от друга.

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

Для, одного событие (приема), это логика заработало, но как добавилось событие (передача завершена), тут застрял. Уже думаю, в прерывании от wiznet, разбирать регистр прерываний wiznet на события и записывать их в разные очереди, но если взять TCP сокет там событий много.. каша, получиться.

В общем как правильно обработать прерывание INT от wiznet и выдать сигналы на продолжение работы задачам, в разных режимах TCP, UDP .

Главная задача получилась такая

Spoiler



InitUdpServer(0,1234,vUdpSocketTask); //Пример запуска задачи vUdpSocketTask на обработку принятого пакета, с 0 сокета на порте 1234

void vUdpServer(void *pvParameters){
    EventIntUdpServer_t Socket_Event[MAX_UDP_SERVER];

	initConfing();  //ожидаем считывание из памяти IP адрес
    SetNetwork(confing.eth);
    //------------------------------------------------------
    for(uint8_t i=0;i<MAX_UDP_SERVER;i++){
        Socket_Event[i].NumSocket=0;
        Socket_Event[i].Sn_IR=0;
        if(UdpServer[i].Callback!=0){
            InitSocket(UdpServer[i].NumSocket, Sn_MR_UDP, UdpServer[i].port);
        }
    }
    //------------------------------------------------------
    while(1){
        //---------------------------------------------------------
        //-----------WaitQueueEventUdp-----------------------------
        //---------------------------------------------------------
    	if(WaitQueueEventUdp(Socket_Event,TIMEOUT_WIZ_INT)){           //
            for(uint8_t i=0;i<MAX_UDP_SERVER;i++){
                //---------------------------------------------------------
                if(UdpServer[i].Callback==0){continue;}
                if(UdpServer[i].activ==0){  //Проверяем I а запускаем S
                    //---------------------------------------------------------
                    if(Socket_Event[i].Sn_IR&Sn_IR_RECV){  //Событие приема данных в сокетом
                        uint8_t s=Socket_Event[i].NumSocket;
                        Socket_Event[i].Sn_IR=0;
                        UdpServer[s].activ=1;
                        xTaskCreate(UdpServer[s].Callback,(char*)"SocketTask", configMINIMAL_STACK_SIZE, &UdpServer[s].NumSocket, tskIDLE_PRIORITY + 2, NULL);
                    }
                    //---------------------------------------------------------
                }
           	}
        }
        //---------------------------------------------------------
        //-------Штатный вызов если было утерено прерывание--------
        // -------------Опрос с периодом 1ms-----------------------
    	//---------------------------------------------------------
        uint16_t SocketLength;
        for(uint8_t i=0;i<MAX_UDP_SERVER;i++){
            if(UdpServer[i].Callback!=0){
                if(UdpServer[i].activ==0){ //Задача уже была вызвана через прерывание! и находиться в исполнении !
                    SocketLength= getSn_RX_RSR(UdpServer[i].NumSocket);
                    if(SocketLength>0){
                        UdpServer[i].activ=1;
                        xTaskCreate(UdpServer[i].Callback,(char*)"SocketTask", configMINIMAL_STACK_SIZE, &UdpServer[i].NumSocket, tskIDLE_PRIORITY + 2, NULL);
                    }
                }
            }
        }
    	//----------------------------------------------
    }
    vTaskDelete(NULL);
}

 

Обработка прерывания вышла так

Spoiler


//==================================================================================================
/*
	* @Описание:
	* @Параметр:
	* @Возврат:		Нету
*/
void W5300_Interrupt(void){
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
Socket_Event_Typedef Socket_Event;
EventIntUdpServer_t EventUdpServer;
uint8_t s=0;
uint16_t ir;
    //----------------------------------------------
    setIMR(0x00);
    while(ir = getIR()){
        for(uint8_t i = 0; i < 8 ; i ++){
            if(ir & (0x0001 << i)){
                if((getSn_MR(i)&0x0F)==Sn_MR_UDP){  //Определяем как настроен сокет в каком режиме UDP TCP
                    //----------------------------------------------
                    EventUdpServer.Sn_IR = getSn_IR(i);
                    EventUdpServer.NumSocket=i;
                    xQueueSendToBackFromISR(xQueueUdpServerInt, &EventUdpServer, &xHigherPriorityTaskWoken);
                    //----------------------------------------------
                    setSn_IR(i, EventUdpServer.Sn_IR);
                }
            }
        }
    }
   setIMR(0xFF);
}
//==================================================================================================
/*
    * @Описание:    Описание функции.
    * @Параметр:
    * @Возврат:     Нету
*/
int8_t WaitQueueEventUdp(EventIntUdpServer_t *Socket_Event,uint16_t timeout){
    uint8_t CntEvent=0;
    uint8_t IndexEvent=0;
    do{
        if(xQueueReceive(xQueueUdpServerInt,&Socket_Event[IndexEvent],timeout)==pdTRUE){
            NOP;
            IndexEvent++;
            break;
        }else{
            NOP;  //TimeOut
        }
        CntEvent=uxQueueMessagesWaiting(xQueueUdpServerInt);
    }while(CntEvent>0);
    //---------------------------------------------------------------
    if(IndexEvent>0){
        return 1;
    }else{
        return -1;
    }
    //---------------------------------------------------------------
}

 

Изменено пользователем pokk

Поделиться сообщением


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

3 часа назад, pokk сказал:

без переключения  1/0/1 и тем самым процессор может пропустить этот сигнал если он работает по прерыванию вывода INT

Настройте прерывание не по спаду, а по низкому уровню на выводе INT. Пока не вычитаете, не обработаете и не обнулите все флаги в статусных регистрах - INT будет в нуле и ваш обработчик будет вызываться. И не нужно никаких периодических проверок.

Поделиться сообщением


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

Что-то не припомню можно ли так сделать в STM32, завтра гляну. Главный вопрос как с этим дальше работать. То ли в очередь сразу байт с набором событий записывать, то ли разобрать все биты из байта и их в очередь записывать. Либо вообще передавать только факт того что произошло прерывание (через xEventGroupSetBits), и уже на месте вычитывать из байт с событиями и принимать решения. В последний варианте конечно, будет происходить активация задачи даже не по своему что не очень нравиться. 

Поделиться сообщением


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

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

из прерывания посылать эвент, как факт того, что произошло прерывание. всю обработку и разбор статусных регистров вести в задаче-обработчике-событий-W5100. в задаче блокироваться до получения эвента со сбросом при получении.  в задаче вычитывать статусные регистры w5300 и обрабатывать все пакеты. обработку вести до тех пор, пока есть INT. как только инт пропал, блокироваться на ожидании нового эвента из прерывания.

Поделиться сообщением


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

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

А как сделать так что бы в задача ждала одно событие из нескольких сокетах, типа ожидает событие "прием пакета" либо со второго сокета или с первого.

В задаче обработке прерываний думаю сгруппировать событие следующим образом.
Создать битовые группы на каждое событие свое, а внутри каждый бит будет указывать какой сокет вызвал это событие.
Тогда я смогу, в udp server ждать прием с любых сокетов (2,3 или 6) и с заданным приоритетом вызвать задачу обработчика пакета данных.
Только для TCP приодеться, много групп городить.

 

Поделиться сообщением


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

37 минут назад, pokk сказал:

Создать битовые группы на каждое событие свое, а внутри каждый бит будет указывать какой сокет вызвал это событие.

В прерывании вычитывайте из статусного регистра номера сокетов, для которых есть флаги прерывания. Сигнальте соответствующий каждому сокету флаг, запрещайте прерывания от сокетов. В отдельную переменную положите битовую маску всех просигналенных флагов. Каждая задача пусть ждет флаг своего сокета. Получив флаг обрабатывает сокет, сбрасывает флаг своего сокета в той общей переменной и если переменная обнулилась - разрешает прерывания от сокетов.

Поделиться сообщением


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

7 hours ago, Сергей Борщ said:

Каждая задача пусть ждет флаг своего сокета

А если одна задача ждет одно событие (прием пакета), из разных сокетах, что бы при одновременно срабатывании прерывании можно было установить приоритет какой сокет первый обрабатывать.

 

Поделиться сообщением


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

3 часа назад, pokk сказал:

А если одна задача ждет одно событие (прием пакета), из разных сокетах

Я не настолько хорошо знаю FreeRTOS (делал на ней один проект 14 лет назад). Наверняка что-то можно придумать. Идея все та же - прерывание от входа INT настраиваем на срабатывание от низкого уровня, в обработчике считываем флаги запросивших сокетов, запрещаем прерывания сокетов, сигналим. В процессе (процессах) обрабатываем запросившие сокеты, когда обработали все - разрешаем прерывания.

Сейчас вспомнилось - в W5100 можно было запрещать прерывания от каждого сокета отдельно. В W5300 наверняка так же, тогда еще проще - в обработчике прерываний запрещаем прерывания каждого запросившего сокета, после обработки каждого сокета в процессе разрешаем его прерывания.

Поделиться сообщением


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

18 часов назад, pokk сказал:

А как сделать так что бы в задача ждала одно событие из нескольких сокетах, типа ожидает событие "прием пакета" либо со второго сокета или с первого.

не очень понятно. .... чтоб не путаться расставим точки над i. есть задача(~и), которая(~ые) ведут обмен по udp(tcp) - пусть будут задачаА, задачаВ. Есть задача, которая обрабатывает прерывания - задачаО (обработчик).

В обработчике прерывания ISR только сигналить, что есть прерывание и пробуждать задачуО. в прерывании ни в коем случае не лезть в старусные регистры... ни какие там SPI не опрашивать. Всё это делать в задачеО. Если у вас два или три W5300 (у меня было 2 шт), то с каждого прерывания посылать свой бит-эвент. Прерывание нужно, только чтобы пробудить МК/задачу. В обработчике прерывания: Послал эвент, сбросил (если нужно) флаг прерывания. ВСЁ!!! Нахождение в прерывании - минимальное время.

 

В задачеО блокироваться до получения этих эвентов, как я говорил ранее.

В задачеО, после пробуждения, вычитывать статусные регистры и по ним определять с какого сокета прерывание/пакет/событие. Если пришел пакет с socked0, то вычитывать пакет из сокета0 и далее.... как вам удобно.... например динамически выделить область памяти, положить туда пакет, адрес (указатель) начала пакета отправить в очередь queueReciveSocked0. если пакет с сокета4, то  выделить область памяти, положить туда пакет, адрес (указатель) начала пакета отправить в очередь queueReciveSocked4.

Или с сокета0 и с сокета4 полученные данные отправлять в одну очередь  queueReciveForTaskA.

Или иметь статический буффер для каждого сокета/задачиА/задачиВ. Вычитываем сокет0 и посылаем эвент в группу что полученны данные от сокета0.

ЗадачаА проверяет очередь queueReciveSocked0/queueReciveForTaskA или смотрит биты в групповом эвенте. при получении в очереди queueReciveSocked0/queueReciveForTaskA сообщения или бита в эвенте, задачаА обрабатывает пакет.

 

Передача из задачиО в задачуА - это алгоритмы на вскидку.... что первое в голову пришло... Обратите ещё внимание на xStreamBuffer и на xMessageBuffer. Возможно они лучше подойдут для вашей задачи.  Можно объявить сделать структуру/класс, что типа

struct

{

int socked; //номер сокета
int len;//размер пакета

uint8_t *data;

IpAdrressSource ip;

AnyType samething;

};

в задачеО вычитывать пакет, создавать эту структуру и отправлять мессадж. А в задачеА и задачеВ обрабатывать.

 

Отправку из МК наружу через W5300 можно нужно тоже сделать через задачуО. Ибо нефиг каждой задаче лезть в SPI... да и вообще... вы можете поменять W5300 на W5100, или поменять SPI на параллельную шину,  или вообще уйти на процессор со встроенным ethernet. Вам достаточно переписать задачуО, а задачи А и В останутся неизменными.

 

 

 

ps повторю своё сообщение, с новой терминологией

всю обработку и разбор статусных регистров вести в задаче-обработчике-событий-W5100 задачеО. в задачеО блокироваться до получения эвента со сбросом при получении.  в задачеО вычитывать статусные регистры w5300 и обрабатывать все пакеты (т.е. вычитывать пакеты и отправлять задачам А и В). обработку вычетку пакетов в задачеО вести до тех пор, пока есть активный уровень INT. Потом блокироваться на ожидании эвента из прерывания.

 

pps эвент - имеется в виду xEventGroup

Поделиться сообщением


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

6 часов назад, Сергей Борщ сказал:

Идея все та же - прерывание от входа INT настраиваем на срабатывание от низкого уровня

нет такого прерывания, по крайней мере в stm32f4* и в stm32f1*. Только по фронтам.

Поделиться сообщением


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

16 minutes ago, juvf said:

Ибо нефиг каждой задаче лезть в SPI... да и вообще... вы можете поменять W5300 на W5100, или вообще уйти на процессор со встроенным ethernet. Вам достаточно переписать задачуО, а задачи А и В останутся неизменными.

Вот это прямо то что надо!!, это одна из основных причин сделать все по уму.  

20 minutes ago, juvf said:

не очень понятно. .... чтоб не путаться расставим точки над i. есть задача(~и), которая(~ые) ведут обмен по udp(tcp) - пусть будут задачаА, задачаВ. Есть задача, которая обрабатывает прерывания - задачаО (обработчик).

Я хотел заложить чуть другую логику,  считывание самого пакета переложить на задачу A и B. Для того что бы в задаче A можно было считывать не весь пакет, а только шапку пакета, и если она  битая (либо по адресу пакета если есть такой внутри протокола), то игнорировать пакет целиком не копируя его в процессор, но по факту я таким пользовался только раз. И походу именно это не дает сделать универсально и разделить, работу через буфер.

На передачу, все так же складываем в буфер и передает сигнал из задачи А в задачу О ?

Поделиться сообщением


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

2 минуты назад, pokk сказал:

На передачу, все так же складываем в буфер и передает сигнал из задачи А в задачу О ?

да. задачуО можно заблокировать на ожидание в группе эвентов на два бита. бит0 - прерывание инт. бит1 - есть в буфере пакет для отправки. ЗадачаО по пробуждении проверит, какой бит и соответственно сделает нужную работу.

11 минут назад, pokk сказал:

Для того что бы в задаче A можно было считывать не весь пакет, а только шапку пакета, и если она  битая

на сколько я помню, udp вам гарантирует целостность пакета. Если пакет, при передаче побьётся, вы его не получите (он будет потерян). Целостность пакета может нарушить только отправитель, если он отправит кривые данные.

Поделиться сообщением


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

51 minutes ago, juvf said:

на сколько я помню, udp вам гарантирует целостность пакета. Если пакет, при передаче побьётся, вы его не получите (он будет потерян)

Нет нет, вы не правильно меня поняли, я имел ввиду когда принимаешь поток данных, в протоколе может быть номер какой это поток данных, и что бы один поток от другого отфильтровать, можно принять сначала шапку, посмотреть на этот номер и дальше принять решения считывать данные или нет,  в w5500 такое можно сделать, а вот в  w5300 увы что бы заигнорить пакет его надо считать целиком.

Поделиться сообщением


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

1 час назад, pokk сказал:

в w5500 такое можно сделать, а вот в  w5300 увы что бы заигнорить пакет его надо считать целиком.

Да ладно! У них со времен W3100 ничего не менялось в этом плане - двигай Rx Read Pointer куда нужно и всех делов.

Поделиться сообщением


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

3 minutes ago, Сергей Борщ said:

Да ладно! У них со времен W3100 ничего не менялось в этом плане - двигай Rx Read Pointer куда нужно и всех делов.

Ну вот поменяли, W5300 подключается только по параллельной шине, по этому что бы не городить большое количество линий адреса, они сделал доступ к буферу RX/TX через небольшой буфер FIFO. И доступ к указателям убрали.  

Поделиться сообщением


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

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

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

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

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

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

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

×
×
  • Создать...