Skip to main content
Graduate
January 28, 2021
Question

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

  • January 28, 2021
  • 11 replies
  • 3543 views

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++);
 }
}

    This topic has been closed for replies.

    11 replies

    arnold_wAuthor
    Graduate
    February 5, 2021

    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;
    }