STM32 UART LLD, character match interrupt

Use this forum for requesting small changes in ChibiOS. Large changes should be discussed in the development forum. This forum is NOT for support.
faisal
Posts: 114
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 15 times
Been thanked: 14 times

STM32 UART LLD, character match interrupt

Postby faisal » Fri Oct 06, 2017 9:41 pm

The character match interrupt is very useful for efficient packetized communications over uart. For example, if you use COBS encoding (https://en.wikipedia.org/wiki/Consisten ... e_Stuffing), you can enable the character match interrupt (CR1->CMIE) for a 0 (which is the escaped character in COBS).
* Then on each character match interrupt, you can enable a DMA transfer for the maximum allowable packet size (ex. for COBS it is 254 bytes).
* If each frame is sandwiched between the escape character (ex. 0x0 for COBS), then on the next character match interrupt, you stop the DMA transfer and notify the user.

You can do really high speed UART comms with framing with very little processor intervention ..

Attached is some code I refactored a bit from working, tested code (this may or may not work - since i just modified it for the sake of demonstrating the concept).

Code: Select all

static uint8_t sCobsUsartRxDmaBuf[COBS_USART_RX_DMA_BUF_LEN];

void character_match_cb(void)
{
   // wait for the current DMA transaction to finish
   int32_t t = 0;
   while ((USART1->ISR & USART_ISR_RXNE) & (t++ < USART_DMA_WAIT_TRANSFER_CYCLES));
   
   // Check how many bytes the DMA has already transfered
   // The DMA counter is reset to the configured transaction length
   // if it completed the transaction
   uint32_t dma_count = DMA1_Channel5->CNDTR & 0xffff;
   
   // If the dma_count is == to the transaction length (COBS_USART_RX_DMA_BUF_LEN)
   // then that either means it already transfered an entire packet, or it received
   // a character match, and the CM interrupt fired before the DMA had a chance to
   // transfer the incoming byte.
   dma_count = (dma_count >= COBS_USART_RX_DMA_BUF_LEN) ? 0 : dma_count;

   // search for cobs frame delimiter character (0x00)
   // This is to account for a race condition between CM interrupt and
   // DMA transfer complete interrupt. Start from the end of the DMA transfer
   // buffer. On a reliable channel, and a well behaving actor on the other side
   // there should only be 1 iteration of this.
   do
   {
      t = COBS_USART_RX_DMA_BUF_LEN - dma_count - 1; // current dma buf index to inspect
      if ((sCobsUsartRxDmaBuf[t] == 0x00) && (t > 0))
      {
         // perform callback here
         got_cobs_frame_cb(sCobsUsartRxDmaBuf, t);
         break;
      }
   } while (++dma_count < COBS_USART_RX_DMA_BUF_LEN);
   
   // stop the current DMA transfer (the frame length could be less than the maximum)
   HAL_UART_AbortReceive(&huart1);
   
   // configure a new DMA transfer for upto the maximum frame length
   HAL_UART_Receive_DMA(&huart1, sCobsUsartRxDmaBuf, COBS_USART_RX_DMA_BUF_LEN);
   
   // This is just a hack to work with the lame STM32 driver library
   DMA1_Channel5->CCR &= ~DMA_CCR_HTIE;
   return;
}

void USART1_IRQHandler(void)
{
   uint32_t isrflags = USART1->ISR;
   if (isrflags & USART_ISR_CMF)
   {
      character_match_cb();
      USART1->ICR = USART_ICR_CMCF;
   }
  HAL_UART_IRQHandler(&huart1);
}

void DMA1_Channel5_IRQHandler(void)
{
   uint32_t isrflags = DMA1->ISR;

   if (isrflags & DMA_FLAG_TC5)
   {
      DMA1->IFCR = DMA_IFCR_CTCIF5;
      HAL_UART_AbortReceive(&huart1);
      HAL_UART_Receive_DMA(&huart1, sCobsUsartRxDmaBuf, COBS_USART_RX_DMA_BUF_LEN);
      DMA1_Channel5->CCR &= ~DMA_CCR_HTIE;
   }
   if (isrflags & DMA_FLAG_HT5)
   {
      DMA1->IFCR = DMA_IFCR_CHTIF5;
   }
}

User avatar
Korken
Posts: 264
Joined: Wed Apr 02, 2014 4:09 pm
Location: Luleå, Sweden
Has thanked: 5 times
Been thanked: 6 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Korken » Mon Oct 09, 2017 1:46 pm

That is a really useful feature!
Which MCUs has it? I could only find it on the STM32F7s.
Could it still be integrated if it is not a generic feature, Giovanni?

User avatar
Giovanni
Site Admin
Posts: 10733
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 306 times
Been thanked: 256 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Giovanni » Mon Oct 09, 2017 1:53 pm

It could be done as an F7-specific extension to the configuration structure, it could also be done in SW, without relying on comparator registers, while not "reading" the driver takes one interrupt for each character received and calls a callback.

Not very high priority anyway.

Giovanni

User avatar
Korken
Posts: 264
Joined: Wed Apr 02, 2014 4:09 pm
Location: Luleå, Sweden
Has thanked: 5 times
Been thanked: 6 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Korken » Mon Oct 09, 2017 2:12 pm

Cool!

>> Not very high priority anyway.
No problem, if we desired it a lot we can provide patches as well :)

Could you point to an example of device specific additions in a config structure for reference?

