[AVR] Add support for USART2/3 on ATmega640/1280/2560 to Serial Driver Topic is solved

Report here problems in any of ChibiOS components. This forum is NOT for support.
User avatar
teknoman117
Posts: 8
Joined: Tue Aug 19, 2025 1:00 am

[AVR] Add support for USART2/3 on ATmega640/1280/2560 to Serial Driver  Topic is solved

Postby teknoman117 » 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

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

User avatar
Giovanni
Site Admin
Posts: 14704
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1146 times
Been thanked: 960 times

Re: [AVR] Add support for USART2/3 on ATmega640/1280/2560 to Serial Driver

Postby Giovanni » Tue Aug 19, 2025 9:31 pm

Hi,

Thanks for the patch, will move this in "bug reports" because there it is where I look for things to do. I will apply the patch without performing any testing, I don't have any AVR to test on.

Giovanni

User avatar
tfAteba
Posts: 549
Joined: Fri Oct 16, 2015 11:03 pm
Location: Strasbourg, France
Has thanked: 91 times
Been thanked: 50 times

Re: [AVR] Add support for USART2/3 on ATmega640/1280/2560 to Serial Driver

Postby tfAteba » Fri Sep 05, 2025 4:06 pm

Hello Giovanni, Nathan,

I can make some test and apply the path if you need I have some hardware with me as well

Best regards,

tfateba


Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 26 guests