V_M_Luck 0 16 октября, 2012 Опубликовано 16 октября, 2012 · Жалоба Продолжаю разбираться с FreeRTOS+LwIP. Камень STM32F207, физика KSZ8041. Делаю простой UDP сервер, netconn, на основе примера ST. Симптом следующий. Посылаю с ПК запросы, устройство отвечает. Примерно через полчаса такой работы ответы приходить перестают. Просмотр состояния задач, очередей показал, что задача ethernetif_input висит в состоянии READY, но sheduler ее не запускает. Cемафор s_xSemaphore заполняется полностью. При этом другие задачи с меньшим приоритетом (мигание лампочки) выполняться продолжают. Прочитал топик, внес изменения в ethernetif.c, это ни к чему не привело. static void ethernet_watchdog(void) { /* When Rx Buffer unavailable flag is set: clear it and resume reception */ if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) { /* Clear RBUS ETHERNET DMA flag */ ETH->DMASR = ETH_DMASR_RBUS; /* Resume DMA reception. The register doesn't care what you write to it. */ ETH->DMARPDR = 0; } } unsigned int thread_cnt = 0; void ethernetif_input( void * pvParameters ) { struct pbuf *p; for( ;; ) { if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) { while ((p = low_level_input( s_pxNetIf )) != 0) // p = low_level_input( s_pxNetIf ); { thread_cnt++; if (p != 0) { if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) { pbuf_free(p); p=NULL; } } } } ethernet_watchdog(); } } Настройки FreeRTOS: #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( ( unsigned long ) 120000000 ) #define configTICK_RATE_HZ ( ( portTickType ) 1000 ) #define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 7 ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 15 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_MALLOC_FAILED_HOOK 0 //For queue trace #define configQUEUE_REGISTRY_SIZE 5 #define configCHECK_FOR_STACK_OVERFLOW 0 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 0 #define INCLUDE_uxTaskPriorityGet 0 #define INCLUDE_vTaskDelete 0 #define INCLUDE_vTaskCleanUpResources 0 #define INCLUDE_vTaskSuspend 0 #define INCLUDE_vTaskDelayUntil 0 #define INCLUDE_vTaskDelay 1 /* This is the raw value as per the Cortex-M3 NVIC. Values can be 255 (lowest) to 0 (1?) (highest). */ #define configKERNEL_INTERRUPT_PRIORITY 255 #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */ /* This is the value being used as per the ST library which permits 16 priority values, 0 to 15. This must correspond to the configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest NVIC value of 255. */ #define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 Настройки LwIP: #define SYS_LIGHTWEIGHT_PROT 1 #define ETHARP_TRUST_IP_MAC 0 #define IP_REASSEMBLY 0 #define IP_FRAG 0 #define ARP_QUEUEING 0 #define LWIP_TCPIP_CORE_LOCKING 0 #define NO_SYS 0 #define MEM_ALIGNMENT 4 #define MEM_SIZE (20*1024) #define MEMP_NUM_PBUF 200 #define MEMP_NUM_UDP_PCB 6 #define MEMP_NUM_TCP_PCB 10 #define MEMP_NUM_TCP_PCB_LISTEN 5 #define MEMP_NUM_TCP_SEG 20 #define MEMP_NUM_SYS_TIMEOUT 10 #define PBUF_POOL_SIZE 20 #define PBUF_POOL_BUFSIZE 500 #define LWIP_TCP 0 #define TCP_TTL 255 #define LWIP_ICMP 1 #define LWIP_DHCP 0 #define LWIP_UDP 1 #define UDP_TTL 255 /* ---------- Statistics options ---------- */ #define LWIP_STATS 1 #define ETHARP_STATS 1 #define UDP_STATS 1 #define LWIP_PROVIDE_ERRNO 1 #define MEM_STATS 1 #define CHECKSUM_BY_HARDWARE #define LWIP_NETCONN 1 #define LWIP_SOCKET 0 #define LWIP_DEBUG 0 #define TCPIP_THREAD_STACKSIZE 1000 #define TCPIP_MBOX_SIZE 50 #define DEFAULT_UDP_RECVMBOX_SIZE 2 #define DEFAULT_TCP_RECVMBOX_SIZE 20 #define DEFAULT_ACCEPTMBOX_SIZE 20 #define DEFAULT_THREAD_STACKSIZE 500 #define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
nis 0 16 октября, 2012 Опубликовано 16 октября, 2012 · Жалоба На работе сталкивались с подобной проблемой, но только на STMF417, могу посмотреть как там решили проблему(честно говоря не помню решили ли её вообще). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
IXUS666 0 17 октября, 2012 Опубликовано 17 октября, 2012 (изменено) · Жалоба V_M_Luck, а в ethernetif.c в low_level_input() тоже внесли изменения? static struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p, *q; u16_t len; uint32_t l=0,i =0; FrameTypeDef frame; u8 *buffer; __IO ETH_DMADESCTypeDef *DMARxNextDesc; p = NULL; /* Get received frame */ frame = ETH_Get_Received_Frame_interrupt(); if (frame.descriptor && frame.buffer) { /* check that frame has no error */ if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) { /* Obtain the size of the packet and put it into the "len" variable. */ len = frame.length; buffer = (u8 *)frame.buffer; /* We allocate a pbuf chain of pbufs from the pool. */ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); /* Copy received frame from ethernet driver buffer to stack buffer */ if (p != NULL) { for (q = p; q != NULL; q = q->next) { memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len); l = l + q->len; } } } /* Release descriptors to DMA */ /* Check if received frame with multiple DMA buffer segments */ if (DMA_RX_FRAME_infos->Seg_Count > 1) { DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; } else { DMARxNextDesc = frame.descriptor; } /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ for (i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++) { DMARxNextDesc->Status = ETH_DMARxDesc_OWN; DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); } /* Clear Segment_Count */ DMA_RX_FRAME_infos->Seg_Count =0; } return p; } Изменено 17 октября, 2012 пользователем IXUS666 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_M_Luck 0 17 октября, 2012 Опубликовано 17 октября, 2012 · Жалоба IXUS666, да эти изменения я внес. Извините, я это сделал сразу в начале разборок и подзабыл где и что именно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_M_Luck 0 17 октября, 2012 Опубликовано 17 октября, 2012 · Жалоба Если отвлечься от LwIP, как во FreeRTOS можно завесить задачу с наивысшим приоритетом в состоянии READY? Что ее может блокировать? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
IXUS666 0 17 октября, 2012 Опубликовано 17 октября, 2012 · Жалоба IXUS666, да эти изменения я внес. Извините, я это сделал сразу в начале разборок и подзабыл где и что именно. Попробуйте запустить сервер на не блокирующих сокетах.. Похожая ситуация выплыла с HTTP сервером, когда размер HTTP запроса был больше чем PBUF_POOL_BUFSIZE-(~40-60байт) и netconn_thread блокировалась на совсем. Пока толком не разбирался с этим, но если запустить входное соединения на не блокирующих сокетах то работает (хотя я полагаю что это неправильно, т.к. должно работать и так). Для этого нужно в определить в lwipopts.h #define LWIP_SO_RCVTIMEO 1 и после newconn = netconn_accept(conn), определить newconn->recv_timeout = XX, XX в ms. Хотя у вас механизм UDP, как с ним работать я не знаю. Если отвлечься от LwIP, как во FreeRTOS можно завесить задачу с наивысшим приоритетом в состоянии READY? Что ее может блокировать? Так если используете плагин AVIX-RT для RTOS, то в окошке Tasks можно посмотреть Event Object. Если задача заблокирована то в Event Object отображается адресс обьекта (очередь/мьютекс) котораый заблокировал задачу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_M_Luck 0 17 октября, 2012 Опубликовано 17 октября, 2012 · Жалоба Попробуйте запустить сервер на не блокирующих сокетах.. Для UDP вродебы нет смысла, там нет установления соединений, ожидания подтверждений и т.п. Да и пакеты я шлю небольшие. От компа 160 байт, в обратную сторону - 60 байт. Так если используете плагин AVIX-RT для RTOS, то в окошке Tasks можно посмотреть Event Object. Если задача заблокирована то в Event Object отображается адресс обьекта (очередь/мьютекс) котораый заблокировал задачу. Я и смотрю. Состояние задачи READY! Ничего не блокирует. А шедулер на эту задачу не переключает. Вот что еще накопал. Во время сбоя стал бряком в vTaskSwitchContext( ). Вижу, что в pxReadyTasksLists на списке уровня приоритета 6 (задача ethernetif_input) задача есть. А в переменной uxTopReadyPriority то 0, то 1. Т.е. шедулер так и не узнал о существовании готовой высокоприоритетной задачи. Или что-то ему помешало переключиться на эту задачу при ее размещении в очереди готовых. Если руками здесь подправить uxTopReadyPriority на 6, все начинает крутиться дальше, правда до следующего сбоя. Справедливости ради, необходимо отметить, что иногда, гораздо реже, чем возникновение проблемы, описанной в топике, я сваливаюсь в HardFault. Видимо разваливается стек. Что из этих проблем первично - не совсем понятно, но проблема с зависанием задачи возникает чаще, пока разбираюсь с ней. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_M_Luck 0 18 октября, 2012 Опубликовано 18 октября, 2012 · Жалоба Кажется разобрался. Дело было в приоритетах прерываний. Я назначил для прерывания Ethernet приоритет выше, чем configMAX_SYSCALL_INTERRUPT_PRIORITY, что совсем неправильно. 1,5 часа - полет нормальный. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ak84ak 0 1 декабря, 2012 Опубликовано 1 декабря, 2012 · Жалоба На самом деле проблема в том, что вот в этом куске кода: // Освобождаем дескрипторы DMA // Проверяем, фрейм растянут на несколько дескрипторов или нет if (DMA_RX_FRAME_infos->Seg_Count > 1) { DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; } else { DMARxNextDesc = frame.descriptor; } // Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++) { DMARxNextDesc->Status = ETH_DMARxDesc_OWN; DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); } // Очищаем счётчик сегментов DMA_RX_FRAME_infos->Seg_Count = 0; // Если стоял флаг о том, что Rx буфер недоступен, то снимаем его и опять включаем приём if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) { // Снимаем флаг Receive buffer unavailable ETH->DMASR = ETH_DMASR_RBUS; // Включаем приём ETH->DMARPDR = 0; } Может возникнуть такая ситуация, что мы работаем с дескриптором, но внутренняя периферия Ethernet принимает ещё один фрейм в это время. И при переключении на следующий дескриптор там уже нет флага OWN и работать с ним DMA не может. Соответственно и в прерывание мы не попадём. Лечится очень просто. В этом куске кода: // Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++) { DMARxNextDesc->Status = ETH_DMARxDesc_OWN; DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); } Добавляем, чтобы стало так: // Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++) { DMARxNextDesc->Status = ETH_DMARxDesc_OWN; DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); // Если после смены дескриптора на следующий он не свободен, то нужно обработать его тоже // фактически ещё раз попасть в это прерывание if ((DMARxNextDesc->Status & ETH_DMARxDesc_OWN) == 0) { again = true; } } А там, где снимаем флаги прерываний (далее по коду) поменять на: // Снимаем флаги прерываний if (!again) { ETH_DMAClearITPendingBit(ETH_DMA_IT_R); ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS); } else { // Сюда можно добавить счётчик и убедиться, что иногда такое бывает } Ну и где-нибудь объявить переменную bool again = false; Мне такой танец с бубном помог. Оставил блок на всю ночь, вот утром проверил - полёт нормальный. А до этого через 2 часа работы зависал стабильно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
PavelMostovoj 0 10 декабря, 2012 Опубликовано 10 декабря, 2012 · Жалоба Добрый день уважаемый V_M_Luck. Работаю с той же физикой KSZ8041. Вопрос следующий: как вы изменили файл stm32_eth.h, а конкретно мне интересно та часть кода где указывается адрес PHY_SR, регистра который у каждой физики разный /** * @brief For LAN8700 */ //#define PHY_SR 31 /*!< Tranceiver Status Register */ /** * @brief For DP83848 */ #define PHY_SR 16 /*!< Tranceiver Status Register */ /* The Speed and Duplex mask values change from a PHY to another so the user have to update this value depending on the used external PHY */ /** * @brief For LAN8700 */ //#define PHY_Speed_Status ((u16)0x0004) /*!< Configured information of Speed: 10Mbps */ //#define PHY_Duplex_Status ((u16)0x0010) /*!< Configured information of Duplex: Full-duplex */ /** * @brief For DP83848 */ #define PHY_Speed_Status ((u16)0x0002) /*!< Configured information of Speed: 10Mbps */ #define PHY_Duplex_Status ((u16)0x0004) /*!< Configured information of Duplex: Full-duplex */ #define IS_ETH_PHY_ADDRESS(ADDRESS) ((ADDRESS) <= 0x20) #define IS_ETH_PHY_REG(REG) (((REG) == PHY_BCR) || \ ((REG) == PHY_BSR) || \ ((REG) == PHY_SR)) за ответ зараннее благодарен Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_M_Luck 0 13 февраля, 2013 Опубликовано 13 февраля, 2013 · Жалоба Добрый день уважаемый V_M_Luck. Работаю с той же физикой KSZ8041. Вопрос следующий: как вы изменили файл stm32_eth.h, а конкретно мне интересно та часть кода где указывается адрес PHY_SR, регистра который у каждой физики разный Извините, что сразу не ответил. Если еще актуально... Все адреса регистров и биты описаны в даташите на KSZ8041. Фактически, если работаете с библиотеками STM, вам нужно оперделить адреса регистров и биты и слегка подправить ETH_Init в файле stm32fxxx_eth.c. У меня в этой функции от настройки физики оставлено только это: /*-------------------- PHY initialization and configuration ----------------*/ /* Put the PHY in reset mode */ if(!(ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_Reset))) { /* Return ERROR in case of write timeout */ return ETH_ERROR; } while(ETH_ReadPHYRegister(PHYAddress, PHY_BCR) & PHY_Reset); /* Enable Auto-Negotiation */ if(!(ETH_WritePHYRegister(PHYAddress, PHY_ANAR, PHY_ANAR_100BTX_FD | PHY_ANAR_100BTX_HD | PHY_ANAR_10BT_FD | PHY_ANAR_10BT_HD | PHY_ANAR_SELECT))) { /* Return ERROR in case of write timeout */ return ETH_ERROR; } /* Start Auto-Negotiation */ if(!(ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_AutoNegotiation | PHY_Restart_AutoNegotiation))) { /* Return ERROR in case of write timeout */ return ETH_ERROR; } И, далее, в какой-нибудь низкоуровневой задаче (или просто в main) проверять состояние подключения (идею и куски кода позаимствовал у кого-то на форуме - большое ему спасибо): if (ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status) { //Соединение установлено switch(prev_link_state) { case 0: RegValue = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_CNTR2) & PHY_CNTR2_OP_MODE; switch(RegValue) { case PHY_CNTR2_OP_MODE_10HD: // 10 BASE T Half-duplex prev_link_state=1; RegValue = PHY_HALFDUPLEX_10M; ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, RegValue); RegValue = ETH->MACCR; RegValue &= ~(ETH_MACCR_FES | ETH_MACCR_DM); ETH->MACCR = RegValue; break; case PHY_CNTR2_OP_MODE_100HD: // 100 BASE TX Half-duplex prev_link_state=1; RegValue = PHY_HALFDUPLEX_100M; ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, RegValue); RegValue = ETH->MACCR; RegValue &= ~(ETH_MACCR_FES | ETH_MACCR_DM); RegValue |= ETH_MACCR_FES; ETH->MACCR = RegValue; break; case PHY_CNTR2_OP_MODE_10FD: // 10 BASE T Full-duplex prev_link_state=2; RegValue = PHY_FULLDUPLEX_10M; ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, RegValue); RegValue = ETH->MACCR; RegValue &= ~(ETH_MACCR_FES | ETH_MACCR_DM); RegValue |= ETH_MACCR_DM; ETH->MACCR = RegValue; break; case PHY_CNTR2_OP_MODE_100FD: // 100 BASE TX Full-duplex prev_link_state=2; RegValue = PHY_FULLDUPLEX_100M; ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, RegValue); RegValue = ETH->MACCR; RegValue |= (ETH_MACCR_FES | ETH_MACCR_DM); ETH->MACCR = RegValue; break; default: break; } case 1: break; } } else { //Нет соединения if (prev_link_state) { //Соединение было потеряно prev_link_state=0; // Enable and start Auto-Negotiation ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation | PHY_Restart_AutoNegotiation); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться