Jump to content

    

Двоичный семафор, ядро Cortex-M3

Добрый день!

Возникла проблемка, которую пока не могу решить. Может кто-то с такой сталкивался.

Пока код не буду постить.

Делаю прием данных из UARTa в оброботчике прерывания. По приему всего пакета отдаю из обработчика двоичный семафор в задачу. В задаче принимаю семафор с ограниченным ожиданием. Все работает нормально, но через какое-то время (1 час, 12 часов, сутки - по разному) происходит зависон задачи. Из отладки выяснил, что этот двоичный семафор не ждет ни прием семафора, ни отдачи, его длинна 1 элемент. Т.е., как я понимаю, он уже получил из обработчика прерывания семафор, но отдавать в задачу не хочет (даже по таймауту задача не продолжается). Что это??? Как это???

Т.е. вроде все класически, но...

Share this post


Link to post
Share on other sites
Как вариант, может где-то в обработчике не хватает __DSB()

Share this post


Link to post
Share on other sites

Вот коллега не поверите - даже не знаю. Специально такую команду я в обработчик от UARTa не вставлял. Если не сложно ответьте - как это влияет на взаимодействие обработчика прерывания и семафора? Я пишу на С, стоит глянуть дизассемблер, может компилятор сам это генерит для обработчика?

 

Приведу, чтобы было от чего отталкиваться:

 

Инит-код для UARTa:

  ***************************************************************************
***
Инициализация аппаратных средств для RS-485
**/
void RS485_Setup(void)
{
  /* Конфигурируем GPIO pins для RS-485 */
  GPIO_PinModeSet(RS485_TX_PORT, RS485_TX_PIN, gpioModePushPull, 1);
  GPIO_PinModeSet(RS485_RX_PORT, RS485_RX_PIN, gpioModeInputPull,  1);
  GPIO_PinModeSet(RS485_CX_PORT, RS485_CX_PIN, gpioModePushPull, 0);  // на прием 
  /* Инициализация UART для RS-485, тактирование USART должно быть включено
        - BaudRate = 19200 baud  
        - Word Length = 9 Bits
        - One Stop Bit
        - No parity 
  Don't enable UART upon intialization */
  const USART_InitAsync_TypeDef init =
  {
    usartDisable,   // Disable RX/TX when init completed
    0,              // Provide information on reference frequency. When set to 0, the reference frequency is 
    19200,          // Baud rate
    usartOVS16,     // Oversampling. Range is 4x, 6x, 8x or 16x
    usartDatabits9, // Number of data bits. Range is 4 to 10
    usartNoParity,  // Parity mode 
    usartStopbits1, // Number of stop bits. Range is 0 to 2
    false,          // Disable majority voting
    false,          // Disable USART Rx via Peripheral Reflex System
    usartPrsRxCh0   // Select PRS channel if enabled 
  };
  
  USART_InitAsync(RS485_USART, &init);      // Инициализируем асинхронный режим
    /* Вкл автоформирование CS для драйвера RS-485, 
       устанавливаем Multi-processor mode, 9-бит = 1 - адрес устройства */
  RS485_USART->CTRL |=   USART_CTRL_AUTOCS | USART_CTRL_CSINV | USART_CTRL_MPM 
                       | USART_CTRL_MPAB | USART_CTRL_TXDELAY_TRIPLE;
    /* Enable signals TX, RX, CS */
  RS485_USART->ROUTE |= USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_CSPEN;
  // | USART_ROUTE_LOCATION_LOC0 - подразумеваем (т.к. = 0);

  /* Prepare UART Rx and Tx interrupts */
  RS485_USART->IFC = _USART_IF_MASK;      // сбрасываем флаги прерывания от UARTa
  // вкл прерывания RX - на прием и MPAF-адрес мультипроцессорной системы
  RS485_USART->IEN |= USART_IEN_RXDATAV | USART_IEN_MPAF;  
  NVIC_ClearPendingIRQ(RS485_RX_IRQn);
  NVIC_EnableIRQ(RS485_RX_IRQn);
  // Включаем UART на прием с блокировкой RX - работает только прием адресов
  RS485_USART->CMD = USART_CMD_RXEN | USART_CMD_RXBLOCKEN; 
}

 

Обработчик прерывания:

/**
  ***************************************************************************
***
Обработчик прерывания на прием RS-485
**/
void RS485_RX_IRQHandler(void)
{
  uint8_t  rxData;
  /* Обработка прерывания по адресу */
  if (RS485_USART->IF & _USART_IF_MPAF_MASK)  
  {
    rxData = (uint8_t)(RS485_USART->RXDATA);  // Считываем адрес из буфера UART
    RS485_USART->IFC = USART_IFC_MPAF;        // Очистить флаг MPAF interrupt
    if(rxData == SVO_ADR)                     // Совпадение с адресом СВО
    {    
      RX_Counter = 0;
      RX_Length = PR_POS_TYPE;                 // Минимум данных до длинны
      RS485_USART->CMD = USART_CMD_RXBLOCKDIS; // Разблокируем прием RX
    }
    else 
      return;
  }
  /* Обработка прерывания по приему данных */
  if (RS485_USART->IF &  _USART_IF_RXDATAV_MASK)
  {
    rxData = (uint8_t)(RS485_USART->RXDATA);   // Считываем адрес из буфера UART
    switch (RX_Counter)                        // На лету декодируем заголовок пакета
    {
    case PR_POS_LENGTH:
      if (rxData >= RS485_RX_BUFFERSIZE)
      {
        RS485_USART->CMD = USART_CMD_RXBLOCKEN;
        return;
      }
      RX_Length = rxData;
      break;
    case PR_POS_TYPE:         // Если не для СВО пакет, останавливаем прием, ждем следующего флага начала пакета
      if (rxData != SVO_TYPE) 
      {
        RS485_USART->CMD = USART_CMD_RXBLOCKEN;
        return;
      }
    }
   }
  RS485_RX_Buf[RX_Counter] = rxData;
  RX_Counter++;
  if (RX_Counter == RX_Length)
  { 
    RS485_USART->CMD = USART_CMD_RXDIS;   // Блокируем прием по RX

    /* Выдаем двоичный семофор в задачу-обработчик по приему полного пакета */
    portBASE_TYPE xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xRxSemaphore ,&xHigherPriorityTaskWoken );
    if( xHigherPriorityTaskWoken == pdTRUE )
      vPortYieldFromISR();
  }
}

 

