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