summaryrefslogtreecommitdiffhomepage
path: root/digital/ucoolib/ucoolib
diff options
context:
space:
mode:
Diffstat (limited to 'digital/ucoolib/ucoolib')
-rw-r--r--digital/ucoolib/ucoolib/hal/uart/test/Config4
-rw-r--r--digital/ucoolib/ucoolib/hal/uart/test/Makefile2
-rw-r--r--digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc100
-rw-r--r--digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc162
-rw-r--r--digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh27
5 files changed, 284 insertions, 11 deletions
diff --git a/digital/ucoolib/ucoolib/hal/uart/test/Config b/digital/ucoolib/ucoolib/hal/uart/test/Config
new file mode 100644
index 00000000..4fd7571d
--- /dev/null
+++ b/digital/ucoolib/ucoolib/hal/uart/test/Config
@@ -0,0 +1,4 @@
+[hal/uart]
+rx_buffer = 32
+tx_buffer = 8
+
diff --git a/digital/ucoolib/ucoolib/hal/uart/test/Makefile b/digital/ucoolib/ucoolib/hal/uart/test/Makefile
index 40623e50..5d84850c 100644
--- a/digital/ucoolib/ucoolib/hal/uart/test/Makefile
+++ b/digital/ucoolib/ucoolib/hal/uart/test/Makefile
@@ -4,6 +4,6 @@ TARGETS = stm32f4
PROGS = test_uart
test_uart_SOURCES = test_uart.cc
-MODULES = hal/uart
+MODULES = hal/uart base/test hal/usb
include $(BASE)/build/top.mk
diff --git a/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc b/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc
index ec6f2cb6..3d37f340 100644
--- a/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc
+++ b/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc
@@ -23,9 +23,103 @@
// }}}
#include "ucoolib/hal/uart/uart.hh"
+#include "ucoolib/arch/arch.hh"
+#include "ucoolib/base/test/test.hh"
+
+#include <libopencm3/stm32/f4/rcc.h>
+#include <libopencm3/stm32/f4/gpio.h>
+
+static void
+check_act (ucoo::Stream &ts, ucoo::Stream &u, char n)
+{
+ char buf[3 + 16 + 1];
+ if (!u.poll ())
+ return;
+ int r = u.read (buf + 3, 16);
+ if (r <= 0)
+ {
+ buf[3] = '#';
+ r = 1;
+ }
+ buf[0] = '<';
+ buf[1] = n;
+ buf[2] = ':';
+ buf[3 + r] = '>';
+ ts.write (buf, 3 + r + 1);
+}
+
int
-main ()
+main (int argc, const char **argv)
{
- ucoo::Uart u (0, 38400, ucoo::Uart::EVEN, 1);
- return 0;
+ ucoo::arch_init (argc, argv);
+ ucoo::Stream &ts = ucoo::test_stream ();
+ ucoo::Uart u1 (0, 38400, ucoo::Uart::EVEN, 1);
+ ucoo::Uart u3 (2, 38400, ucoo::Uart::EVEN, 1);
+ ucoo::Uart u4 (3, 38400, ucoo::Uart::EVEN, 1);
+ // For this test, shorten B6 & B7 to have a loopback on UART1, shorten C10
+ // & C11 to connect UART3 to UART4.
+ rcc_peripheral_enable_clock (&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN
+ | RCC_AHB1ENR_IOPCEN);
+ gpio_mode_setup (GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE,
+ GPIO6 | GPIO7);
+ gpio_set_af (GPIOB, GPIO_AF7, GPIO6 | GPIO7);
+ gpio_mode_setup (GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE,
+ GPIO10 | GPIO11);
+ gpio_set_af (GPIOC, GPIO_AF7, GPIO10);
+ gpio_set_af (GPIOC, GPIO_AF8, GPIO11);
+ // Loop to report any activity on ports and provide a simple UI.
+ char buf[64];
+ int buf_i = 0;
+ ucoo::Uart *u = &u1;
+ while (1)
+ {
+ check_act (ts, u1, '1');
+ check_act (ts, u3, '3');
+ check_act (ts, u4, '4');
+ while (ts.poll ())
+ {
+ char c = ts.getc ();
+ switch (c)
+ {
+ case '?':
+ static const char help[] =
+ "? - help\n"
+ "1, 3, 4 - set output uart\n"
+ ": - reset output buffer index\n"
+ "! - send output buffer\n"
+ "O, E, N - change parity to Odd, Even or None\n"
+ "any - fill output buffer\n";
+ ts.write (help, sizeof (help));
+ break;
+ case '1':
+ u = &u1;
+ break;
+ case '3':
+ u = &u3;
+ break;
+ case '4':
+ u = &u4;
+ break;
+ case ':':
+ buf_i = 0;
+ break;
+ case '!':
+ u->write (buf, buf_i);
+ break;
+ case 'O':
+ u->setup (38400, ucoo::Uart::ODD, 1);
+ break;
+ case 'E':
+ u->setup (38400, ucoo::Uart::EVEN, 1);
+ break;
+ case 'N':
+ u->setup (38400, ucoo::Uart::NONE, 1);
+ break;
+ default:
+ if (buf_i < static_cast<int> (sizeof (buf)))
+ buf[buf_i++] = c;
+ break;
+ }
+ }
+ }
}
diff --git a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc
index cadac3f6..0fc97efe 100644
--- a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc
+++ b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc
@@ -23,39 +23,193 @@
// }}}
#include "uart.stm32.hh"
-#include "config/hal/uart.hh"
+#include <libopencm3/stm32/f4/usart.h>
+#include <libopencm3/stm32/f4/rcc.h>
+#include <libopencm3/stm32/nvic.h>
+
+#ifndef TARGET_stm32f4
+// Need RCC adaptations and USART6 different handling for F1.
+# error "it's a trap, only implemented for F4 for the moment"
+#endif
+
+namespace ucoo {
+
+static const int uart_nb = 6;
+
+/// Information on UART hardware structure.
+struct uart_hardware_t
+{
+ /// UART base address.
+ uint32_t base;
+ /// APB number.
+ int apb;
+ /// RCC enable bit.
+ uint32_t rcc_en;
+ /// Corresponding IRQ.
+ int irq;
+};
+
+/// Information on UART hardware array, this is zero indexed, USART1 is at
+/// index 0.
+static const uart_hardware_t uart_hardware[uart_nb] =
+{
+ { USART1, 2, RCC_APB2ENR_USART1EN, NVIC_USART1_IRQ },
+ { USART2, 1, RCC_APB1ENR_USART2EN, NVIC_USART2_IRQ },
+ { USART3, 1, RCC_APB1ENR_USART3EN, NVIC_USART3_IRQ },
+ { UART4, 1, RCC_APB1ENR_UART4EN, NVIC_UART4_IRQ },
+ { UART5, 1, RCC_APB1ENR_UART5EN, NVIC_UART5_IRQ },
+ { USART6, 2, RCC_APB2ENR_USART6EN, NVIC_USART6_IRQ },
+};
+
+static Uart *uart_instances[uart_nb];
+
+} // namespace ucoo
+
+extern "C" {
+
+void usart1_isr () { ucoo::Uart::isr (0); }
+
+void usart2_isr () { ucoo::Uart::isr (1); }
+
+void usart3_isr () { ucoo::Uart::isr (2); }
+
+void uart4_isr () { ucoo::Uart::isr (3); }
+
+void uart5_isr () { ucoo::Uart::isr (4); }
+
+void usart6_isr () { ucoo::Uart::isr (5); }
+
+}
namespace ucoo {
Uart::Uart (int n, int speed, Parity parity, int stop_bits)
+ : n_ (n), error_char_ (default_error_char)
{
+ assert (n < uart_nb);
+ assert (!uart_instances[n]);
+ uart_instances[n] = this;
+ setup (speed, parity, stop_bits);
}
Uart::~Uart ()
{
+ setup (0);
+ uart_instances[n_] = 0;
}
void
Uart::setup (int speed, Parity parity, int stop_bits)
{
+ uint32_t base = uart_hardware[n_].base;
+ if (speed)
+ {
+ // Turn on.
+ rcc_peripheral_enable_clock
+ (uart_hardware[n_].apb == 1 ? &RCC_APB1ENR : &RCC_APB2ENR,
+ uart_hardware[n_].rcc_en);
+ // Set speed, rounded to nearest.
+ int apb_freq = uart_hardware[n_].apb == 1 ? rcc_ppre1_frequency
+ : rcc_ppre2_frequency;
+ USART_BRR (base) = (2 * apb_freq + speed) / (2 * speed);
+ // Set parameters and enable.
+ if (stop_bits == 1)
+ USART_CR2 (base) = USART_CR2_STOPBITS_1;
+ else if (stop_bits == 2)
+ USART_CR2 (base) = USART_CR2_STOPBITS_2;
+ else
+ assert_unreachable ();
+ USART_CR3 (base) = 0;
+ uint32_t cr1 = USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
+ if (parity != NONE)
+ cr1 |= USART_CR1_M | USART_CR1_PCE;
+ if (parity == ODD)
+ cr1 |= USART_CR1_PS;
+ USART_CR1 (base) = cr1;
+ // Reset status.
+ (void) USART_SR (base);
+ (void) USART_DR (base);
+ // Enable interrupts.
+ nvic_enable_irq (uart_hardware[n_].irq);
+ }
+ else
+ {
+ // Stop UART.
+ nvic_disable_irq (uart_hardware[n_].irq);
+ USART_CR1 (base) = 0;
+ // Turn off.
+ rcc_peripheral_disable_clock
+ (uart_hardware[n_].apb == 1 ? &RCC_APB1ENR : &RCC_APB2ENR,
+ uart_hardware[n_].rcc_en);
+ }
+}
+
+void
+Uart::set_error_char (char c)
+{
+ error_char_ = c;
}
int
Uart::read (char *buf, int count)
{
- return -1;
+ if (block_)
+ while (rx_fifo_.empty ())
+ barrier ();
+ return rx_fifo_.read (buf, count);
}
int
Uart::write (const char *buf, int count)
{
- return -1;
+ int left = count;
+ while (left)
+ {
+ int r = tx_fifo_.write (buf, left);
+ if (r)
+ {
+ USART_CR1 (uart_hardware[n_].base) |= USART_CR1_TXEIE;
+ buf += r;
+ left -= r;
+ }
+ if (!block_)
+ break;
+ }
+ return count - left;
}
int
Uart::poll ()
{
- return 0;
+ return rx_fifo_.empty () ? 0 : 1;
+}
+
+void
+Uart::isr (int n)
+{
+ uint32_t base = uart_hardware[n].base;
+ uint32_t sr = USART_SR (base);
+ uint32_t dr = USART_DR (base);
+ assert (uart_instances[n]);
+ Uart &uart = *uart_instances[n];
+ if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE))
+ {
+ dr = uart.error_char_;
+ // Datasheet is not really clear about this when error bits are set.
+ sr |= USART_SR_RXNE;
+ }
+ if (sr & USART_SR_RXNE)
+ {
+ if (!uart.rx_fifo_.full ())
+ uart.rx_fifo_.push (dr);
+ }
+ if (sr & USART_SR_TXE)
+ {
+ if (!uart.tx_fifo_.empty ())
+ USART_DR (base) = static_cast<uint8_t> (uart.tx_fifo_.pop ());
+ if (uart.tx_fifo_.empty ())
+ USART_CR1 (base) &= ~USART_CR1_TXEIE;
+ }
}
} // namespace ucoo
diff --git a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh
index d10d12d8..e807e9e9 100644
--- a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh
+++ b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh
@@ -24,28 +24,49 @@
//
// }}}
#include "ucoolib/intf/stream.hh"
+#include "ucoolib/utils/fifo.hh"
+
+#include "config/hal/uart.hh"
namespace ucoo {
/// Universal asynchronous receiver transmitter (UART).
+///
+/// When an error is detected on RX, error character is inserted in receive
+/// FIFO.
class Uart : public Stream
{
public:
/// Parity setting.
enum Parity { ODD, EVEN, NONE };
+ /// Default error character.
+ static const char default_error_char = '~';
public:
/// Initialise the Nth UART with given parameters.
- Uart (int n, int speed, Parity parity, int stop_bits);
+ Uart (int n, int speed = 0, Parity parity = NONE, int stop_bits = 1);
/// Shutdown UART.
~Uart ();
- /// Change UART settings.
- void setup (int speed, Parity parity, int stop_bits);
+ /// Change UART settings, use speed 0 to stop UART.
+ void setup (int speed, Parity parity = NONE, int stop_bits = 1);
+ /// Change the error character.
+ void set_error_char (char c);
/// See Stream::read.
int read (char *buf, int count);
/// See Stream::write.
int write (const char *buf, int count);
/// See Stream::poll.
int poll ();
+ /// Handle interrupts.
+ static void isr (int n);
+ private:
+ /// UART number.
+ int n_;
+ /// RX FIFO, filled by interrupt handler.
+ Fifo<char, UCOO_CONFIG_HAL_UART_RX_BUFFER> rx_fifo_;
+ /// TX FIFO, emptied by interrupt handler.
+ Fifo<char, UCOO_CONFIG_HAL_UART_TX_BUFFER> tx_fifo_;
+ /// Error character, inserted in case of error.
+ char error_char_;
};
} // namespace ucoo