|
STM32F107RCT6 + KSZ8721 (ethernet) |
|
|
|
May 11 2018, 04:24
|
Участник

Группа: Участник
Сообщений: 25
Регистрация: 19-10-16
Пользователь №: 93 818

|
Пытаюсь запустить эзернет на МК. В качестве внешнего PHY использую KSZ8721. Задача следующая: сформировать пакет определенной структуры (т.е. условно 20-28 байт параметр1, 29-36 байт параметр2, 37-50 параметр3 и т.д.), записать в эти параметры значения с АЦП и передать определенное количество пакетов в секунду. Подключение MII, режим full duplex. Собственно в связи с вышеизложенным следующие вопросы: 1. Каким образом можно сформировать произвольную структуру пакета? 2. Будет ли достаточно для данных целей обычной инициализации эзернета в кубе и передачи пакетов функцией HAL_ETH_TransmitFrame()? Код функции представлен ниже. Код /** * @brief Sends an Ethernet frame. * @param heth: pointer to a ETH_HandleTypeDef structure that contains * the configuration information for ETHERNET module * @param FrameLength: Amount of data to be sent * @retval HAL status */ HAL_StatusTypeDef HAL_ETH_TransmitFrame(ETH_HandleTypeDef *heth, uint32_t FrameLength) { uint32_t bufcount = 0U, size = 0U, i = 0U; /* Process Locked */ __HAL_LOCK(heth); /* Set the ETH peripheral state to BUSY */ heth->State = HAL_ETH_STATE_BUSY; if (FrameLength == 0U) { /* Set ETH HAL state to READY */ heth->State = HAL_ETH_STATE_READY; /* Process Unlocked */ __HAL_UNLOCK(heth); return HAL_ERROR; } /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */ if(((heth->TxDesc)->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { /* OWN bit set */ heth->State = HAL_ETH_STATE_BUSY_TX; /* Process Unlocked */ __HAL_UNLOCK(heth); return HAL_ERROR; } /* Get the number of needed Tx buffers for the current frame */ if (FrameLength > ETH_TX_BUF_SIZE) { bufcount = FrameLength/ETH_TX_BUF_SIZE; if (FrameLength % ETH_TX_BUF_SIZE) { bufcount++; } } else { bufcount = 1U; } if (bufcount == 1U) { /* Set LAST and FIRST segment */ heth->TxDesc->Status |=ETH_DMATXDESC_FS|ETH_DMATXDESC_LS; /* Set frame size */ heth->TxDesc->ControlBufferSize = (FrameLength & ETH_DMATXDESC_TBS1); /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ heth->TxDesc->Status |= ETH_DMATXDESC_OWN; /* Point to next descriptor */ heth->TxDesc= (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr); } else { for (i=0U; i< bufcount; i++) { /* Clear FIRST and LAST segment bits */ heth->TxDesc->Status &= ~(ETH_DMATXDESC_FS | ETH_DMATXDESC_LS); if (i == 0U) { /* Setting the first segment bit */ heth->TxDesc->Status |= ETH_DMATXDESC_FS; } /* Program size */ heth->TxDesc->ControlBufferSize = (ETH_TX_BUF_SIZE & ETH_DMATXDESC_TBS1); if (i == (bufcount-1U)) { /* Setting the last segment bit */ heth->TxDesc->Status |= ETH_DMATXDESC_LS; size = FrameLength - (bufcount-1U)*ETH_TX_BUF_SIZE; heth->TxDesc->ControlBufferSize = (size & ETH_DMATXDESC_TBS1); } /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ heth->TxDesc->Status |= ETH_DMATXDESC_OWN; /* point to next descriptor */ heth->TxDesc = (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr); } } /* When Tx Buffer unavailable flag is set: clear it and resume transmission */ if (((heth->Instance)->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET) { /* Clear TBUS ETHERNET DMA flag */ (heth->Instance)->DMASR = ETH_DMASR_TBUS; /* Resume DMA transmission*/ (heth->Instance)->DMATPDR = 0U; } /* Set ETH HAL State to Ready */ heth->State = HAL_ETH_STATE_READY; /* Process Unlocked */ __HAL_UNLOCK(heth); /* Return function status */ return HAL_OK; }
|
|
|
|
|
 |
Ответов
|
May 16 2018, 06:59
|
Участник

Группа: Участник
Сообщений: 25
Регистрация: 19-10-16
Пользователь №: 93 818

|
В указанном примере как раз таки MAC и ip адреса вбиваются вручную, все хидеры lwip проставляет автоматически, все что остается это засунуть данные в payload. По советам jcxz решил, что надо добавлять в проект и RTOS. Из той информации, которую изучил на данный момент, имею следующее представление: 1. Создаю 2 задачи. В первой провожу инициализацию АЦП и в бесконечном цикле отправляю запрос на единичное преобразование, записывая результат в соответствующий элемент структуры и ставя её в очередь. Примерный код: Код // Добавляю глобальную переменную для очереди // osMailQId strout_Queue
// Указываю размер очереди // #define MAIL_SIZE (uint32_t) 1 // Насколько я понимаю, чтобы гарантированно передать по ethernet значение преобразования, в очереди должен быть всего 1 элемент, соответственно если она заполнена, то первая задача находится в блокированном состоянии
// Задаю структуру с данными // typedef data_t { int32_t temperature; uint8_t time; uint8_t date; } data;
// Создаю задачу RTOS // osThreadDef(ADC, StartTaskADC, osPriorityNormal, 0, 128); ADCHandle = osThreadCreate(osThread(ADC), NULL);
// Создаю очередь // osMailQDef(stroutqueue, MAIL_SIZE, data; strout_Queue = osMailCreate(osMailQ(stroutqueue), NULL);
// Создаю функцию первой задачи // void StartTaskADC(void const * argument) { /* USER CODE BEGIN StartTaskADC */ struct_out *qstruct; // добавляю переменную типа передаваемой в очереди структуры ADC_Init; // выполняю инициализацию АЦП (отправляю набор команд по SPI, использую функцию HAL_SPI_Transmit, можно ли взять её или для таких применений нужно с DMA каким-нибудь?) /* Infinite loop */ for(;;) { qstruct = osMailAlloc(strout_Queue, osWaitForever); // выделяю память под очередь qstruct->temperature = ADC_ReadValue; // записываю в элемент структуры temperature полученное значение из АЦП (посылаю команду единичного преобразования через HAL_SPI_Transmit, аналогичный вопрос) qstruct->time = 1 // записываю в элемент структуры time рандомное значение (пока не важно) qstruct->date = 2 // записываю в элемент структуры date рандомное значение (пока не важно) osMailPut(strout_Queue, qstruct); // отправляю структуру в очередь osDelay(1); } /* USER CODE END StartTaskADC */ } 2. Создаю вторую задачу, которая будет инициализировать UDP, получать измеренное значение температуры из очереди и отправлять его. Код // Создаю глобальные переменные структур, требующихся для передачи по ethernet // extern struct netif gnetif; static struct udp_pcb *udp_Pcb1_p;
// Создаю задачу RTOS // osThreadDef(ETH, StartTaskETH, osPriorityNormal, 0, 128); ETHHandle = osThreadCreate(osThread(ETH), NULL);
// Создаю функцию второй задачи // void StartTaskETH(void const * argument) { /* USER CODE BEGIN StartTaskETH */ osEvent event; // добавляю переменную типа структуры состояния очереди struct_out *qstruct; // добавляю переменную типа передаваемой в очереди структуры UDP_Init; // выполняю инициализацию UDP (в ней создается новое подключение UDP (udp_new), вручную записывается mac и ip адрес компа, биндится соединение (udp_bind) и проверяется на наличие ошибок) /* Infinite loop */ for(;;) { static const char packet[]; // создаю переменную для данных, которые буду класть в payload event = osMailGet(strout_Queue, osWaitForever); // забираю данные из очереди if (event.status == osEventMail) // проверяю состояние очереди qstruct = event.value.p; // присваиваю переменной типа структуры очереди указатель на элемент очереди packet[] = qstruct->temperature, qstruct->time, qstruct->date; // записываю полученные данные в переменную для payload SendPacket; // вызываю функцию отправки udp (здесь указывается ip адрес клиента, создается буффер для передачи по eth, в который заносим вышеуказанный packet[], заносим все это в payload и отправляем (udp_sendto) ethernetif_input(&gnetif); // вписываю стандартный код для обеспечения постоянной работы стека sys_check_timeouts; // продолжение... (говорят, что эти 2 строки можно заменить MX_LWIP_Process() тогда и extern struct netif gnetif не нужно, но пока попробую запуститься так) osDelay(1); } /* USER CODE END StartTaskETH */ } Где-то что-то упустил или вообще все кардинально неправильно?))
Сообщение отредактировал remixx - May 16 2018, 07:03
|
|
|
|
|
May 17 2018, 06:35
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(remixx @ May 16 2018, 09:59)  По советам jcxz решил, что надо добавлять в проект и RTOS. ... osDelay(1); ... osDelay(1); ... Где-то что-то упустил или вообще все кардинально неправильно?)) А что кардинально изменилось? Точно так же всё на задержках сделали. "Использовать ОС" - это не просто рассовать функции по задачам. Это в первую очередь означает, что работа должна производиться event-driven. Т.е. - Ваши задачи должны не всё время долбить что-то (и зачем тогда вообще многозадачность?), а должны обрабатывать события. Т.е. - в задаче АЦП дали старт преобразования и после этого задача должна ждать завершения преобразования, после чего считать данные, обработать их, и запустить следующее преобразование. Ждать она должна, ожидая готовности какого-либо элемента синхронизации ОС (мэйлбокса, семафора и т.п.). Этот мэйлбокс переводиться в сигнальное состояние может например из ISR завершения преобразования АЦП (а лучше конечно из ISR завершения DMA-пересылки блока данных АЦП->ОЗУ). То же самое и с задачей отправки данных в ETHERNET. Если у Вас задачи всё время что-то долбят, то тут никакой многозадачности нет, одна фикция, пыль в глаза. Наберите в гугле "event-driven programming". В упрощённом виде работа с АЦП должна выглядеть так: CODE void AdcReadyIsr() { AdcConfirm(); OSMboxPost(mboxAdcReady); } void AdcTask() { AdcInit(); ClearAndEnableIrqAdcReady(); while (1) { AdcStart(); OSMboxPend(mboxAdcReady); DataQueueWrite(AdcRead()); PingEthernetSendTask(); } } Это если время преобразования АЦП == необходимому периоду измерений (или можно так сконфигурить АЦП). Если нет, то чуть сложнее (с использованием таймера): CODE void AdcReadyIsr() { AdcConfirm(); OSMboxPost(mboxAdcReady); } void TimerPeriodicIsr() { TimerConfirm(); OSMboxPost(mboxTimerPeriodic); } void AdcTask() { AdcInit(); TimerInit(DESIRED_FREQUENCY); ClearAndEnableIrqAdcReady(); ClearAndEnableIrqTimerPeriodic(); while (1) { OSMboxPend(mboxTimerPeriodic); AdcStart(); OSMboxPend(mboxAdcReady); DataQueueWrite(AdcRead()); PingEthernetSendTask(); } }
|
|
|
|
Сообщений в этой теме
remixx STM32F107RCT6 + KSZ8721 (ethernet) May 11 2018, 04:24 haker_fox Я не знаю куб, но вопрос: вы хотите данные гнать н... May 11 2018, 05:38 remixx Цитата(haker_fox @ May 11 2018, 05:38) Я ... May 14 2018, 03:36  haker_fox QUOTE (remixx @ May 14 2018, 11:36) еобхо... May 14 2018, 06:33  jcxz Цитата(remixx @ May 14 2018, 06:36) Вообщ... May 14 2018, 14:33   haker_fox QUOTE (jcxz @ May 14 2018, 22:33) Ну если... May 15 2018, 01:59   XVR Цитата(jcxz @ May 14 2018, 17:33) Да ладн... May 15 2018, 08:24    jcxz Цитата(XVR @ May 15 2018, 11:24) Любой, у... May 15 2018, 09:09     XVR Цитата(jcxz @ May 15 2018, 12:09) Чтобы з... May 15 2018, 11:46      jcxz Цитата(XVR @ May 15 2018, 14:46) А кто за... May 15 2018, 12:56       haker_fox QUOTE (jcxz @ May 15 2018, 20:56) Вы пред... May 15 2018, 14:23        jcxz Цитата(haker_fox @ May 15 2018, 17:23) Пр... May 15 2018, 15:49       XVR Цитата(jcxz @ May 15 2018, 15:56) Вы пред... May 16 2018, 04:07  iosifk Цитата(remixx @ May 14 2018, 06:36) Функц... May 14 2018, 14:41 XVR Если вам нужно просто передать пакеты, то можно сд... May 14 2018, 11:51 remixx Мне действительно интересно этим заниматься, в буд... May 15 2018, 05:58 jcxz Цитата(remixx @ May 15 2018, 08:58) Перед... May 15 2018, 06:19 haker_fox QUOTE (remixx @ May 15 2018, 13:58) Мне д... May 15 2018, 06:42 mcheb Посмотрите здесь https://github.com/ntruchsess/ard... May 15 2018, 08:42 Alex11 ЦитатаМне кажется можно статично вбить ip-адреса ж... May 15 2018, 16:26 haker_fox QUOTE (Alex11 @ May 16 2018, 00:26) Работ... May 15 2018, 23:41 haker_fox Я бы рекомендовал не использовать ось. Я не знаю в... May 16 2018, 07:52 remixx Цитата(haker_fox @ May 16 2018, 07:52) Я ... May 17 2018, 02:35 remixx В рбщем поговорил с преподавателем, решили т.к. вр... May 22 2018, 16:42 haker_fox QUOTE (remixx @ May 23 2018, 00:42) Но во... May 23 2018, 00:28
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|