cancel
Showing results for 
Search instead for 
Did you mean: 

Why does my Tx-only software 3 MBaud UART sometimes send strange characters?

arnold_w
Senior

I am working with the STM32F769 microcontroller and it's using FreeRTOS operating system. It uses the following clock configuration (216 MHz SystemCoreClock):

/** System Clock Configuration
*/
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
 
    /**Configure the main internal regulator output voltage
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
 
    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 432;
  RCC_OscInitStruct.PLL.PLLQ = 9;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 
  RCC_OscInitStruct.PLL.PLLR = 2;  /* Even when DSI is disabled PLLR with 2 <= PLLR <= 7 according to RM0410 p.163 */
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Activate the Over-Drive mode
  */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
 
 
    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
  {
    Error_Handler();
  }

I have written a software transmit-only UART running at 3 MBaud on any arbitrary GPIO pin. For the most part it works great, but every once in a while, especially in interrupts, it starts "writing in Chinese" and strange characters appear in my terminal window. Does anybody know what's wrong?

#define IDLE_STATE                      1
#define DISABLE_ALL_INTS_IF_NECESSARY()             uint32_t old_primask;                 \
                                                    old_primask = __get_PRIMASK();        \
                                                    __disable_irq()
 
#define ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED()      if (!old_primask)                     \
                                                    {                                     \
                                                        __enable_irq();                   \
                                                    }
static GPIO_TypeDef* TxPort_;
static uint16_t TxPin_;
 
void LL_UART_SW_TxOnly_enable(GPIO_TypeDef* TxPort, uint16_t TxPin) {
    TxPort_ = TxPort;
    TxPin_ = TxPin;
    GPIO_InitTypeDef initStruct = {TxPin_, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_LOW, 0};
    HAL_GPIO_Init(TxPort_, &initStruct);
    SET_PIN(TxPort_, TxPin_, IDLE_STATE);
}
 
void LL_UART_SW_TxOnly_disable(void) {
    GPIO_InitTypeDef initStruct = {TxPin_, GPIO_MODE_ANALOG, GPIO_NOPULL, GPIO_SPEED_LOW, 0};
    HAL_GPIO_Init(TxPort_, &initStruct);
}
 
                                             /* 216 MHz system clock */
#define OUTPUT_BIT(__BIT_NO__)               *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];   \
                                             *BSRR = BSRRvalues[__BIT_NO__];
 
inline static void __attribute__((optimize("O0"))) outputByte(volatile uint32_t* BSRR, uint32_t BSRRvalues[10]) {
    OUTPUT_BIT(0);
    OUTPUT_BIT(1);
    OUTPUT_BIT(2);
    OUTPUT_BIT(3);
    OUTPUT_BIT(4);
    OUTPUT_BIT(5);
    OUTPUT_BIT(6);
    OUTPUT_BIT(7);
    OUTPUT_BIT(8);
    *BSRR = BSRRvalues[9];
}
 
// Superfast (3 MBit/s) UART
void __attribute__((optimize("O3"))) LL_UART_SW_TxOnly_transmitByte(uint8_t byteToSend) {
    uint32_t BSRRvalues[10];
    BSRRvalues[0]  = (IDLE_STATE == 1) ? (((uint32_t)TxPin_) << 16) : TxPin_;  // Start bit
    BSRRvalues[9]  = (IDLE_STATE == 0) ? (((uint32_t)TxPin_) << 16) : TxPin_;  // Stop bit
    BSRRvalues[1]  = BSRRvalues[(byteToSend & 0x01) ? 9 : 0];                  // Bit 0
    BSRRvalues[2]  = BSRRvalues[(byteToSend & 0x02) ? 9 : 0];                  // Bit 1
    BSRRvalues[3]  = BSRRvalues[(byteToSend & 0x04) ? 9 : 0];                  // Bit 2
    BSRRvalues[4]  = BSRRvalues[(byteToSend & 0x08) ? 9 : 0];                  // Bit 3
    BSRRvalues[5]  = BSRRvalues[(byteToSend & 0x10) ? 9 : 0];                  // Bit 4
    BSRRvalues[6]  = BSRRvalues[(byteToSend & 0x20) ? 9 : 0];                  // Bit 5
    BSRRvalues[7]  = BSRRvalues[(byteToSend & 0x40) ? 9 : 0];                  // Bit 6
    BSRRvalues[8]  = BSRRvalues[(byteToSend & 0x80) ? 9 : 0];                  // Bit 7
    DISABLE_ALL_INTS_IF_NECESSARY();
    outputByte(&TxPort_->BSRR, BSRRvalues);
    ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
 
 
void LL_UART_SW_TxOnly_transmitBuffer(uint8_t* pData, uint16_t numBytes) {
    while (0 < numBytes--) {
        LL_UART_SW_TxOnly_transmitByte(*pData++);
    }
}
 
void LL_UART_SW_TxOnly_transmitNullTermString(const char* pData) {
    while (*pData != 0) {
        LL_UART_SW_TxOnly_transmitByte(*pData++);
    }
}

21 REPLIES 21

The NOP can even not happen at all!

NOP does nothing. NOP is not necessarily a time-consuming NOP. The processor might remove it from the pipeline before it reaches the execution stage.

Use NOP for padding, for example to place the following instruction on a 64-bit boundary.

https://developer.arm.com/documentation/dui0646/a/the-cortex-m7-instruction-set/miscellaneous-instructions/nop

Both SysTick and DWT counters are local to CPU core and could be used with appropriate code. Though DWT could be easier as it's 32-bit. Still GPIO on AHB with polling some timer on APB also should be good enough.

Take a look on SEGGER RTT, J-Scope and SystemView. 😉

https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/

https://www.segger.com/products/debug-probes/j-link/tools/j-scope/

https://www.segger.com/products/development-tools/systemview/

DSB is often used as a time-wasting instruction instead of NOP.

What's wrong with using hardware UARTs? Yes, there's limitation on which pins you can use them, and yes, there are only 8 of them in the 'F769; but OTOH they tend to work properly all the time.

JW

KnarfB
Principal III

If its TX only you could try 10-bit SPI transfers and ignore the clock pin.

> What's wrong with using hardware UARTs?

We have 10 microcontrollers on the PCB, all are ball grid arrays and all of them have hardware UART:s. On 9 of them we have filters that degrade the signals at high frequencies so we can't go any faster than 115200. The 10th microcontroller has trace implemented and I tried it once and never would I touch that again, not even with a polestick. So, if I want fast logging (highspeed buses are difficult to debug with a 115200 UART) without making another spin of the PCB, the only option left it to try to find nets that appear on pad/vias and can double-function as UART as well and then solder wires onto them.

Or, if you have 2 lines available, you can try to bitbang SPI.

Btw on a development board, I'd simply remove the filters.

JW

Gudgel.boB
Senior

I sure wouldn't want to bit-bang at 3-megabaud !

> I sure wouldn't want to bit-bang at 3-megabaud !

Next week I'm planning to order 12 MBaud cables ( https://ftdichip.com/products/c232hd-ddhsp-0 ). Btw, does anybody know if there are even faster cables available on the market?

DSB worked great! Btw, I read your great website, lots of very helpful information! But now I'm afraid to program STM32 microcontrollers, now that I know how much complexity there is "under the surface"...

Have a look at orbuculum https://github.com/orbcode/orbuculum and the help you can get from Mubes on the discord orbuculum channel before you dump SWO/Trace.