14
14
#include <modm/architecture/interface/uart.hpp>
15
15
#include <modm/platform/core/peripherals.hpp>
16
16
#include <modm/platform/gpio/connector.hpp>
17
+ #include <modm/math/algorithm/prescaler.hpp>
17
18
18
19
#include <hardware/structs/uart.h>
19
20
@@ -51,14 +52,30 @@ public:
51
52
static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }};
52
53
53
54
public:
54
-
55
- enum class Parity : uint32_t
55
+ enum class
56
+ Parity : uint32_t
56
57
{
57
- Even = 2 ,
58
- Odd = 1 ,
58
+ Even = UART_UARTLCR_H_PEN_BITS | UART_UARTLCR_H_EPS_BITS ,
59
+ Odd = UART_UARTLCR_H_PEN_BITS ,
59
60
Disabled = 0,
60
61
};
61
62
63
+ enum class
64
+ WordLength : uint32_t
65
+ {
66
+ Bit5 = 0 << UART_UARTLCR_H_WLEN_LSB,
67
+ Bit6 = 1 << UART_UARTLCR_H_WLEN_LSB,
68
+ Bit7 = 2 << UART_UARTLCR_H_WLEN_LSB,
69
+ Bit8 = 3 << UART_UARTLCR_H_WLEN_LSB,
70
+ };
71
+
72
+ enum class
73
+ StopBits : uint32_t
74
+ {
75
+ Bit1 = 0,
76
+ Bit2 = UART_UARTLCR_H_STP2_BITS,
77
+ };
78
+
62
79
template< class... Signals >
63
80
static void
64
81
connect()
@@ -76,68 +93,62 @@ public:
76
93
static void reset();
77
94
static void unreset();
78
95
79
- static void setFormat(uint8_t data_bits,uint8_t stop_bits,Parity parity) {
96
+ static void
97
+ setFormat(Parity parity, WordLength length, StopBits stop)
98
+ {
80
99
hw_write_masked(&uart{{ id }}_hw->lcr_h,
81
- ((data_bits - 5u) << UART_UARTLCR_H_WLEN_LSB) |
82
- ((stop_bits - 1u) << UART_UARTLCR_H_STP2_LSB) |
83
- ((parity != Parity::Disabled ? 1 : 0) << UART_UARTLCR_H_PEN_LSB) |
84
- ((parity == Parity::Even ? 1 : 0) << UART_UARTLCR_H_EPS_LSB),
85
- UART_UARTLCR_H_WLEN_BITS |
86
- UART_UARTLCR_H_STP2_BITS |
87
- UART_UARTLCR_H_PEN_BITS |
88
- UART_UARTLCR_H_EPS_BITS);
100
+ uint32_t(parity) | uint32_t(length) | uint32_t(stop),
101
+ UART_UARTLCR_H_WLEN_BITS | UART_UARTLCR_H_STP2_BITS |
102
+ UART_UARTLCR_H_PEN_BITS | UART_UARTLCR_H_EPS_BITS);
89
103
}
90
104
91
- template< class SystemClock, baudrate_t baudrate >
92
- static uint32_t setBaudrate() {
93
- constexpr uint32_t baud_rate_div = (8 * SystemClock::PeriFrequency / baudrate);
94
- constexpr uint32_t baud_ibrd = baud_rate_div >> 7;
95
- uint32_t baud_fbrd;
96
- if constexpr (baud_ibrd == 0) {
97
- baud_ibrd = 1;
98
- baud_fbrd = 0;
99
- } else if constexpr (baud_ibrd >= 65535) {
100
- baud_ibrd = 65535;
101
- baud_fbrd = 0;
102
- } else {
103
- baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2;
104
- }
105
+ template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) >
106
+ static baudrate_t
107
+ setBaudrate()
108
+ {
109
+ static_assert(baudrate * 16 <= SystemClock::PeriFrequency and
110
+ SystemClock::PeriFrequency <= baudrate * 16 * 65535ull,
111
+ "SystemClock::PeriFrequency must be in the range [16 x baudrate, 16 x 65535 x baudrate].");
112
+ // 16.6 fractional baudrate generator with 16x oversampling
113
+ constexpr uint32_t min = (1ul << 7);
114
+ constexpr uint32_t max = (1ul << 22) - 1ul;
115
+ constexpr auto result = Prescaler::from_range(SystemClock::PeriFrequency*4, baudrate, min, max);
116
+ modm::PeripheralDriver::assertBaudrateInTolerance< result.frequency, baudrate, tolerance >();
105
117
// Load PL011's baud divisor registers
106
- uart{{ id }}_hw->ibrd = baud_ibrd;
107
- uart{{ id }}_hw->fbrd = baud_fbrd;
108
-
118
+ uart{{ id }}_hw->ibrd = result.prescaler >> 6;
119
+ uart{{ id }}_hw->fbrd = result.prescaler & 0x3f;
109
120
// PL011 needs a (dummy) line control register write to latch in the
110
121
// divisors. We don't want to actually change LCR contents here.
111
122
hw_set_bits(&uart{{ id }}_hw->lcr_h, 0);
112
- // See datasheet
113
- return (4 * SystemClock::PeriFrequency) / (64 * baud_ibrd + baud_fbrd) ;
123
+
124
+ return result.frequency ;
114
125
}
126
+
115
127
template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) >
116
- static void inline
117
- initialize()
128
+ static void
129
+ initialize(Parity parity=Parity::Disabled, WordLength length=WordLength::Bit8, StopBits stop=StopBits::Bit1 )
118
130
{
119
131
reset();
120
132
unreset();
121
- setBaudrate<SystemClock,baudrate>();
122
- setFormat(8,1,Parity::Disabled );
133
+ setBaudrate<SystemClock, baudrate, tolerance >();
134
+ setFormat(parity, length, stop );
123
135
// Enable the UART, both TX and RX
124
136
uart{{ id }}_hw->cr = UART_UARTCR_UARTEN_BITS | UART_UARTCR_TXE_BITS | UART_UARTCR_RXE_BITS;
125
137
// Enable FIFOs
126
138
hw_set_bits(&uart{{ id }}_hw->lcr_h, UART_UARTLCR_H_FEN_BITS);
127
139
// Always enable DREQ signals -- no harm in this if DMA is not listening
128
140
uart{{ id }}_hw->dmacr = UART_UARTDMACR_TXDMAE_BITS | UART_UARTDMACR_RXDMAE_BITS;
129
141
130
- %% if options["buffer.rx"] > fifo_size
142
+ %% if options["buffer.rx"] > fifo_size
131
143
/* If RX buffering with more than {{ fifo_size }} bytes is requested a software queue
132
144
* must be used for receiving. This involves the Rx Interrupt only. */
133
145
uart{{ id }}_hw->imsc = UART_UARTIMSC_RXIM_BITS;
134
- %% endif
146
+ %% endif
135
147
136
- %% if options["buffer.tx"] > fifo_size or options["buffer.rx"] > fifo_size
148
+ %% if options["buffer.tx"] > fifo_size or options["buffer.rx"] > fifo_size
137
149
/* Enable the UART Interrupt */
138
150
NVIC_EnableIRQ(UART{{ id }}_IRQ_IRQn);
139
- %% endif
140
-
151
+ %% endif
141
152
}
142
153
143
154
static void
0 commit comments