[AVR] Add support for USART2/3 on ATmega640/1280/2560 to Serial Driver Topic is solved
Posted: Tue Aug 19, 2025 7:46 pm
Hello!
Hopefully this qualifies as a "small change", but I extended the serial driver for the ATmega AVRs to support USART2 and 3 on the ATmega640/1280/2560 family of AVRs (commonly found in the Arduino Mega).
I duplicated the existing serial driver logic for the two UARTs, but I did remove the checks for the ATmega162 in the USART2/3 implementation as the ATmega162 only has USART0/1.
AVR_SERIAL_USE_USARTn is typically defined as TRUE if undefined, but I did default AVR_SERIAL_USE_USART2/3 to FALSE to prevent needed to change all of the AVR mcuconf.h headers. I do have a patch for that is the desired behavior.
- Nathan
Hopefully this qualifies as a "small change", but I extended the serial driver for the ATmega AVRs to support USART2 and 3 on the ATmega640/1280/2560 family of AVRs (commonly found in the Arduino Mega).
I duplicated the existing serial driver logic for the two UARTs, but I did remove the checks for the ATmega162 in the USART2/3 implementation as the ATmega162 only has USART0/1.
AVR_SERIAL_USE_USARTn is typically defined as TRUE if undefined, but I did default AVR_SERIAL_USE_USART2/3 to FALSE to prevent needed to change all of the AVR mcuconf.h headers. I do have a patch for that is the desired behavior.
- Nathan
Code: Select all
From a626f6f9ff40044cc18659545b75f8885d0b8963 Mon Sep 17 00:00:00 2001
From: Nathan Lewis <git@nrlewis.dev>
Date: Mon, 18 Aug 2025 17:03:42 -0700
Subject: [PATCH 1/2] AVR: add support for USART2 and USART3 on the ATmega2560
---
.../AVR/MEGA/LLD/USARTv1/hal_serial_lld.c | 266 ++++++++++++++++++
.../AVR/MEGA/LLD/USARTv1/hal_serial_lld.h | 24 ++
2 files changed, 290 insertions(+)
diff --git a/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.c b/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.c
index 95a758c06..ef340c090 100644
--- a/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.c
+++ b/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.c
@@ -73,6 +73,46 @@ SerialDriver SD2;
#endif
#endif /* AVR_SERIAL_USE_USART1 */
+/**
+ * @brief USART2 serial driver identifier.
+ * @note The name does not follow the convention used in the other ports
+ * (COMn) because a name conflict with the AVR headers.
+ */
+#if AVR_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+SerialDriver SD3;
+
+ /* Check if USART2 exists for this MCU. */
+ #ifdef USART2_RX_vect
+ #define AVR_SD3_RX_VECT USART2_RX_vect
+ #define AVR_SD3_TX_VECT USART2_UDRE_vect
+ #elif defined (USART2_RXC_vect)
+ #define AVR_SD3_RX_VECT USART2_RXC_vect
+ #define AVR_SD3_TX_VECT USART2_UDRE_vect
+ #else
+ #error "Cannot find USART to use for SD3"
+ #endif
+#endif /* AVR_SERIAL_USE_USART2 */
+
+/**
+ * @brief USART3 serial driver identifier.
+ * @note The name does not follow the convention used in the other ports
+ * (COMn) because a name conflict with the AVR headers.
+ */
+#if AVR_SERIAL_USE_USART3 || defined(__DOXYGEN__)
+SerialDriver SD4;
+
+ /* Check if USART3 exists for this MCU. */
+ #ifdef USART3_RX_vect
+ #define AVR_SD4_RX_VECT USART3_RX_vect
+ #define AVR_SD4_TX_VECT USART3_UDRE_vect
+ #elif defined (USART3_RXC_vect)
+ #define AVR_SD4_RX_VECT USART3_RXC_vect
+ #define AVR_SD4_TX_VECT USART3_UDRE_vect
+ #else
+ #error "Cannot find USART to use for SD4"
+ #endif
+#endif /* AVR_SERIAL_USE_USART3 */
+
/*==========================================================================*/
/* Driver local variables and types. */
/*==========================================================================*/
@@ -111,6 +151,22 @@ static void set_error(uint8_t sra, SerialDriver *sdp) {
}
#endif
+#if AVR_SERIAL_USE_USART2
+ if (&SD3 == sdp) {
+ dor = (1 << DOR2);
+ upe = (1 << UPE2);
+ fe = (1 << FE2);
+ }
+#endif
+
+#if AVR_SERIAL_USE_USART3
+ if (&SD4 == sdp) {
+ dor = (1 << DOR3);
+ upe = (1 << UPE3);
+ fe = (1 << FE3);
+ }
+#endif
+
if (sra & dor)
sts |= SD_OVERRUN_ERROR;
if (sra & upe)
@@ -244,6 +300,104 @@ static void usart1_deinit(void) {
}
#endif
+#if AVR_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+static void notify3(io_queue_t *qp) {
+
+ (void)qp;
+ UCSR2B |= (1 << UDRIE2);
+}
+
+/**
+ * @brief USART2 initialization.
+ *
+ * @param[in] config the architecture-dependent serial driver configuration
+ */
+static void usart2_init(const SerialConfig *config) {
+
+ UBRR2L = config->sc_brr;
+ UBRR2H = (config->sc_brr >> 8) & 0x0f;
+ UCSR2A = (1 << U2X2);
+ UCSR2B = (1 << RXEN2) | (1 << TXEN2) | (1 << RXCIE2);
+ switch (config->sc_bits_per_char) {
+ case USART_CHAR_SIZE_5:
+ UCSR2C = 0;
+ break;
+ case USART_CHAR_SIZE_6:
+ UCSR2C = (1 << UCSZ20);
+ break;
+ case USART_CHAR_SIZE_7:
+ UCSR2C = (1 << UCSZ21);
+ break;
+ case USART_CHAR_SIZE_9:
+ UCSR2B |= (1 << UCSZ22);
+ UCSR2C = (1 << UCSZ20) | (1 << UCSZ21);
+ break;
+ case USART_CHAR_SIZE_8:
+ default:
+ UCSR2C = (1 << UCSZ20) | (1 << UCSZ21);
+ }
+}
+
+/**
+ * @brief USART2 de-initialization.
+ */
+static void usart2_deinit(void) {
+
+ UCSR2A = 0;
+ UCSR2B = 0;
+ UCSR2C = 0;
+}
+#endif
+
+#if AVR_SERIAL_USE_USART3 || defined(__DOXYGEN__)
+static void notify4(io_queue_t *qp) {
+
+ (void)qp;
+ UCSR3B |= (1 << UDRIE3);
+}
+
+/**
+ * @brief USART3 initialization.
+ *
+ * @param[in] config the architecture-dependent serial driver configuration
+ */
+static void usart3_init(const SerialConfig *config) {
+
+ UBRR3L = config->sc_brr;
+ UBRR3H = (config->sc_brr >> 8) & 0x0f;
+ UCSR3A = (1 << U2X3);
+ UCSR3B = (1 << RXEN3) | (1 << TXEN3) | (1 << RXCIE3);
+ switch (config->sc_bits_per_char) {
+ case USART_CHAR_SIZE_5:
+ UCSR3C = 0;
+ break;
+ case USART_CHAR_SIZE_6:
+ UCSR3C = (1 << UCSZ30);
+ break;
+ case USART_CHAR_SIZE_7:
+ UCSR3C = (1 << UCSZ31);
+ break;
+ case USART_CHAR_SIZE_9:
+ UCSR3B |= (1 << UCSZ32);
+ UCSR3C = (1 << UCSZ30) | (1 << UCSZ31);
+ break;
+ case USART_CHAR_SIZE_8:
+ default:
+ UCSR3C = (1 << UCSZ30) | (1 << UCSZ31);
+ }
+}
+
+/**
+ * @brief USART3 de-initialization.
+ */
+static void usart3_deinit(void) {
+
+ UCSR3A = 0;
+ UCSR3B = 0;
+ UCSR3C = 0;
+}
+#endif
+
/*==========================================================================*/
/* Driver interrupt handlers. */
/*==========================================================================*/
@@ -334,6 +488,92 @@ OSAL_IRQ_HANDLER(AVR_SD2_TX_VECT) {
}
#endif /* AVR_SERIAL_USE_USART1 */
+#if AVR_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+/**
+ * @brief USART2 RX interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(AVR_SD3_RX_VECT) {
+ uint8_t sra;
+
+ OSAL_IRQ_PROLOGUE();
+
+ sra = UCSR2A;
+ if (sra & ((1 << DOR2) | (1 << UPE2) | (1 << FE2)))
+ set_error(sra, &SD3);
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD3, UDR2);
+ osalSysUnlockFromISR();
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief USART2 TX interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(AVR_SD3_TX_VECT) {
+ msg_t b;
+
+ OSAL_IRQ_PROLOGUE();
+
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD3);
+ osalSysUnlockFromISR();
+ if (b < MSG_OK)
+ UCSR2B &= ~(1 << UDRIE2);
+ else
+ UDR2 = b;
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* AVR_SERIAL_USE_USART2 */
+
+#if AVR_SERIAL_USE_USART3 || defined(__DOXYGEN__)
+/**
+ * @brief USART3 RX interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(AVR_SD4_RX_VECT) {
+ uint8_t sra;
+
+ OSAL_IRQ_PROLOGUE();
+
+ sra = UCSR3A;
+ if (sra & ((1 << DOR3) | (1 << UPE3) | (1 << FE3)))
+ set_error(sra, &SD4);
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD4, UDR3);
+ osalSysUnlockFromISR();
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief USART3 TX interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(AVR_SD4_TX_VECT) {
+ msg_t b;
+
+ OSAL_IRQ_PROLOGUE();
+
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD4);
+ osalSysUnlockFromISR();
+ if (b < MSG_OK)
+ UCSR3B &= ~(1 << UDRIE3);
+ else
+ UDR3 = b;
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* AVR_SERIAL_USE_USART3 */
+
/*==========================================================================*/
/* Driver exported functions. */
/*==========================================================================*/
@@ -351,6 +591,12 @@ void sd_lld_init(void) {
#if AVR_SERIAL_USE_USART1
sdObjectInit(&SD2, NULL, notify2);
#endif
+#if AVR_SERIAL_USE_USART2
+ sdObjectInit(&SD3, NULL, notify3);
+#endif
+#if AVR_SERIAL_USE_USART3
+ sdObjectInit(&SD4, NULL, notify4);
+#endif
}
/**
@@ -380,6 +626,18 @@ void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
return;
}
#endif
+#if AVR_SERIAL_USE_USART2
+ if (&SD3 == sdp) {
+ usart2_init(config);
+ return;
+ }
+#endif
+#if AVR_SERIAL_USE_USART3
+ if (&SD4 == sdp) {
+ usart3_init(config);
+ return;
+ }
+#endif
}
/**
@@ -401,6 +659,14 @@ void sd_lld_stop(SerialDriver *sdp) {
if (&SD2 == sdp)
usart1_deinit();
#endif
+#if AVR_SERIAL_USE_USART2
+ if (&SD3 == sdp)
+ usart2_deinit();
+#endif
+#if AVR_SERIAL_USE_USART3
+ if (&SD4 == sdp)
+ usart3_deinit();
+#endif
}
#endif /* HAL_USE_SERIAL */
diff --git a/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.h b/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.h
index 4a31d7801..299ef9832 100644
--- a/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.h
+++ b/os/hal/ports/AVR/MEGA/LLD/USARTv1/hal_serial_lld.h
@@ -53,6 +53,24 @@
#define AVR_SERIAL_USE_USART1 TRUE
#endif
+/**
+ * @brief USART2 driver enable switch.
+ * @details If set to @p TRUE the support for USART2 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(AVR_SERIAL_USE_USART2) || defined(__DOXYGEN__)
+#define AVR_SERIAL_USE_USART2 FALSE
+#endif
+
+/**
+ * @brief USART3 driver enable switch.
+ * @details If set to @p TRUE the support for USART3 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(AVR_SERIAL_USE_USART3) || defined(__DOXYGEN__)
+#define AVR_SERIAL_USE_USART3 FALSE
+#endif
+
/*==========================================================================*/
/* Derived constants and error checks. */
/*==========================================================================*/
@@ -140,6 +158,12 @@ extern SerialDriver SD1;
#if AVR_SERIAL_USE_USART1 && !defined(__DOXYGEN__)
extern SerialDriver SD2;
#endif
+#if AVR_SERIAL_USE_USART2 && !defined(__DOXYGEN__)
+extern SerialDriver SD3;
+#endif
+#if AVR_SERIAL_USE_USART3 && !defined(__DOXYGEN__)
+extern SerialDriver SD4;
+#endif
#ifdef __cplusplus
extern "C" {
--
2.50.0