|
|
  |
STM32F207+FreeRTOS+LwIP замирает задача ethernetif_input |
|
|
|
Oct 16 2012, 14:03
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
Продолжаю разбираться с FreeRTOS+LwIP. Камень STM32F207, физика KSZ8041. Делаю простой UDP сервер, netconn, на основе примера ST. Симптом следующий. Посылаю с ПК запросы, устройство отвечает. Примерно через полчаса такой работы ответы приходить перестают. Просмотр состояния задач, очередей показал, что задача ethernetif_input висит в состоянии READY, но sheduler ее не запускает. Cемафор s_xSemaphore заполняется полностью. При этом другие задачи с меньшим приоритетом (мигание лампочки) выполняться продолжают. Прочитал топик, внес изменения в ethernetif.c, это ни к чему не привело. CODE 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: CODE #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: CODE #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)
|
|
|
|
|
Oct 16 2012, 17:35
|
Группа: Новичок
Сообщений: 7
Регистрация: 16-10-12
Пользователь №: 73 970

|
На работе сталкивались с подобной проблемой, но только на STMF417, могу посмотреть как там решили проблему(честно говоря не помню решили ли её вообще).
|
|
|
|
|
Oct 17 2012, 03:25
|
Группа: Новичок
Сообщений: 4
Регистрация: 22-09-12
Пользователь №: 73 641

|
V_M_Luck, а в ethernetif.c в low_level_input() тоже внесли изменения? CODE 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; }
Сообщение отредактировал IXUS666 - Oct 17 2012, 03:30
|
|
|
|
|
Oct 17 2012, 06:33
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
IXUS666, да эти изменения я внес. Извините, я это сделал сразу в начале разборок и подзабыл где и что именно.
|
|
|
|
|
Oct 17 2012, 07:44
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
Если отвлечься от LwIP, как во FreeRTOS можно завесить задачу с наивысшим приоритетом в состоянии READY? Что ее может блокировать?
|
|
|
|
|
Oct 17 2012, 08:01
|
Группа: Новичок
Сообщений: 4
Регистрация: 22-09-12
Пользователь №: 73 641

|
Цитата(V_M_Luck @ Oct 17 2012, 10:33)  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, как с ним работать я не знаю. Цитата(V_M_Luck @ Oct 17 2012, 11:44)  Если отвлечься от LwIP, как во FreeRTOS можно завесить задачу с наивысшим приоритетом в состоянии READY? Что ее может блокировать? Так если используете плагин AVIX-RT для RTOS, то в окошке Tasks можно посмотреть Event Object. Если задача заблокирована то в Event Object отображается адресс обьекта (очередь/мьютекс) котораый заблокировал задачу.
|
|
|
|
|
Oct 17 2012, 09:09
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
Цитата(IXUS666 @ Oct 17 2012, 11:01)  Попробуйте запустить сервер на не блокирующих сокетах.. Для UDP вродебы нет смысла, там нет установления соединений, ожидания подтверждений и т.п. Да и пакеты я шлю небольшие. От компа 160 байт, в обратную сторону - 60 байт. Цитата(IXUS666 @ Oct 17 2012, 11:01)  Так если используете плагин AVIX-RT для RTOS, то в окошке Tasks можно посмотреть Event Object. Если задача заблокирована то в Event Object отображается адресс обьекта (очередь/мьютекс) котораый заблокировал задачу. Я и смотрю. Состояние задачи READY! Ничего не блокирует. А шедулер на эту задачу не переключает. Вот что еще накопал. Во время сбоя стал бряком в vTaskSwitchContext( ). Вижу, что в pxReadyTasksLists на списке уровня приоритета 6 (задача ethernetif_input) задача есть. А в переменной uxTopReadyPriority то 0, то 1. Т.е. шедулер так и не узнал о существовании готовой высокоприоритетной задачи. Или что-то ему помешало переключиться на эту задачу при ее размещении в очереди готовых. Если руками здесь подправить uxTopReadyPriority на 6, все начинает крутиться дальше, правда до следующего сбоя. Справедливости ради, необходимо отметить, что иногда, гораздо реже, чем возникновение проблемы, описанной в топике, я сваливаюсь в HardFault. Видимо разваливается стек. Что из этих проблем первично - не совсем понятно, но проблема с зависанием задачи возникает чаще, пока разбираюсь с ней.
|
|
|
|
|
Oct 18 2012, 08:21
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
Кажется разобрался. Дело было в приоритетах прерываний. Я назначил для прерывания Ethernet приоритет выше, чем configMAX_SYSCALL_INTERRUPT_PRIORITY, что совсем неправильно. 1,5 часа - полет нормальный.
|
|
|
|
|
Dec 1 2012, 09:26
|
Группа: Новичок
Сообщений: 1
Регистрация: 1-12-12
Пользователь №: 74 638

|
На самом деле проблема в том, что вот в этом куске кода: Код // Освобождаем дескрипторы 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 часа работы зависал стабильно.
|
|
|
|
|
Dec 10 2012, 16:24
|
Группа: Новичок
Сообщений: 1
Регистрация: 25-09-12
Пользователь №: 73 677

|
Добрый день уважаемый 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)) за ответ зараннее благодарен
|
|
|
|
|
Feb 13 2013, 05:45
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 26-11-05
Пользователь №: 11 420

|
Цитата(PavelMostovoj @ Dec 10 2012, 19:24)  Добрый день уважаемый 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); } }
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|