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