User avatar
Giovanni
Site Admin
Posts: 10733
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 306 times
Been thanked: 256 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Giovanni » Mon Oct 09, 2017 2:16 pm

Just add the extra fields you need at the end of the structure.

Giovanni

faisal
Posts: 114
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 15 times
Been thanked: 14 times

Re: STM32 UART LLD, character match interrupt

Postby faisal » Mon Oct 09, 2017 2:36 pm

Korken wrote:That is a really useful feature!
Which MCUs has it? I could only find it on the STM32F7s.
Could it still be integrated if it is not a generic feature, Giovanni?


Many MCUs support the character match interrupt. Im currently using it on the stm32L4 series. Works like a charm, and is almost necessary if you want to do very high speed comms over UART and still have some CPU left over ..

User avatar
Korken
Posts: 264
Joined: Wed Apr 02, 2014 4:09 pm
Location: Luleå, Sweden
Has thanked: 5 times
Been thanked: 6 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Korken » Mon Oct 09, 2017 2:55 pm

I quite like this feature (as I also use COBS-ZPE/ZRE).
I have an NUCLEO-F7 on the way, I could probably add the missing parts if no one feels up to the task.
Last edited by Korken on Mon Oct 09, 2017 3:01 pm, edited 1 time in total.

faisal
Posts: 114
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 15 times
Been thanked: 14 times

Re: STM32 UART LLD, character match interrupt

Postby faisal » Mon Oct 09, 2017 3:00 pm

Korken wrote:I quite like this feature (as I also use COBS-ZP/ZE).
I have an NUCLEO-F7 on the way, I could probably add the missing parts if no one feels up to the task.


Cool, check out my example code in the first post. I actually have a ring buffer *of* COBS frames (like 5 framed deep) that the interrupt pushes the completed frame into, which another task then processes. Im quite please with performance, usually using DMA for UART receive is rarely used for messaging protocols - but COBS like encoding allows for precisely that.

User avatar
Korken
Posts: 264
Joined: Wed Apr 02, 2014 4:09 pm
Location: Luleå, Sweden
Has thanked: 5 times
Been thanked: 6 times
Contact:

Re: STM32 UART LLD, character match interrupt

Postby Korken » Mon Oct 09, 2017 3:02 pm

Thanks, I will take a look!

faisal
Posts: 114
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 15 times
Been thanked: 14 times

Re: STM32 UART LLD, character match interrupt

Postby faisal » Sun Mar 18, 2018 4:05 am

Patch for hal_uart.h to support character match on devices which support it. Shouldn't break anything if LLD doesn't support it ...

Code: Select all

Left base folder: /chibios-svn2-stable_18.2.x/os
Right base folder: /ChibiOS_18.2.0/os
*** hal/include/hal_uart.h   2018-03-17 21:17:00.000000000
--- hal/include/hal_uart.h   2018-03-17 21:42:16.000000000
***************
*** 206,217 ****
--- 206,234 ----
    osalSysUnlockFromISR();                                                   \
  }
  #else /* !UART_USE_WAIT */
  #define _uart_wakeup_rx_timeout_isr(uartp)
  #endif /* !UART_USE_WAIT */
 
+ #if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__)
+ /**
+  * @brief   Wakes up the waiting thread in case of RX character match.
+  *
+  * @param[in] uartp     pointer to the @p UARTDriver object
+  *
+  * @notapi
+  */
+ #define _uart_wakeup_rx_cm_isr(uartp) {                                \
+   osalSysLockFromISR();                                                     \
+   osalThreadResumeI(&(uartp)->threadrx, MSG_TIMEOUT);                       \
+   osalSysUnlockFromISR();                                                   \
+ }
+ #else /* !UART_USE_WAIT */
+ #define _uart_wakeup_rx_cm_isr(uartp)
+ #endif /* !UART_USE_WAIT */
+
  /**
   * @brief   Common ISR code for early TX.
   * @details This code handles the portable part of the ISR code:
   *          - Callback invocation.
   *          - Waiting thread wakeup, if any.
   *          - Driver state transitions.
***************
*** 340,351 ****
--- 357,389 ----
    if ((uartp)->config->timeout_cb != NULL) {                                \
      (uartp)->config->timeout_cb(uartp);                                     \
    }                                                                         \
    _uart_wakeup_rx_timeout_isr(uartp);                                       \
  }
 
+ /**
+  * @brief   Character match ISR code for receiver.
+  * @details This code handles the portable part of the ISR code:
+  *          - Callback invocation.
+  *          - Waiting thread wakeup, if any.
+  *          - Driver state transitions.
+  *          .
+  * @note    This macro is meant to be used in the low level drivers
+  *          implementation only.
+  *
+  * @param[in] uartp     pointer to the @p UARTDriver object
+  *
+  * @notapi
+  */
+ #define _uart_rx_char_match_isr_code(uartp) {                                     \
+   if ((uartp)->config->rx_cm_cb != NULL) {                                \
+     (uartp)->config->rx_cm_cb(uartp);                                     \
+   }                                                                         \
+   _uart_wakeup_rx_cm_isr(uartp);                                       \
+ }
+
  /** @} */
 
  /*===========================================================================*/
  /* External declarations.                                                    */
  /*===========================================================================*/
 


Return to “Small Change Requests”

Who is online

Users browsing this forum: No registered users and 1 guest