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

Thanks! :)

Respect, no fear.

JW

arnold_w
Senior

I have now received faster USB-to-serial cables ( https://ftdichip.com/products/c232hd-ddhsp-0 ) and can confirm that it works great to run at 8 MBaud. I'm using very thin wires with no shielding and no impedance matching. I would like to thank everybody who contributed to this thread! Here's the final source code:

#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};
    SET_PIN(TxPort_, TxPin_, IDLE_STATE);
    HAL_GPIO_Init(TxPort_, &initStruct);
}
 
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__];                                                                      \
                                asm("DSB");    /* DSB is often used as a time-wasting instruction according to info on STM Forum */  \
                                asm("DSB");    /* DSB is often used as a time-wasting instruction according to info on STM Forum */  \
                                asm("DSB");    /* DSB is often used as a time-wasting instruction according to info on STM Forum */  \
                                asm("DSB")     /* DSB is often used as a time-wasting instruction according to info on STM Forum */
 
inline static void __attribute__((optimize("O0"))) __RAM_FUNC 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];
}
 
// Super-duper-extra fast (8 MBit/s) UART
void __attribute__((optimize("O3"))) __RAM_FUNC 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++);
    }
}
 
void LL_UART_SW_TxOnly_transmitHexNumberAsASCII(uint32_t hexNum, uint8_t paddingTotalWidth) {
    uint8_t ASCIIbuffer[8];
    (void)convertNumberToHexASCIIstring(hexNum, ASCIIbuffer, sizeof(ASCIIbuffer));
    LL_UART_SW_TxOnly_transmitBuffer(ASCIIbuffer + sizeof(ASCIIbuffer) - paddingTotalWidth, paddingTotalWidth);
}
 
void LL_UART_SW_TxOnly_transmitDecNumberAsASCII(int32_t decNum) {
    uint8_t ASCIIbuffer[11];
    int16_t startOffset = convertSignedNumberToDecASCIIstring(decNum, ASCIIbuffer, sizeof(ASCIIbuffer));
    LL_UART_SW_TxOnly_transmitBuffer(ASCIIbuffer + startOffset, sizeof(ASCIIbuffer) - startOffset);
}
 
static uint8_t convertDigitToHexASCII(uint8_t digit) {
    if (digit < 10) {
        return (uint8_t) (digit + '0');
    } else {
        return (uint8_t) (digit + 'A' - 10);
    }
}
 
int16_t convertNumberToHexASCIIstring(uint32_t numberToConvert, uint8_t* ASCIIbuffer, uint16_t ASCIIbufferSizeBytes) {
    uint16_t i;
    const uint32_t MAX_NUMBERS[] = {0, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000};
    if ((ASCIIbufferSizeBytes < 8) && (MAX_NUMBERS[ASCIIbufferSizeBytes] <= numberToConvert)) {
        // The number to convert will not fit inside the provided buffer
        return -32768;
    }
 
    for (i = 0; i < ASCIIbufferSizeBytes; i++) {
        uint8_t digitToConvert = (uint8_t) ((numberToConvert >> (i << 2)) & 0x0000000F);
        ASCIIbuffer[ASCIIbufferSizeBytes - i - 1] = convertDigitToHexASCII(digitToConvert);
    }
    for (i = 0; i < ASCIIbufferSizeBytes; i++) {
        if (ASCIIbuffer[i] != '0') {
            return i;
        }
    }
    return ASCIIbufferSizeBytes - 1;
}
 
int16_t convertUnsignedNumberToDecASCIIstring(uint32_t numberToConvert, uint8_t* ASCIIbuffer, uint16_t ASCIIbufferSizeBytes) {
    int16_t i;
    const uint32_t MAX_NUMBERS[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
    if ((ASCIIbufferSizeBytes < 10) && (MAX_NUMBERS[ASCIIbufferSizeBytes] <= numberToConvert)) {
        // The number to convert will not fit inside the provided buffer
        return -32768;
    }
 
    for (i = 0; i < ASCIIbufferSizeBytes; i++) {
        uint32_t divisor = getPowerOfTen(ASCIIbufferSizeBytes - 1 - i);
        uint32_t digitToConvert = numberToConvert / divisor;
        ASCIIbuffer[i] = convertDigitToHexASCII(digitToConvert);
        numberToConvert -= divisor * digitToConvert;
    }
    for (i = 0; i < ASCIIbufferSizeBytes; i++) {
        if (ASCIIbuffer[i] != '0') {
            return i;
        }
    }
    return ASCIIbufferSizeBytes - 1;
}
 
int16_t convertSignedNumberToDecASCIIstring(int32_t numberToConvert, uint8_t* ASCIIbuffer, uint16_t ASCIIbufferSizeBytes) {
    int16_t startIndex;
    if (numberToConvert < 0) { // It's a negative number
        startIndex = convertUnsignedNumberToDecASCIIstring((uint32_t)(0 - numberToConvert), ASCIIbuffer + 1, ASCIIbufferSizeBytes - 1);
        if (0 <= startIndex) {
            ASCIIbuffer[startIndex] = '-';
        }
    } else {
        startIndex = convertUnsignedNumberToDecASCIIstring((uint32_t)numberToConvert, ASCIIbuffer, ASCIIbufferSizeBytes);
    }
    return startIndex;
}