diff --git a/debug.gdb b/debug.gdb new file mode 100644 index 0000000..06c673f --- /dev/null +++ b/debug.gdb @@ -0,0 +1,2 @@ +dir bazel-mjmech_pi3_hat +target remote localhost:3333 diff --git a/fw/BUILD b/fw/BUILD index f7ec77e..3e4961a 100644 --- a/fw/BUILD +++ b/fw/BUILD @@ -19,7 +19,10 @@ load("@com_github_ARMmbed_mbed-os//:rules.bzl", "mbed_binary") mbed_binary( name = "pi3_hat", srcs = [ + "millisecond_timer.h", "pi3_hat.cc", + "stm32_serial.cc", + "stm32_serial.h", ], ) diff --git a/fw/millisecond_timer.h b/fw/millisecond_timer.h new file mode 100644 index 0000000..8feb69c --- /dev/null +++ b/fw/millisecond_timer.h @@ -0,0 +1,66 @@ +// Copyright 2018-2019 Josh Pieper, jjp@pobox.com. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "mbed.h" + +namespace fw { + +class MillisecondTimer { + public: + MillisecondTimer() { + __HAL_RCC_TIM2_CLK_ENABLE(); + + handle_.Instance = TIM2; + handle_.Init.Period = 0xFFFFFFFF; + handle_.Init.Prescaler = + (uint32_t)(SystemCoreClock / 1U / 1000000U) - 1; // 1 us tick + handle_.Init.ClockDivision = 1; + handle_.Init.CounterMode = TIM_COUNTERMODE_UP; + handle_.Init.RepetitionCounter = 0; + + HAL_TIM_Base_Init(&handle_); + } + + uint32_t read_ms() { + return TIM2->CNT / 1000; + } + + uint32_t read_us() { + return TIM2->CNT; + } + + void wait_ms(uint32_t delay_ms) { + wait_us(delay_ms * 1000); + } + + void wait_us(uint32_t delay_us) { + uint32_t current = TIM2->CNT; + uint32_t elapsed = 0; + while (true) { + const uint32_t next = TIM2->CNT; + elapsed += next - current; + // We check delay_us + 1 since we don't know where in the + // current microsecond we started. + if (elapsed >= (delay_us + 1)) { return; } + current = next; + } + } + + private: + TIM_HandleTypeDef handle_; +}; + +} diff --git a/fw/pi3_hat.cc b/fw/pi3_hat.cc index c0198ce..bd61489 100644 --- a/fw/pi3_hat.cc +++ b/fw/pi3_hat.cc @@ -14,13 +14,75 @@ #include "mbed.h" +#include "fw/millisecond_timer.h" +#include "fw/stm32_serial.h" + int main(void) { - DigitalOut led(PB_3); + DigitalOut led1(PB_3, 1); + DigitalOut led2(PB_4, 1); + + fw::Stm32Serial master_serial{[]() { + fw::Stm32Serial::Options options; + options.tx = PA_9; + options.rx = PA_10; + options.baud_rate = 3000000; + return options; + }()}; + auto* const master_uart = master_serial.uart(); + + master_uart->CR1 &= ~USART_CR1_UE; + master_uart->CR1 |= USART_CR1_TE | USART_CR1_RE; + // master_uart->CR3 |= USART_CR3_OVRDIS; + master_uart->CR1 |= USART_CR1_UE; + + fw::Stm32Serial slave_serial{[]() { + fw::Stm32Serial::Options options; + options.tx = PA_2; + options.rx = PA_3; + options.baud_rate = 3000000; + return options; + }()}; + auto* const slave_uart = slave_serial.uart(); + + slave_uart->CR1 &= ~USART_CR1_UE; + slave_uart->CR3 |= USART_CR3_DEM; + // slave_uart->CR3 |= USART_CR3_OVRDIS; + slave_uart->CR1 |= USART_CR1_TE | USART_CR1_RE; + slave_uart->CR1 = + (slave_uart->CR1 & ~USART_CR1_DEAT) | (6 << USART_CR1_DEAT_Pos); + slave_uart->CR1 = + (slave_uart->CR1 & ~USART_CR1_DEDT) | (6 << USART_CR1_DEDT_Pos); + slave_uart->CR1 |= USART_CR1_UE; + + pinmap_pinout(PA_1, PinMap_UART_RTS); + + fw::MillisecondTimer timer; + uint32_t flash_count = 0; + + // We don't want *any* interrupts while we are running. Our inner + // loop runs at about 1us per iteration, and any glitches can + // cause a receiver overrun. + __disable_irq(); while (true) { - led.write(1); - wait_us(1000000); - led.write(0); - wait_us(1000000); + if (master_uart->ISR & USART_ISR_RXNE) { + const uint8_t value = master_uart->RDR; + // Wait for any previous write to complete. + while ((slave_uart->ISR & USART_ISR_TXE) == 0); + slave_uart->TDR = value; + } + if (slave_uart->ISR & USART_ISR_RXNE) { + const int value = slave_uart->RDR; + while ((master_uart->ISR & USART_ISR_TXE) == 0); + master_uart->TDR = value; + } + if (true) { + const uint32_t time_s = timer.read_us() >> 20; + if (time_s != flash_count) { + flash_count = time_s; + led1.write(!led1.read()); + } + } + led2.write(!led2.read()); } } diff --git a/fw/stm32_serial.cc b/fw/stm32_serial.cc new file mode 100644 index 0000000..7a14443 --- /dev/null +++ b/fw/stm32_serial.cc @@ -0,0 +1,99 @@ +// Copyright 2018-2019 Josh Pieper, jjp@pobox.com. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fw/stm32_serial.h" + +namespace fw { + +namespace { +int32_t GetMax16OversamplingBaud(UARTName uart) { + switch (uart) { + case UART_1: + return 5620000; + case UART_2: + return 2810000; + } + mbed_die(); + return 0; +} + +void EnableClock(UARTName uart) { + if (uart == UART_1) { + __HAL_RCC_USART1_CLK_ENABLE(); + } else if (uart == UART_2) { + __HAL_RCC_USART2_CLK_ENABLE(); + } else { + mbed_die(); + } +#if defined(UART7_BASE) || defined(USART7_BASE) + #error "Need to handle more uarts" +#endif +} + +} + +Stm32Serial::Stm32Serial(const Options& options) { + uart_= [&]() { + const auto uart_tx = static_cast( + pinmap_peripheral(options.tx, PinMap_UART_TX)); + const auto uart_rx = static_cast( + pinmap_peripheral(options.rx, PinMap_UART_RX)); + return reinterpret_cast(pinmap_merge(uart_tx, uart_rx)); + }(); + if (uart_ == nullptr) { + mbed_die(); + } + + // Reset and enable clock. + EnableClock(uart_name()); + + // Configure pins for alternate functions. + pinmap_pinout(options.tx, PinMap_UART_TX); + pinmap_pinout(options.rx, PinMap_UART_RX); + if (options.tx != NC) { + pin_mode(options.tx, PullUp); + } + if (options.rx != NC) { + pin_mode(options.rx, PullUp); + } + + // Then configure the UART itself. + UART_HandleTypeDef huart{}; + + huart.Instance = uart_; + huart.Init.BaudRate = options.baud_rate; + huart.Init.WordLength = 8; + huart.Init.StopBits = 1; + huart.Init.Parity = UART_PARITY_NONE; + huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; + + const int32_t max_16oversampling_baud = + GetMax16OversamplingBaud(uart_name()); + huart.Init.OverSampling = + (options.baud_rate > max_16oversampling_baud ? + UART_OVERSAMPLING_8 : + UART_OVERSAMPLING_16); + + if (options.tx == NC) { + huart.Init.Mode = UART_MODE_RX; + } else if (options.rx == NC) { + huart.Init.Mode = UART_MODE_TX; + } else { + huart.Init.Mode = UART_MODE_TX_RX; + } + + HAL_UART_Init(&huart); +} + +} diff --git a/fw/stm32_serial.h b/fw/stm32_serial.h new file mode 100644 index 0000000..064955f --- /dev/null +++ b/fw/stm32_serial.h @@ -0,0 +1,51 @@ +// Copyright 2018 Josh Pieper, jjp@pobox.com. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "mbed.h" +#include "serial_api_hal.h" +#include "PeripheralPins.h" + +namespace fw { + +/// When constructed, this will configure the given serial port, +/// including marking the relevant pins as alternate function. +/// +/// NOTE: Unlike the Mbed RawSerial class, this will use 8X +/// oversampling when necessary to achieve higher baud rates. +class Stm32Serial { + public: + struct Options { + PinName tx = NC; + PinName rx = NC; + + int baud_rate = 115200; + }; + + Stm32Serial(const Options&); + + UARTName uart_name() const { + return static_cast(reinterpret_cast(uart_)); + } + + USART_TypeDef* uart() { + return uart_; + } + + private: + USART_TypeDef* uart_; +}; + +} diff --git a/run_openocd.sh b/run_openocd.sh new file mode 100755 index 0000000..2f40e11 --- /dev/null +++ b/run_openocd.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +openocd \ + -f /usr/share/openocd/scripts/interface/stlink-v2.cfg \ + -f /usr/share/openocd/scripts/target/stm32f0x_stlink.cfg \ + -c init -c "reset_config none separate; reset init" diff --git a/run_openocd_noreset.sh b/run_openocd_noreset.sh new file mode 100755 index 0000000..4b333ff --- /dev/null +++ b/run_openocd_noreset.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +openocd \ + -f /usr/share/openocd/scripts/interface/stlink-v2.cfg \ + -f /usr/share/openocd/scripts/target/stm32f0x_stlink.cfg \ + -c "reset_config none separate"