Jump to content

    
pokk

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

Recommended Posts

Добрый день, пишу модуль 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;
    }
    //---------------------------------------------------------------
}

 

Edited by pokk

Share this post


Link to post
Share on other sites
3 часа назад, pokk сказал:

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

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

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

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

 

Share this post


Link to post
Share on other sites
37 минут назад, pokk сказал:

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

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

Share this post


Link to post
Share on other sites
7 hours ago, Сергей Борщ said:

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

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

 

Share this post


Link to post
Share on other sites
3 часа назад, pokk сказал:

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

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

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
6 часов назад, Сергей Борщ сказал:

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

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

Share this post


Link to post
Share on other sites
16 minutes ago, juvf said:

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

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

20 minutes ago, juvf said:

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

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

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

Share this post


Link to post
Share on other sites
2 минуты назад, pokk сказал:

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

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

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

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

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

Share this post


Link to post
Share on other sites
51 minutes ago, juvf said:

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

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

Share this post


Link to post
Share on other sites
1 час назад, pokk сказал:

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

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

Share this post


Link to post
Share on other sites
3 minutes ago, Сергей Борщ said:

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

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.