1 // Copyright 2019 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
17 #include "pw_preprocessor/compiler.h"
18 #include "pw_sys_io/sys_io.h"
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;
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;
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.
36 PW_PACKED(struct) RccBlock {
37 uint32_t reserved1[12];
39 uint32_t reserved2[4];
43 // Mask for ahb1_config (AHB1ENR) to enable the "A" GPIO pins.
44 constexpr uint32_t kGpioAEnable = 0x1u;
46 // Mask for apb2_config (APB2ENR) to enable USART1.
47 constexpr uint32_t kUsart1Enable = 0x1u << 4;
49 // GPIO register block definition.
50 PW_PACKED(struct) GpioBlock {
54 uint32_t pull_up_down;
57 uint32_t gpio_bit_set;
58 uint32_t port_config_lock;
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;
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;
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;
78 // Constants related to GPIO port speed register masks.
79 constexpr uint32_t kGpio9AltModeHighPos = 4;
80 constexpr uint32_t kGpio10AltModeHighPos = 8;
82 // Alternate function for pins A9 and A10 that enable USART1.
83 constexpr uint8_t kGpioAlternateFunctionUsart1 = 0x07u;
85 // USART status flags.
86 constexpr uint32_t kTxRegisterEmpty = 0x1u << 7;
88 // USART configuration flags for config1 register.
89 // Note: a large number of configuration flags have been omitted as they default
90 // to sane 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 kReadDataReady = 0x1u << 5;
94 constexpr uint32_t kEnableUsart = 0x1 << 13;
96 // Layout of memory mapped registers for USART blocks.
97 PW_PACKED(struct) UsartBlock {
99 // Only the lower 8 bits are valid. Read for RX data, write to TX data.
100 uint32_t data_register;
108 // Sets the UART baud register using the peripheral clock and target baud rate.
109 // These calculations are specific to the default oversample by 16 mode.
110 // TODO(amontanez): Document magic calculations in full UART implementation.
111 uint32_t CalcBaudRegister(uint32_t clock, uint32_t target_baud) {
112 uint32_t div_fac = (clock * 25) / (4 * target_baud);
113 uint32_t mantissa = div_fac / 100;
114 uint32_t fraction = ((div_fac - mantissa * 100) * 16 + 50) / 100;
116 return (mantissa << 4) + (fraction & 0xFFu);
119 // Declare a reference to the memory mapped RCC block.
120 volatile RccBlock& platform_rcc =
121 *reinterpret_cast<volatile RccBlock*>(kAhb1PeripheralBase + 0x3800U);
123 // Declare a reference to the 'A' GPIO memory mapped block.
124 volatile GpioBlock& gpio_a =
125 *reinterpret_cast<volatile GpioBlock*>(kAhb1PeripheralBase + 0x0000U);
127 // Declare a reference to the memory mapped block for USART1.
128 volatile UsartBlock& usart1 =
129 *reinterpret_cast<volatile UsartBlock*>(kApb2PeripheralBase + 0x1000U);
133 extern "C" void pw_sys_io_Init() {
134 // Enable 'A' GIPO clocks.
135 platform_rcc.ahb1_config |= kGpioAEnable;
137 // Enable Uart TX pin.
138 // Output type defaults to push-pull (rather than open/drain).
139 gpio_a.modes |= kGpioPortModeAlternate << kGpio9PortModePos;
140 gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio9PortSpeedPos;
141 gpio_a.pull_up_down |= kPullTypePullUp << kGpio9PullTypePos;
142 gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio9AltModeHighPos;
144 // Enable Uart RX pin.
145 // Output type defaults to push-pull (rather than open/drain).
146 gpio_a.modes |= kGpioPortModeAlternate << kGpio10PortModePos;
147 gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio10PortSpeedPos;
148 gpio_a.pull_up_down |= kPullTypePullUp << kGpio10PullTypePos;
149 gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio10AltModeHighPos;
151 // Initialize USART1. Initialized to 8N1 at the specified baud rate.
152 platform_rcc.apb2_config |= kUsart1Enable;
154 // Warning: Normally the baud rate register calculation is based off
155 // peripheral 2 clock. For this code, the peripheral clock defaults to
156 // the system core clock so it can be used directly.
157 usart1.baud_rate = CalcBaudRegister(kSystemCoreClock, /*target_baud=*/115200);
159 usart1.config1 = kEnableUsart | kReceiveEnable | kTransmitEnable;
162 namespace pw::sys_io {
164 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
165 // extremely inefficient as it requires the target to burn CPU cycles polling to
166 // see if a byte is ready yet.
167 Status ReadByte(std::byte* dest) {
169 if (TryReadByte(dest).ok()) {
175 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
176 // extremely inefficient as it requires the target to burn CPU cycles polling to
177 // see if a byte is ready yet.
178 Status TryReadByte(std::byte* dest) {
179 if (!(usart1.status & kReadDataReady)) {
180 return Status::Unavailable();
182 *dest = static_cast<std::byte>(usart1.data_register);
186 // Send a byte over USART1. Since this blocks on every byte, it's rather
187 // inefficient. At the default baud rate of 115200, one byte blocks the CPU for
188 // ~87 micro seconds. This means it takes only 10 bytes to block the CPU for
190 Status WriteByte(std::byte b) {
191 // Wait for TX buffer to be empty. When the buffer is empty, we can write
192 // a value to be dumped out of UART.
193 while (!(usart1.status & kTxRegisterEmpty)) {
195 usart1.data_register = static_cast<uint32_t>(b);
199 // Writes a string using pw::sys_io, and add newline characters at the end.
200 StatusWithSize WriteLine(const std::string_view& s) {
201 size_t chars_written = 0;
202 StatusWithSize result = WriteBytes(std::as_bytes(std::span(s)));
206 chars_written += result.size();
208 // Write trailing newline.
209 result = WriteBytes(std::as_bytes(std::span("\r\n", 2)));
210 chars_written += result.size();
212 return StatusWithSize(result.status(), chars_written);
215 } // namespace pw::sys_io