STM32duino UART / Serial

STM32duino III. – How to use Serial (USART)


STM32duino Wiki

Welcome to the stm32duino wiki!
API HardwareSerial


PlatformIO

How to use stm32duino build_opt.h in PlatformIO?
How to use stm32duino (official STM32 core) build_opt.h in PlatformIO
Include header files using command line option?

build_flags = 
    -DENABLE_HWSERIAL3
    -DPIN_SERIAL3_RX=PB11
    -DPIN_SERIAL3_TX=PB10

or

build_flags = 
     -include build_opt.h


Variant

ldscript.ld
PeripheralPins.c
PinNamesVar.h
variant.cpp
variant.h
#define PC5  0
#define PC4  1
#define PA10 2
[...]
HAL_ADC_MODULE_ENABLED
HAL_DAC_MODULE_ENABLED
HAL_I2C_MODULE_ENABLED
HAL_TIM_MODULE_ENABLED
HAL_UART_MODULE_ENABLED
HAL_SPI_MODULE_ENABLED
WEAK const PinMap PinMap_UART_TX[] = {
  [...]
  {PA_5,  USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART3)},
  {PB_2,  USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART3)},
  [...]
                                    ^                 ^
                                    |                 |
                          STM_MODE_INPUT           GPIO_NOPULL
                          STM_MODE_OUTPUT_PP       GPIO_PULLUP
                          STM_MODE_OUTPUT_OD       GPIO_PULLDOWN
                          STM_MODE_ANALOG
                          STM_MODE_AF_OD => Alternate Function, Open-Drain
                          STM_MODE_AF_PP => Alternate Function, Push-Pull
#define STM_PIN_DATA(FUNC_OD, PUPD, AFNUM) \
            STM_PIN_DEFINE(FUNC_OD, PUPD, AFNUM)
#define STM_PIN_DATA_EXT(FUNC_OD, PUPD, AFNUM, CHANNEL, INVERTED) \
            STM_PIN_DEFINE_EXT(FUNC_OD, PUPD, AFNUM, CHANNEL, INVERTED)


Digital

Arduino_Core_STM32—pinMode() implementation analysis

void pinMode(uint32_t ulPin, uint32_t ulMode)
{
  PinName p = digitalPinToPinName(ulPin);

  if (p != NC) {
    [...]
    switch (ulMode) {
      case INPUT: /* INPUT_FLOATING */
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
        break;
      case INPUT_PULLUP:
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLUP, 0));
        break;
      case INPUT_PULLDOWN:
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLDOWN, 0));
        break;

      [...]
    }
  }
}


PinMap_UART_TX

typedef struct {
  PinName pin;
  void *peripheral;
  int function;
} PinMap;
typedef enum {
  // Not connected
  NC = (int)0xFFFFFFFF,

  // Pin name definition
  PA_0  = (PortA << 4) + 0x00,
  [...]

  // Specific pin name define in the variant
  #include "PinNamesVar.h"

  P_END = NC
} PinName;
extern GPIO_TypeDef *GPIOPort[];

typedef enum {
  FirstPort = 0x00,
  PortA = FirstPort,
  PortB,
#if defined GPIOC_BASE
  PortC,
#endif
  [...]
  PortEND,
  LastPort = PortEND - 1
} PortName;

#define MAX_NB_PORT (LastPort-FirstPort+1)
extern const PinName digitalPin[];
extern const uint32_t analogInputPin[];

[...]

#define digitalPinToPinName(p)      (((uint32_t)p < NUM_DIGITAL_PINS) ? digitalPin[p] : \
            ((uint32_t)p >= NUM_ANALOG_FIRST) && ((uint32_t)p <= NUM_ANALOG_LAST) ? \
            digitalPin[analogInputPin[p-NUM_ANALOG_FIRST]] : NC)
  • STM_PIN_DATA()
  • STM_PIN_DATA_EXT()
  • pin_in_pinmap()
    bool pin_in_pinmap(PinName pin, const PinMap *map)
    Loop
  • pinmap_peripheral()
    void *pinmap_peripheral(PinName pin, const PinMap *map)
  • pinmap_pinout()
    void pinmap_pinout(PinName pin, const PinMap *map)
  • pinmap_pin()
    PinName pinmap_pin(void *peripheral, const PinMap *map)
