1 // Copyright 2019 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include <cinttypes>
16
17 #include "pw_preprocessor/compiler.h"
18 #include "pw_sys_io/sys_io.h"
19
20 namespace {
21
22 // Default core clock. This is technically not a constant, but since this app
23 // doesn't change the system clock a constant will suffice.
24 constexpr uint32_t kSystemCoreClock = 16000000;
25
26 // Base address for everything peripheral-related on the STM32F4xx.
27 constexpr uint32_t kPeripheralBaseAddr = 0x40000000u;
28 // Base address for everything AHB1-related on the STM32F4xx.
29 constexpr uint32_t kAhb1PeripheralBase = kPeripheralBaseAddr + 0x00020000U;
30 // Base address for everything APB2-related on the STM32F4xx.
31 constexpr uint32_t kApb2PeripheralBase = kPeripheralBaseAddr + 0x00010000U;
32
33 // Reset/clock configuration block (RCC).
34 // `reserved` fields are unimplemented features, and are present to ensure
35 // proper alignment of registers that are in use.
PW_PACKED(struct)36 PW_PACKED(struct) RccBlock {
37 uint32_t reserved1[12];
38 uint32_t ahb1_config;
39 uint32_t reserved2[4];
40 uint32_t apb2_config;
41 };
42
43 // Mask for ahb1_config (AHB1ENR) to enable the "A" GPIO pins.
44 constexpr uint32_t kGpioAEnable = 0x1u;
45
46 // Mask for apb2_config (APB2ENR) to enable USART1.
47 constexpr uint32_t kUsart1Enable = 0x1u << 4;
48
49 // GPIO register block definition.
PW_PACKED(struct)50 PW_PACKED(struct) GpioBlock {
51 uint32_t modes;
52 uint32_t out_type;
53 uint32_t out_speed;
54 uint32_t pull_up_down;
55 uint32_t input_data;
56 uint32_t output_data;
57 uint32_t gpio_bit_set;
58 uint32_t port_config_lock;
59 uint32_t alt_low;
60 uint32_t alt_high;
61 };
62
63 // Constants related to GPIO mode register masks.
64 constexpr uint32_t kGpio9PortModePos = 18;
65 constexpr uint32_t kGpio10PortModePos = 20;
66 constexpr uint32_t kGpioPortModeAlternate = 2;
67
68 // Constants related to GPIO port speed register masks.
69 constexpr uint32_t kGpio9PortSpeedPos = 18;
70 constexpr uint32_t kGpio10PortSpeedPos = 20;
71 constexpr uint32_t kGpioSpeedVeryHigh = 3;
72
73 // Constants related to GPIO pull up/down resistor type masks.
74 constexpr uint32_t kGpio9PullTypePos = 18;
75 constexpr uint32_t kGpio10PullTypePos = 20;
76 constexpr uint32_t kPullTypePullUp = 1;
77
78 // Constants related to GPIO port speed register masks.
79 constexpr uint32_t kGpio9AltModeHighPos = 4;
80 constexpr uint32_t kGpio10AltModeHighPos = 8;
81
82 // Alternate function for pins A9 and A10 that enable USART1.
83 constexpr uint8_t kGpioAlternateFunctionUsart1 = 0x07u;
84
85 // USART status flags.
86 constexpr uint32_t kTxRegisterEmpty = 0x1u << 7;
87
88 // USART configuration flags for config1 register.
89 // Note: a large number of configuration flags have been omitted as they default
90 // to reasonable values and we don't need to change them.
91 constexpr uint32_t kReceiveEnable = 0x1 << 2;
92 constexpr uint32_t kTransmitEnable = 0x1 << 3;
93 constexpr uint32_t kEnableUsart = 0x1 << 13;
94
95 // USART configuration flags for status register.
96 constexpr uint32_t kReadDataReady = 0x1u << 5;
97
98 // Layout of memory mapped registers for USART blocks.
PW_PACKED(struct)99 PW_PACKED(struct) UsartBlock {
100 uint32_t status;
101 // Only the lower 8 bits are valid. Read for RX data, write to TX data.
102 uint32_t data_register;
103 uint32_t baud_rate;
104 uint32_t config1;
105 uint32_t config2;
106 uint32_t config3;
107 uint32_t config4;
108 };
109
110 // Sets the UART baud register using the peripheral clock and target baud rate.
111 // These calculations are specific to the default oversample by 16 mode.
112 // TODO(amontanez): Document magic calculations in full UART implementation.
CalcBaudRegister(uint32_t clock,uint32_t target_baud)113 uint32_t CalcBaudRegister(uint32_t clock, uint32_t target_baud) {
114 uint32_t div_fac = (clock * 25) / (4 * target_baud);
115 uint32_t mantissa = div_fac / 100;
116 uint32_t fraction = ((div_fac - mantissa * 100) * 16 + 50) / 100;
117
118 return (mantissa << 4) + (fraction & 0xFFu);
119 }
120
121 // Declare a reference to the memory mapped RCC block.
122 volatile RccBlock& platform_rcc =
123 *reinterpret_cast<volatile RccBlock*>(kAhb1PeripheralBase + 0x3800U);
124
125 // Declare a reference to the 'A' GPIO memory mapped block.
126 volatile GpioBlock& gpio_a =
127 *reinterpret_cast<volatile GpioBlock*>(kAhb1PeripheralBase + 0x0000U);
128
129 // Declare a reference to the memory mapped block for USART1.
130 volatile UsartBlock& usart1 =
131 *reinterpret_cast<volatile UsartBlock*>(kApb2PeripheralBase + 0x1000U);
132
133 } // namespace
134
pw_sys_io_stm32f429_Init()135 extern "C" void pw_sys_io_stm32f429_Init() {
136 // Enable 'A' GIPO clocks.
137 platform_rcc.ahb1_config = platform_rcc.ahb1_config | kGpioAEnable;
138
139 // Enable Uart TX pin.
140 // Output type defaults to push-pull (rather than open/drain).
141 gpio_a.modes = gpio_a.modes | (kGpioPortModeAlternate << kGpio9PortModePos);
142 gpio_a.out_speed =
143 gpio_a.out_speed | (kGpioSpeedVeryHigh << kGpio9PortSpeedPos);
144 gpio_a.pull_up_down =
145 gpio_a.pull_up_down | (kPullTypePullUp << kGpio9PullTypePos);
146 gpio_a.alt_high =
147 gpio_a.alt_high | (kGpioAlternateFunctionUsart1 << kGpio9AltModeHighPos);
148
149 // Enable Uart RX pin.
150 // Output type defaults to push-pull (rather than open/drain).
151 gpio_a.modes = gpio_a.modes | (kGpioPortModeAlternate << kGpio10PortModePos);
152 gpio_a.out_speed =
153 gpio_a.out_speed | (kGpioSpeedVeryHigh << kGpio10PortSpeedPos);
154 gpio_a.pull_up_down =
155 gpio_a.pull_up_down | (kPullTypePullUp << kGpio10PullTypePos);
156 gpio_a.alt_high =
157 gpio_a.alt_high | (kGpioAlternateFunctionUsart1 << kGpio10AltModeHighPos);
158
159 // Initialize USART1. Initialized to 8N1 at the specified baud rate.
160 platform_rcc.apb2_config = platform_rcc.apb2_config | (kUsart1Enable);
161
162 // Warning: Normally the baud rate register calculation is based off
163 // peripheral 2 clock. For this code, the peripheral clock defaults to
164 // the system core clock so it can be used directly.
165 usart1.baud_rate = CalcBaudRegister(kSystemCoreClock, /*target_baud=*/115200);
166
167 usart1.config1 = kEnableUsart | kReceiveEnable | kTransmitEnable;
168 }
169
170 namespace pw::sys_io {
171
172 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
173 // extremely inefficient as it requires the target to burn CPU cycles polling to
174 // see if a byte is ready yet.
ReadByte(std::byte * dest)175 Status ReadByte(std::byte* dest) {
176 while (true) {
177 if (TryReadByte(dest).ok()) {
178 return OkStatus();
179 }
180 }
181 }
182
183 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
184 // extremely inefficient as it requires the target to burn CPU cycles polling to
185 // see if a byte is ready yet.
TryReadByte(std::byte * dest)186 Status TryReadByte(std::byte* dest) {
187 if (!(usart1.status & kReadDataReady)) {
188 return Status::Unavailable();
189 }
190 *dest = static_cast<std::byte>(usart1.data_register);
191 return OkStatus();
192 }
193
194 // Send a byte over USART1. Since this blocks on every byte, it's rather
195 // inefficient. At the default baud rate of 115200, one byte blocks the CPU for
196 // ~87 micro seconds. This means it takes only 10 bytes to block the CPU for
197 // 1ms!
WriteByte(std::byte b)198 Status WriteByte(std::byte b) {
199 // Wait for TX buffer to be empty. When the buffer is empty, we can write
200 // a value to be dumped out of UART.
201 while (!(usart1.status & kTxRegisterEmpty)) {
202 }
203 usart1.data_register = static_cast<uint32_t>(b);
204 return OkStatus();
205 }
206
207 // Writes a string using pw::sys_io, and add newline characters at the end.
WriteLine(std::string_view s)208 StatusWithSize WriteLine(std::string_view s) {
209 size_t chars_written = 0;
210 StatusWithSize result = WriteBytes(as_bytes(span(s)));
211 if (!result.ok()) {
212 return result;
213 }
214 chars_written += result.size();
215
216 // Write trailing newline.
217 result = WriteBytes(as_bytes(span("\r\n", 2)));
218 chars_written += result.size();
219
220 return StatusWithSize(result.status(), chars_written);
221 }
222
223 } // namespace pw::sys_io
224