Задача:

/**
  ***************************************************************************
***
Задача работы с RS-485
**/
void taskRS485_ISR(void * pvParameters)
{                                       
  portBASE_TYPE xStatus;
  extern xQueueHandle xQueueRelay;      // Очередь на реле в модуле Board.с
  extern uint16_t TermoArray[];
  extern tDiscreteInput DiscreteArray[];
  extern uint8_t DIPackByte;
  uint16_t CRC;
  uint8_t* TxPacket;
  uint8_t i, ContNum, ReadCount, idx;

   vSemaphoreCreateBinary(xRxSemaphore);
   vQueueAddToRegistry(xRxSemaphore, "SEM");

  vTaskDelay( TimeoutBeforeStart );     // Пауза для точной идентификации перезагрузки СВО 
                                        // и прихода паспортного пакета от контроллера

  for (;; )
  {
    xStatus = xSemaphoreTake(xRxSemaphore, 200);   // Ждем приема пакета от ISR по RX
    if (xStatus == pdPASS)
    {
      CRC = GetCRC(RS485_RX_Buf, RS485_RX_Buf[PR_POS_LENGTH]-2);    // Проверяем CRC пакета
      if (CRC == PACK_WORD(RS485_RX_Buf[RS485_RX_Buf[PR_POS_LENGTH]-2],  RS485_RX_Buf[RS485_RX_Buf[PR_POS_LENGTH]-1]))
      {
   ................................................................................
.........................................................

        SendPacket( TxPacket, TxPacket[PR_POS_LENGTH] );
      }
      // Включаем UART на прием с блокировкой RX - работает только прием адресов
      RS485_USART->CMD = USART_CMD_RXEN | USART_CMD_RXBLOCKEN; 
    }
  }
  vTaskDelete( NULL );
}

Share this post


Link to post
Share on other sites
Вот коллега не поверите - даже не знаю. Специально такую команду я в обработчик от UARTa не вставлял. Если не сложно ответьте - как это влияет на взаимодействие обработчика прерывания и семафора? Я пишу на С, стоит глянуть дизассемблер, может компилятор сам это генерит для обработчика?
Хорошо что на си пишете... Какой компилятор используете?

Барьер в обработчике нужен для того, чтобы не залететь в него повторно и так бесконечно из-за того, что, например, флаг запроса на прерывание физически не успевает очиститься до выхода из обработчика. Ну а в контексте семафоров... не могу дать вам развёрнутого ответа, не использую free-rtos.

Share this post


Link to post
Share on other sites
Хорошо что на си пишете... Какой компилятор используете?

Барьер в обработчике нужен для того, чтобы не залететь в него повторно и так бесконечно из-за того, что, например, флаг запроса на прерывание физически не успевает очиститься до выхода из обработчика. Ну а в контексте семафоров... не могу дать вам развёрнутого ответа, не использую free-rtos.

 

Использую IAR для армов. Понял, спасибо за совет, и это попробуем.

Share this post


Link to post
Share on other sites

Не заметил установки приоритета прерывания по уарту.

 

Функции freertos можно дергать только из прерываний с приоритетом не выше, чем настроено в конфиге freertos-а. Обратите внимание, что приоритет тем выше, чем меньше его числовое значение.

Share this post


Link to post
Share on other sites

Спасибо! Похоже это и есть косячок. Еще вчера занялся этим вопросом. Приоритет установил 7-ку (для 3-битного поля). Тестю! Еще раз спасибо!

Share this post


Link to post
Share on other sites
Делаю прием данных из UARTa в оброботчике прерывания. По приему всего пакета отдаю из обработчика двоичный семафор в задачу. В задаче принимаю семафор с ограниченным ожиданием.

FreeRTOS не использую. Но - зачем ограниченное ожидание? Если семафора нет, нечего делать в задаче. Поставьте бесконечное ожидание.

Share this post


Link to post
Share on other sites
FreeRTOS не использую. Но - зачем ограниченное ожидание? Если семафора нет, нечего делать в задаче. Поставьте бесконечное ожидание.

 

Проблема решилась - именно приоритет прерывания, если из него есть вызовы функции API оси. Теперь почему не бесконечное ожидание семафора. Тут просто в коде задачи не хватает строчек для WDT. Т.е. задача переодически должна говорить, что она не зависла.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this