./cores/arduino/pins_arduino.h:           pin_in_pinmap(digitalPinToPinName(p), PinMap_UART_TX))
./cores/arduino/stm32/PeripheralPins.h:   extern const PinMap PinMap_UART_TX[];
./libraries/SrcWrapper/src/stm32/uart.c:  #define DEBUG_UART          pinmap_peripheral(digitalPinToPinName(PIN_SERIAL_TX), PinMap_UART_TX)
./libraries/SrcWrapper/src/stm32/uart.c:  USART_TypeDef *uart_tx = pinmap_peripheral(obj->pin_tx, PinMap_UART_TX);
./libraries/SrcWrapper/src/stm32/uart.c:  pinmap_pinout(obj->pin_tx, PinMap_UART_TX);
./libraries/SrcWrapper/src/stm32/uart.c:  serial_debug.pin_tx = pinmap_pin(DEBUG_UART, PinMap_UART_TX);


Serial3

  • HAVE_HWSERIAL3
  • ENABLE_HWSERIAL3
  • SERIAL_UART_INSTANCE
[...]
extern HardwareSerial Serial3;
extern HardwareSerial Serial4;
[...]
  [...]
  #if defined(HAVE_HWSERIAL3)
    HardwareSerial Serial3(USART3);
    void serialEvent3() __attribute__((weak));
  #endif

  #if defined(HAVE_HWSERIAL4)
    #if defined(USART4)
      HardwareSerial Serial4(USART4);
    #else
      HardwareSerial Serial4(UART4);
    #endif
    void serialEvent4() __attribute__((weak));
  #endif

  [...]

// Constructors ////////////////////////////////////////////////////////////////
/* ex. PA0 from #define in variant.h */
HardwareSerial::HardwareSerial(uint32_t _rx, uint32_t _tx)
{
  init(digitalPinToPinName(_rx), digitalPinToPinName(_tx));
}

/* ex. PA_0 from enum PinName in PinNames.h*/
HardwareSerial::HardwareSerial(PinName _rx, PinName _tx)
{
  init(_rx, _tx);
}

/* ex. peripheral Serial2 (stm32g071xx.h: USART2 => USART2_BASE => 0x40004400) */
HardwareSerial::HardwareSerial(void *peripheral, HalfDuplexMode_t halfDuplex)
{
  [...]

  /* if pins are defined (variant.h or compiler flag -D), use them, otherwise use from PinMap */

#if defined(Serial) && defined(PIN_SERIAL_TX)
  if ((void *)this == (void *)&Serial) {
#if defined(PIN_SERIAL_RX)
    setRx(PIN_SERIAL_RX);
#endif
    setTx(PIN_SERIAL_TX);
  } else
#endif

  [...]

#if defined(PIN_SERIAL3_TX) && defined(USART3_BASE)
        if (peripheral == USART3) {
#if defined(PIN_SERIAL3_RX)
          setRx(PIN_SERIAL3_RX);
#endif
          setTx(PIN_SERIAL3_TX);
        } else
#endif

  [...]

                          // else get the pins of the first peripheral occurence in PinMap
                        {
                          _serial.pin_rx = pinmap_pin(peripheral, PinMap_UART_RX);
                          _serial.pin_tx = pinmap_pin(peripheral, PinMap_UART_TX);
                        }

  [...]
}

[...]

void HardwareSerial::init(PinName _rx, PinName _tx)
{
  [...]
}
/*!< Peripheral declaration */
#define USART2              ((USART_TypeDef *) USART2_BASE)
#define USART3              ((USART_TypeDef *) USART3_BASE)
#define USART4              ((USART_TypeDef *) USART4_BASE)
[...]
#define LPUART1             ((USART_TypeDef *) LPUART1_BASE)

