реклама на сайте
подробности

 
 
> STM32F107RCT6 + KSZ8721 (ethernet)
remixx
сообщение May 11 2018, 04:24
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 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;
}
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
remixx
сообщение May 16 2018, 06:59
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 17 2018, 06:35
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(remixx @ May 16 2018, 09:59) *
По советам jcxz решил, что надо добавлять в проект и RTOS.
...
osDelay(1);
...
osDelay(1);
...
Где-то что-то упустил или вообще все кардинально неправильно?))

А что кардинально изменилось? Точно так же всё на задержках сделали. laughing.gif
"Использовать ОС" - это не просто рассовать функции по задачам. Это в первую очередь означает, что работа должна производиться 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();
}
}
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- 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


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 14:07
Рейтинг@Mail.ru


Страница сгенерированна за 0.01412 секунд с 7
ELECTRONIX ©2004-2016