Skip to content

Commit

Permalink
✨ Add lpc40::dma_spi
Browse files Browse the repository at this point in the history
- 🐛 Reading back from dma_spi does not work!
- 🐛 Fix linker file path for conanfile, fixing where the default
  linker is used, segfaulting the lpc40 because the top of stack was in
  0x2000'0000 and not 0x1000'0000.
- ⚡ In the `lpc40::spi`, reduce time gap between transmitted bytes
  by 46% (from 1160ns to 630ns @ 3MHz) by using the "fifo not full" status vs
  the busy() status. This allows the software to keep the spi peripheral
  fed with data.
- 🧪 Manually tested using saleae to confirm signals. Rx is
  tested by pulling the line to 3v3 and checking that 0xFF is returned
  for all bytes in the sequence and by removing the connection and
  confirming that 0x00 is in all bytes of the receive buffer.
  • Loading branch information
kammce committed Aug 15, 2024
1 parent 0a9b053 commit b5f3f4e
Show file tree
Hide file tree
Showing 9 changed files with 470 additions and 44 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ libhal_test_and_make_library(
src/lpc40/power.cpp
src/lpc40/pwm.cpp
src/lpc40/spi.cpp
src/lpc40/dma_spi.cpp
src/lpc40/stream_dac.cpp
src/lpc40/uart.cpp

Expand Down
5 changes: 4 additions & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ def package_info(self):
self.cpp_info.exelinkflags = [
"-L" + os.path.join(self.package_folder, "linker_scripts")]

full_linker_path = os.path.join(
self.package_folder, "linker_scripts", platform + ".ld")
# if the file exists, then we should use it as the linker
if os.path.isfile(platform + ".ld"):
if os.path.isfile(full_linker_path):
self.output.info(f"linker file '{full_linker_path}' found!")
self.cpp_info.exelinkflags.append("-T" + platform + ".ld")

# if there is no match, then the linker script could be a pattern
Expand Down
7 changes: 5 additions & 2 deletions demos/applications/spi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ void application(resource_list& p_map)

while (true) {
using namespace std::literals;
std::array<hal::byte, 4> payload{ 0xDE, 0xAD, 0xBE, 0xEF };

std::array<hal::byte, 4> const payload{ 0xDE, 0xAD, 0xBE, 0xEF };
std::array<hal::byte, 8> buffer{};

hal::print(console, "Write operation\n");
chip_select.level(false);
hal::write(spi, payload);
chip_select.level(true);
hal::delay(clock, 1s);

hal::print(console, "Read operation: [ ");
Expand All @@ -64,7 +67,7 @@ void application(resource_list& p_map)
std::array<hal::byte, 4> id_data{};

chip_select.level(false);
hal::delay(clock, 250ns);
hal::delay(clock, 250ns); // wait at least 250ns
hal::write_then_read(spi, read_manufacturer_id, id_data, 0xA5);
chip_select.level(true);

Expand Down
10 changes: 9 additions & 1 deletion demos/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ resource_list resources{};
[[noreturn]] void terminate_handler() noexcept
{

if (not resources.status_led && not resources.console) {
if (not resources.status_led && not resources.status_led) {
// spin here until debugger is connected
while (true) {
continue;
Expand Down Expand Up @@ -73,3 +73,11 @@ int main()

std::terminate();
}

extern "C"
{
// This gets rid of an issue with libhal-exceptions in Debug mode.
void __assert_func()
{
}
}
9 changes: 5 additions & 4 deletions demos/platforms/lpc4078.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
#include <libhal-arm-mcu/dwt_counter.hpp>
#include <libhal-arm-mcu/lpc40/adc.hpp>
#include <libhal-arm-mcu/lpc40/can.hpp>
#include <libhal-arm-mcu/lpc40/clock.hpp>
#include <libhal-arm-mcu/lpc40/constants.hpp>
#include <libhal-arm-mcu/lpc40/dma_spi.hpp>
#include <libhal-arm-mcu/lpc40/i2c.hpp>
#include <libhal-arm-mcu/lpc40/input_pin.hpp>
#include <libhal-arm-mcu/lpc40/interrupt_pin.hpp>
Expand All @@ -27,6 +27,7 @@
#include <libhal-arm-mcu/lpc40/uart.hpp>
#include <libhal-arm-mcu/startup.hpp>
#include <libhal-arm-mcu/system_control.hpp>
#include <libhal-lpc40/clock.hpp>

#include <resource_list.hpp>

Expand All @@ -44,7 +45,7 @@ resource_list initialize_platform()
static hal::lpc40::uart uart0(0,
receive_buffer,
hal::serial::settings{
.baud_rate = 38400,
.baud_rate = 115200,
});

static hal::lpc40::can can(2,
Expand All @@ -58,8 +59,8 @@ resource_list initialize_platform()
static hal::lpc40::i2c i2c2(2);
static hal::lpc40::interrupt_pin interrupt_pin(0, 29);
static hal::lpc40::pwm pwm(1, 6);
static hal::lpc40::spi spi2(2);
static hal::lpc40::output_pin chip_select(1, 10);
static hal::lpc40::dma_spi spi2(2);
static hal::lpc40::output_pin chip_select(1, 8);
static hal::lpc40::stream_dac_u8 stream_dac;

return {
Expand Down
84 changes: 84 additions & 0 deletions include/libhal-arm-mcu/lpc40/dma_spi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2024 Khalil Estell
//
// 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 <cstdint>
#include <span>

#include <libhal/io_waiter.hpp>
#include <libhal/spi.hpp>

#include "constants.hpp"
#include "pin.hpp"

namespace hal::lpc40 {
class dma_spi final : public hal::spi
{
public:
/// Information used to configure the spi bus
struct bus_info
{
/// peripheral id used to power on the spi peripheral at creation
peripheral peripheral_id;
/// spi data pin
pin clock;
/// spi clock pin
pin data_out;
/// spi clock pin
pin data_in;
/// clock function code
std::uint8_t clock_function;
/// scl pin function code
std::uint8_t data_out_function;
/// scl pin function code
std::uint8_t data_in_function;
};

/**
* @brief Construct a new spi object
*
* @param p_bus - bus number to use
* @param p_waiter - provides the implementation strategy for the time the CPU
* waits until the transfer has completed.
* @param p_settings - spi settings to achieve
* @throws hal::operation_not_supported - if the p_bus is not 0, 1, or 2 or if
* the spi settings could not be achieved.
*/
dma_spi(std::uint8_t p_bus,
hal::io_waiter& p_waiter = hal::polling_io_waiter(),
spi::settings const& p_settings = {});
/**
* @brief Construct a new spi object using bus info directly
*
* @param p_bus - Full bus information
*/
dma_spi(bus_info p_bus);

dma_spi(dma_spi const& p_other) = delete;
dma_spi& operator=(dma_spi const& p_other) = delete;
dma_spi(dma_spi&& p_other) noexcept = delete;
dma_spi& operator=(dma_spi&& p_other) noexcept = delete;
virtual ~dma_spi();

private:
void driver_configure(settings const& p_settings) override;
void driver_transfer(std::span<hal::byte const> p_data_out,
std::span<hal::byte> p_data_in,
hal::byte p_filler) override;

hal::io_waiter* m_io_waiter;
bus_info m_bus;
};
} // namespace hal::lpc40
Loading

0 comments on commit b5f3f4e

Please sign in to comment.