[...]

/*!< APB peripherals */
#define USART2_BASE           (APBPERIPH_BASE + 0x00004400UL)
#define USART3_BASE           (APBPERIPH_BASE + 0x00004800UL)
#define USART4_BASE           (APBPERIPH_BASE + 0x00004C00UL)
[...]
#define LPUART1_BASE          (APBPERIPH_BASE + 0x00008000UL)
  [...]
  #if defined(ENABLE_HWSERIAL3)
    #if defined(USART3_BASE)
      #define HAVE_HWSERIAL3
    #endif
  #endif
  #if defined(ENABLE_HWSERIAL4)
    #if defined(USART4_BASE) || defined(UART4_BASE)
      #define HAVE_HWSERIAL4
    #endif
  #endif
  [...]
WEAK void serialEventRun(void)
{
  [...]
#if defined(HAVE_HWSERIAL3)
  if (serialEvent3 && Serial3.available()) {
    serialEvent3();
  }
#endif
#if defined(HAVE_HWSERIAL4)
  if (serialEvent4 && Serial4.available()) {
    serialEvent4();
  }
#endif
  [...]
}


Callstack

size_t HardwareSerial::write(uint8_t c)
{
  [...]

  if (!serial_tx_active(&_serial)) {
    uart_attach_tx_callback(&_serial, _tx_complete_irq);
  }

  [...]
}

int HardwareSerial::_tx_complete_irq(serial_t *obj)
{
  // If interrupts are enabled, there must be more data in the output
  // buffer. Send the next byte
  obj->tx_tail = (obj->tx_tail + 1) % SERIAL_TX_BUFFER_SIZE;

  if (obj->tx_head == obj->tx_tail) {
    return -1;
  }

  return 0;
}
typedef struct serial_s serial_t;

struct serial_s {
  /*  The 1st 2 members USART_TypeDef *uart
   *  and UART_HandleTypeDef handle should
   *  be kept as the first members of this struct
   *  to have get_serial_obj() function work as expected
   */
  USART_TypeDef *uart;
  UART_HandleTypeDef handle;
  void (*rx_callback)(serial_t *);
  int (*tx_callback)(serial_t *);
  PinName pin_tx;
  PinName pin_rx;
  IRQn_Type irq;
  uint8_t index;
  uint8_t recv;
  uint8_t *rx_buff;
  uint8_t *tx_buff;
  uint16_t rx_tail;
  uint16_t tx_head;
  volatile uint16_t rx_head;
  volatile uint16_t tx_tail;
};
/**
 * Begin asynchronous TX transfer.
 *
 * @param obj : pointer to serial_t structure
 * @param callback : function call at the end of transmission
 * @retval none
 */
void uart_attach_tx_callback(serial_t *obj, int (*callback)(serial_t *))
{
  if (obj == NULL) {
    return;
  }
  obj->tx_callback = callback;

  /* Must disable interrupt to prevent handle lock contention */
  HAL_NVIC_DisableIRQ(obj->irq);

  /* The following function will enable UART_IT_TXE and error interrupts */
  HAL_UART_Transmit_IT(uart_handlers[obj->index], &obj->tx_buff[obj->tx_tail], 1);

  /* Enable interrupt */
  HAL_NVIC_SetPriority(obj->irq, UART_IRQ_PRIO, UART_IRQ_SUBPRIO);
  HAL_NVIC_EnableIRQ(obj->irq);
}

/**
  * @brief  Tx Transfer completed callback
  * @param  UartHandle pointer on the uart reference
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  serial_t *obj = get_serial_obj(huart);

  if (obj && obj->tx_callback(obj) != -1) {
    if (HAL_UART_Transmit_IT(huart, &obj->tx_buff[obj->tx_tail], 1) != HAL_OK) {
      return;
    }
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *