diff --git a/meson.build b/meson.build
index c307ba67..478ac40e 100644
--- a/meson.build
+++ b/meson.build
@@ -140,6 +140,7 @@ stm32f405_src = ['platform/mcu/STM32F4xx/boot/startup.cpp',
'platform/mcu/STM32F4xx/drivers/delays.cpp',
'platform/mcu/STM32F4xx/drivers/rtc.c',
'platform/mcu/STM32F4xx/drivers/SPI2.c',
+ 'platform/mcu/STM32F4xx/drivers/USART3.cpp',
'platform/mcu/CMSIS/Device/ST/STM32F4xx/Source/system_stm32f4xx.c']
stm32f405_inc = ['platform/mcu/CMSIS/Include',
diff --git a/openrtx/src/threads.c b/openrtx/src/threads.c
index d8c126fc..7d97e21a 100644
--- a/openrtx/src/threads.c
+++ b/openrtx/src/threads.c
@@ -261,7 +261,7 @@ void *rtx_task(void *arg)
}
}
-#ifdef HAS_GPS
+#if defined(HAS_GPS) && !defined(MD3x0_ENABLE_DBG)
/**
* \internal Task function for parsing GPS data and updating radio state.
*/
@@ -345,7 +345,7 @@ void create_threads()
pthread_attr_setstacksize(&kbd_attr, KBD_TASK_STKSIZE);
pthread_create(&kbd_thread, &kbd_attr, kbd_task, NULL);
-#ifdef HAS_GPS
+#if defined(HAS_GPS) && !defined(MD3x0_ENABLE_DBG)
// Create GPS thread
pthread_t gps_thread;
pthread_attr_t gps_attr;
diff --git a/platform/drivers/GPS/GPS_MDx.cpp b/platform/drivers/GPS/GPS_MDx.cpp
index a98f1b5e..2a1c9a24 100644
--- a/platform/drivers/GPS/GPS_MDx.cpp
+++ b/platform/drivers/GPS/GPS_MDx.cpp
@@ -94,7 +94,11 @@ void __attribute__((naked)) USART1_IRQHandler()
#endif
{
saveContext();
+ #if defined(PLATFORM_MD3x0) && defined(MD3x0_ENABLE_DBG)
+ asm volatile("bl _Z13usart3irqImplv");
+ #else
asm volatile("bl _Z12GpsUsartImplv");
+ #endif
restoreContext();
}
@@ -117,7 +121,7 @@ void gps_init(const uint16_t baud)
PORT->BRR = quot/2 + (quot & 1);
PORT->CR3 |= USART_CR3_ONEBIT;
PORT->CR1 = USART_CR1_RE
- | USART_CR1_RXNEIE;
+ | USART_CR1_RXNEIE;
#ifdef PLATFORM_MD3x0
NVIC_ClearPendingIRQ(USART3_IRQn);
diff --git a/platform/mcu/STM32F4xx/drivers/USART3.cpp b/platform/mcu/STM32F4xx/drivers/USART3.cpp
new file mode 100644
index 00000000..2545ecbe
--- /dev/null
+++ b/platform/mcu/STM32F4xx/drivers/USART3.cpp
@@ -0,0 +1,193 @@
+/***************************************************************************
+ * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, *
+ * Niccolò Izzo IU2KIN *
+ * Frederik Saraci IU2NRO *
+ * Silvano Seva IU2KWO *
+ * *
+ * Adapted from STM32 USART driver for miosix kernel written by Federico *
+ * Terraneo. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include "USART3.h"
+
+#define U3TXD GPIOD,8
+#define U3RXD GPIOD,8
+
+using namespace miosix;
+
+static constexpr int rxQueueMin = 16; // Minimum queue size
+
+DynUnsyncQueue< char > rxQueue(128); // Queue for incoming data
+Thread *rxWaiting = 0; // Thread waiting on RX
+bool rxIdle = true; // Flag for RX idle
+FastMutex rxMutex; // Mutex locked during reception
+FastMutex txMutex; // Mutex locked during transmission
+
+/**
+ * \internal
+ * Wait until all characters have been written to the serial port.
+ * Needs to be callable from interrupts disabled (it is used in IRQwrite)
+ */
+inline void waitSerialTxFifoEmpty()
+{
+ while((USART3->SR & USART_SR_TC) == 0) ;
+}
+
+/**
+ * \internal
+ * Interrupt handler function, called by USART3_IRQHandler.
+ */
+void __attribute__((noinline)) usart3irqImpl()
+{
+ unsigned int status = USART3->SR;
+ char c;
+
+ if(status & USART_SR_RXNE)
+ {
+ //Always read data, since this clears interrupt flags
+ c = USART3->DR;
+
+ //If no error put data in buffer
+ if((status & USART_SR_FE) == 0)
+ {
+ if(rxQueue.tryPut(c) == false) {/*fifo overflow*/}
+ }
+
+ rxIdle = false;
+ }
+
+ if(status & USART_SR_IDLE)
+ {
+ c = USART3->DR; //clears interrupt flags
+ rxIdle = true;
+ }
+
+ if((status & USART_SR_IDLE) || rxQueue.size() >= rxQueueMin)
+ {
+ //Enough data in buffer or idle line, awake thread
+ if(rxWaiting)
+ {
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ rxWaiting = 0;
+ }
+ }
+}
+
+void usart3_init(unsigned int baudrate)
+{
+ gpio_setMode(U3RXD, ALTERNATE);
+ gpio_setMode(U3TXD, ALTERNATE);
+ gpio_setAlternateFunction(U3RXD, 7);
+ gpio_setAlternateFunction(U3TXD, 7);
+
+ RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
+ __DSB();
+
+ unsigned int freq = SystemCoreClock;
+ if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/= 1<<(((RCC->CFGR >> 10) & 0x3)+1);
+
+ const unsigned int quot = 2*freq/baudrate; // 2*freq for round to nearest
+ USART3->BRR = quot/2 + (quot & 1); // Round to nearest
+ USART3->CR3 |= USART_CR3_ONEBIT;
+ USART3->CR1 = USART_CR1_UE // Enable port
+ | USART_CR1_RXNEIE // Interrupt on data received
+ | USART_CR1_IDLEIE // Interrupt on idle line
+ | USART_CR1_TE // Transmission enbled
+ | USART_CR1_RE; // Reception enabled
+
+ NVIC_SetPriority(USART3_IRQn,15); // Lowest priority for serial
+ NVIC_EnableIRQ(USART3_IRQn);
+}
+
+void usart3_terminate()
+{
+ waitSerialTxFifoEmpty();
+
+ NVIC_DisableIRQ(USART3_IRQn);
+
+ USART3->CR1 &= ~USART_CR1_UE;
+ RCC->APB1ENR &= ~RCC_APB1ENR_USART3EN;
+ __DSB();
+}
+
+ssize_t usart3_readBlock(void *buffer, size_t size, off_t where)
+{
+ miosix::Lock< miosix::FastMutex > l(rxMutex);
+ char *buf = reinterpret_cast< char* >(buffer);
+ size_t result = 0;
+ FastInterruptDisableLock dLock;
+
+ for(;;)
+ {
+ //Try to get data from the queue
+ for(; result < size; result++)
+ {
+ if(rxQueue.tryGet(buf[result])==false) break;
+ //This is here just not to keep IRQ disabled for the whole loop
+ FastInterruptEnableLock eLock(dLock);
+ }
+ if(rxIdle && result > 0) break;
+ if(result == size) break;
+ //Wait for data in the queue
+ do {
+ rxWaiting = Thread::IRQgetCurrentThread();
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(rxWaiting);
+ }
+
+ return result;
+}
+
+ssize_t usart3_writeBlock(void *buffer, size_t size, off_t where)
+{
+ miosix::Lock< miosix::FastMutex > l(txMutex);
+ const char *buf = reinterpret_cast< const char* >(buffer);
+ for(size_t i = 0; i < size; i++)
+ {
+ while((USART3->SR & USART_SR_TXE) == 0) ;
+ USART3->DR = *buf++;
+ }
+
+ return size;
+}
+
+void usart3_IRQwrite(const char *str)
+{
+ // We can reach here also with only kernel paused, so make sure
+ // interrupts are disabled. This is important for the DMA case
+ bool interrupts = areInterruptsEnabled();
+ if(interrupts) fastDisableInterrupts();
+
+ while(*str)
+ {
+ while((USART3->SR & USART_SR_TXE) == 0) ;
+ USART3->DR = *str++;
+ }
+
+ waitSerialTxFifoEmpty();
+ if(interrupts) fastEnableInterrupts();
+}
diff --git a/platform/mcu/STM32F4xx/drivers/USART3.h b/platform/mcu/STM32F4xx/drivers/USART3.h
new file mode 100644
index 00000000..df000f24
--- /dev/null
+++ b/platform/mcu/STM32F4xx/drivers/USART3.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, *
+ * Niccolò Izzo IU2KIN *
+ * Frederik Saraci IU2NRO *
+ * Silvano Seva IU2KWO *
+ * *
+ * Adapted from STM32 USART driver for miosix kernel written by Federico *
+ * Terraneo. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#ifndef USART3_H
+#define USART3_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Initialise USART3 peripheral with a given baud rate. Serial communication is
+ * configured for 8 data bits, no parity, one stop bit.
+ *
+ * @param baudrate: serial port baud rate, in bits per second.
+ */
+void usart3_init(unsigned int baudrate);
+
+/**
+ * Shut down USART3 peripheral.
+ */
+void usart3_terminate();
+
+/**
+ * Read a block of data.
+ *
+ * \param buffer buffer where read data will be stored.
+ * \param size buffer size.
+ * \param where where to read from.
+ * \return number of bytes read or a negative number on failure. Note that
+ * it is normal for this function to return less character than the amount
+ * asked.
+ */
+ssize_t usart3_readBlock(void *buffer, size_t size, off_t where);
+
+/**
+ * Write a block of data.
+ *
+ * \param buffer buffer where take data to write.
+ * \param size buffer size.
+ * \param where where to write to.
+ * \return number of bytes written or a negative number on failure.
+ */
+ssize_t usart3_writeBlock(void *buffer, size_t size, off_t where);
+
+/**
+ * Write a string.
+ * Can be used to write debug information before the kernel is started or in
+ * case of serious errors, right before rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ *
+ * \param str the string to write. The string must be NUL terminated.
+ */
+void usart3_IRQwrite(const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USART3_H */