diff --git a/software/CMakeLists.txt b/software/CMakeLists.txt index 1647df5..cb7662f 100644 --- a/software/CMakeLists.txt +++ b/software/CMakeLists.txt @@ -13,6 +13,7 @@ set(CMAKE_CXX_STANDARD 20) # debug generated makefile #set(CMAKE_VERBOSE_MAKEFILE TRUE) +message("*** COMPILER ${CMAKE_CXX_COMPILER_ID}") message("*** TYPE ${CMAKE_BUILD_TYPE}") message("*** PLATFORM ${PLATFORM}") message("*** BOARD ${BOARD}") @@ -25,8 +26,13 @@ if("Linux" IN_LIST PLATFORM) set(LINUX 1) endif() +# windows +if("Windows" IN_LIST PLATFORM) + set(WINDOWS 1) +endif() + # posix compatible platforms -if(LINUX OR "Macos" IN_LIST PLATFORM OR "FreeBSD" IN_LIST PLATFORM) +if(LINUX OR WINDOWS OR "Macos" IN_LIST PLATFORM OR "FreeBSD" IN_LIST PLATFORM) set(POSIX 1) endif() @@ -82,13 +88,16 @@ if(POSIX) Ogg::ogg gtest::gtest ) + if(WINDOWS) + set(LIBRARIES ${LIBRARIES} wsock32 ws2_32 winmm) + endif() # enable address sanitizer - if(CMAKE_BUILD_TYPE STREQUAL Debug) - message("Enable Address Sanitizer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif() + #if(CMAKE_BUILD_TYPE STREQUAL Debug) + # message("Enable Address Sanitizer") + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + #endif() # set rpath so that shared libraries are searched in ../lib if(APPLE) @@ -149,6 +158,9 @@ endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # clang set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts -Wno-user-defined-literals") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # visual studio + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4455") else() # gcc set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines -fconcepts -Wno-literal-suffix") @@ -246,7 +258,13 @@ if(POSIX) system/src/posix/FlashImpl.hpp system/src/posix/FlashImpl.cpp ) - set(LOOP system/src/Loop.hpp system/src/posix/Loop.cpp system/src/posix/Loop2.cpp) + set(LOOP + system/src/SystemTime.hpp + system/src/Loop.hpp + system/src/posix/Loop.hpp + system/src/posix/Loop.inc.hpp + system/src/posix/Loop.cpp + ) set(NETWORK system/src/Network.hpp system/src/posix/Network.cpp) set(OUTPUT system/src/Output.hpp system/src/posix/Output.cpp system/src/Debug.hpp) set(SOUND system/src/Sound.hpp system/src/posix/Sound.cpp) @@ -263,7 +281,12 @@ if(POSIX) system/src/posix/StorageImpl.cpp ) set(TERMINAL system/src/Terminal.hpp system/src/posix/Terminal.cpp) - set(TIMER system/src/SystemTime.hpp system/src/Timer.hpp system/src/posix/Timer.cpp) + set(USB_DEVICE + system/src/UsbDevice.hpp + system/src/UsbDevice.cpp + system/src/posix/UsbDeviceImpl.hpp + system/src/posix/UsbDeviceImpl.cpp + ) endif() if(LINUX) if(TARGET PkgConfig::BlueZ) @@ -285,9 +308,10 @@ if(EMU) ) set(INPUT system/src/Input.hpp system/src/emu/Input.hpp system/src/emu/Input.cpp) set(LOOP + system/src/SystemTime.hpp system/src/Loop.hpp system/src/posix/Loop.hpp - system/src/posix/Loop.cpp + system/src/posix/Loop.inc.hpp system/src/emu/Loop.hpp system/src/emu/Loop.cpp system/src/emu/Gui.cpp @@ -310,6 +334,8 @@ if(EMU) set(SPI_MASTER system/src/SpiMaster.hpp system/src/SpiMaster.cpp + system/src/posix/SpiMasterImpl.hpp + system/src/posix/SpiMasterImpl.cpp system/src/emu/SpiBME680.hpp system/src/emu/SpiBME680.cpp system/src/emu/SpiMPQ6526.hpp @@ -319,7 +345,6 @@ if(EMU) system/src/emu/SpiSSD1309.hpp system/src/emu/SpiSSD1309.cpp ) - set(USB_DEVICE system/src/UsbDevice.hpp system/src/emu/UsbDevice.cpp) endif() if("nrf52" IN_LIST PLATFORM) set(BUS_MASTER @@ -337,7 +362,9 @@ if("nrf52" IN_LIST PLATFORM) ) set(INPUT system/src/Input.hpp system/src/nrf52/Input.cpp) set(LOOP - system/src/Loop.hpp system/src/nrf52/Loop.hpp + system/src/SystemTime.hpp + system/src/Loop.hpp + system/src/nrf52/Loop.hpp system/src/nrf52/Loop.cpp system/src/nrf52/system/gcc_startup_nrf52840.S #system/src/nrf52/system/system_nrf52840.c @@ -362,8 +389,12 @@ if("nrf52" IN_LIST PLATFORM) system/src/Storage.cpp ) set(TERMINAL system/src/Terminal.hpp system/src/nrf52/Terminal.cpp) - set(TIMER system/src/SystemTime.hpp system/src/Timer.hpp system/src/nrf52/Timer.cpp) - set(USB_DEVICE system/src/UsbDevice.hpp system/src/nrf52/UsbDevice.cpp) + set(USB_DEVICE + system/src/UsbDevice.hpp + system/src/UsbDevice.cpp + system/src/nrf52/UsbDeviceImpl.hpp + system/src/nrf52/UsbDeviceImpl.cpp + ) endif() if("stm32f0" IN_LIST PLATFORM) set(BUS_NODE @@ -380,11 +411,13 @@ if("stm32f0" IN_LIST PLATFORM) ) set(INPUT system/src/Input.hpp system/src/stm32f0/gpio.hpp system/src/stm32f0/Input.cpp) set(LOOP + system/src/SystemTime.hpp system/src/Loop.hpp system/src/stm32f0/defs.hpp system/src/stm32f0/Loop.hpp system/src/stm32f0/Loop.cpp system/src/stm32f0/system/startup_stm32f042x6.s + #system/src/stm32f0/system/startup_stm32f051x8.s ) set(OUTPUT system/src/Output.hpp system/src/stm32f0/Output.cpp system/src/Debug.hpp) set(SPI_MASTER @@ -392,13 +425,14 @@ if("stm32f0" IN_LIST PLATFORM) system/src/SpiMaster.cpp system/src/stm32f0/SpiMasterImpl.hpp system/src/stm32f0/SpiMasterImpl.cpp + #system/src/stm32f0/SpiMasterSingle16.hpp + #system/src/stm32f0/SpiMasterSingle16.cpp ) set(STORAGE system/src/Storage.hpp system/src/Storage.cpp ) set(TERMINAL system/src/Terminal.hpp system/src/stm32f0/Terminal.cpp) - set(TIMER system/src/SystemTime.hpp system/src/Timer.hpp system/src/stm32f0/Timer.cpp) endif() set(SYSTEM @@ -418,7 +452,6 @@ set(SYSTEM ${SPI_MASTER} ${STORAGE} ${TERMINAL} - ${TIMER} ${USB_DEVICE} system/src/FeRamStorage4.cpp system/src/FeRamStorage4.hpp @@ -664,9 +697,11 @@ endfunction() add_system_test_executable(Ble) add_system_test_executable(BusMaster) +add_system_test_executable(BusNode) add_system_test_executable(Calendar) add_system_test_executable(Flash) add_system_test_executable(Input) +add_system_test_executable(Loop) add_system_test_executable(Network) add_system_test_executable(QuadratureDecoder) add_system_test_executable(Radio) @@ -674,7 +709,6 @@ add_system_test_executable(Random) add_system_test_executable(Sound) add_system_test_executable(SpiMaster) add_system_test_executable(Storage) -add_system_test_executable(Timer) add_system_test_executable(UsbDevice) @@ -752,7 +786,7 @@ target_link_libraries(MqttSnBrokerTest ${LIBRARIES}) endif() # POSIX AND NOT EMU -if(${BOARD} STREQUAL "emuControl") +if(${BOARD} STREQUAL "None") # test for BME680 air sensor add_executable(BME680Test @@ -778,6 +812,9 @@ target_include_directories(BME680Test ) target_link_libraries(BME680Test ${LIBRARIES}) +endif() + +if(${BOARD} STREQUAL "emuControl") # test for SSD1309 display add_executable(SSD1309Test diff --git a/software/board/None/boardConfig.hpp b/software/board/None/boardConfig.hpp index db2d5bd..81ad0e0 100644 --- a/software/board/None/boardConfig.hpp +++ b/software/board/None/boardConfig.hpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -33,6 +34,12 @@ struct Drivers { FlashStorage storage{flash}; }; +struct DriversSpiMasterTest { + SpiMasterImpl transfer{"transfer"}; + SpiMasterImpl command{"command"}; + SpiMasterImpl data{"data"}; +}; + struct DriversFlashTest { FlashImpl flash{"flashTest.bin", 2, 4096, 4}; }; diff --git a/software/board/emuControl/boardConfig.hpp b/software/board/emuControl/boardConfig.hpp index a29fb62..36afe94 100644 --- a/software/board/emuControl/boardConfig.hpp +++ b/software/board/emuControl/boardConfig.hpp @@ -1,12 +1,15 @@ #pragma once #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -60,7 +63,8 @@ constexpr int DISPLAY_HEIGHT = 64; struct Drivers { SpiBME680 airSensor; - SpiSSD1309 display{DISPLAY_WIDTH, DISPLAY_HEIGHT}; + SpiSSD1309 displayCommand{DISPLAY_WIDTH, DISPLAY_HEIGHT}; + SpiSSD1309::Data displayData{displayCommand}; QuadratureDecoderImpl quadratureDecoder; BusMasterImpl busMaster; @@ -74,6 +78,20 @@ struct Drivers { FeRamStorage4 counters{feRam}; }; +struct DriversSpiMasterTest { + SpiMasterImpl transfer{"transfer"}; + SpiMasterImpl command{"command"}; + SpiMasterImpl data{"data"}; +}; + +struct DriversBusMasterTest { + BusMasterImpl busMaster; +}; + +struct DriversBusNodeTest { + BusNodeImpl busNode; +}; + struct DriversFlashTest { FlashImpl flash{"flashTest.bin", 2, 4096, 4}; }; diff --git a/software/board/emuSwitch/boardConfig.hpp b/software/board/emuSwitch/boardConfig.hpp index 1faa194..26d1cec 100644 --- a/software/board/emuSwitch/boardConfig.hpp +++ b/software/board/emuSwitch/boardConfig.hpp @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -78,6 +80,20 @@ struct SwitchDrivers { StorageImpl counters{"switchCounters.bin", 0xff, 4}; }; +struct DriversSpiMasterTest { + SpiMasterImpl transfer{"transfer"}; + SpiMasterImpl command{"command"}; + SpiMasterImpl data{"data"}; +}; + +struct DriversBusMasterTest { + BusMasterImpl busMaster; +}; + +struct DriversBusNodeTest { + BusNodeImpl busNode; +}; + struct DriversFlashTest { FlashImpl flash{"flashTest.bin", 2, 1024, 4}; }; diff --git a/software/board/nrf52Dongle/boardConfig.hpp b/software/board/nrf52Dongle/boardConfig.hpp index 92d4692..a5fc183 100644 --- a/software/board/nrf52Dongle/boardConfig.hpp +++ b/software/board/nrf52Dongle/boardConfig.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -74,20 +75,36 @@ constexpr int USB_ENDPOINT_COUNT = 3; // ------- struct Drivers { - SpiMasterImpl spi{3, + SpiMasterImpl spiDevice{ gpio::P0(19), gpio::P0(20), gpio::P0(21), gpio::P0(21)}; // data/command for write-only display, can be same as MISO - SpiMasterImpl::Channel airSensor{spi, gpio::P0(2)}; - SpiMasterImpl::Channel display{spi, gpio::P0(3), true}; - SpiMasterImpl::Channel feRam{spi, gpio::P0(3)}; + SpiMasterImpl::Channel airSensor{spiDevice, gpio::P0(2)}; + SpiMasterImpl::Channel displayCommand{spiDevice, gpio::P0(3), SpiMasterImpl::Channel::Mode::COMMAND}; + SpiMasterImpl::Channel displayData{spiDevice, gpio::P0(3), SpiMasterImpl::Channel::Mode::DATA}; + SpiMasterImpl::Channel feRam{spiDevice, gpio::P0(3)}; QuadratureDecoderImpl quadratureDecoder{gpio::P0(4), gpio::P0(5)}; - BusMasterImpl busMaster{gpio::P0(2), gpio::P0(3)}; + //BusMasterImpl busMaster{gpio::P0(2), gpio::P0(3)}; - FlashImpl flash{0xe0000 - 0x20000, 4, 32768}; - FlashStorage storage{flash}; + //FlashImpl flash{0xe0000 - 0x20000, 4, 32768}; + //FlashStorage storage{flash}; +}; + +struct DriversSpiMasterTest { + SpiMasterImpl spi{ + gpio::P0(19), // SCK + gpio::P0(20), // MOSI + gpio::P0(21), // MISO + gpio::P0(21)}; // DC (data/command for write-only display, can be same as MISO) + SpiMasterImpl::Channel transfer{spi, gpio::P0(2)}; + SpiMasterImpl::Channel command{spi, gpio::P0(3), SpiMasterImpl::Channel::Mode::COMMAND}; + SpiMasterImpl::Channel data{spi, gpio::P0(3), SpiMasterImpl::Channel::Mode::DATA}; +}; + +struct DriversBusMasterTest { + BusMasterImpl busMaster{gpio::P0(2), gpio::P0(3)};; }; struct DriversFlashTest { diff --git a/software/board/switch/boardConfig.hpp b/software/board/switch/boardConfig.hpp index 203ede3..ad41d76 100644 --- a/software/board/switch/boardConfig.hpp +++ b/software/board/switch/boardConfig.hpp @@ -3,6 +3,7 @@ #include #include #include +//#include #include #include #include @@ -31,8 +32,10 @@ constexpr int TRIGGER_COUNT = INPUT_COUNT; // https://github.com/candle-usb/candleLight_fw/blob/master/include/config.h constexpr gpio::OutputConfig OUTPUTS[] = { {gpio::PB(2), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // dummy - {gpio::PB(0), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // green led - {gpio::PB(1), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // blue led + //{gpio::PB(0), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // green led + //{gpio::PB(1), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // blue led + {gpio::PC(9), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // green led STM32F051 Discovry Kit + {gpio::PC(8), gpio::Pull::DISABLED, gpio::Speed::LOW, gpio::Drive::PUSH_PULL, true, false, false}, // blue led STM32F051 Discovry Kit }; constexpr int OUTPUT_COUNT = array::count(OUTPUTS); @@ -45,37 +48,64 @@ constexpr int MPQ6526_MAPPING[] = {0, 1, 2, 3, 4, 5}; // ------- struct Drivers { - SpiMasterImpl spi{1, + /*SpiMasterImpl spi{1, gpio::PA(5), gpio::PA(7), gpio::PA(6)}; - SpiMasterImpl::Channel airSensor{spi, gpio::PA(3)}; - SpiMasterImpl::Channel display{spi, gpio::PA(4)}; + //SpiMasterImpl::Channel airSensor{spi, gpio::PA(3)}; + //SpiMasterImpl::Channel display{spi, gpio::PA(4)}; +*/ }; constexpr int FLASH_ADDRESS = 0x8000000; struct SwitchDrivers { SpiMasterImpl spi{1, - gpio::PA(5), - gpio::PA(7), - gpio::PA(6)}; + gpio::PA(5), // sck + gpio::PA(7), // mosi + gpio::PA(6)}; // miso SpiMasterImpl::Channel relayDriver{spi, gpio::PA(3)}; - - BusNodeImpl busNode; - - FlashImpl storageFlash{FLASH_ADDRESS + 20 * 1024, 2, 1024}; +/* + SpiMasterSingle16 relayDriver{ + gpio::PA(5), // sck + gpio::PA(7), // mosi + gpio::PA(6), // miso + gpio::PA(3)}; // cs +*/ + BusNodeImpl busNode{gpio::PA(10), gpio::PA(9)}; + + FlashImpl storageFlash{FLASH_ADDRESS + 24 * 1024, 2, 1024}; FlashStorage storage{storageFlash}; - FlashImpl countersFlash{FLASH_ADDRESS + 20 * 1024 + 2048, 10, 1024}; + FlashImpl countersFlash{FLASH_ADDRESS + 24 * 1024 + 2048, 10, 1024}; FlashStorage counters{countersFlash}; }; struct DriversFlashTest { - FlashImpl flash{FLASH_ADDRESS + 20 * 1024, 2, 1024}; + FlashImpl flash{FLASH_ADDRESS + 24 * 1024, 2, 1024}; }; struct DriversStorageTest { - FlashImpl flash{FLASH_ADDRESS + 20 * 1024, 2, 4096}; + FlashImpl flash{FLASH_ADDRESS + 24 * 1024, 2, 4096}; FlashStorage storage{flash}; }; + +struct DriversSpiMasterTest { + SpiMasterImpl spi{1, + gpio::PA(5), // sck + gpio::PA(7), // mosi + gpio::PA(6)}; // miso + SpiMasterImpl::Channel transfer{spi, gpio::PA(3)}; + SpiMasterImpl::Channel command{spi, gpio::PA(4)}; + SpiMasterImpl::Channel data{spi, gpio::PA(4)}; +/* + SpiMasterSingle16 transfer{ + gpio::PA(5), // sck + gpio::PA(7), // mosi + gpio::PA(6), // miso + gpio::PA(3)}; // cs*/ +}; + +struct DriversBusNodeTest { + BusNodeImpl busNode{gpio::PA(10), gpio::PA(9)}; +}; diff --git a/software/board/switch/link.ld b/software/board/switch/link.ld index 11aba25..1c95a81 100644 --- a/software/board/switch/link.ld +++ b/software/board/switch/link.ld @@ -62,7 +62,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 6K -FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 20K +FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 24K } /* Define output sections */ diff --git a/software/conanfile.py b/software/conanfile.py index 6228b4a..9aed4ce 100644 --- a/software/conanfile.py +++ b/software/conanfile.py @@ -82,7 +82,7 @@ def generate(self): toolchain.variables["CPU"] = self.options.cpu toolchain.variables["FPU"] = self.options.fpu - # https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/toolchain.py + # https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/toolchain/blocks.py if str(self.settings.os) not in p: toolchain.blocks["generic_system"].values["cmake_system_name"] = "Generic" toolchain.blocks["generic_system"].values["cmake_system_processor"] = self.settings.arch @@ -93,8 +93,12 @@ def generate(self): toolchain.variables["GENERATE_HEX"] = self.env.get("GENERATE_HEX", None) # bake CC and CXX from profile into toolchain - toolchain.blocks["generic_system"].values["compiler"] = self.env.get("CC", None) - toolchain.blocks["generic_system"].values["compiler_cpp"] = self.env.get("CXX", None) + cc = self.env.get("CC", None) + if cc != None: + toolchain.variables["CMAKE_C_COMPILER "] = cc + cxx = self.env.get("CXX", None) + if cxx != None: + toolchain.variables["CMAKE_CXX_COMPILER "] = cxx toolchain.generate() diff --git a/software/control/src/AlarmInterface.cpp b/software/control/src/AlarmInterface.cpp index b7258e3..db97f84 100644 --- a/software/control/src/AlarmInterface.cpp +++ b/software/control/src/AlarmInterface.cpp @@ -1,5 +1,4 @@ #include "AlarmInterface.hpp" -#include #include #include #include diff --git a/software/control/src/BME680.cpp b/software/control/src/BME680.cpp index a122e6a..299c702 100644 --- a/software/control/src/BME680.cpp +++ b/software/control/src/BME680.cpp @@ -1,5 +1,5 @@ #include "BME680.hpp" -#include +#include #include @@ -116,7 +116,7 @@ AwaitableCoroutine BME680::measure() { co_await this->spi.transfer(2, this->buffer, 0, nullptr); // wait until measurement is ready - co_await Timer::sleep(1s); + co_await loop::sleep(1s); // read measurements this->buffer[0] = READ(0x1D); diff --git a/software/control/src/BusInterface.cpp b/software/control/src/BusInterface.cpp index 1a0f816..6932e72 100644 --- a/software/control/src/BusInterface.cpp +++ b/software/control/src/BusInterface.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -484,7 +484,7 @@ Coroutine BusInterface::receive() { Terminal::out << 'B' << dec(device->data.busDeviceId) << ": security counter error " << dec(securityCounter) << " <= " << dec(device->securityCounter) << '\n'; //break; } else { - //Terminal::out << 'B' << dec(device->data.busDeviceId) << ": security counter ok " << dec(securityCounter) << " > " << dec(device->securityCounter) << '\n'; + Terminal::out << 'B' << dec(device->data.busDeviceId) << ": security counter ok " << dec(securityCounter) << " > " << dec(device->securityCounter) << '\n'; } device->securityCounter = securityCounter; @@ -646,7 +646,7 @@ AwaitableCoroutine BusInterface::handleCommission(uint32_t busDeviceId, uint8_t MessageReader plugs(length, message); for (int i = 0; i < plugCount; ++i) { data->plugs[i] = plugs.e16L(); - Terminal::out << dec(endpointIndex) << " " << getTypeLabel(data->plugs[i]) << "\n"; + Terminal::out << "Ep " << dec(endpointIndex) << " Plug " << dec(i) << ' ' << getTypeLabel(data->plugs[i]) << "\n"; } // set id @@ -691,7 +691,6 @@ AwaitableCoroutine BusInterface::handleCommission(uint32_t busDeviceId, uint8_t auto oldEndpoint = oldDevice->endpoints; auto endpoint = device->endpoints; while (oldEndpoint != nullptr && endpoint != nullptr) { - //endpoint->subscribers.add(static_cast(static_cast &>(oldEndpoint->subscribers))); endpoint->subscribers.add(oldEndpoint->subscribers); // only works because old list removes itself from linked list oldEndpoint = oldEndpoint->next; endpoint = endpoint->next; @@ -728,13 +727,14 @@ AwaitableCoroutine BusInterface::handleCommission(uint32_t busDeviceId, uint8_t device.ptr = nullptr; } -AwaitableCoroutine BusInterface::readAttribute(int &length, uint8_t (&message)[MESSAGE_LENGTH], Device &device, +// uint8_t (&message)[MESSAGE_LENGTH] does not work on MSVC +AwaitableCoroutine BusInterface::readAttribute(int &length, uint8_t *message, Device &device, uint8_t endpointIndex, bus::Attribute attribute) { for (int retry = 0; ; ++retry) { // request attribute { - bus::MessageWriter w(message); + bus::MessageWriter w(MESSAGE_LENGTH, message); // set start of header w.setHeader(); @@ -775,11 +775,12 @@ AwaitableCoroutine BusInterface::readAttribute(int &length, uint8_t (&message)[M // wait for a response from the device int r = co_await select( this->responseBarrier.wait(length, message, device.data.address, endpointIndex, attribute), - Timer::sleep(TIMEOUT)); + loop::sleep(TIMEOUT)); // check if response was received - if (r == 1) + if (r == 1) { break; + } if (retry == MAX_RETRY) { length = -1; diff --git a/software/control/src/BusInterface.hpp b/software/control/src/BusInterface.hpp index af3a86f..47fa7b5 100644 --- a/software/control/src/BusInterface.hpp +++ b/software/control/src/BusInterface.hpp @@ -166,7 +166,7 @@ class BusInterface : public Interface { [[nodiscard]] AwaitableCoroutine handleCommission(uint32_t busDeviceId, uint8_t endpointCount); - [[nodiscard]] AwaitableCoroutine readAttribute(int &length, uint8_t (&message)[MESSAGE_LENGTH], Device &device, + [[nodiscard]] AwaitableCoroutine readAttribute(int &length, uint8_t *message, Device &device, uint8_t endpointIndex, bus::Attribute attribute); // publish messages to bus nodes diff --git a/software/control/src/FunctionInterface.cpp b/software/control/src/FunctionInterface.cpp index e38d489..a92981f 100644 --- a/software/control/src/FunctionInterface.cpp +++ b/software/control/src/FunctionInterface.cpp @@ -1,6 +1,6 @@ #include "FunctionInterface.hpp" #include "Cie1931.hpp" -#include +#include #include #include #include @@ -78,7 +78,7 @@ static const TypeInfo typeInfos[] = { co_await function->barrier.wait(info, &message); } else { // on: wait for message or timeout - int s = co_await select(function->barrier.wait(info, &message), Timer::sleep(timeout)); + int s = co_await select(function->barrier.wait(info, &message), loop::sleep(timeout)); if (s == 2) { // timeout: switch off message = 0; @@ -140,16 +140,16 @@ static const TypeInfo typeInfos[] = { // timeout not active and no transition in progress: wait for message co_await function->barrier.wait(info, &message); //Terminal::out << "plug " << dec(info.plug.id) << '\n'; - now = Timer::now(); + now = loop::now(); } else { // on: wait for message or timeout //Terminal::out << "select" << '\n'; bool off = timeoutActive && (!transition || offTime <= endTime); now = off ? offTime : endTime; - int s = co_await select(function->barrier.wait(info, &message), Timer::sleep(now)); + int s = co_await select(function->barrier.wait(info, &message), loop::sleep(now)); // "relaxed" time to prevent lagging behind - now = Timer::now(); + now = loop::now(); if (s == 1) { // switched on or off @@ -293,16 +293,16 @@ static const TypeInfo typeInfos[] = { // timeout not active and no transition in progress: wait for message co_await function->barrier.wait(info, &message); //Terminal::out << "plug " << dec(info.plug.id) << '\n'; - now = Timer::now(); + now = loop::now(); } else { // on: wait for message or timeout //Terminal::out << "select" << '\n'; bool off = timeoutActive && (!transition || offTime <= endTime); now = off ? offTime : endTime; - int s = co_await select(function->barrier.wait(info, &message), Timer::sleep(now)); + int s = co_await select(function->barrier.wait(info, &message), loop::sleep(now)); // "relaxed" time to prevent lagging behind - now = Timer::now(); + now = loop::now(); if (s == 1) { // switched on or off @@ -481,7 +481,7 @@ static const TypeInfo typeInfos[] = { // off: wait for message co_await function->barrier.wait(info, &message); //Terminal::out << "plug " << dec(info.plug.id) << '\n'; - now = Timer::now(); + now = loop::now(); //setColor = true; } else { // on: wait for message or timeout @@ -492,10 +492,10 @@ static const TypeInfo typeInfos[] = { bool off = timeout > 0ms && on && offTime <= now; if (off) now = offTime; - int s = co_await select(function->barrier.wait(info, &message), Timer::sleep(now)); + int s = co_await select(function->barrier.wait(info, &message), loop::sleep(now)); // "relaxed" time to prevent lagging behind - now = Timer::now(); + now = loop::now(); if (s == 1) { // switched on or off @@ -693,7 +693,7 @@ static const TypeInfo typeInfos[] = { auto d = targetPosition - position; // wait for event or timeout with a maximum to regularly report the current position - int s = co_await select(function->barrier.wait(info, &message), Timer::sleep(min(up ? -d : d, 200ms))); + int s = co_await select(function->barrier.wait(info, &message), loop::sleep(min(up ? -d : d, 200ms))); // set invalid plug index when timeout occurred if (s != 1) @@ -701,7 +701,7 @@ static const TypeInfo typeInfos[] = { //Terminal::out << "select " << dec(s) << " plug " << dec(info.plug.id) << '\n'; // get time since last time - auto time = Timer::now(); + auto time = loop::now(); d = time - lastTime; lastTime = time; @@ -724,7 +724,7 @@ static const TypeInfo typeInfos[] = { // up/down or trigger in if (message.value.u8 == 0) { // released: stop if timeout elapsed - if (Timer::now() > startTime + holdTime) + if (loop::now() > startTime + holdTime) targetPosition = position; } else { // up or down pressed @@ -783,7 +783,7 @@ static const TypeInfo typeInfos[] = { // start up = targetPosition < position; state = up ? 1 : 2; - lastTime = startTime = Timer::now(); + lastTime = startTime = loop::now(); } // publish up/down diff --git a/software/control/src/LocalInterface.cpp b/software/control/src/LocalInterface.cpp index 5a2b647..8d7b949 100644 --- a/software/control/src/LocalInterface.cpp +++ b/software/control/src/LocalInterface.cpp @@ -1,5 +1,5 @@ #include "LocalInterface.hpp" -#include +#include #include #include #include @@ -239,9 +239,9 @@ Coroutine LocalInterface::readAirSensor(SpiMaster &spi) { // wait #ifdef DEBUG - co_await Timer::sleep(10s); + co_await loop::sleep(10s); #else - co_await timer::sleep(60s); + co_await loop::sleep(60s); #endif } } diff --git a/software/control/src/Menu.cpp b/software/control/src/Menu.cpp index 5f7c15e..ab16379 100644 --- a/software/control/src/Menu.cpp +++ b/software/control/src/Menu.cpp @@ -1,5 +1,4 @@ #include "Menu.hpp" -#include #include #include #include "tahoma_8pt.hpp" // font diff --git a/software/control/src/RadioInterface.cpp b/software/control/src/RadioInterface.cpp index b77695b..94391ef 100644 --- a/software/control/src/RadioInterface.cpp +++ b/software/control/src/RadioInterface.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -939,7 +939,7 @@ bool RadioInterface::writeZclCommand(PacketWriter &w, uint8_t zclCounter, int pl } case zcl::Cluster::COLOR_CONTROL: { uint16_t color = clamp(int(message.value.f32 * 65279.0f + 0.5f), 0, 65279); - auto time = Timer::now(); + auto time = loop::now(); auto d = time - endpoint->time; if (d > 100ms || d < 0ms || endpoint->index == plugIndex) { //todo: doing it here does not work with retry @@ -1044,14 +1044,15 @@ void RadioInterface::writeFooter(PacketWriter &w, Radio::SendFlags sendFlags) { w.finish(sendFlags); } -AwaitableCoroutine RadioInterface::readAttribute(uint8_t (&packet)[MESSAGE_LENGTH], ZbDevice &device, +// uint8_t (&packet)[MESSAGE_LENGTH] does not work on MSVC +AwaitableCoroutine RadioInterface::readAttribute(uint8_t *packet, ZbDevice &device, uint8_t dstEndpoint, zcl::Cluster clusterId, zcl::Profile profile, uint8_t srcEndpoint, uint16_t attribute) { uint8_t zclCounter = this->zclCounter++; for (int retry = 0;; ++retry) { // send read attributes { - PacketWriter w(packet); + PacketWriter w(MESSAGE_LENGTH, packet); // nwk header writeNwkData(w, device); @@ -1076,7 +1077,7 @@ AwaitableCoroutine RadioInterface::readAttribute(uint8_t (&packet)[MESSAGE_LENGT int length; int r = co_await select( this->responseBarrier.wait(length, packet, srcEndpoint, zclCounter, - uint16_t(zcl::Command::READ_ATTRIBUTES_RESPONSE)), Timer::sleep(timeout)); + uint16_t(zcl::Command::READ_ATTRIBUTES_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) @@ -1119,12 +1120,12 @@ optional getString(uint8_t (&packet)[N]) { Coroutine RadioInterface::broadcast() { uint8_t packet[52]; uint8_t sendResult; - auto linkTime = Timer::now() + 1s; + auto linkTime = loop::now() + 1s; auto routeTime = linkTime + 1s; while (true) { // wait until the next timeout auto time = linkTime;//min(linkTime, routeTime); - co_await Timer::sleep(time); + co_await loop::sleep(time); if (time == linkTime) { // broadcast link status every 15 seconds @@ -1233,7 +1234,7 @@ Coroutine RadioInterface::sendBeacon() { co_await Radio::send(RADIO_ZBEE, packet, sendResult); // "cool down" before a new beacon can be sent - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); } } /* @@ -2752,7 +2753,7 @@ AwaitableCoroutine RadioInterface::handleZbCommission(uint64_t deviceLongAddress if (sendResult != 0) { // wait for a response from the device int r = co_await select(this->responseBarrier.wait(length, packet1, uint8_t(0), zdpCounter, - uint16_t(zb::ZdpCommand::NODE_DESCRIPTOR_RESPONSE)), Timer::sleep(timeout)); + uint16_t(zb::ZdpCommand::NODE_DESCRIPTOR_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) @@ -2793,7 +2794,7 @@ AwaitableCoroutine RadioInterface::handleZbCommission(uint64_t deviceLongAddress if (sendResult != 0) { // wait for a response from the device int r = co_await select(this->responseBarrier.wait(length, packet1, uint8_t(0), zdpCounter, - uint16_t(zb::ZdpCommand::ACTIVE_ENDPOINT_RESPONSE)), Timer::sleep(timeout)); + uint16_t(zb::ZdpCommand::ACTIVE_ENDPOINT_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) @@ -2860,7 +2861,7 @@ AwaitableCoroutine RadioInterface::handleZbCommission(uint64_t deviceLongAddress if (sendResult != 0) { // wait for a response from the device int r = co_await select(this->responseBarrier.wait(length, packet2, uint8_t(0), zdpCounter, - uint16_t(zb::ZdpCommand::SIMPLE_DESCRIPTOR_RESPONSE)), Timer::sleep(timeout)); + uint16_t(zb::ZdpCommand::SIMPLE_DESCRIPTOR_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) @@ -2999,7 +3000,7 @@ AwaitableCoroutine RadioInterface::handleZbCommission(uint64_t deviceLongAddress if (sendResult != 0) { // wait for a response from the device int r = co_await select(this->responseBarrier.wait(length, packet2, uint8_t(0), zdpCounter, - uint16_t(zb::ZdpCommand::BIND_RESPONSE)), Timer::sleep(timeout)); + uint16_t(zb::ZdpCommand::BIND_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) @@ -3141,7 +3142,7 @@ Coroutine RadioInterface::publish() { continue; // wait for first route reply or timeout (more route replies with lower cost may arrive later) - co_await select(device->routeBarrier.wait(), Timer::sleep(timeout)); + co_await select(device->routeBarrier.wait(), loop::sleep(timeout)); Terminal::out << "Router for " << hex(device->data.shortAddress) << ": " << hex(device->routerAddress) << '\n'; @@ -3199,7 +3200,7 @@ Coroutine RadioInterface::publish() { // wait for a response from the device int length; int r = co_await select(this->responseBarrier.wait(length, packet, endpoint->data->id, - zclCounter, uint16_t(zcl::Command::DEFAULT_RESPONSE)), Timer::sleep(timeout)); + zclCounter, uint16_t(zcl::Command::DEFAULT_RESPONSE)), loop::sleep(timeout)); // check if response was received if (r == 1) { diff --git a/software/control/src/RadioInterface.hpp b/software/control/src/RadioInterface.hpp index 3b80569..c5991f0 100644 --- a/software/control/src/RadioInterface.hpp +++ b/software/control/src/RadioInterface.hpp @@ -359,6 +359,12 @@ class RadioInterface : public Interface { #endif {} + PacketWriter(int N, uint8_t *packet) : EncryptWriter(packet + 1) +#ifdef DEBUG + , end(packet + N) +#endif + {} + void security(zb::SecurityControl securityControl, uint32_t securityCounter) { this->securityControl = this->current; u8(uint8_t(securityControl)); @@ -454,7 +460,7 @@ class RadioInterface : public Interface { zcl::Cluster cluster, Message &message, ZbEndpoint *endpoint); void writeFooter(PacketWriter &w, Radio::SendFlags sendFlags); - [[nodiscard]] AwaitableCoroutine readAttribute(uint8_t (&packet)[MESSAGE_LENGTH], ZbDevice &device, + [[nodiscard]] AwaitableCoroutine readAttribute(uint8_t *packet, ZbDevice &device, uint8_t dstEndpoint, zcl::Cluster clusterId, zcl::Profile profile, uint8_t srcEndpoint, uint16_t attribute); diff --git a/software/control/src/RoomControl.cpp b/software/control/src/RoomControl.cpp index 6f34236..9a3f264 100644 --- a/software/control/src/RoomControl.cpp +++ b/software/control/src/RoomControl.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include "tahoma_8pt.hpp" // font @@ -114,7 +114,7 @@ RoomControl::RoomControl(Drivers &drivers) , alarmInterface(ALARM_INTERFACE, drivers.storage) , functionInterface(FUNCTION_INTERFACE, drivers.storage) , interfaces{&localInterface, &busInterface, &radioInterface, &alarmInterface, &functionInterface} - , swapChain(drivers.display) + , swapChain({drivers.displayCommand, drivers.displayData}) { // load configuration int configurationSize = sizeof(Configuration); @@ -458,11 +458,11 @@ Coroutine RoomControl::displayMessageFilter() { continue; // wait for more messages and force resume from event loop in case the message originates from the idle menu - auto timeout = Timer::now() + 100ms; + auto timeout = loop::now() + 100ms; while (true) { ListenerInfo info2; Message message2; - int s = co_await select(barrier.wait(info2, &message2), Timer::sleep(timeout)); + int s = co_await select(barrier.wait(info2, &message2), loop::sleep(timeout)); if (s == 1) { if (this->displaySourcesList[info2.source.interfaceId].contains(info2.source.elementId, info2.source.plugIndex)) { info = info2; @@ -1194,7 +1194,7 @@ Coroutine RoomControl::idleMenu() { // wheel button pressed s = co_await select( Input::trigger(0, 1 << INPUT_WHEEL_BUTTON, index, value), - Timer::sleep(1s)); + loop::sleep(1s)); if (s == 1) { wheelPlugIndex = wheelPlugIndex + 1 >= this->configuration.wheelPlugCount ? 0 : wheelPlugIndex + 1; //Terminal::out << "next " << dec(wheelPlugIndex) << '\n'; @@ -1263,7 +1263,7 @@ AwaitableCoroutine RoomControl::devicesMenu(int interfaceIndex) { break; // show menu and wait for new event until timeout so that we can show newly commissioned devices - co_await select(menu.show(), Timer::sleep(250ms)); + co_await select(menu.show(), loop::sleep(250ms)); } interface.setCommissioning(false); } @@ -1820,7 +1820,7 @@ AwaitableCoroutine RoomControl::measureRunTime(uint8_t deviceId, uint8_t plugInd // start state = up ? 1 : 2; up = !up; - startTime = Timer::now(); + startTime = loop::now(); } else { // stop state = 0; @@ -1841,10 +1841,10 @@ AwaitableCoroutine RoomControl::measureRunTime(uint8_t deviceId, uint8_t plugInd co_await menu.show(); } else { // running: timeout so that the duration gets updated on display - co_await select(menu.show(), Timer::sleep(25ms)); + co_await select(menu.show(), loop::sleep(25ms)); if (state != 0) - duration = (Timer::now() - startTime) / 10ms; + duration = (loop::now() - startTime) / 10ms; } } } @@ -1919,7 +1919,7 @@ AwaitableCoroutine RoomControl::connectionsMenu(Array plugs, break; // show menu and wait for new event until timeout so that we can show endpoints of recommissioned device - co_await select(menu.show(), Timer::sleep(250ms)); + co_await select(menu.show(), loop::sleep(250ms)); } } @@ -2235,7 +2235,7 @@ AwaitableCoroutine RoomControl::plugsMenu(Interface &interface, uint8_t deviceId break; // show menu and wait for new event until timeout so that we can show endpoints of recommissioned device - co_await select(menu.show(), Timer::sleep(250ms)); + co_await select(menu.show(), loop::sleep(250ms)); } } @@ -2276,7 +2276,7 @@ AwaitableCoroutine RoomControl::messageLogger(Interface &interface, uint8_t devi Event &event = queue.getBack(); // show menu or receive event (event gets filled in) - int selected = co_await select(menu.show(), barrier.wait(event.info, &event.message), Timer::sleep(250ms)); + int selected = co_await select(menu.show(), barrier.wait(event.info, &event.message), loop::sleep(250ms)); if (selected == 2 && event.info.source.elementId == deviceId) { // received an event: add new empty event at the back of the queue event.type = interface.getPlugs(deviceId)[event.info.source.plugIndex]; diff --git a/software/control/src/RoomControl.hpp b/software/control/src/RoomControl.hpp index 70f6d42..36bcd0d 100644 --- a/software/control/src/RoomControl.hpp +++ b/software/control/src/RoomControl.hpp @@ -14,6 +14,8 @@ #include #include #include +#undef DELETE +#undef IGNORE /** diff --git a/software/control/src/SSD1309.cpp b/software/control/src/SSD1309.cpp index b8554ed..5db66be 100644 --- a/software/control/src/SSD1309.cpp +++ b/software/control/src/SSD1309.cpp @@ -7,83 +7,83 @@ AwaitableCoroutine SSD1309::init() { // command unlock command[0] = 0xFD; command[1] = 0x12; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set display off command[0] = 0xAE; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); // set display clock divide ratio / oscillator frequency command[0] = 0xD5; command[1] = 0xA0; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set multiplex ratio command[0] = 0xA8; command[1] = 0x3F; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set display offset command[0] = 0xD3; command[1] = 0x00; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set display start line command[0] = 0x40; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); // set segment remap command[0] = 0xA1; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); // set com output scan direction command[0] = 0xC8; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); // set com pins hardware configuration command[0] = 0xDA; command[1] = 0x12; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set current control command[0] = 0x81; command[1] = 0xDF; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set pre-charge period command[0] = 0xD9; command[1] = 0x82; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set vcomh deselect level command[0] = 0xDB; command[1] = 0x34; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); // set entire display to normal command[0] = 0xA4; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); // set inverse display to normal command[0] = 0xA6; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); } AwaitableCoroutine SSD1309::enable() { //display::enableVcc(true); uint8_t command[1] = {0xAF}; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); this->enabled = true; } AwaitableCoroutine SSD1309::disable() { uint8_t command[1] = {0xAE}; - co_await this->spi.writeCommand(1, command); + co_await this->spi.command.write(1, command); this->enabled = false; //display::enableVcc(false); } AwaitableCoroutine SSD1309::setContrast(uint8_t contrast) { uint8_t command[2] = {0x81, contrast}; - co_await this->spi.writeCommand(2, command); + co_await this->spi.command.write(2, command); } diff --git a/software/control/src/SSD1309.hpp b/software/control/src/SSD1309.hpp index 938248b..7952585 100644 --- a/software/control/src/SSD1309.hpp +++ b/software/control/src/SSD1309.hpp @@ -12,7 +12,12 @@ */ class SSD1309 { public: - explicit SSD1309(SpiMaster &spi) : spi(spi) {} + struct Spi { + SpiMaster &command; + SpiMaster &data; + }; + + explicit SSD1309(Spi &spi) : spi(spi) {} /** * Initializate the display @@ -49,10 +54,10 @@ class SSD1309 { * @return use co_await on return value to await end of operation */ auto set(Bitmap const &bitmap) { - return this->spi.writeData(array::count(bitmap.data), bitmap.data); + return this->spi.data.write(array::count(bitmap.data), bitmap.data); } protected: - SpiMaster &spi; + Spi spi; bool enabled = false; }; diff --git a/software/control/src/SwapChain.hpp b/software/control/src/SwapChain.hpp index 4a53e9f..5f3c3e5 100644 --- a/software/control/src/SwapChain.hpp +++ b/software/control/src/SwapChain.hpp @@ -12,7 +12,7 @@ class SwapChain { /** * Constructor starts a coroutine that transfers the bitmaps to the display */ - SwapChain(SpiMaster &spi) : freeList{&bitmaps[0], &bitmaps[1]}, display(spi) { + SwapChain(SSD1309::Spi spi) : freeList{&bitmaps[0], &bitmaps[1]}, display(spi) { // start transfer coroutine transfer(); } diff --git a/software/control/src/main.cpp b/software/control/src/main.cpp index 22d2d73..e6b6103 100644 --- a/software/control/src/main.cpp +++ b/software/control/src/main.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -14,8 +13,7 @@ */ int main(int argc, const char **argv) { // init drivers - Loop::init(); - Timer::init(); + loop::init(); Calendar::init(); Radio::init(); Network::init(); @@ -47,7 +45,7 @@ int main(int argc, const char **argv) { // the room control application RoomControl roomControl(drivers); - Loop::run(); + loop::run(); return 0; } diff --git a/software/control/test/BME680Test.cpp b/software/control/test/BME680Test.cpp index 202491a..2523c54 100644 --- a/software/control/test/BME680Test.cpp +++ b/software/control/test/BME680Test.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -31,7 +30,7 @@ struct UsbConfiguration { struct usb::ConfigDescriptor config; struct usb::InterfaceDescriptor interface; struct usb::EndpointDescriptor endpoints[1]; -} __attribute__((packed)); +}; static const UsbConfiguration configurationDescriptor = { .config = { @@ -67,14 +66,14 @@ static const UsbConfiguration configurationDescriptor = { uint8_t timerId; -StringBuffer<128> string __attribute__((aligned(4))); +StringBuffer<128> string;// __attribute__((aligned(4))); #define READ(reg) ((reg) | 0x80) #define WRITE(reg) ((reg) & 0x7f) constexpr int CHIP_ID = 0x61; -uint8_t buffer[129] __attribute__((aligned(4))); +uint8_t buffer[129];// __attribute__((aligned(4))); // get chip id and output result on debug led's Coroutine getId(SpiMaster &spi) { @@ -85,33 +84,33 @@ Coroutine getId(SpiMaster &spi) { // check chip id if (buffer[1] == CHIP_ID) - Debug::toggleGreenLed(); + debug::toggleGreen(); else - Debug::toggleRedLed(); + debug::toggleRed(); // wait for 1s - co_await Timer::sleep(1s); + co_await loop::sleep(1s); } } // read all registers and send to usb host -Coroutine getRegisters(SpiMaster &spi) { +Coroutine getRegisters(SpiMaster &spi, UsbDevice &usb) { while (true) { - // read upper page + // read upper page from sensor buffer[0] = READ(0); co_await spi.transfer(1, buffer, 129, buffer); // send to usb host - co_await UsbDevice::send(1, 128, buffer + 1); - Debug::toggleBlueLed(); + co_await usb.send(1, 128, buffer + 1); + debug::toggleBlue(); // wait for 5s - co_await Timer::sleep(5s); + co_await loop::sleep(5s); } } // measure and send values to usb host -Coroutine measure(SpiMaster &spi) { +Coroutine measure(SpiMaster &spi, UsbDevice &usb) { BME680 sensor(spi); co_await sensor.init(); @@ -119,7 +118,7 @@ Coroutine measure(SpiMaster &spi) { 2, 5, 2, // temperature and pressure oversampling and filter 1, // humidity oversampling 300, 100); // heater temperature (celsius) and duration (ms) - Debug::setBlueLed(true); + debug::setBlue(true); while (true) { co_await sensor.measure(); @@ -129,19 +128,17 @@ Coroutine measure(SpiMaster &spi) { + "Humidity: " + flt(sensor.getHumidity(), 1, 1) + "%\n" + "Gas: " + flt(sensor.getGasResistance(), 1, 1) + "Ω\n"; - co_await UsbDevice::send(1, string.count(), string.data()); - Debug::toggleRedLed(); + co_await usb.send(1, string.count(), string.data()); + debug::toggleRed(); - co_await Timer::sleep(10s); + co_await loop::sleep(10s); } } - int main(void) { - Loop::init(); - Timer::init(); + loop::init(); Output::init(); - UsbDevice::init( + UsbDeviceImpl usb( [](usb::DescriptorType descriptorType) { switch (descriptorType) { case usb::DescriptorType::DEVICE: @@ -152,9 +149,9 @@ int main(void) { return ConstData(); } }, - [](uint8_t bConfigurationValue) { + [](UsbDevice &usb, uint8_t bConfigurationValue) { // enable bulk endpoints in 1 (keep control endpoint 0 enabled in both directions) - UsbDevice::enableEndpoints(1 | (1 << 1), 1); + usb.enableEndpoints(1 | (1 << 1), 1); //getRegisters(); }, @@ -164,9 +161,9 @@ int main(void) { Drivers drivers; // test raw values - //getId(drivers.airSensor); + //getId(drivers.airSensor, usb); - measure(drivers.airSensor); - - Loop::run(); + measure(drivers.airSensor, usb); + + loop::run(); } diff --git a/software/control/test/SSD1309Test.cpp b/software/control/test/SSD1309Test.cpp index 714a5d7..cd202a4 100644 --- a/software/control/test/SSD1309Test.cpp +++ b/software/control/test/SSD1309Test.cpp @@ -1,12 +1,11 @@ #include -#include #include #include #include #include -Coroutine draw(SpiMaster &spi) { +Coroutine draw(SSD1309::Spi spi) { SSD1309 display(spi); co_await display.init(); co_await display.enable(); @@ -21,20 +20,19 @@ Coroutine draw(SpiMaster &spi) { y = (y + 1) & (DISPLAY_HEIGHT - 1); co_await display.set(bitmap); - co_await Timer::sleep(200ms); + co_await loop::sleep(200ms); - Debug::toggleRedLed(); + debug::toggleRed(); } } int main(void) { - Loop::init(); - Timer::init(); + loop::init(); Output::init(); Drivers drivers; - draw(drivers.display); - - Loop::run(); + draw({drivers.displayCommand, drivers.displayData}); + + loop::run(); } diff --git a/software/node/src/MqttSnBroker.cpp b/software/node/src/MqttSnBroker.cpp index 0473722..a615373 100644 --- a/software/node/src/MqttSnBroker.cpp +++ b/software/node/src/MqttSnBroker.cpp @@ -1,5 +1,5 @@ #include "MqttSnBroker.hpp" -#include +#include #include #include @@ -79,7 +79,7 @@ AwaitableCoroutine MqttSnBroker::connect(Network::Endpoint const &gatewayEndpoin { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(0), mqttsn::MessageType::CONNACK, - uint16_t(0), length, message), Timer::sleep(RECONNECT_TIME)); + uint16_t(0), length, message), loop::sleep(RECONNECT_TIME)); // check if we received a message if (s == 1) { @@ -114,7 +114,7 @@ AwaitableCoroutine MqttSnBroker::keepAlive() { // ping gateway as long as we are connected while (isGatewayConnected()) { - int s = co_await select(Timer::sleep(KEEP_ALIVE_TIME), this->keepAliveEvent.wait()); + int s = co_await select(loop::sleep(KEEP_ALIVE_TIME), this->keepAliveEvent.wait()); if (s == 1) { if (isGatewayConnected()) { @@ -131,7 +131,7 @@ AwaitableCoroutine MqttSnBroker::keepAlive() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(0), mqttsn::MessageType::PINGRESP, - uint16_t(0), length, message), Timer::sleep(RETRANSMISSION_TIME)); + uint16_t(0), length, message), loop::sleep(RETRANSMISSION_TIME)); if (s == 1) break; } @@ -176,7 +176,7 @@ AwaitableCoroutine MqttSnBroker::keepAlive() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(0), mqttsn::MessageType::SUBACK, msgId, length, - message), Timer::sleep(RETRANSMISSION_TIME)); + message), loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isGatewayConnected()) @@ -222,7 +222,7 @@ AwaitableCoroutine MqttSnBroker::keepAlive() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(0), mqttsn::MessageType::UNSUBACK, msgId, length, - message), Timer::sleep(RETRANSMISSION_TIME)); + message), loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isGatewayConnected()) @@ -259,7 +259,7 @@ AwaitableCoroutine MqttSnBroker::keepAlive() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(0), mqttsn::MessageType::REGACK, msgId, length, - message), Timer::sleep(RETRANSMISSION_TIME)); + message), loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isGatewayConnected()) @@ -570,7 +570,7 @@ Coroutine MqttSnBroker::publish() { int length = array::count(messageData); int s = co_await select(this->ackWaitlist.wait(uint8_t(connectionIndex), mqttsn::MessageType::PUBACK, msgId, length, messageData), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected(connectionIndex)) @@ -785,7 +785,7 @@ Coroutine MqttSnBroker::receive() { auto keepAliveDuration = r.u16B(); auto clientId = r.string(); #ifdef DEBUG_PRINT - Terminal::out << (clientId + " connects to " + thisName + '\n'); + Terminal::out << clientId << " connects to " << thisName << '\n'; #endif if (connectionIndex == -1) { // try to find an unused connection @@ -835,7 +835,7 @@ Coroutine MqttSnBroker::receive() { auto msgId = r.u16B(); auto topicName = r.string(); #ifdef DEBUG_PRINT - Terminal::out << (this->connections[connectionIndex].name + " registers " + topicName + " at " + thisName + '\n'); + Terminal::out << this->connections[connectionIndex].name << " registers " << topicName << " at " << thisName << '\n'; #endif // register the topic auto returnCode = mqttsn::ReturnCode::ACCEPTED; @@ -875,7 +875,7 @@ Coroutine MqttSnBroker::receive() { auto msgId = r.u16B(); auto topicName = r.string(); #ifdef DEBUG_PRINT - Terminal::out << (this->connections[connectionIndex].name + " subscribes " + topicName + " at " + thisName + '\n'); + Terminal::out << this->connections[connectionIndex].name << " subscribes " << topicName << " at " << thisName << '\n'; #endif // register the topic int topicIndex = -1; @@ -918,7 +918,7 @@ Coroutine MqttSnBroker::receive() { auto msgId = r.u16B(); auto topicName = r.string(); #ifdef DEBUG_PRINT - Terminal::out << (this->connections[connectionIndex].name + " subscribes " + topicName + " from " + thisName + '\n'); + Terminal::out << this->connections[connectionIndex].name << " subscribes " << topicName << " from " << thisName << '\n'; #endif // register the topic if (connectionIndex == 0) { @@ -1008,12 +1008,12 @@ Coroutine MqttSnBroker::receive() { TopicInfo &topic = this->topics[topicIndex]; #ifdef DEBUG_PRINT - Terminal::out << (thisName + " receives " + dec(pubLength) + " bytes from "); + Terminal::out << thisName << " receives " << dec(pubLength) << " bytes from "; if (connectionIndex == 0) - Terminal::out << ("gateway"); + Terminal::out << "gateway"; else - Terminal::out << (connection.name); - Terminal::out << (" on topic '" + this->topics.get(topicIndex)->key + "' msgid " + dec(msgId) + '\n'); + Terminal::out << connection.name; + Terminal::out << " on topic '" << this->topics.get(topicIndex)->key << "' msgid " << dec(msgId) << '\n'; #endif // check if message is duplicate @@ -1104,12 +1104,12 @@ Coroutine MqttSnBroker::receive() { } #ifdef DEBUG_PRINT - Terminal::out << (thisName + " received " + typeString + " from "); + Terminal::out << thisName << " received " << typeString << " from "; if (connectionIndex == 0) - Terminal::out << ("gateway"); + Terminal::out << "gateway"; else - Terminal::out << (this->connections[connectionIndex].name); - Terminal::out << (" msgid " + dec(msgId) + '\n'); + Terminal::out << this->connections[connectionIndex].name; + Terminal::out << " msgid " << dec(msgId) << '\n'; #endif // check if we read past the end of the message @@ -1130,6 +1130,7 @@ Coroutine MqttSnBroker::receive() { }); } } + co_return; } Coroutine MqttSnBroker::forward() { @@ -1172,12 +1173,12 @@ Coroutine MqttSnBroker::forward() { uint16_t msgId = qos <= 0 ? 0 : getNextMsgId(); #ifdef DEBUG_PRINT - Terminal::out << (thisName + " forwards " + dec(length) + " bytes to "); + Terminal::out << thisName << " forwards " << dec(length) << " bytes to "; if (connectionIndex == 0) - Terminal::out << ("gateway"); + Terminal::out << "gateway"; else - Terminal::out << (connection.name); - Terminal::out << (" on topic '" + this->topics.get(topicIndex)->key + "' msgid " + dec(msgId) + '\n'); + Terminal::out << connection.name; + Terminal::out << " on topic '" << this->topics.get(topicIndex)->key << "' msgid " << dec(msgId) << '\n'; #endif // message flags @@ -1205,7 +1206,7 @@ Coroutine MqttSnBroker::forward() { int length2 = array::count(message); int s = co_await select(this->ackWaitlist.wait(uint8_t(connectionIndex), mqttsn::MessageType::PUBACK, msgId, length2, message2), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected(connectionIndex)) diff --git a/software/node/src/MqttSnClient.cpp b/software/node/src/MqttSnClient.cpp index 19c915f..2fe1dd2 100644 --- a/software/node/src/MqttSnClient.cpp +++ b/software/node/src/MqttSnClient.cpp @@ -1,5 +1,5 @@ #include "MqttSnClient.hpp" -#include +#include #include @@ -65,7 +65,7 @@ AwaitableCoroutine MqttSnClient::connect(Result &result, Network::Endpoint const Network::Endpoint source; int length = MAX_MESSAGE_LENGTH; int s = co_await select(Network::receive(NETWORK_MQTT, source, length, this->tempMessage), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); if (s == 1) { // check if the message is from the gateway // todo @@ -126,7 +126,7 @@ AwaitableCoroutine MqttSnClient::disconnect() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::DISCONNECT, uint16_t(0), length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); if (s == 1) break; } @@ -164,12 +164,12 @@ AwaitableCoroutine MqttSnClient::registerTopic(Result &result, uint16_t &topicId { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::REGACK, msgId, length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected()) { // make sure we are resumed from the event loop, not from ping() - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); result = Result::INVALID_STATE; co_return; } @@ -230,12 +230,12 @@ AwaitableCoroutine MqttSnClient::publish(Result &result, uint16_t topicId, mqtts { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::PUBACK, msgId, length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected()) { // make sure we are resumed from the event loop, not from ping() - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); result = Result::INVALID_STATE; break; } @@ -295,12 +295,12 @@ AwaitableCoroutine MqttSnClient::subscribeTopic(Result &result, uint16_t &topicI { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::SUBACK, msgId, length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected()) { // make sure we are resumed from the event loop, not from ping() - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); result = Result::INVALID_STATE; co_return; } @@ -354,12 +354,12 @@ AwaitableCoroutine MqttSnClient::unsubscribeTopic(Result &result, String topicFi { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::UNSUBACK, msgId, length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); // check if still connected if (!isConnected()) { // make sure we are resumed from the event loop, not from ping() - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); result = Result::INVALID_STATE; co_return; } @@ -390,7 +390,7 @@ AwaitableCoroutine MqttSnClient::receive(Result &result, uint16_t &msgId, uint16 // check if still connected if (!isConnected()) { // make sure we are resumed from the event loop, not from ping() - co_await Timer::sleep(100ms); + co_await loop::sleep(100ms); result = Result::INVALID_STATE; co_return; } @@ -440,7 +440,7 @@ AwaitableCoroutine MqttSnClient::ping() { uint8_t message[4]; while (true) { - co_await Timer::sleep(KEEP_ALIVE_TIME); + co_await loop::sleep(KEEP_ALIVE_TIME); for (int retry = 0; ; ++retry) { // send idle ping @@ -454,7 +454,7 @@ AwaitableCoroutine MqttSnClient::ping() { { int length = array::count(message); int s = co_await select(this->ackWaitlist.wait(mqttsn::MessageType::PINGRESP, uint16_t(0), length, message), - Timer::sleep(RETRANSMISSION_TIME)); + loop::sleep(RETRANSMISSION_TIME)); if (s == 1) break; } diff --git a/software/node/src/MqttSnClient.hpp b/software/node/src/MqttSnClient.hpp index b7e5ef0..597668f 100644 --- a/software/node/src/MqttSnClient.hpp +++ b/software/node/src/MqttSnClient.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/software/node/test/MqttSnBrokerTest.cpp b/software/node/test/MqttSnBrokerTest.cpp index 5fa3348..d4deb77 100644 --- a/software/node/test/MqttSnBrokerTest.cpp +++ b/software/node/test/MqttSnBrokerTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -95,8 +94,7 @@ int main(void) { inTopic += "in"; outTopic += "out"; - Loop::init(); - Timer::init(); + loop::init(); Network::init(); Output::init(); @@ -104,5 +102,5 @@ int main(void) { test.connect(gatewayPort, name); test.function(); - Loop::run(); + loop::run(); } diff --git a/software/node/test/MqttSnClientTest.cpp b/software/node/test/MqttSnClientTest.cpp index 65c820b..d1fd3be 100644 --- a/software/node/test/MqttSnClientTest.cpp +++ b/software/node/test/MqttSnClientTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -59,7 +58,7 @@ Coroutine publish() { Terminal::out << "registered topic 'bar' " << dec(bar) << "\n"; while (client.isConnected()) { - co_await Timer::sleep(1s); + co_await loop::sleep(1s); // publish on topics co_await client.publish(result, foo, mqttsn::makeQos(defaultQos), "a"); @@ -73,12 +72,11 @@ Coroutine publish() { } int main(void) { - Loop::init(); - Timer::init(); + loop::init(); Network::init(); Output::init(); publish(); - Loop::run(); + loop::run(); } diff --git a/software/protocol/src/crc.cpp b/software/protocol/src/crc.cpp index b8e23e2..c24f5b7 100644 --- a/software/protocol/src/crc.cpp +++ b/software/protocol/src/crc.cpp @@ -1,5 +1,34 @@ #include "crc.hpp" + +static const uint8_t crc8Table[256] = { + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, + 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, + 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, + 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, + 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, + 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, + 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, + 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, + 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, + 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, + 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, + 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, + 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, + 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, + 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, + 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3, +}; + +uint8_t crc8(int size, const void *data, uint16_t crc) { + auto *d = reinterpret_cast(data); + for (int i = 0; i < size; ++i) { + crc = crc8Table[crc ^ d[i]]; + } + return crc; +} + + // see https://www.mikrocontroller.net/attachment/91385/crc16.c // generated in protocolTest.cpp @@ -25,7 +54,7 @@ const uint16_t crc16Table[] { uint16_t crc16(int size, const void *data, uint16_t crc) { auto *d = reinterpret_cast(data); for (int i = 0; i < size; ++i) { - crc = crc16Table[(crc >> 8) ^ *d++] ^ (crc << 8); + crc = crc16Table[(crc >> 8) ^ d[i]] ^ (crc << 8); } return crc; } diff --git a/software/protocol/src/crc.hpp b/software/protocol/src/crc.hpp index 97d12ac..5bf31e5 100644 --- a/software/protocol/src/crc.hpp +++ b/software/protocol/src/crc.hpp @@ -3,6 +3,15 @@ #include +/** + * Calculate CRC-8, polynom 0x07, see https://crccalc.com/ + * @param size size of data + * @param data data + * @param crc chained CRC, can be used to calculate crc in multiple sections + * @return resulting CRC + */ +uint8_t crc8(int size, const void *data, uint16_t crc = 0); + /** * Calculate CRC-16/CCITT-FALSE, polynom 0x1021, see https://crccalc.com/ * @param size size of data diff --git a/software/protocol/src/usb.hpp b/software/protocol/src/usb.hpp index 51b53b0..3b4ae10 100644 --- a/software/protocol/src/usb.hpp +++ b/software/protocol/src/usb.hpp @@ -3,6 +3,12 @@ #include #include +#ifdef _MSC_VER +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#else +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + namespace usb { @@ -52,12 +58,13 @@ enum class Request : uint8_t { STANDARD_DEVICE_IN = TYPE_STANDARD | RECIPIENT_DEVICE | IN, STANDARD_INTERFACE_OUT = TYPE_STANDARD | RECIPIENT_INTERFACE | OUT, STANDARD_INTERFACE_IN = TYPE_STANDARD | RECIPIENT_INTERFACE | IN, + VENDOR_DEVICE_OUT = TYPE_VENDOR | RECIPIENT_DEVICE | OUT, VENDOR_INTERFACE_OUT = TYPE_VENDOR | RECIPIENT_INTERFACE | OUT, }; FLAGS_ENUM(Request) // device descriptor -struct DeviceDescriptor { +PACK(struct DeviceDescriptor { uint8_t bLength; DescriptorType bDescriptorType; uint16_t bcdUSB; @@ -72,10 +79,10 @@ struct DeviceDescriptor { uint8_t iProduct; uint8_t iSerialNumber; uint8_t bNumConfigurations; -} __attribute__((packed)); +}); // configuration descriptor -struct ConfigDescriptor { +PACK(struct ConfigDescriptor { uint8_t bLength; DescriptorType bDescriptorType; uint16_t wTotalLength; @@ -84,10 +91,10 @@ struct ConfigDescriptor { uint8_t iConfiguration; uint8_t bmAttributes; uint8_t bMaxPower; -} __attribute__((packed)); +}); // interface descriptor -struct InterfaceDescriptor { +PACK(struct InterfaceDescriptor { uint8_t bLength; DescriptorType bDescriptorType; uint8_t bInterfaceNumber; @@ -97,16 +104,16 @@ struct InterfaceDescriptor { uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t iInterface; -} __attribute__((packed)); +}); // endpoint descriptor -struct EndpointDescriptor { +PACK(struct EndpointDescriptor { uint8_t bLength; DescriptorType bDescriptorType; uint8_t bEndpointAddress; EndpointType bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; -} __attribute__((packed)); +}); } // namespace usb diff --git a/software/protocol/src/zcl.hpp b/software/protocol/src/zcl.hpp index 8e3126d..c5779c7 100644 --- a/software/protocol/src/zcl.hpp +++ b/software/protocol/src/zcl.hpp @@ -2,6 +2,7 @@ #include #include +#undef READ_ATTRIBUTES /** diff --git a/software/protocol/test/protocolTest.cpp b/software/protocol/test/protocolTest.cpp index 95428c5..d9d192c 100644 --- a/software/protocol/test/protocolTest.cpp +++ b/software/protocol/test/protocolTest.cpp @@ -18,11 +18,19 @@ TEST(protocolTest, DataBuffer) { b.setData(0, b); } +TEST(protocolTest, crc8) { + // calc crc-8 + uint16_t crc = crc8(12, "Hello World!"); + + // expected result from CRC-8 on https://crccalc.com/ + EXPECT_EQ(crc, 0x1c); +} + TEST(protocolTest, crc16) { - // calc crc with CRC-16/CCITT-FALSE + // calc crc-16 uint16_t crc = crc16(12, "Hello World!"); - // expected result from https://crccalc.com/ + // expected result from CRC-16/CCITT-FALSE on https://crccalc.com/ EXPECT_EQ(crc, 0x882a); } @@ -201,15 +209,39 @@ void generateZa09Keys() { printKey("za09KeyLoadAesKey", aesKey); } + +// generate lookup table for crc8 +void generateCrc8Table(uint8_t polynomial) { + uint8_t crc_table[256]; + for (int b = 0; b <= 255; ++b) { + uint8_t v; + int i; + for (v = b, i = 8; --i >= 0;) { + if ((v & 0x80) != 0) + v = (v << 1) ^ polynomial; + else + v = v << 1; + } + crc_table[b] = v; + } + + for (int j = 0; j < 16; ++j) { + for (int i = 0; i < 16; ++i) { + std::cout << "0x" << std::hex << std::setfill('0') << std::setw(2) << int(crc_table[j * 16 + i]) << ", "; + } + std::cout << std::endl; + } +} + // generate lookup table for crc16 void generateCrc16Table(uint16_t polynomial) { - uint16_t crc_table[256]; + uint16_t crc_table[256]; for (int b = 0; b <= 255; ++b) { uint16_t v; int i; for (v = b << 8, i = 8; --i >= 0;) { - if ((v & 0x8000) != 0x0000) - v = ( v << 1 ) ^ polynomial; + if ((v & 0x8000) != 0) + v = (v << 1) ^ polynomial; else v = v << 1; } @@ -227,6 +259,7 @@ void generateCrc16Table(uint16_t polynomial) { int main(int argc, char **argv) { //generateBusKey(); //generateZa09Keys(); + //generateCrc8Table(0x07); //generateCrc16Table(0x1021); testing::InitGoogleTest(&argc, argv); diff --git a/software/switch/src/main.cpp b/software/switch/src/main.cpp index 62cb75c..2cf25d3 100644 --- a/software/switch/src/main.cpp +++ b/software/switch/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -102,7 +101,7 @@ class Switch { //Terminal::out << "trigger " << dec(index) << ' ' << dec(int(state)) << '\n'; // configure when at least one button was held for 3s after all were pressed - if (this->configure && Timer::now() > this->allTime + 3s) { + if (this->configure && loop::now() > this->allTime + 3s) { // use button state as configuration int a = this->buttons & 3; if (a != 0) @@ -123,7 +122,7 @@ class Switch { // check if all buttons are pressed if (this->buttons == 0x0f) { - this->allTime = Timer::now(); + this->allTime = loop::now(); this->commission = true; this->configure = true; //Terminal::out << "all\n"; @@ -527,18 +526,21 @@ class Switch { //Terminal::out << "ab " << dec(ax) << dec(ay) << dec(az) << " " << dec(bx) << dec(by) << dec(bz) << "\n"; // build 16 bit word for relay driver - uint16_t word = 0x8000 // enable + uint16_t w = 0x8000 // enable | (ax << (MPQ6526_MAPPING[0] * 2 + 1)) | (ay << (MPQ6526_MAPPING[1] * 2 + 1)) | (az << (MPQ6526_MAPPING[2] * 2 + 1)) | (bx << (MPQ6526_MAPPING[3] * 2 + 1)) | (by << (MPQ6526_MAPPING[4] * 2 + 1)) | (bz << (MPQ6526_MAPPING[5] * 2 + 1)); - //Terminal::out << "spi " << hex(word) << "\n"; - co_await this->drivers.relayDriver.transfer(1, &word, 0, nullptr); + //Terminal::out << "spi " << hex(w) << "\n"; + + // transfer + uint16_t r; + co_await this->drivers.relayDriver.transfer(2, &w, 2, &r); // wait some time until relay contacts react - co_await Timer::sleep(RELAY_TIME); + co_await loop::sleep(RELAY_TIME); } // switch off all half bridges @@ -594,12 +596,11 @@ class Switch { int main() { - Loop::init(); + loop::init(); Output::init(); // for debug signals on pins Input::init(); - Timer::init(); Switch s; - Loop::run(); + loop::run(); } diff --git a/software/system/src/BusMaster.hpp b/software/system/src/BusMaster.hpp index a9ad37a..4655ff6 100644 --- a/software/system/src/BusMaster.hpp +++ b/software/system/src/BusMaster.hpp @@ -15,41 +15,49 @@ Protocol: The master always starts by sending a BREAK which are 13 zero bits and a stop bit followed by SYNC which is 0x55. Then a command or message follows which is sent by the master or a node. A message is acknowledged by the receiver. + If a node wants to send a command or a message, it sends a single bit (pulls the bus low for one bit time) to wake + up the master which then starts by sending a BREAK. BREAK SYNC | BREAK SYNC | | - Command: + BREAK: + 13 zero bits followed by one stop bit + + SYNC: + 0x55 + + : A device can be set to commissioning mode e.g. by pressing a button or button combination for a longer time or by power cycle, depending on security requirements. All devices in commissioning mode will send an enumerate command, simultaneous sending is resolved by bus arbitration. Commissioning command by master overrides all enumerate commands (by sending a leading zero) and causes the commissioned device to leave commissioning mode. - Enumerate: 0 - Commission: 0 0
+ Enumerate: 0 + Commission: 0 0
- Message: + : The message starts with the encoded device address for bus arbitration (always loses against commands as it never starts with zero) and contains either an attribute read request, attribute data or a plug message - attribute read: <255> - attribute data: <255> - plug message: - - Ack: - CRC-8 sent in response as acknowledgement + Attribute Read: <255> + Attribute Data: <255> + Plug Message: + (not implemented yet): + CRC-8 of Command or Message (not including sync) sent in response as acknowledgement - : + : Each byte encodes 3 bit of the device id as number of bits from 1 to 8, starting with lowest bits, 11 bytes in total -
: +
: One byte unique address that gets assigned to a device during commissioning - : - Two bytes, first is ((address & 7) + 1), second is (address / 8) encoded as number of bits from 0 to 8 + : + Two bytes, first is ((address & 7) + 1) and therefore never 0 to to be different from commands, + second is (address / 8) encoded as number of bits from 0 to 8 - : + : Four bytes security counter - + Message integrity code using the default key or a configured network key */ class BusMaster { @@ -61,7 +69,7 @@ class BusMaster { struct SendParameters { int length; - uint8_t const *data; + const uint8_t *data; }; diff --git a/software/system/src/BusNode.hpp b/software/system/src/BusNode.hpp index a3652cf..997f038 100644 --- a/software/system/src/BusNode.hpp +++ b/software/system/src/BusNode.hpp @@ -11,17 +11,17 @@ class BusNode { public: struct ReceiveParameters { - int *receiveLength; - uint8_t *receiveData; + int *length; + uint8_t *data; }; struct SendParameters { - int sendLength; - uint8_t const *sendData; + int length; + const uint8_t *data; }; - ~BusNode(); + virtual ~BusNode(); /** * Receive data from bus master diff --git a/software/system/src/Debug.hpp b/software/system/src/Debug.hpp index 11129d3..1924444 100644 --- a/software/system/src/Debug.hpp +++ b/software/system/src/Debug.hpp @@ -5,27 +5,27 @@ #include -namespace Debug { - -inline void setRedLed() {Output::set(OUTPUT_DEBUG_RED);} -inline void clearRedLed() {Output::clear(OUTPUT_DEBUG_RED);} -inline void setRedLed(bool value) {Output::set(OUTPUT_DEBUG_RED, value);} -inline void toggleRedLed() {Output::toggle(OUTPUT_DEBUG_RED);} - -inline void setGreenLed() {Output::set(OUTPUT_DEBUG_GREEN);} -inline void clearGreenLed() {Output::clear(OUTPUT_DEBUG_GREEN);} -inline void setGreenLed(bool value) {Output::set(OUTPUT_DEBUG_GREEN, value);} -inline void toggleGreenLed() {Output::toggle(OUTPUT_DEBUG_GREEN);} - -inline void setBlueLed() {Output::set(OUTPUT_DEBUG_BLUE);} -inline void clearBlueLed() {Output::clear(OUTPUT_DEBUG_BLUE);} -inline void setBlueLed(bool value) {Output::set(OUTPUT_DEBUG_BLUE, value);} -inline void toggleBlueLed() {Output::toggle(OUTPUT_DEBUG_BLUE);} - -inline void setLeds(int state) { - Debug::setRedLed(state & 1); - Debug::setGreenLed(state & 2); - Debug::setBlueLed(state & 4); +namespace debug { + +inline void setRed() {Output::set(OUTPUT_DEBUG_RED);} +inline void clearRed() {Output::clear(OUTPUT_DEBUG_RED);} +inline void setRed(bool value) {Output::set(OUTPUT_DEBUG_RED, value);} +inline void toggleRed() {Output::toggle(OUTPUT_DEBUG_RED);} + +inline void setGreen() {Output::set(OUTPUT_DEBUG_GREEN);} +inline void clearGreen() {Output::clear(OUTPUT_DEBUG_GREEN);} +inline void setGreen(bool value) {Output::set(OUTPUT_DEBUG_GREEN, value);} +inline void toggleGreen() {Output::toggle(OUTPUT_DEBUG_GREEN);} + +inline void setBlue() {Output::set(OUTPUT_DEBUG_BLUE);} +inline void clearBlue() {Output::clear(OUTPUT_DEBUG_BLUE);} +inline void setBlue(bool value) {Output::set(OUTPUT_DEBUG_BLUE, value);} +inline void toggleBlue() {Output::toggle(OUTPUT_DEBUG_BLUE);} + +inline void set(int state) { + setRed(state & 1); + setGreen(state & 2); + setBlue(state & 4); } enum Color { @@ -42,11 +42,11 @@ enum Color { WHITE = 7, }; -inline void setColor(Color color) { +inline void set(Color color) { int c = int(color); - Debug::setRedLed(c & 1); - Debug::setGreenLed(c & 2); - Debug::setBlueLed(c & 4); + setRed(c & 1); + setGreen(c & 2); + setBlue(c & 4); } class Counter { @@ -54,17 +54,17 @@ class Counter { Counter &operator ++() { ++this->c; - setLeds(this->c); + set(this->c); return *this; } Counter &operator --() { --this->c; - setLeds(this->c); + set(this->c); return *this; } int c = 0; }; -} // namespace Debug +} // namespace debug diff --git a/software/system/src/FlashStorage.cpp b/software/system/src/FlashStorage.cpp index 9691ce1..d9c6509 100644 --- a/software/system/src/FlashStorage.cpp +++ b/software/system/src/FlashStorage.cpp @@ -1,4 +1,5 @@ #include "FlashStorage.hpp" +#include #include #include #include @@ -72,7 +73,7 @@ FlashStorage::FlashStorage(Flash &flash) switch (foundState) { case SectorState::EMPTY: // this happens if the flash is empty, make sure the sector is really empty - flash.eraseSectorBlocking(head); + //flash.eraseSectorBlocking(head); this->sectorIndex = head; this->sector = this->sectorIndex * this->info.sectorSize; @@ -103,7 +104,9 @@ FlashStorage::FlashStorage(Flash &flash) this->dataWriteOffset = this->info.sectorSize; // garbage collect tail sector + //Debug::setGreenLed(); gc(next); + //Debug::clearGreenLed(); break; } @@ -235,17 +238,17 @@ FlashStorage::SectorState FlashStorage::detectSectorState(int sectorIndex) { flash.readBlocking(sector, sizeof(entry), &entry); if (entry.isEmpty()) { - // section is empty or open: read first entry + // sector is empty or open: read first entry flash.readBlocking(sector + this->entrySize, sizeof(entry), &entry); if (entry.isEmpty()) { - // section is empty + // sector is empty return SectorState::EMPTY; } else { - // section is open + // sector is open return SectorState::OPEN; } } else { - // section is closed + // sector is closed return SectorState::CLOSED; } } diff --git a/software/system/src/Loop.hpp b/software/system/src/Loop.hpp index 228023f..6fd2506 100644 --- a/software/system/src/Loop.hpp +++ b/software/system/src/Loop.hpp @@ -1,7 +1,10 @@ #pragma once +#include "SystemTime.hpp" +#include -namespace Loop { + +namespace loop { /** * Initialize the event loop @@ -13,7 +16,25 @@ void init(); */ void run(); +/** + * Get current time in milliseconds + * @return current time + */ +SystemTime now(); + +/** + * Suspend execution using co_await until a given time. + * @param time time point + */ +[[nodiscard]] Awaitable sleep(SystemTime time); + +/** + * Suspend execution using co_await for a given duration. + * @param duration duration + */ +[[nodiscard]] inline Awaitable sleep(SystemDuration duration) {return sleep(now() + duration);} + // busy waiting, only for debug purposes, not very precise -void busyWait(int us); +void sleepBlocking(int us); -} // namespace Loop +} // namespace loop diff --git a/software/system/src/SpiMaster.hpp b/software/system/src/SpiMaster.hpp index 798093c..76d5f43 100644 --- a/software/system/src/SpiMaster.hpp +++ b/software/system/src/SpiMaster.hpp @@ -13,7 +13,7 @@ class SpiMaster { // Internal helper: Stores the parameters in the awaitable during co_await struct Parameters { // pointer to configuration - void const *config; + void *config; // write data int writeCount; @@ -28,29 +28,33 @@ class SpiMaster { virtual ~SpiMaster(); /** - * Transfer data to/from SPI device - * @param writeCount number of 8/16/32 bit values to write - * @param writeData array of 8/16/32 bit values to write (ram-only dependent on driver) - * @param readCount number of 8/16/32 bit values to read - * @param readData array of 8/16/32 bit values to read (ram-only dependent on driver) + * Transfer data to/from SPI device. Zero length transfers are not supported, i.e. writeCount or readCount must be + * greater than zero. + * @param writeCount number of bytes to write + * @param writeData data to write (driver may require that the data is located in RAM) + * @param readCount number of bytes to read + * @param readData data to read * @return use co_await on return value to await completion */ [[nodiscard]] virtual Awaitable transfer(int writeCount, void const *writeData, int readCount, void *readData) = 0; /** - * Write a command to an SPI device, e.g. a display, indicating a command using a separate data/command line if supported - * @param index index of spi context - * @param writeCount length of data to write - * @param writeData data to write + * Write to an SPI device, convenience method. + * @param count number of bytes to write + * @param data data to write (driver may require that the data is located in RAM) * @return use co_await on return value to await completion */ - [[nodiscard]] inline Awaitable writeCommand(int count, void const *command) { - return transfer(count | 0x80000000, command, 0, nullptr); - } - - [[nodiscard]] inline Awaitable writeData(int count, void const *data) { + [[nodiscard]] inline Awaitable write(int count, void const *data) { return transfer(count, data, 0, nullptr); } + /** + * Transfer data to/from SPI device and block until finished. Zero length transfers are not supported, i.e. + * writeCount or readCount must be greater than zero. + * @param writeCount number of bytes to write + * @param writeData data to write (driver may require that the data is located in RAM) + * @param readCount number of bytes to read + * @param readData data to read + */ virtual void transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) = 0; }; diff --git a/software/system/src/Timer.hpp b/software/system/src/Timer.hpp deleted file mode 100644 index 5df39ca..0000000 --- a/software/system/src/Timer.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "SystemTime.hpp" -#include -#include - - -namespace Timer { - -/** - * Initialize the timer - */ -void init(); - -/** - * Get current time in 1/1024 seconds - * @return current time - */ -SystemTime now(); - -/** - * Suspend execution using co_await until a given time. Only up to TIMER_COUNT coroutines can wait simultaneously. - * @param time time point - */ -[[nodiscard]] Awaitable sleep(SystemTime time); - -/** - * Suspend execution using co_await for a given duration. Only up to TIMER_COUNT coroutines can wait simultaneously. - * @param duration duration - */ -[[nodiscard]] inline Awaitable sleep(SystemDuration duration) {return sleep(now() + duration);} - -} // namespace Timer diff --git a/software/system/src/UsbDevice.cpp b/software/system/src/UsbDevice.cpp new file mode 100644 index 0000000..722b71d --- /dev/null +++ b/software/system/src/UsbDevice.cpp @@ -0,0 +1,5 @@ +#include "UsbDevice.hpp" + + +UsbDevice::~UsbDevice() { +} diff --git a/software/system/src/UsbDevice.hpp b/software/system/src/UsbDevice.hpp index d9b3cf0..cb4f3fe 100644 --- a/software/system/src/UsbDevice.hpp +++ b/software/system/src/UsbDevice.hpp @@ -7,53 +7,50 @@ #include -namespace UsbDevice { - -// Internal helper: Stores the parameters and a reference to the result value in the awaitable during co_await -struct ReceiveParameters { - int &length; - void *data; -}; - -// Internal helper: Stores the parameters in the awaitable during co_await -struct SendParameters { - int length; - void const *data; -}; - - -/** - * Initialize USB - * @param getDescriptor callback for obtaining descriptors - * @param onSetConfiguration callback for setting the configuration (libusb_set_configuration() on host), always called from event loop - * @param onRequest callback for vendor specific request - */ -void init( - std::function const &getDescriptor, - std::function const &onSetConfiguration, - std::function const &onRequest); - -/** - * Enable endpoints. Can be done in onSetConfiguration. Endpoint 0 should stay enabled - * @param inFlags an enabled flag for each in endpoint - * @param outFlags an enabled flag for each out endpoint - */ -void enableEndpoints(uint8_t inFlags, uint8_t outFlags); - /** - * Suspend execution using co_await until data is received from an endpoint (OUT transfer) - * @param index endpoint index (1-7) - * @param length in: length of data buffer, out: length of data actually received - * @param data data to receive, must be in RAM + * Interface to an USB device + * + * https://www.beyondlogic.org/usbnutshell/usb1.shtml */ -[[nodiscard]] Awaitable receive(int index, int &length, void *data); +class UsbDevice { +public: + + // Internal helper: Stores the parameters and a reference to the result value in the awaitable during co_await + struct ReceiveParameters { + int &length; + void *data; + }; + + // Internal helper: Stores the parameters in the awaitable during co_await + struct SendParameters { + int length; + void const *data; + }; + + + virtual ~UsbDevice(); + + /** + * Enable endpoints. Can be done in onSetConfiguration. Endpoint 0 should stay enabled + * @param inFlags an enabled flag for each in endpoint + * @param outFlags an enabled flag for each out endpoint + */ + virtual void enableEndpoints(uint8_t inFlags, uint8_t outFlags) = 0; + + /** + * Suspend execution using co_await until data is received from an endpoint (OUT transfer) + * @param index endpoint index (1-7) + * @param length in: length of data buffer, out: length of data actually received + * @param data data to receive, must be in RAM + */ + [[nodiscard]] virtual Awaitable receive(int index, int &length, void *data) = 0; + + /** + * Suspend execution using co_await until data is sent over an endpoint (IN transfer) + * @param index endpoint index (1-7) + * @param length data length + * @param data data to send, must be in RAM + */ + [[nodiscard]] virtual Awaitable send(int index, int length, void const *data) = 0; -/** - * Suspend execution using co_await until data is sent over an endpoint (IN transfer) - * @param index endpoint index (1-7) - * @param length data length - * @param data data to send, must be in RAM - */ -[[nodiscard]] Awaitable send(int index, int length, void const *data); - -} // namespace UsbDevice +}; diff --git a/software/system/src/emu/BusMasterImpl.cpp b/software/system/src/emu/BusMasterImpl.cpp index ab100d7..c49bf75 100644 --- a/software/system/src/emu/BusMasterImpl.cpp +++ b/software/system/src/emu/BusMasterImpl.cpp @@ -5,9 +5,6 @@ #include #include #include -#include -#include -#include namespace { @@ -75,14 +72,16 @@ BusMasterImpl::BusMasterImpl() : file("busMaster.bin", File::Mode::READ_WRITE) { } // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable BusMasterImpl::receive(int &length, uint8_t *data) { + assert(length > 0); return {this->receiveWaitlist, &length, data}; } Awaitable BusMasterImpl::send(int length, uint8_t const *data) { + assert(length > 0); return {this->sendWaitlist, length, data}; } @@ -128,7 +127,7 @@ void BusMasterImpl::handle(Gui &gui) { setKey(device.persistentState.aesKey, r.data8<16>()); // reset security counter - device.persistentState.securityCounter = 0; + device.persistentState.securityCounter = 1; // set index device.persistentState.index = device.nextIndex; diff --git a/software/system/src/emu/BusMasterImpl.hpp b/software/system/src/emu/BusMasterImpl.hpp index e3804cf..d2d7d8a 100644 --- a/software/system/src/emu/BusMasterImpl.hpp +++ b/software/system/src/emu/BusMasterImpl.hpp @@ -7,7 +7,7 @@ #include -class BusMasterImpl : public BusMaster, public Loop::Handler2 { +class BusMasterImpl : public BusMaster, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/BusNodeImpl.cpp b/software/system/src/emu/BusNodeImpl.cpp index a0e5eec..7d8b2ae 100644 --- a/software/system/src/emu/BusNodeImpl.cpp +++ b/software/system/src/emu/BusNodeImpl.cpp @@ -15,7 +15,7 @@ BusNodeImpl::BusNodeImpl() : file("busNode.bin", File::Mode::READ_WRITE) { } // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable BusNodeImpl::receive(int &length, uint8_t *data) { @@ -29,14 +29,14 @@ Awaitable BusNodeImpl::send(int length, uint8_t const * void BusNodeImpl::handle(Gui &gui) { // handle pending send operations this->sendWaitlist.resumeFirst([this](SendParameters &p) { - if (p.sendLength == 0) + if (p.length == 0) return true; uint8_t data[64]; - array::copy(p.sendLength, data, p.sendData); + array::copy(p.length, data, p.data); // read message - bus::MessageReader r(p.sendLength, data); + bus::MessageReader r(p.length, data); r.setHeader(); // get address @@ -216,9 +216,9 @@ void BusNodeImpl::sendToNode(bus::MessageWriter &w) { int length = w.getLength(); auto data = w.begin; this->receiveWaitlist.resumeFirst([length, data](ReceiveParameters &p) { - int len = min(length, *p.receiveLength); - array::copy(len, p.receiveData, data); - *p.receiveLength = len; + int len = min(length, *p.length); + array::copy(len, p.data, data); + *p.length = len; return true; }); } diff --git a/software/system/src/emu/BusNodeImpl.hpp b/software/system/src/emu/BusNodeImpl.hpp index 556f6d6..fda5ec0 100644 --- a/software/system/src/emu/BusNodeImpl.hpp +++ b/software/system/src/emu/BusNodeImpl.hpp @@ -6,7 +6,7 @@ #include -class BusNodeImpl : public BusNode, public Loop::Handler2 { +class BusNodeImpl : public BusNode, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/Gui.cpp b/software/system/src/emu/Gui.cpp index 9a83d43..5f500dc 100644 --- a/software/system/src/emu/Gui.cpp +++ b/software/system/src/emu/Gui.cpp @@ -1,3 +1,5 @@ +#define _USE_MATH_DEFINES +#include #include "Gui.hpp" #include #include @@ -5,7 +7,6 @@ #include #include #include -#include #include diff --git a/software/system/src/emu/Input.cpp b/software/system/src/emu/Input.cpp index d64e233..2340039 100644 --- a/software/system/src/emu/Input.cpp +++ b/software/system/src/emu/Input.cpp @@ -42,7 +42,7 @@ void set(int index, bool value) { // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle(Gui &gui) { // call next handler in chain Input::nextHandler(gui); @@ -68,7 +68,7 @@ void init() { return; // add to event loop handler chain - Input::nextHandler = Loop::addHandler(handle); + Input::nextHandler = loop::addHandler(handle); // set initial state using config for (int i = 0; i < INPUT_COUNT; ++i) { diff --git a/software/system/src/emu/Loop.cpp b/software/system/src/emu/Loop.cpp index 06c661a..8f07e60 100644 --- a/software/system/src/emu/Loop.cpp +++ b/software/system/src/emu/Loop.cpp @@ -1,10 +1,10 @@ #include "Loop.hpp" -#include "../posix/Loop.hpp" +#include "../posix/Loop.inc.hpp" #include #include -namespace Loop { +namespace loop { // opengl window GLFWwindow *window = nullptr; @@ -14,21 +14,9 @@ void nop(Gui &gui) {} Handler nextHandler = nop; Handler addHandler(Handler handler) { Handler h = nextHandler; - Loop::nextHandler = handler; + loop::nextHandler = handler; return h; } -/* -// handler -void Handler2::handle(Gui &) {} - -// handler chain -Handler2 nopHandler; -Handler2 *firstHandler = &nopHandler; -Handler2 *setHandler(Handler2 *handler) { - auto h = firstHandler; - Loop::firstHandler = handler; - return h; -}*/ HandlerList handlers; @@ -56,6 +44,8 @@ static void mouseCallback(GLFWwindow* window, int button, int action, int mods) } void init() { + initTimer(); + // init GLFW glfwSetErrorCallback(errorCallback); if (!glfwInit()) @@ -81,16 +71,16 @@ void init() { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GL_TRUE); - Loop::window = glfwCreateWindow(width, height, "RoomControl", NULL, NULL); + loop::window = glfwCreateWindow(width, height, "RoomControl", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } - glfwSetKeyCallback(Loop::window, keyCallback); - glfwSetMouseButtonCallback(Loop::window, mouseCallback); + glfwSetKeyCallback(loop::window, keyCallback); + glfwSetMouseButtonCallback(loop::window, mouseCallback); // make OpenGL context current - glfwMakeContextCurrent(Loop::window); + glfwMakeContextCurrent(loop::window); // load OpenGL functions gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); @@ -101,43 +91,43 @@ void init() { void run() { // check if loop::init() was called - assert(Loop::window != nullptr); + assert(loop::window != nullptr); // emulator user interface Gui gui; int frameCount = 0; //auto start = std::chrono::steady_clock::now(); - while (!glfwWindowShouldClose(Loop::window)) { + while (!glfwWindowShouldClose(loop::window)) { //auto frameStart = std::chrono::steady_clock::now(); // process events glfwPollEvents(); - Loop::runOnce(false); + loop::handleEvents(false); // mouse gui.doMouse(window); // set viewport int width, height; - glfwGetFramebufferSize(Loop::window, &width, &height); + glfwGetFramebufferSize(loop::window, &width, &height); glViewport(0, 0, width, height); // clear screen glClear(GL_COLOR_BUFFER_BIT); // handle gui - auto it = Loop::handlers.begin(); - while (it != Loop::handlers.end()) { + auto it = loop::handlers.begin(); + while (it != loop::handlers.end()) { // increment iterator beforehand because a handler can remove() itself auto current = it; ++it; current->handle(gui); } - Loop::nextHandler(gui); + loop::nextHandler(gui); // swap render buffer to screen - glfwSwapBuffers(Loop::window); + glfwSwapBuffers(loop::window); // show frames per second /*auto now = std::chrono::steady_clock::now(); @@ -151,8 +141,8 @@ void run() { } // cleanup - glfwDestroyWindow(Loop::window); + glfwDestroyWindow(loop::window); glfwTerminate(); } -} // namespace Loop +} // namespace loop diff --git a/software/system/src/emu/Loop.hpp b/software/system/src/emu/Loop.hpp index aff56cb..6ff9670 100644 --- a/software/system/src/emu/Loop.hpp +++ b/software/system/src/emu/Loop.hpp @@ -5,7 +5,7 @@ #include -namespace Loop { +namespace loop { using Handler = void (*)(Gui &); @@ -15,7 +15,6 @@ using Handler = void (*)(Gui &); Handler addHandler(Handler handler); - /** * Event handler that handles activity of the emulated peripherals on the gui */ @@ -26,4 +25,4 @@ class Handler2 : public LinkedListNode { using HandlerList = LinkedList; extern HandlerList handlers; -} // namespace Loop +} // namespace loop diff --git a/software/system/src/emu/Output.cpp b/software/system/src/emu/Output.cpp index 1326fff..4fb5b5a 100644 --- a/software/system/src/emu/Output.cpp +++ b/software/system/src/emu/Output.cpp @@ -20,7 +20,7 @@ State states[OUTPUT_COUNT]; // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle(Gui &gui) { // call next handler in chain Output::nextHandler(gui); @@ -38,7 +38,7 @@ void init() { return; // add to event loop handler chain - Output::nextHandler = Loop::addHandler(handle); + Output::nextHandler = loop::addHandler(handle); // set initial state using config for (int i = 0; i < OUTPUT_COUNT; ++i) { diff --git a/software/system/src/emu/QuadratureDecoderImpl.cpp b/software/system/src/emu/QuadratureDecoderImpl.cpp index 5db2aee..ac5c9f6 100644 --- a/software/system/src/emu/QuadratureDecoderImpl.cpp +++ b/software/system/src/emu/QuadratureDecoderImpl.cpp @@ -5,7 +5,7 @@ QuadratureDecoderImpl::QuadratureDecoderImpl() { // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable QuadratureDecoderImpl::change(int8_t& delta) { diff --git a/software/system/src/emu/QuadratureDecoderImpl.hpp b/software/system/src/emu/QuadratureDecoderImpl.hpp index af35c2d..146a9ca 100644 --- a/software/system/src/emu/QuadratureDecoderImpl.hpp +++ b/software/system/src/emu/QuadratureDecoderImpl.hpp @@ -4,7 +4,7 @@ #include "Loop.hpp" -class QuadratureDecoderImpl : public QuadratureDecoder, public Loop::Handler2 { +class QuadratureDecoderImpl : public QuadratureDecoder, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/Radio.cpp b/software/system/src/emu/Radio.cpp index b951004..dd0f123 100644 --- a/software/system/src/emu/Radio.cpp +++ b/software/system/src/emu/Radio.cpp @@ -348,7 +348,7 @@ void SendParameters::cancel() noexcept { } // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle(Gui &gui) { for (int index = 0; index < RADIO_CONTEXT_COUNT; ++index) { auto &context = Radio::contexts[index]; @@ -467,7 +467,7 @@ void handle(Gui &gui) { int controlTransfer(libusb_device_handle *handle, Request request, uint16_t wValue, uint16_t wIndex) { return libusb_control_transfer(handle, - uint8_t(usb::Request::OUT | usb::Request::TYPE_VENDOR | usb::Request::RECIPIENT_INTERFACE), + uint8_t(usb::Request::VENDOR_DEVICE_OUT), uint8_t(request), wValue, wIndex, nullptr, 0, 1000); } @@ -478,7 +478,7 @@ void init() { return; // add to event loop handler chain - Radio::nextHandler = Loop::addHandler(handle); + Radio::nextHandler = loop::addHandler(handle); // radio connected via USB int r = libusb_init(NULL); diff --git a/software/system/src/emu/SpiBME680.cpp b/software/system/src/emu/SpiBME680.cpp index 7f7a18f..88c1326 100644 --- a/software/system/src/emu/SpiBME680.cpp +++ b/software/system/src/emu/SpiBME680.cpp @@ -43,7 +43,7 @@ SpiBME680::SpiBME680() { setTemperature(20.0f); // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable SpiBME680::transfer(int writeCount, void const *writeData, int readCount, void *readData) { diff --git a/software/system/src/emu/SpiBME680.hpp b/software/system/src/emu/SpiBME680.hpp index 38f52b4..e399776 100644 --- a/software/system/src/emu/SpiBME680.hpp +++ b/software/system/src/emu/SpiBME680.hpp @@ -4,7 +4,7 @@ #include "Loop.hpp" -class SpiBME680 : public SpiMaster, public Loop::Handler2 { +class SpiBME680 : public SpiMaster, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/SpiMPQ6526.cpp b/software/system/src/emu/SpiMPQ6526.cpp index aa306c0..0ec66a8 100644 --- a/software/system/src/emu/SpiMPQ6526.cpp +++ b/software/system/src/emu/SpiMPQ6526.cpp @@ -7,7 +7,7 @@ SpiMPQ6526::SpiMPQ6526() : relayStates{Gui::LightState::DISABLED, Gui::LightState::DISABLED, Gui::LightState::DISABLED, Gui::LightState::DISABLED} { // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable SpiMPQ6526::transfer(int writeCount, void const *writeData, int readCount, void *readData) { diff --git a/software/system/src/emu/SpiMPQ6526.hpp b/software/system/src/emu/SpiMPQ6526.hpp index d7cbb9e..83c36d4 100644 --- a/software/system/src/emu/SpiMPQ6526.hpp +++ b/software/system/src/emu/SpiMPQ6526.hpp @@ -7,7 +7,7 @@ /** * Emulates a MPQ6526 motor/relay driver */ -class SpiMPQ6526 : public SpiMaster, public Loop::Handler2 { +class SpiMPQ6526 : public SpiMaster, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/SpiMR45Vxxx.cpp b/software/system/src/emu/SpiMR45Vxxx.cpp index 66540b1..853f6c7 100644 --- a/software/system/src/emu/SpiMR45Vxxx.cpp +++ b/software/system/src/emu/SpiMR45Vxxx.cpp @@ -18,7 +18,7 @@ SpiMR45Vxxx::SpiMR45Vxxx(std::string const &filename, int size) this->file.resize(size, 0xff); // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable SpiMR45Vxxx::transfer(int writeCount, void const *writeData, int readCount, void *readData) { diff --git a/software/system/src/emu/SpiMR45Vxxx.hpp b/software/system/src/emu/SpiMR45Vxxx.hpp index d88f8ca..5cf678b 100644 --- a/software/system/src/emu/SpiMR45Vxxx.hpp +++ b/software/system/src/emu/SpiMR45Vxxx.hpp @@ -9,7 +9,7 @@ /** * Emulates a MR45V064B FeRam an SPI slave device */ -class SpiMR45Vxxx : public SpiMaster, public Loop::Handler2 { +class SpiMR45Vxxx : public SpiMaster, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/emu/SpiSSD1309.cpp b/software/system/src/emu/SpiSSD1309.cpp index e98fa33..868c093 100644 --- a/software/system/src/emu/SpiSSD1309.cpp +++ b/software/system/src/emu/SpiSSD1309.cpp @@ -7,16 +7,16 @@ SpiSSD1309::SpiSSD1309(int width, int height) : width(width), height(height) { int size = width * height / 8; - this->display = new uint8_t[size]; - array::fill(size, this->display, 0); + this->data = new uint8_t[size]; + array::fill(size, this->data, 0); this->displayBuffer = new uint8_t[width * height]; // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } SpiSSD1309::~SpiSSD1309() { - delete [] this->display; + delete [] this->data; delete [] this->displayBuffer; } @@ -25,61 +25,51 @@ Awaitable SpiSSD1309::transfer(int writeCount, void cons } void SpiSSD1309::transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) { - bool command = writeCount < 0; - writeCount &= 0x7fffffff; auto w = reinterpret_cast(writeData); - auto r = reinterpret_cast(readData); - if (command) { - // execute commands - for (int i = 0; i < writeCount; ++i) { - switch (w[i]) { - // set contrast control - case 0x81: - this->displayContrast = w[++i]; - break; - - // entire display on - case 0xA4: - this->displayOn = false; - break; - case 0xA5: - this->displayOn = true; - break; - - // set normal/inverse display - case 0xA6: - this->displayInverse = false; - break; - case 0xA7: - this->displayInverse = true; - break; - - // set display on/off - case 0xAE: - this->displayEnabled = false; - break; - case 0xAF: - this->displayEnabled = true; - break; - } - } - } else { - // set data - for (int i = 0; i < writeCount; ++i) { - // copy byte (8 pixels in a column) - this->display[page * this->width + this->column] = w[i]; - - // increment column index - this->column = (this->column == this->width - 1) ? 0 : this->column + 1; - if (this->column == 0) - this->page = this->page == (this->height / 8 - 1) ? 0 : this->page + 1; + + // execute commands + for (int i = 0; i < writeCount; ++i) { + switch (w[i]) { + // set contrast control + case 0x81: + this->displayContrast = w[++i]; + break; + + // entire display on + case 0xA4: + this->displayOn = false; + break; + case 0xA5: + this->displayOn = true; + break; + + // set normal/inverse display + case 0xA6: + this->displayInverse = false; + break; + case 0xA7: + this->displayInverse = true; + break; + + // set display on/off + case 0xAE: + this->displayEnabled = false; + break; + case 0xAF: + this->displayEnabled = true; + break; } } } void SpiSSD1309::handle(Gui &gui) { this->waitlist.resumeFirst([this](Parameters &p) { - transferBlocking(p.writeCount, p.writeData, p.readCount, p.readData); + if (p.config == nullptr) { + transferBlocking(p.writeCount, p.writeData, p.readCount, p.readData); + } else { + auto &d = *reinterpret_cast(p.config); + d.transferBlocking(p.writeCount, p.writeData, p.readCount, p.readData); + } return true; }); @@ -99,8 +89,29 @@ void SpiSSD1309::getDisplay(uint8_t *buffer) { for (int j = 0; j < height; ++j) { uint8_t *b = &buffer[width * j]; for (int i = 0; i < width; ++i) { - bool bit = (this->display[i + width * (j >> 3)] & (1 << (j & 7))) != 0; + bool bit = (this->data[i + width * (j >> 3)] & (1 << (j & 7))) != 0; b[i] = bit ? foreground : background; } } } + + +Awaitable SpiSSD1309::Data::transfer(int writeCount, const void *writeData, int readCount, void *readData) { + return {this->display.waitlist, this, writeCount, writeData, readCount, readData}; +} + +void SpiSSD1309::Data::transferBlocking(int writeCount, const void *writeData, int readCount, void *readData) { + auto &d = this->display; + auto w = reinterpret_cast(writeData); + + // set data + for (int i = 0; i < writeCount; ++i) { + // copy byte (8 pixels in a column) + d.data[d.page * d.width + d.column] = w[i]; + + // increment column index + d.column = (d.column == d.width - 1) ? 0 : d.column + 1; + if (d.column == 0) + d.page = d.page == (d.height / 8 - 1) ? 0 : d.page + 1; + } +} diff --git a/software/system/src/emu/SpiSSD1309.hpp b/software/system/src/emu/SpiSSD1309.hpp index e426e10..9bf89d5 100644 --- a/software/system/src/emu/SpiSSD1309.hpp +++ b/software/system/src/emu/SpiSSD1309.hpp @@ -7,7 +7,7 @@ /** * Emulates a SSD1309 based display as an SPI slave device */ -class SpiSSD1309 : public SpiMaster, public Loop::Handler2 { +class SpiSSD1309 : public SpiMaster, public loop::Handler2 { public: /** * Constructor @@ -22,7 +22,7 @@ class SpiSSD1309 : public SpiMaster, public Loop::Handler2 { void handle(Gui &gui) override; - // get dipslay contents into an 8 bit grayscale image + // get display contents into an 8 bit grayscale image void getDisplay(uint8_t *buffer); @@ -39,8 +39,29 @@ class SpiSSD1309 : public SpiMaster, public Loop::Handler2 { bool displayOn = false; // all pixels on bool displayInverse = false; bool displayEnabled = false; - uint8_t *display; + uint8_t *data; uint8_t *displayBuffer; Waitlist waitlist; + + + /** + * Separate data channel + */ + class Data : public SpiMaster { + public: + /** + * Constructor + * @param device the SPI device to operate on + * @param csPin chip select pin of the slave + * @param writeOnly ture if we only write data and can use MISO as DC (data/command) signal (e.g. for a display) + */ + Data(SpiSSD1309 &display) : display(display) {} + + Awaitable transfer(int writeCount, const void *writeData, int readCount, void *readData) override; + void transferBlocking(int writeCount, const void *writeData, int readCount, void *readData) override; + + + SpiSSD1309 &display; + }; }; diff --git a/software/system/src/emu/UsbDevice.cpp b/software/system/src/emu/UsbDevice.cpp deleted file mode 100644 index 70d0219..0000000 --- a/software/system/src/emu/UsbDevice.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "../UsbDevice.hpp" -#include "Loop.hpp" -#include - - -namespace UsbDevice { - -bool text; - -// endpoints 1 - 7 -struct Endpoint { - Waitlist receiveWaitlist; - Waitlist sendWaitlist; -}; - -Endpoint endpoints[1]; - -std::function onSetConfiguration; - -// event loop handler chain -Loop::Handler nextHandler; -void handle(Gui &gui) { - // call onSetConfiguration once - if (UsbDevice::onSetConfiguration != nullptr) { - UsbDevice::onSetConfiguration(1); - UsbDevice::onSetConfiguration = nullptr; - } - - for (auto &endpoint : UsbDevice::endpoints) { - - endpoint.sendWaitlist.resumeFirst([](SendParameters p) { - if (UsbDevice::text) { - printf("%.*s", p.length, reinterpret_cast(p.data)); - } else { - // binary - printf("%d: ", p.length); - for (int i = 0; i < p.length; ++i) { - if ((i & 15) == 0) { - if (i != 0) - printf(",\n"); - } else { - printf(", "); - } - printf("0x%02x", reinterpret_cast(p.data)[i]); - } - printf("\n"); - } - return true; - }); - } - - // call next handler in chain - UsbDevice::nextHandler(gui); -} - -void init( - std::function const &getDescriptor, - std::function const &onSetConfiguration, - std::function const &onRequest) -{ - // get device descriptor - auto *deviceDescriptor = getDescriptor(usb::DescriptorType::DEVICE).cast(); - UsbDevice::text = deviceDescriptor->bDeviceProtocol == 1; - - // set configuration - UsbDevice::onSetConfiguration = onSetConfiguration; - //Loop::context.post([onSetConfiguration]() {onSetConfiguration(1);}); - - // add to event loop handler chain - UsbDevice::nextHandler = Loop::addHandler(handle); -} - -void enableEndpoints(uint8_t inFlags, uint8_t outFlags) { -} - -Awaitable receive(int index, int &length, void *data) { - assert(index == 1); - auto &endpoint = UsbDevice::endpoints[index - 1]; - - return {endpoint.receiveWaitlist, length, data}; -} - -Awaitable send(int index, int length, void const *data) { - assert(index == 1); - auto &endpoint = UsbDevice::endpoints[index - 1]; - - return {endpoint.sendWaitlist, length, data}; -} - -} // namespace Usb diff --git a/software/system/src/linux/Ble.cpp b/software/system/src/linux/Ble.cpp index 0def93a..bfc51a5 100644 --- a/software/system/src/linux/Ble.cpp +++ b/software/system/src/linux/Ble.cpp @@ -1,6 +1,5 @@ #include "../Ble.hpp" #include "../posix/Loop.hpp" -#include "Timer.hpp" #include "bt.hpp" #include #include @@ -18,7 +17,7 @@ constexpr int attCid = 4; char const *results[] = {"E8:85:47:17:BF:5A"}; // emulates a scanner -class Scanner : public Loop::Timeout { +class Scanner : public loop::TimeHandler { public: void activate() override { auto time = this->time; @@ -50,7 +49,7 @@ class Scanner : public Loop::Timeout { Scanner scanner; -class Context : public Loop::FileDescriptor { +class Context : public loop::SocketHandler { public: Waitlist receiveWaitlist; @@ -61,7 +60,7 @@ class Context : public Loop::FileDescriptor { if (events & POLLIN) { this->receiveWaitlist.resumeFirst([this](ReceiveParameters &p) { // receive - auto receivedCount = read(this->fd, p.data, *p.length); + auto receivedCount = read(this->socket, p.data, *p.length); if (receivedCount >= 0) { *p.length = receivedCount; return true; @@ -74,7 +73,7 @@ class Context : public Loop::FileDescriptor { if (events & POLLOUT) { this->sendWaitlist.resumeFirst([this](SendParameters &p) { // send - auto sentCount = write(this->fd, p.data, p.length); + auto sentCount = write(this->socket, p.data, p.length); return sentCount >= 0; }); if (this->sendWaitlist.isEmpty()) @@ -93,8 +92,8 @@ void init() { Awaitable scan(ScanResult &result) { // add to event loop if necessary if (!Ble::scanner.isInList()) { - Ble::scanner.time = Timer::now(); - Loop::timeouts.add(Ble::scanner); + Ble::scanner.time = loop::now(); + loop::timeHandlers.add(Ble::scanner); } // add to wait list @@ -107,22 +106,22 @@ bool open(int index, Address const &address) { auto &context = Ble::contexts[index]; // create socket - assert(context.fd == -1); - int fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + assert(context.socket == -1); + auto s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); // bind sockaddr_l2 srcaddr = { .l2_family = AF_BLUETOOTH, .l2_cid = htobs(attCid)}; - if (bind(fd, (sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) { - ::close(fd); + if (bind(s, (sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) { + ::close(s); return false; } // set security level struct bt_security btsec = {.level = BT_SECURITY_LOW}; - if (setsockopt(fd, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) { - ::close(fd); + if (setsockopt(s, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) { + ::close(s); return false; } @@ -135,15 +134,15 @@ bool open(int index, Address const &address) { array::copy(dstaddr.l2_bdaddr.b, address.u8); // connect - if (connect(fd, (sockaddr *)&dstaddr, sizeof(dstaddr)) < 0) { - ::close(fd); + if (connect(s, (sockaddr *)&dstaddr, sizeof(dstaddr)) < 0) { + ::close(s); return false; } - fcntl(context.fd, F_SETFL, O_NONBLOCK); + fcntl(context.socket, F_SETFL, O_NONBLOCK); // set file descriptor - context.fd = fd; + context.socket = s; context.events = 0; return true; } @@ -151,10 +150,10 @@ bool open(int index, Address const &address) { void close(int index) { assert(uint(index) < BLUETOOTH_CONTEXT_COUNT); auto &context = Ble::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); - ::close(context.fd); - context.fd = -1; + ::close(context.socket); + context.socket = -1; context.events = 0; // resume waiting coroutines @@ -172,13 +171,13 @@ void close(int index) { Awaitable receive(int index, int &length, uint8_t *data) { assert(uint(index) < BLUETOOTH_CONTEXT_COUNT); auto &context = Ble::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); context.events |= POLLIN; // add to event loop if necessary if (!context.isInList()) - Loop::fileDescriptors.add(context); + loop::socketHandlers.add(context); // add to wait list return {context.receiveWaitlist, &length, data}; @@ -187,13 +186,13 @@ Awaitable receive(int index, int &length, uint8_t *data) { Awaitable send(int index, int length, uint8_t const *data) { assert(uint(index) < BLUETOOTH_CONTEXT_COUNT); auto &context = Ble::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); context.events |= POLLOUT; // add to event loop if necessary if (!context.isInList()) - Loop::fileDescriptors.add(context); + loop::socketHandlers.add(context); // add to wait list return {context.sendWaitlist, length, data}; diff --git a/software/system/src/nrf52/BusMasterImpl.cpp b/software/system/src/nrf52/BusMasterImpl.cpp index c2e8a07..fc1879b 100644 --- a/software/system/src/nrf52/BusMasterImpl.cpp +++ b/software/system/src/nrf52/BusMasterImpl.cpp @@ -1,209 +1,377 @@ #include "BusMasterImpl.hpp" #include "gpio.hpp" #include +#include "Debug.hpp" // debug /* -#define initSignal() configureOutput(3) -#define setSignal(value) setOutput(3, value) -#define toggleSignal() toggleOutput(3) +#define initSignal() gpio::configureOutput(20) +#define setSignal(value) gpio::setOutput(20, value) +#define toggleSignal() gpio::toggleOutput(20) */ #define initSignal() #define setSignal(value) #define toggleSignal() + /* + How to calculate the value for the BAUDRATE register: desired baudrate * 2^32 / 16000000 (https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values) Dependencies: Config: Resources: NRF_UART0 - NRF_TIMER1 + NRF_TIMER1: Break and rx timeout + NRF_EGU1 + TRIGGERED[0]: Received a packet + TRIGGERED[1]: Send operation has finished + TRIGGERED[2]: Request to send by node */ +constexpr int TIMER_CLOCK = 1000000; // 1MHz +constexpr int BAUD_RATE = 19200; + namespace { -BusMasterImpl *busMaster[1]; +BusMasterImpl *busMaster; } -BusMasterImpl::BusMasterImpl(int rxPin, int txPin) : txPin(txPin) { - this->uart = NRF_UART0; - this->timer = NRF_TIMER1; - busMaster[0] = this; +BusMasterImpl::BusMasterImpl(int rxPin, int txPin) : rxPin(rxPin), txPin(txPin) { + const auto uart = NRF_UART0; + const auto timer = NRF_TIMER1; + const auto egu = NRF_EGU1; + + // set instance pointer + busMaster = this; + initSignal(); - // init uart + // configure rx pin (pull-up) and tx pin (idle state is high) + gpio::configureInput(rxPin);//, gpio::Pull::UP); gpio::setOutput(txPin, true); gpio::configureOutput(txPin); - configureInput(rxPin, gpio::Pull::UP); - //NRF_UART0->PSEL.TXD = BUS_TX_PIN; - NRF_UART0->PSEL.RXD = rxPin; - NRF_UART0->CONFIG = N(UART_CONFIG_STOP, One) | N(UART_CONFIG_PARITY, Excluded); - //NRF_UART0->INTENSET = N(UART_INTENSET_TXDRDY, Set) | N(UART_INTENSET_RXDRDY, Set); + + // init uart + uart->PSEL.RXD = rxPin; + uart->PSEL.TXD = txPin; + uart->CONFIG = N(UART_CONFIG_STOP, One) | N(UART_CONFIG_PARITY, Excluded); + uart->BAUDRATE = N(UARTE_BAUDRATE_BAUDRATE, Baud19200); + uart->INTENSET = N(UART_INTENSET_RXDRDY, Set);// | N(UART_INTENSET_TXDRDY, Set); + uart->ENABLE = N(UART_ENABLE_ENABLE, Enabled); enableInterrupt(UARTE0_UART0_IRQn); + // start receiving requests from nodes + uart->TASKS_STARTRX = TRIGGER; // -> EVENTS_RXRDY + // init timer - NRF_TIMER1->MODE = N(TIMER_MODE_MODE, Timer); - NRF_TIMER1->BITMODE = N(TIMER_BITMODE_BITMODE, 32Bit); - NRF_TIMER1->PRESCALER = 4; // 1MHz - NRF_TIMER1->CC[0] = 1250; - NRF_TIMER1->INTENSET = N(TIMER_INTENSET_COMPARE0, Set); + timer->MODE = N(TIMER_MODE_MODE, Timer); + timer->BITMODE = N(TIMER_BITMODE_BITMODE, 32Bit); + timer->PRESCALER = 4; // 1MHz + timer->INTENSET = N(TIMER_INTENSET_COMPARE0, Set); enableInterrupt(TIMER1_IRQn); + // init event generator + egu->INTENSET = + N(EGU_INTENSET_TRIGGERED0, Set) + | N(EGU_INTENSET_TRIGGERED1, Set) + | N(EGU_INTENSET_TRIGGERED2, Set); + // add to list of handlers -// Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable BusMasterImpl::receive(int &length, uint8_t *data) { + // start receiving immediately if no rx is pending and bus is idle + if (this->rxState == RxState::IDLE) { + ReceiveParameters p{&length, data}; + startReceive(p); + } + return {this->receiveWaitlist, &length, data}; } -Awaitable BusMasterImpl::send(int length, uint8_t const *data) { +Awaitable BusMasterImpl::send(int length, const uint8_t *data) { + if (length <= 0) + return {}; + + // start sending immediately if no tx is pending and bus is idle + if (this->txState == TxState::IDLE) { + //Debug::toggleRedLed(); + SendParameters p{length, data}; + startSend(p); + } + return {this->sendWaitlist, length, data}; } void BusMasterImpl::handle() { - if (rxReady) { - rxReady = false; - this->onTransferred(this->rxIndex); - setSignal(true); - } - if (requestReady) { - requestReady = false; - if (this->onRequest) - this->onRequest(this->requestData[0]);//, BusMaster::requestIndex); - this->requestIndex = 0; - } -} + const auto egu = NRF_EGU1; + + if (isInterruptPending(SWI1_EGU1_IRQn)) { + + // check if tx or rx operation has finished + if (egu->EVENTS_TRIGGERED[1]) { + // send operation complete + egu->EVENTS_TRIGGERED[1] = 0; + this->txState = TxState::IDLE; + + // check if a next send operation is pending + this->sendWaitlist.visitSecond([this](const SendParameters &p) { + startSend(p); + }); + + // resume first waiting coroutine + this->sendWaitlist.resumeFirst([](const SendParameters &p) { + return true; + }); + } else if (egu->EVENTS_TRIGGERED[0]) { + // receive operation complete + egu->EVENTS_TRIGGERED[0] = 0; + int rxLength = this->rxData - this->rxBegin; + this->rxState = RxState::IDLE; + + // check if a next receive operation is pending + this->receiveWaitlist.visitSecond([this](const ReceiveParameters &p) { + startReceive(p); + }); + + // resume first waiting coroutine + this->receiveWaitlist.resumeFirst([rxLength](const ReceiveParameters &p) { + *p.length = rxLength; + return true; + }); + } -void BusMasterImpl::uartIrqHandler() { - if (this->uart->EVENTS_TXDRDY) { - this->uart->EVENTS_TXDRDY = 0; - - // check if end of send buffer - int txIndex = this->txIndex; - if (txIndex < this->txLength) { - // send next byte - this->uart->TXD = this->txData[txIndex]; - this->txIndex = txIndex + 1; - } else { - // stop transmit - this->uart->TASKS_STOPTX = TRIGGER; + if (egu->EVENTS_TRIGGERED[2]) { + // a node requested to be read: Start break if we are in idle state (no new tx was started above) + egu->EVENTS_TRIGGERED[2] = 0; + if (this->state == State::IDLE) + startBreak(); } + + // clear pending interrupt flag at NVIC + clearInterrupt(SWI1_EGU1_IRQn); } - if (this->uart->EVENTS_RXDRDY) { - this->uart->EVENTS_RXDRDY = 0; - toggleSignal(); - //setSignal(true); +} + +void BusMasterImpl::startReceive(const ReceiveParameters &p) { + this->rxState = RxState::PENDING; + this->rxBegin = p.data; + this->rxEnd = p.data + *p.length; + + // don't start yet, wait for send or request signal from node +} + +void BusMasterImpl::startSend(const SendParameters &p) { + this->txState = TxState::PENDING; + this->txBegin = p.data; + this->txEnd = p.data + p.length; + startBreak(); +} + +void BusMasterImpl::startBreak() { + const auto uart = NRF_UART0; + const auto timer = NRF_TIMER1; + + // disconnect uart from tx and set high baud rate to force stop of rx/tx during break + uart->TASKS_STOPRX = TRIGGER; + uart->TASKS_STOPTX = TRIGGER; + uart->ENABLE = 0; + uart->INTENCLR = 0xffffffff; + uart->PSEL.TXD = gpio::DISCONNECTED; + uart->BAUDRATE = N(UARTE_BAUDRATE_BAUDRATE, Baud1M); + uart->ENABLE = N(UART_ENABLE_ENABLE, Enabled); + + // generate break: 13 bit times, 677us + gpio::setOutput(this->txPin, false); + timer->TASKS_CLEAR = TRIGGER; + timer->CC[0] = 677; + timer->TASKS_START = TRIGGER; + + // set state + this->state = State::BREAK; + + //Debug::toggleRedLed(); +} + + +// interrupt handlers + +void UARTE0_UART0_IRQHandler() { + busMaster->uartIrqHandler(); +} - if (this->state == SYNC) { +void BusMasterImpl::uartIrqHandler() { + const auto uart = NRF_UART0; + const auto timer = NRF_TIMER1; + const auto egu = NRF_EGU1; + + if (uart->EVENTS_RXDRDY) { + uart->EVENTS_RXDRDY = 0; + setSignal(false); + + // restart rx timeout + timer->TASKS_CLEAR = TRIGGER; + + auto b = uart->RXD; + switch (this->state) { + case State::IDLE: + // received a request signal from a node + egu->TASKS_TRIGGER[2] = TRIGGER; + break; + case State::SYNC: // check if sync byte was received correctly - uint8_t sync = this->uart->RXD; - if (sync != 0x55) { + //Debug::toggleRedLed(); + if (b != 0x55) { // error: sync byte not received correctly - // indicate that rx buffer is ready (zero length to indicate error) - this->rxReady = true; - - // stop timer - this->timer->TASKS_STOP = TRIGGER; + // repeat + startBreak(); - // now in request mode - this->state = REQUEST; + //todo indicate bus error after 3-5 retries } else { - // now receive data - this->state = RX; + // sync ok: now start to transfer data + if (this->rxState >= RxState::PENDING) { + this->rxState = RxState::ACTIVE; + this->rxData = this->rxBegin; + } + + // send first byte if tx is pending + if (this->txState >= TxState::PENDING) { + this->txState = TxState::ACTIVE; + auto data = this->txBegin; + this->txData = data + 1; + b = *data; + uart->TXD = b; + this->txByte = b; + } else { + // tx is idle: stop transmit + uart->TASKS_STOPTX = TRIGGER; + } + + // now in transfer state + this->state = State::TRANSFER; } - } else if (this->state == RX) { - // check if rx buffer full - int rxIndex = this->rxIndex; - this->rxData[rxIndex] = this->uart->RXD; - this->rxIndex = rxIndex + 1; - if (this->rxIndex == this->rxLength) { - // indicate that rx buffer is ready - this->rxReady = true; - - // stop timer - this->timer->TASKS_STOP = TRIGGER; - - // now in request mode - this->state = REQUEST; + break; + case State::TRANSFER: { + // received next byte + //Debug::toggleGreenLed(); + + // when tx is active, check if received byte is equal to the last sent byte + if (this->txState == TxState::ACTIVE) { + // check if byte was transferred ok and the current state of the bus is high + if (b == this->txByte && gpio::readInput(this->rxPin)) { + // byte was transferred ok + auto data = this->txData; + if (data < this->txEnd) { + // send next byte + this->txData = data + 1; + b = *data; + uart->TXD = b; + this->txByte = b; + } else { + // end: stop transmit and set tx state to success + uart->TASKS_STOPTX = TRIGGER; + this->txState = TxState::END; + } + } else { + // error: stop transmit as probably a node won the bus arbitration + uart->TASKS_STOPTX = TRIGGER; + this->txState = TxState::PENDING; + } + } else if (this->txState == TxState::END) { + // error: extra byte received after successful transmission + this->txState = TxState::PENDING; } - // restart rx timeout (unless stopped) - this->timer->TASKS_CLEAR = TRIGGER; - } else { - // check if request buffer full - int requestIndex = this->requestIndex; - if (requestIndex < array::count(this->requestData)) { - this->requestData[requestIndex] = this->uart->RXD; - this->requestIndex = requestIndex + 1; - if (requestIndex == array::count(this->requestData)) { - // indicate that requests are ready - this->requestReady = true; + // check if receive is pending + if (this->rxState == RxState::ACTIVE) { + auto data = this->rxData; + if (data < this->rxEnd) { + // add next byte to receive buffer + this->rxData = data + 1; + *data = b; } } + break; } - - //toggleSignal(); - } -} - -void BusMasterImpl::timerIrqHandler() { - if (this->timer->EVENTS_COMPARE[0]) { - this->timer->EVENTS_COMPARE[0] = 0; - - if (state == BREAK) { - gpio::setOutput(this->txPin, true); - - // reconfigure uart for transmission - this->uart->ENABLE = 0; - this->uart->EVENTS_RXDRDY = 0; - this->uart->EVENTS_TXDRDY = 0; - this->uart->BAUDRATE = N(UARTE_BAUDRATE_BAUDRATE, Baud19200); - this->uart->PSEL.TXD = this->txPin; - this->uart->INTENSET = N(UART_INTENSET_RXDRDY, Set) | N(UART_INTENSET_TXDRDY, Set); - this->uart->ENABLE = N(UART_ENABLE_ENABLE, Enabled); - - // start transfer with sync byte - this->uart->TASKS_STARTRX = TRIGGER; // -> EVENTS_RXRDY - this->uart->TASKS_STARTTX = TRIGGER; // -> EVENTS_TXRDY - this->uart->TXD = 0x55; - - // restart timer for rx timeout - this->timer->TASKS_CLEAR = TRIGGER; - this->timer->CC[0] = 1250; - - // expect receive of sync byte - this->state = SYNC; - setSignal(false); - } else { - //toggleSignal(); - - // stop timer - this->timer->TASKS_STOP = TRIGGER; - - // indicate that rx buffer is ready - this->rxReady = true; - - // now in request mode - this->state = REQUEST; - - // toggleSignal(); - setSignal(true); + default: + // ignore when in other state + break; } } -} -extern "C" { -void UARTE0_UART0_IRQHandler(void); -void TIMER1_IRQHandler(void); + // debug + /*if (uart->EVENTS_TXDRDY) { + uart->EVENTS_TXDRDY = 0; + setSignal(true); + }*/ } -void UARTE0_UART0_IRQHandler(void) { - busMaster[0]->uartIrqHandler(); +void TIMER1_IRQHandler() { + busMaster->timerIrqHandler(); } -void TIMER1_IRQHandler(void) { - busMaster[0]->timerIrqHandler(); +void BusMasterImpl::timerIrqHandler() { + const auto uart = NRF_UART0; + const auto timer = NRF_TIMER1; + const auto egu = NRF_EGU1; + + // clear interrupt flag + timer->EVENTS_COMPARE[0] = 0; + + if (state == State::BREAK) { + gpio::setOutput(this->txPin, true); + + // restart timer for pause (half bit) + timer->CC[0] = TIMER_CLOCK / (BAUD_RATE * 2); + timer->TASKS_CLEAR = TRIGGER; + this->state = State::PAUSE; + } else if (state == State::PAUSE) { + // end of break signal + //Debug::toggleBlueLed(); + gpio::setOutput(this->txPin, true); + + // reconfigure uart for transmission + uart->ENABLE = 0; + uart->EVENTS_RXDRDY = 0; + uart->EVENTS_TXDRDY = 0; + uart->PSEL.TXD = this->txPin; + uart->BAUDRATE = N(UARTE_BAUDRATE_BAUDRATE, Baud19200); + uart->INTENSET = N(UART_INTENSET_RXDRDY, Set);// | N(UART_INTENSET_TXDRDY, Set); + uart->ENABLE = N(UART_ENABLE_ENABLE, Enabled); + + // start transfer with sync byte + uart->TASKS_STARTRX = TRIGGER; // -> EVENTS_RXRDY + uart->TASKS_STARTTX = TRIGGER; // -> EVENTS_TXRDY + uart->TXD = 0x55; + + // restart timer for rx timeout (two characters = 20 bit) + timer->CC[0] = (20 * TIMER_CLOCK) / BAUD_RATE; + timer->TASKS_CLEAR = TRIGGER; + + // expect receiving the sync byte + this->state = State::SYNC; + } else { + // receive timeout + //Debug::toggleRedLed(); + + // stop timer + timer->TASKS_STOP = TRIGGER; + + // inform event loop if tx or rx operation has finished + if (this->txState == TxState::END) { + //Debug::toggleRedLed(); + this->txState = TxState::FINISHED; + egu->TASKS_TRIGGER[1] = TRIGGER; + } else if (this->rxState == RxState::ACTIVE) { + this->rxState = RxState::FINISHED; + egu->TASKS_TRIGGER[0] = TRIGGER; + } + + // return to idle state + this->state = State::IDLE; + + // now the next transfer will start either on send by the master or on request by a node + } } diff --git a/software/system/src/nrf52/BusMasterImpl.hpp b/software/system/src/nrf52/BusMasterImpl.hpp index 46b5539..c6c9654 100644 --- a/software/system/src/nrf52/BusMasterImpl.hpp +++ b/software/system/src/nrf52/BusMasterImpl.hpp @@ -6,10 +6,19 @@ #include -class BusMasterImpl : public BusMaster, public Loop::Handler2 { +extern "C" { +void UARTE0_UART0_IRQHandler(); +void TIMER1_IRQHandler(); +} + +class BusMasterImpl : public BusMaster, public loop::Handler2 { + friend void UARTE0_UART0_IRQHandler(); + friend void TIMER1_IRQHandler(); public: /** * Constructor + * @param rxPin receive pin from LIN driver + * @param txPin transmit pin to LIN driver */ BusMasterImpl(int rxPin, int txPin); @@ -18,42 +27,64 @@ class BusMasterImpl : public BusMaster, public Loop::Handler2 { void handle() override; +protected: + void startReceive(const ReceiveParameters &p); + void startSend(const SendParameters &p); + void startBreak(); + void uartIrqHandler(); void timerIrqHandler(); -protected: - + int rxPin; int txPin; - NRF_UART_Type *uart; - NRF_TIMER_Type *timer; - enum State { + // state + enum class State : uint8_t { + // idle, waiting for receive/send by application or a request from a node + IDLE, + + // sending break signal BREAK, + PAUSE, + + // sending sync byte SYNC, - RX, - REQUEST - }; - State volatile state; - // transmit buffer - uint8_t const *volatile txData; - int volatile txLength; - int volatile txIndex; + // receive until timeout or send until last character + TRANSFER, - // receive buffer - uint8_t *volatile rxData; - int volatile rxLength; - int volatile rxIndex; - bool rxReady; - std::function onTransferred; + // finished, waiting for handling in event loop + FINISHED, + }; + volatile State state = State::IDLE; - // request buffer - uint8_t requestData[1]; - int volatile requestIndex; - bool requestReady; - std::function onRequest; + // rx + enum class RxState : uint8_t { + IDLE, + FINISHED, + PENDING, + ACTIVE + }; + volatile RxState rxState = RxState::IDLE; + uint8_t *rxBegin; + uint8_t *rxEnd; + uint8_t *rxData; + // tx + enum class TxState : uint8_t { + IDLE, + FINISHED, + PENDING, + ACTIVE, + END + }; + volatile TxState txState = TxState::IDLE; + const uint8_t *txBegin; + const uint8_t *txEnd; + const uint8_t *txData; + uint8_t txByte; + // lists for coroutines waiting for receive or send to complete Waitlist receiveWaitlist; Waitlist sendWaitlist; }; diff --git a/software/system/src/nrf52/Calendar.cpp b/software/system/src/nrf52/Calendar.cpp index 8a387e2..4b98667 100644 --- a/software/system/src/nrf52/Calendar.cpp +++ b/software/system/src/nrf52/Calendar.cpp @@ -26,7 +26,7 @@ uint8_t hours = 0; uint8_t weekday = 0; // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle() { if (NRF_RTC0->EVENTS_COMPARE[2]) { // clear pending interrupt flags at peripheral and NVIC @@ -61,7 +61,7 @@ void init() { return; // add to event loop handler chain - Calendar::nextHandler = Loop::addHandler(handle); + Calendar::nextHandler = loop::addHandler(handle); // use channel 2 of RTC0 NRF_RTC0->CC[2] = (NRF_RTC0->COUNTER + 16384 + 256) & ~16383; diff --git a/software/system/src/nrf52/FlashImpl.cpp b/software/system/src/nrf52/FlashImpl.cpp index ecf7a0b..f6e7379 100644 --- a/software/system/src/nrf52/FlashImpl.cpp +++ b/software/system/src/nrf52/FlashImpl.cpp @@ -1,11 +1,21 @@ #include "FlashImpl.hpp" #include "nrf52.hpp" +#include +/* + https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fmemory.html&cp=4_0_0_3_1_1&anchor=flash + https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fnvmc.html&cp=4_0_0_3_2 + + Resources: + NRF_NVMC +*/ FlashImpl::FlashImpl(uint32_t baseAddress, int sectorCount, int sectorSize) - : baseAddress(baseAddress), sectorCount(sectorCount), sectorSize(sectorSize & ~(PAGE_SIZE - 1)) + : baseAddress(baseAddress), sectorCount(sectorCount), sectorSize(sectorSize) { + assert((baseAddress & (PAGE_SIZE - 1)) == 0); + assert((sectorSize & (PAGE_SIZE - 1)) == 0); } Flash::Info FlashImpl::getInfo() { diff --git a/software/system/src/nrf52/FlashImpl.hpp b/software/system/src/nrf52/FlashImpl.hpp index e5c2410..ab5f836 100644 --- a/software/system/src/nrf52/FlashImpl.hpp +++ b/software/system/src/nrf52/FlashImpl.hpp @@ -10,7 +10,10 @@ */ class FlashImpl : public Flash { public: + // size of a page that has to be erased at once static constexpr int PAGE_SIZE = 4096; + + // size of a block that has to be written at once static constexpr int BLOCK_SIZE = 4; /** diff --git a/software/system/src/nrf52/Input.cpp b/software/system/src/nrf52/Input.cpp index b387f9b..58ecc91 100644 --- a/software/system/src/nrf52/Input.cpp +++ b/software/system/src/nrf52/Input.cpp @@ -1,5 +1,4 @@ #include "../Input.hpp" -#include "../Timer.hpp" #include "Loop.hpp" #include "gpio.hpp" #include @@ -42,7 +41,7 @@ State states[TRIGGER_COUNT]; Waitlist waitlist; // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle() { if (isInterruptPending(GPIOTE_IRQn)) { // debounce timeout after about 50ms @@ -128,14 +127,14 @@ void init() { // configure triggers if (TRIGGER_COUNT > 0) { - Timer::init(); + //Timer::init(); // check if already initialized if (Input::nextHandler != nullptr) return; // add to event loop handler chain - Input::nextHandler = Loop::addHandler(handle); + Input::nextHandler = loop::addHandler(handle); for (int index = 0; index < TRIGGER_COUNT; ++index) { auto &input = INPUTS[index]; diff --git a/software/system/src/nrf52/Loop.cpp b/software/system/src/nrf52/Loop.cpp index d9090fc..522246e 100644 --- a/software/system/src/nrf52/Loop.cpp +++ b/software/system/src/nrf52/Loop.cpp @@ -2,6 +2,18 @@ #include "nrf52.hpp" +/* + https://infocenter.nordicsemi.com/topic/struct_nrf52/struct/nrf52840.html + + Dependencies: + + Config: + + Resources: + NRF_RTC0 + CC[0] +*/ + // called by system/gcc_startup_nrf52840.S extern "C" { void SystemInit() { @@ -16,7 +28,19 @@ void SystemInit() { } } -namespace Loop { +namespace loop { + +// timer interval is 1024 seconds (2^24 / 16384Hz), given in milliseconds +constexpr int INTERVAL = 1024000; + +uint32_t baseTime = 0; + +// next timeout of a timer in the list +SystemTime next; + +// waiting coroutines +Waitlist waitlist; + // wait for event // see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHICBGB.html @@ -32,7 +56,7 @@ void waitForEvent() { Handler nextHandler = waitForEvent; Handler addHandler(Handler handler) { Handler h = nextHandler; - Loop::nextHandler = handler; + loop::nextHandler = handler; return h; } @@ -55,13 +79,47 @@ void init() { // disabled interrupts trigger an event and wake up the processor from WFE SCB->SCR = SCB->SCR | SCB_SCR_SEVONPEND_Msk; + + // initialize RTC0 + loop::next.value = INTERVAL - 1; + NRF_RTC0->CC[0] = next.value << 4; + NRF_RTC0->EVTENSET = N(RTC_EVTENSET_OVRFLW, Set); + NRF_RTC0->INTENSET = N(RTC_INTENSET_COMPARE0, Set); + NRF_RTC0->PRESCALER = 1; // 16384Hz + NRF_RTC0->TASKS_START = TRIGGER; } void run() { while (true) { + // check if a sleep time has elapsed + if (NRF_RTC0->EVENTS_COMPARE[0]) { + do { + // clear pending interrupt flags at peripheral and NVIC + NRF_RTC0->EVENTS_COMPARE[0] = 0; + clearInterrupt(RTC0_IRQn); + + auto time = loop::next; + loop::next.value += INTERVAL - 1; + + // resume all coroutines that where timeout occurred + loop::waitlist.resumeAll([time](SystemTime timeout) { + if (timeout == time) + return true; + + // check if this time is the next to elapse + if (timeout < loop::next) + loop::next = timeout; + return false; + }); + NRF_RTC0->CC[0] = ((loop::next.value - loop::baseTime) << (7 + 4)) / 125;//Timer::next.value << 4; + + // repeat until next timeout is in the future + } while (now() >= loop::next); + } + // call all handlers - auto it = Loop::handlers.begin(); - while (it != Loop::handlers.end()) { + auto it = loop::handlers.begin(); + while (it != loop::handlers.end()) { // increment iterator beforehand because a handler can remove() itself auto current = it; ++it; @@ -72,15 +130,44 @@ void run() { // waitForEvent(); // call handler chain of drivers - Loop::nextHandler(); + loop::nextHandler(); } } -void busyWait(int us) { +SystemTime now() { + // time resolution 1/1000 s + uint32_t counter = NRF_RTC0->COUNTER; + if (NRF_RTC0->EVENTS_OVRFLW) { + NRF_RTC0->EVENTS_OVRFLW = 0; + + // reload counter in case overflow happened after reading the counter + counter = NRF_RTC0->COUNTER; + + // advance base time by one interval (1024 seconds) + loop::baseTime += INTERVAL; + } + return {loop::baseTime + ((counter * 125 + 1024) >> (7 + 4))}; +} + +Awaitable sleep(SystemTime time) { + // check if this time is the next to elapse + if (time < loop::next) { + loop::next = time; + NRF_RTC0->CC[0] = ((time.value - loop::baseTime) << (7 + 4)) / 125; + } + + // check if timeout already elapsed + if (now() >= time) + NRF_RTC0->EVENTS_COMPARE[0] = GENERATED; + + return {loop::waitlist, time}; +} + +void sleepBlocking(int us) { auto cycles = us * 8; for (int i = 0; i < cycles; ++i) { __NOP(); } } -} // namespace Loop +} // namespace loop diff --git a/software/system/src/nrf52/Loop.hpp b/software/system/src/nrf52/Loop.hpp index 81cd46f..ec4230f 100644 --- a/software/system/src/nrf52/Loop.hpp +++ b/software/system/src/nrf52/Loop.hpp @@ -4,7 +4,7 @@ #include -namespace Loop { +namespace loop { using Handler = void (*)(); @@ -25,4 +25,4 @@ class Handler2 : public LinkedListNode { using HandlerList = LinkedList; extern HandlerList handlers; -} // namespace Loop +} // namespace loop diff --git a/software/system/src/nrf52/QuadratureDecoderImpl.cpp b/software/system/src/nrf52/QuadratureDecoderImpl.cpp index 45c2dc7..d3cc63e 100644 --- a/software/system/src/nrf52/QuadratureDecoderImpl.cpp +++ b/software/system/src/nrf52/QuadratureDecoderImpl.cpp @@ -26,7 +26,7 @@ QuadratureDecoderImpl::QuadratureDecoderImpl(int aPin, int bPin) { NRF_QDEC->TASKS_START = TRIGGER; // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable QuadratureDecoderImpl::change(int8_t& delta) { diff --git a/software/system/src/nrf52/QuadratureDecoderImpl.hpp b/software/system/src/nrf52/QuadratureDecoderImpl.hpp index e2a5634..6113b64 100644 --- a/software/system/src/nrf52/QuadratureDecoderImpl.hpp +++ b/software/system/src/nrf52/QuadratureDecoderImpl.hpp @@ -4,7 +4,7 @@ #include "Loop.hpp" -class QuadratureDecoderImpl : public QuadratureDecoder, public Loop::Handler2 { +class QuadratureDecoderImpl : public QuadratureDecoder, public loop::Handler2 { public: /** * Constructor diff --git a/software/system/src/nrf52/Radio.cpp b/software/system/src/nrf52/Radio.cpp index a90043a..cb7fadd 100644 --- a/software/system/src/nrf52/Radio.cpp +++ b/software/system/src/nrf52/Radio.cpp @@ -30,9 +30,9 @@ NRF_PPI CH[27]: RADIO->EVENTS_END -> TIMER0->TASKS_CAPTURE[2] NRF_EGU0 - TRIGGER[0]: energy detection - TRIGGER[1]: receive queue - TRIGGER[2]: send queue + TRIGGERED[0]: energy detection + TRIGGERED[1]: received a packet + TRIGGERED[2]: send operation has finished Glossary: CCA: Clear Channel Assessment (-> ED and/or carrier detection) @@ -543,7 +543,7 @@ void SendParameters::cancel() noexcept { // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle() { if (isInterruptPending(SWI0_EGU0_IRQn)) { // check energy detection @@ -565,7 +565,7 @@ void handle() { } } - // check receive queue + // check if a packet was received if (NRF_EGU0->EVENTS_TRIGGERED[1]) { NRF_EGU0->EVENTS_TRIGGERED[1] = 0; do { @@ -617,7 +617,7 @@ void init() { return; // add to event loop handler chain - Radio::nextHandler = Loop::addHandler(handle); + Radio::nextHandler = loop::addHandler(handle); // init random number generator Random::init(); diff --git a/software/system/src/nrf52/SpiMasterImpl.cpp b/software/system/src/nrf52/SpiMasterImpl.cpp index 4c31dd1..436cd91 100644 --- a/software/system/src/nrf52/SpiMasterImpl.cpp +++ b/software/system/src/nrf52/SpiMasterImpl.cpp @@ -17,14 +17,14 @@ // SpiMasterDevice -SpiMasterImpl::SpiMasterImpl(int index, int sckPin, int mosiPin, int misoPin, int dcPin) +SpiMasterImpl::SpiMasterImpl(int sckPin, int mosiPin, int misoPin, int dcPin) : misoPin(misoPin), sharedPin(misoPin == dcPin) { // configure SCK pin: output, low on idle gpio::configureOutput(sckPin); NRF_SPIM3->PSEL.SCK = sckPin; - // configure MOSI pin: output, high on ilde + // configure MOSI pin: output, high on idle gpio::setOutput(mosiPin, true); gpio::configureOutput(mosiPin); NRF_SPIM3->PSEL.MOSI = mosiPin; @@ -51,7 +51,7 @@ SpiMasterImpl::SpiMasterImpl(int index, int sckPin, int mosiPin, int misoPin, in | N(SPIM_CONFIG_ORDER, MsbFirst); // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } void SpiMasterImpl::handle() { @@ -60,7 +60,7 @@ void SpiMasterImpl::handle() { NRF_SPIM3->EVENTS_END = 0; clearInterrupt(SPIM3_IRQn); - // disable SPI + // disable SPI (indicates idle state) NRF_SPIM3->ENABLE = 0; // check for more transfers @@ -83,8 +83,8 @@ void SpiMasterImpl::startTransfer(const SpiMaster::Parameters &p) { // check if MISO and DC (data/command) are on the same pin if (this->sharedPin) { - if (channel.writeOnly) { - // write only: use MISO pin for DC (command/data) signal + if (channel.mode != Channel::Mode::NONE) { + // DC (data/command signal) overrides MISO, i.e. write-only mode NRF_SPIM3->PSEL.MISO = gpio::DISCONNECTED; NRF_SPIM3->PSELDCX = this->misoPin; //configureOutput(this->dcPin); // done automatically by hardware @@ -96,7 +96,7 @@ void SpiMasterImpl::startTransfer(const SpiMaster::Parameters &p) { } // set command/data length - NRF_SPIM3->DCXCNT = p.writeCount >> 31; // 0 for data and 0xf for command + NRF_SPIM3->DCXCNT = channel.mode == Channel::Mode::COMMAND ? 0xf : 0; // 0 for data and 0xf for command // set write data NRF_SPIM3->TXD.MAXCNT = p.writeCount; @@ -108,14 +108,14 @@ void SpiMasterImpl::startTransfer(const SpiMaster::Parameters &p) { // enable and start NRF_SPIM3->ENABLE = N(SPIM_ENABLE_ENABLE, Enabled); - NRF_SPIM3->TASKS_START = TRIGGER; + NRF_SPIM3->TASKS_START = TRIGGER; // -> END } // SpiMasterImpl::Channel -SpiMasterImpl::Channel::Channel(SpiMasterImpl &master, int csPin, bool writeOnly) - : master(master), csPin(csPin), writeOnly(writeOnly) +SpiMasterImpl::Channel::Channel(SpiMasterImpl &master, int csPin, Mode mode) + : master(master), csPin(csPin), mode(mode) { // configure CS pin: output, high on idle gpio::setOutput(csPin, true); @@ -127,8 +127,8 @@ Awaitable SpiMasterImpl::Channel::transfer(int writeCount { // start transfer immediately if SPI is idle if (!NRF_SPIM3->ENABLE) { - Parameters parameters{this, writeCount, writeData, readCount, readData}; - this->master.startTransfer(parameters); + Parameters p{this, writeCount, writeData, readCount, readData}; + this->master.startTransfer(p); } return {master.waitlist, this, writeCount, writeData, readCount, readData}; @@ -144,6 +144,10 @@ void SpiMasterImpl::Channel::transferBlocking(int writeCount, const void *writeD __NOP(); } + // clear pending interrupt flag and disable SPI + NRF_SPIM3->EVENTS_END = 0; + NRF_SPIM3->ENABLE = 0; + Parameters parameters{this, writeCount, writeData, readCount, readData}; this->master.startTransfer(parameters); @@ -155,5 +159,8 @@ void SpiMasterImpl::Channel::transferBlocking(int writeCount, const void *writeD if (!running) { NRF_SPIM3->EVENTS_END = 0; clearInterrupt(SPIM3_IRQn); + + // disable SPI + NRF_SPIM3->ENABLE = 0; } } diff --git a/software/system/src/nrf52/SpiMasterImpl.hpp b/software/system/src/nrf52/SpiMasterImpl.hpp index 1820a68..f30ad0c 100644 --- a/software/system/src/nrf52/SpiMasterImpl.hpp +++ b/software/system/src/nrf52/SpiMasterImpl.hpp @@ -5,9 +5,9 @@ #include "gpio.hpp" /** - * Implementation of SPI hardware interface for nrf52 platform + * Implementation of SPI hardware interface for nrf52 with multiple virtual channels */ -class SpiMasterImpl : public Loop::Handler2 { +class SpiMasterImpl : public loop::Handler2 { public: /** * Constructor @@ -15,9 +15,9 @@ class SpiMasterImpl : public Loop::Handler2 { * @param sckPin clock pin * @param mosiPin master out slave in pin * @param misoPin master in slave out pin - * @param dcPin data/command pin e.g. for displays + * @param dcPin data/command pin e.g. for displays, can be same as MISO for read-only devices */ - SpiMasterImpl(int index, int sckPin, int mosiPin, int misoPin, int dcPin = gpio::DISCONNECTED); + SpiMasterImpl(int sckPin, int mosiPin, int misoPin, int dcPin = gpio::DISCONNECTED); void handle() override; @@ -26,6 +26,8 @@ class SpiMasterImpl : public Loop::Handler2 { int misoPin; bool sharedPin; + + // list for coroutines waiting for transfer to complete Waitlist waitlist; @@ -34,13 +36,20 @@ class SpiMasterImpl : public Loop::Handler2 { */ class Channel : public SpiMaster { public: + // mode of data/command signal + enum class Mode { + NONE, + COMMAND, // set DC low + DATA // set DC high + }; + /** * Constructor * @param device the SPI device to operate on * @param csPin chip select pin of the slave - * @param writeOnly ture if we only write data and can use MISO as DC (data/command) signal (e.g. for a display) + * @param mode mode of data/command pin */ - Channel(SpiMasterImpl &master, int csPin, bool writeOnly = false); + Channel(SpiMasterImpl &master, int csPin, Mode mode = Mode::NONE); Awaitable transfer(int writeCount, const void *writeData, int readCount, void *readData) override; void transferBlocking(int writeCount, const void *writeData, int readCount, void *readData) override; @@ -48,6 +57,6 @@ class SpiMasterImpl : public Loop::Handler2 { SpiMasterImpl &master; int csPin; - bool writeOnly; + Mode mode; }; }; diff --git a/software/system/src/nrf52/Timer.cpp b/software/system/src/nrf52/Timer.cpp deleted file mode 100644 index ff921a0..0000000 --- a/software/system/src/nrf52/Timer.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "../Timer.hpp" -#include "Loop.hpp" -#include "nrf52.hpp" -#include - - -/* - https://infocenter.nordicsemi.com/topic/struct_nrf52/struct/nrf52840.html - - Dependencies: - - Config: - - Resources: - NRF_RTC0 - CC[0] -*/ -namespace Timer { - -// timer interval is 1024 seconds (2^24 / 16384Hz), given in milliseconds -constexpr int INTERVAL = 1024000; - -uint32_t baseTime = 0; - -// next timeout of a timer in the list -SystemTime next; - -// waiting coroutines -Waitlist waitlist; - - -// event loop handler chain -Loop::Handler nextHandler = nullptr; -void handle() { - if (NRF_RTC0->EVENTS_COMPARE[0]) { - do { - // clear pending interrupt flags at peripheral and NVIC - NRF_RTC0->EVENTS_COMPARE[0] = 0; - clearInterrupt(RTC0_IRQn); - - auto time = Timer::next; - Timer::next.value += INTERVAL - 1; - - // resume all coroutines that where timeout occurred - Timer::waitlist.resumeAll([time](SystemTime timeout) { - if (timeout == time) - return true; - - // check if this time is the next to elapse - if (timeout < Timer::next) - Timer::next = timeout; - return false; - }); - NRF_RTC0->CC[0] = ((Timer::next.value - Timer::baseTime) << (7 + 4)) / 125;//Timer::next.value << 4; - - // repeat until next timeout is in the future - } while (now() >= Timer::next); - } - - // call next handler in chain - Timer::nextHandler(); -} - -void init() { - // check if already initialized - if (Timer::nextHandler != nullptr) - return; - - // add to event loop handler chain - Timer::nextHandler = Loop::addHandler(handle); - - // initialize RTC0 - Timer::next.value = INTERVAL - 1; - NRF_RTC0->CC[0] = next.value << 4; - NRF_RTC0->EVTENSET = N(RTC_EVTENSET_OVRFLW, Set); - NRF_RTC0->INTENSET = N(RTC_INTENSET_COMPARE0, Set); - NRF_RTC0->PRESCALER = 1; // 16384Hz - NRF_RTC0->TASKS_START = TRIGGER; -} - -SystemTime now() { - // time resolution 1/1000 s - uint32_t counter = NRF_RTC0->COUNTER; - if (NRF_RTC0->EVENTS_OVRFLW) { - NRF_RTC0->EVENTS_OVRFLW = 0; - - // reload counter in case overflow happened after reading the counter - counter = NRF_RTC0->COUNTER; - - // advance base time by one interval (1024 seconds) - Timer::baseTime += INTERVAL; - } - return {Timer::baseTime + ((counter * 125 + 1024) >> (7 + 4))}; -} - -Awaitable sleep(SystemTime time) { - // check if this time is the next to elapse - if (time < Timer::next) { - Timer::next = time; - NRF_RTC0->CC[0] = ((time.value - Timer::baseTime) << (7 + 4)) / 125; - } - - // check if timeout already elapsed - if (now() >= time) - NRF_RTC0->EVENTS_COMPARE[0] = GENERATED; - - return {Timer::waitlist, time}; -} - -} // namespace Timer diff --git a/software/system/src/nrf52/UsbDevice.cpp b/software/system/src/nrf52/UsbDeviceImpl.cpp similarity index 63% rename from software/system/src/nrf52/UsbDevice.cpp rename to software/system/src/nrf52/UsbDeviceImpl.cpp index 84428ab..7ebd661 100644 --- a/software/system/src/nrf52/UsbDevice.cpp +++ b/software/system/src/nrf52/UsbDeviceImpl.cpp @@ -1,14 +1,10 @@ -#include "../UsbDevice.hpp" +#include "UsbDeviceImpl.hpp" #include "../Debug.hpp" #include "Loop.hpp" #include "nrf52.hpp" -#include -#include /* - usb overview: https://www.beyondlogic.org/usbnutshell/usb5.shtml - Dependencies: Config: @@ -19,100 +15,81 @@ Bugs: Workaround for Errata 199 needs to be added when using more than one endpoint: https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev2/ERR/nRF52840/Rev2/latest/anomaly_840_199.html */ -namespace UsbDevice { - -std::function getDescriptor; -std::function onSetConfiguration; -std::function onRequest; - -// endpoint 0 -uint8_t ep0Buffer[64] __attribute__((aligned(4))); -uint8_t const *ep0Data; -int ep0SendLength = 0; - -void ep0Send(void const *data, int length) { - auto d = reinterpret_cast(data); - int l = min(length, 64); - array::copy(ep0Buffer, ep0Buffer + l, d); - NRF_USBD->EPIN[0].PTR = intptr_t(ep0Buffer); - NRF_USBD->EPIN[0].MAXCNT = l; - ep0Data = d; - ep0SendLength = length; - - NRF_USBD->TASKS_STARTEPIN[0] = TRIGGER; -} -// endpoints 1 - 7 -struct Endpoint { - enum State : uint8_t { - IDLE, - - BUFFER, +UsbDeviceImpl::UsbDeviceImpl( + std::function const &getDescriptor, + std::function const &onSetConfiguration, + std::function const &onRequest) + : getDescriptor(getDescriptor), onSetConfiguration(onSetConfiguration), onRequest(onRequest) +{ + NRF_USBD->INTENSET = N(USBD_INTENSET_USBEVENT, Set) + | N(USBD_INTENSET_EP0SETUP, Set) + | N(USBD_INTENSET_EP0DATADONE, Set) + | N(USBD_INTENSET_EPDATA, Set) + | 0xef << USBD_INTENSET_ENDEPOUT1_Pos // OUT endpoint 1-7 + | 0xef << USBD_INTENSET_ENDEPIN1_Pos // IN endpoint 1-7 + | N(USBD_INTENSET_USBRESET, Set); - // data is transferred via usb from host to internal buffer - USB, + NRF_USBD->ENABLE = N(USBD_ENABLE_ENABLE, Enabled); - // data is transferred via dma from internal buffer to memory - DMA, + // add to list of handlers + loop::handlers.add(*this); +} - // wait for dma channel to be available (there is only one per endpoint) - WAIT - }; +void UsbDeviceImpl::enableEndpoints(uint8_t inFlags, uint8_t outFlags) { + NRF_USBD->EPINEN = inFlags; + NRF_USBD->EPOUTEN = outFlags; - // receive (OUT) - State receiveState = IDLE; - int maxReceiveLength; - int receiveLength = 0; - Waitlist receiveWaitlist; - void prepareReceive(int index, intptr_t data) { - // set pointer and wait for USB transfer into internal buffer - NRF_USBD->EPOUT[index].PTR = data; - this->receiveState = USB; - } - void startReceive(int index) { - // length of data in internal buffer - int bufferLength = NRF_USBD->SIZE.EPOUT[index]; - NRF_USBD->EPOUT[index].MAXCNT = min(this->receiveLength, bufferLength); - if (this->sendState == DMA) { - // DMA is currently in use: wait until DMA is free - this->receiveState = WAIT; - } else { - triggerReceive(index); + for (int index = 1; index < 8; ++index) { + if (inFlags & (1 << index)) { + NRF_USBD->DTOGGLE = index | N(USBD_DTOGGLE_IO, In) | N(USBD_DTOGGLE_VALUE, Data0); } - } - void triggerReceive(int index) { - // start DMA - NRF_USBD->TASKS_STARTEPOUT[index] = TRIGGER; - this->receiveState = DMA; - } + if (outFlags & (1 << index)) { + NRF_USBD->DTOGGLE = index | N(USBD_DTOGGLE_IO, Out) | N(USBD_DTOGGLE_VALUE, Data0); - // send (IN) - State sendState = IDLE; - int sendLength = 0; - Waitlist sendWaitlist; - void startSend(int index, intptr_t data) { - NRF_USBD->EPIN[index].PTR = data; - NRF_USBD->EPIN[index].MAXCNT = min(this->sendLength, 64); - if (this->receiveState == Endpoint::DMA) { - // DMA is currently in use: wait until DMA is free - this->sendState = Endpoint::WAIT; - } else { - triggerSend(index); + // write any value to start receiving OUT transfers into intermediate buffer + NRF_USBD->SIZE.EPOUT[index] = 0; } } - void triggerSend(int index) { - // start DMA - NRF_USBD->TASKS_STARTEPIN[index] = TRIGGER; - this->sendState = Endpoint::DMA; +} + +Awaitable UsbDeviceImpl::receive(int index, int &length, void *data) { + assert(index >= 1 && index <= ENDPOINT_COUNT); + auto& ep = this->endpoints[index - 1]; + + // check if usb receiver is idle or a buffer is ready + if (ep.receiveState <= Endpoint::BUFFER) { + // set receive data + ep.maxReceiveLength = length; + ep.receiveLength = length; + bool buffer = ep.receiveState == Endpoint::BUFFER; + ep.prepareReceive(index, intptr_t(data)); + + // we can immediately start receiving via DMA if data is already in the internal buffer + if (buffer) + ep.startReceive(index); // -> ENDEPOUT[index] } -}; -Endpoint endpoints[USB_ENDPOINT_COUNT - 1]; + return {ep.receiveWaitlist, length, data}; +} + +Awaitable UsbDeviceImpl::send(int index, int length, void const *data) { + assert(index >= 1 && index <= ENDPOINT_COUNT); + auto& ep = this->endpoints[index - 1]; + + // check if usb sender is idle + if (ep.sendState == Endpoint::IDLE) { + // set send data + ep.sendLength = length; + + // start first DMA transfer from memory to internal buffer + ep.startSend(index, intptr_t(data)); // -> ENDEPIN[index] + } + return {ep.sendWaitlist, length, data}; +} -// event loop handler chain -Loop::Handler nextHandler = nullptr; -void handle() { +void UsbDeviceImpl::handle() { if (isInterruptPending(USBD_IRQn)) { if (NRF_USBD->EVENTS_USBEVENT) { // clear pending interrupt flag at peripheral @@ -143,7 +120,7 @@ void handle() { } else if (bRequest == 0x09) { // set configuration uint8_t bConfigurationValue = NRF_USBD->WVALUEL; - UsbDevice::onSetConfiguration(bConfigurationValue); + this->onSetConfiguration(*this, bConfigurationValue); // enter status stage NRF_USBD->TASKS_EP0STATUS = TRIGGER; @@ -157,11 +134,11 @@ void handle() { if (bRequest == 0x06) { // get descriptor from user code by using the callback auto descriptorType = usb::DescriptorType(NRF_USBD->WVALUEH); - ConstData descriptor = UsbDevice::getDescriptor(descriptorType); + ConstData descriptor = this->getDescriptor(descriptorType); if (descriptor.size() > 0) { // send descriptor int wLength = (NRF_USBD->WLENGTHH << 8) | NRF_USBD->WLENGTHL; - int size = min(descriptor.size(), wLength); + int size = std::min(descriptor.size(), wLength); ep0Send(descriptor.data(), size); } else { // unsupported descriptor type: stall @@ -186,13 +163,13 @@ void handle() { NRF_USBD->TASKS_EP0STALL = TRIGGER; } break; - case usb::Request::VENDOR_INTERFACE_OUT: + case usb::Request::VENDOR_DEVICE_OUT: { int wValue = (NRF_USBD->WVALUEH << 8) | NRF_USBD->WVALUEL; int wIndex = (NRF_USBD->WINDEXH << 8) | NRF_USBD->WINDEXL; // let user code handle the request - bool result = UsbDevice::onRequest(bRequest, wValue, wIndex); + bool result = this->onRequest(bRequest, wValue, wIndex); if (result) { // enter status stage NRF_USBD->TASKS_EP0STATUS = TRIGGER; @@ -219,8 +196,9 @@ void handle() { // more to send ep0Data += sentCount; - int l = min(length, 64); - array::copy(ep0Buffer, ep0Buffer + l, ep0Data); + int l = std::min(length, 64); + //array::copy(ep0Buffer, ep0Buffer + l, ep0Data); + std::copy(ep0Data, ep0Data + l, ep0Buffer); NRF_USBD->EPIN[0].MAXCNT = l; NRF_USBD->TASKS_STARTEPIN[0] = TRIGGER; } else { @@ -234,13 +212,13 @@ void handle() { uint32_t EPDATASTATUS = NRF_USBD->EPDATASTATUS; // handle end of DMA transfers - for (int index = 1; index < USB_ENDPOINT_COUNT; ++index) { + for (int index = 1; index <= ENDPOINT_COUNT; ++index) { // check if we sent via DMA to IN endpoint buffer if (NRF_USBD->EVENTS_ENDEPIN[index]) { // clear pending interrupt flag at peripheral NRF_USBD->EVENTS_ENDEPIN[index] = 0; - auto& ep = UsbDevice::endpoints[index - 1]; + auto& ep = this->endpoints[index - 1]; // check if receiver waits for DMA if (ep.receiveState == Endpoint::WAIT) @@ -256,7 +234,7 @@ void handle() { // clear pending interrupt flag at peripheral NRF_USBD->EVENTS_ENDEPOUT[index] = 0; - auto& ep = UsbDevice::endpoints[index - 1]; + auto& ep = this->endpoints[index - 1]; // check if sender waits for DMA if (ep.sendState == Endpoint::WAIT) @@ -296,14 +274,14 @@ void handle() { if (EPDATASTATUS != 0) { // clear flags NRF_USBD->EPDATASTATUS = EPDATASTATUS; - for (int index = 1; index < USB_ENDPOINT_COUNT; ++index) { + for (int index = 1; index <= ENDPOINT_COUNT; ++index) { int inFlag = 1 << index; int outFlag = inFlag << 16; // EPDATA IN if (EPDATASTATUS & inFlag) { // finished send to host (IN) - auto &ep = UsbDevice::endpoints[index - 1]; + auto &ep = this->endpoints[index - 1]; // check if more to send int sentCount = NRF_USBD->EPIN[index].AMOUNT; @@ -334,7 +312,7 @@ void handle() { // EPDATA OUT if (EPDATASTATUS & outFlag) { // finished receive from host (OUT) - auto& ep = UsbDevice::endpoints[index - 1]; + auto& ep = this->endpoints[index - 1]; if (ep.receiveState <= Endpoint::BUFFER) { // idle: mark that data is already waiting in the buffer @@ -357,101 +335,4 @@ void handle() { // clear pending interrupt flag at NVIC clearInterrupt(USBD_IRQn); } - - // call next handler in chain - UsbDevice::nextHandler(); -} - -void init( - std::function const &getDescriptor, - std::function const &onSetConfiguration, - std::function const &onRequest) -{ - // check if already initialized - if (UsbDevice::nextHandler != nullptr) - return; - - // add to event loop handler chain - UsbDevice::nextHandler = Loop::addHandler(handle); - - UsbDevice::getDescriptor = getDescriptor; - UsbDevice::onSetConfiguration = onSetConfiguration; - UsbDevice::onRequest = onRequest; - - NRF_USBD->INTENSET = N(USBD_INTENSET_USBEVENT, Set) - | N(USBD_INTENSET_EP0SETUP, Set) - | N(USBD_INTENSET_EP0DATADONE, Set) - | N(USBD_INTENSET_EPDATA, Set) - | (1 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT1, Set) : 0) - | (2 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT2, Set) : 0) - | (3 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT3, Set) : 0) - | (4 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT4, Set) : 0) - | (5 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT5, Set) : 0) - | (6 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT6, Set) : 0) - | (7 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPOUT7, Set) : 0) - | (1 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN1, Set) : 0) - | (2 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN2, Set) : 0) - | (3 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN3, Set) : 0) - | (4 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN4, Set) : 0) - | (5 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN5, Set) : 0) - | (6 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN6, Set) : 0) - | (7 < USB_ENDPOINT_COUNT ? N(USBD_INTENSET_ENDEPIN7, Set) : 0) - | N(USBD_INTENSET_USBRESET, Set); - - NRF_USBD->ENABLE = N(USBD_ENABLE_ENABLE, Enabled); -} - -void enableEndpoints(uint8_t inFlags, uint8_t outFlags) { - NRF_USBD->EPINEN = inFlags; - NRF_USBD->EPOUTEN = outFlags; - - for (int index = 1; index < 8; ++index) { - if (inFlags & (1 << index)) { - NRF_USBD->DTOGGLE = index | N(USBD_DTOGGLE_IO, In) | N(USBD_DTOGGLE_VALUE, Data0); - } - if (outFlags & (1 << index)) { - NRF_USBD->DTOGGLE = index | N(USBD_DTOGGLE_IO, Out) | N(USBD_DTOGGLE_VALUE, Data0); - - // write any value to start receiving OUT transfers into intermediate buffer - NRF_USBD->SIZE.EPOUT[index] = 0; - } - } } - -Awaitable receive(int index, int &length, void *data) { - assert(index >= 1 && index < USB_ENDPOINT_COUNT); - auto& ep = UsbDevice::endpoints[index - 1]; - - // check if usb receiver is idle or a buffer is ready - if (ep.receiveState <= Endpoint::BUFFER) { - // set receive data - ep.maxReceiveLength = length; - ep.receiveLength = length; - bool buffer = ep.receiveState == Endpoint::BUFFER; - ep.prepareReceive(index, intptr_t(data)); - - // we can immediately start receiving via DMA if data is already in the internal buffer - if (buffer) - ep.startReceive(index); // -> ENDEPOUT[index] - } - - return {ep.receiveWaitlist, length, data}; -} - -Awaitable send(int index, int length, void const *data) { - assert(index >= 1 && index < USB_ENDPOINT_COUNT); - auto& ep = UsbDevice::endpoints[index - 1]; - - // check if usb sender is idle - if (ep.sendState == Endpoint::IDLE) { - // set send data - ep.sendLength = length; - - // start first DMA transfer from memory to internal buffer - ep.startSend(index, intptr_t(data)); // -> ENDEPIN[index] - } - - return {ep.sendWaitlist, length, data}; -} - -} // namespace UsbDevice diff --git a/software/system/src/nrf52/UsbDeviceImpl.hpp b/software/system/src/nrf52/UsbDeviceImpl.hpp new file mode 100644 index 0000000..c49a78f --- /dev/null +++ b/software/system/src/nrf52/UsbDeviceImpl.hpp @@ -0,0 +1,127 @@ +#include "../UsbDevice.hpp" +#include "Loop.hpp" +#include "nrf52.hpp" + + +/** + * Implementation of an SPI master that simply writes info about the transfer operations to Terminal::out + */ +class UsbDeviceImpl : public UsbDevice, public loop::Handler2 { +public: + // number of endpoints without endpoint 0 + static constexpr int ENDPOINT_COUNT = 7; + + /** + * Constructor + * @param getDescriptor callback for obtaining descriptors + * @param onSetConfiguration callback for setting the configuration (libusb_set_configuration() on host), always called from event loop + * @param onRequest callback for vendor specific request + */ + UsbDeviceImpl( + std::function const &getDescriptor, + std::function const &onSetConfiguration, + std::function const &onRequest); + + void enableEndpoints(uint8_t inFlags, uint8_t outFlags) override; + [[nodiscard]] Awaitable receive(int index, int &length, void *data) override; + [[nodiscard]] Awaitable send(int index, int length, void const *data) override; + + void handle() override; + +protected: + + std::function getDescriptor; + std::function onSetConfiguration; + std::function onRequest; + + + // endpoint 0 + uint8_t ep0Buffer[64] __attribute__((aligned(4))); + uint8_t const *ep0Data; + int ep0SendLength = 0; + + void ep0Send(void const *data, int length) { + auto d = reinterpret_cast(data); + int l = std::min(length, 64); + //array::copy(ep0Buffer, ep0Buffer + l, d); + std::copy(d, d + l, ep0Buffer); + NRF_USBD->EPIN[0].PTR = intptr_t(ep0Buffer); + NRF_USBD->EPIN[0].MAXCNT = l; + ep0Data = d; + ep0SendLength = length; + + NRF_USBD->TASKS_STARTEPIN[0] = TRIGGER; + } + + + // endpoints 1 - 7 + struct Endpoint { + enum State : uint8_t { + IDLE, + + BUFFER, + + // data is transferred via usb from host to internal buffer + USB, + + // data is transferred via dma from internal buffer to memory + DMA, + + // wait for dma channel to be available (there is only one per endpoint) + WAIT + }; + + // receive (OUT) + State receiveState = IDLE; + int maxReceiveLength; + int receiveLength = 0; + Waitlist receiveWaitlist; + + void prepareReceive(int index, intptr_t data) { + // set pointer and wait for USB transfer into internal buffer + NRF_USBD->EPOUT[index].PTR = data; + this->receiveState = USB; + } + + void startReceive(int index) { + // length of data in internal buffer + int bufferLength = NRF_USBD->SIZE.EPOUT[index]; + NRF_USBD->EPOUT[index].MAXCNT = std::min(this->receiveLength, bufferLength); + if (this->sendState == DMA) { + // DMA is currently in use: wait until DMA is free + this->receiveState = WAIT; + } else { + triggerReceive(index); + } + } + + void triggerReceive(int index) { + // start DMA + NRF_USBD->TASKS_STARTEPOUT[index] = TRIGGER; + this->receiveState = DMA; + } + + // send (IN) + State sendState = IDLE; + int sendLength = 0; + Waitlist sendWaitlist; + + void startSend(int index, intptr_t data) { + NRF_USBD->EPIN[index].PTR = data; + NRF_USBD->EPIN[index].MAXCNT = std::min(this->sendLength, 64); + if (this->receiveState == Endpoint::DMA) { + // DMA is currently in use: wait until DMA is free + this->sendState = Endpoint::WAIT; + } else { + triggerSend(index); + } + } + + void triggerSend(int index) { + // start DMA + NRF_USBD->TASKS_STARTEPIN[index] = TRIGGER; + this->sendState = Endpoint::DMA; + } + }; + Endpoint endpoints[ENDPOINT_COUNT]; +}; diff --git a/software/system/src/nrf52/nrf52.hpp b/software/system/src/nrf52/nrf52.hpp index c5a05f9..a47dce8 100644 --- a/software/system/src/nrf52/nrf52.hpp +++ b/software/system/src/nrf52/nrf52.hpp @@ -6,6 +6,7 @@ #include "system/nrf52840_bitfields.h" #pragma GCC diagnostic pop + // construct bitfield from value #define V(field, value) ((value) << field##_Pos) @@ -26,9 +27,7 @@ constexpr int GENERATED = 1; - -// nvic -// see NVIC_Type in system/core_cm4.h +// NVIC (nested vectored interrupt controller) inline void enableInterrupt(int n) { NVIC->ISER[n >> 5] = 1 << (n & 31); } diff --git a/software/system/src/posix/Calendar.cpp b/software/system/src/posix/Calendar.cpp index 0ac17b2..f3fcbf9 100644 --- a/software/system/src/posix/Calendar.cpp +++ b/software/system/src/posix/Calendar.cpp @@ -5,7 +5,7 @@ namespace Calendar { -class Context : public Loop::Timeout { +class Context : public loop::TimeHandler { public: void activate() override { // next activation in 1s @@ -29,8 +29,8 @@ void init() { return; Calendar::inited = true; - Calendar::context.time = Loop::now() + 1s; - Loop::timeouts.add(Calendar::context); + Calendar::context.time = loop::now() + 1s; + loop::timeHandlers.add(Calendar::context); } ClockTime now() { diff --git a/software/system/src/posix/File.hpp b/software/system/src/posix/File.hpp index e68f6a6..1278592 100644 --- a/software/system/src/posix/File.hpp +++ b/software/system/src/posix/File.hpp @@ -2,19 +2,39 @@ #include #include +#include +#ifdef _WIN32 +#define NOMINMAX +#include +#undef interface +#undef INTERFACE +#undef IN +#undef OUT +#else +#include #include #include -#include +#endif +namespace fs = std::filesystem; + class File { public: - enum class Mode { + enum class Mode : uint32_t { +#ifdef _WIN32 + READ = GENERIC_READ, + WRITE = GENERIC_WRITE, + READ_WRITE = READ | WRITE, + + TRUNCATE = 1 +#else READ = O_RDONLY, WRITE = O_WRONLY, READ_WRITE = O_RDWR, TRUNCATE = O_TRUNC +#endif }; /** @@ -22,27 +42,39 @@ class File { * @param name file name * @param mode combination of Mode elements */ - File(const std::string &name, Mode mode) { - this->fd = open(name.c_str(), O_CREAT | int(mode), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - } + File(const fs::path &path, Mode mode); /** * Destructor */ ~File() { - close(this->fd); +#ifdef _WIN32 + CloseHandle(this->file); +#else + close(this->file); +#endif } - bool isOpen() {return this->fd != -1;} + bool isOpen() { +#ifdef _WIN32 + return this->file != nullptr; +#else + return this->file != -1; +#endif + } /** * Get size of file * @return file size */ int getSize() { +#ifdef _WIN32 + return GetFileSize(this->file, nullptr); +#else struct stat stat; - fstat(this->fd, &stat); + fstat(this->file, &stat); return stat.st_size; +#endif } /** @@ -51,32 +83,72 @@ class File { */ void resize(int size, uint8_t value = 0) { if (value != 0) { - int fileSize = getSize(); - fill(fileSize, size - fileSize, value); + int currentSize = getSize(); + if (currentSize <= size) { + fill(currentSize, size - currentSize, value); + return; + } } - ftruncate(this->fd, size); +#ifdef _WIN32 + SetFilePointer(this->file, size, nullptr, FILE_BEGIN); + SetEndOfFile(this->file); +#else + ftruncate(this->file, size); +#endif } int read(int offset, int length, void *data) { - return pread(this->fd, data, length, offset); +#ifdef _WIN32 + SetFilePointer(this->file, offset, nullptr, FILE_BEGIN); + DWORD numRead; + ReadFile(this->file, data, length, &numRead, nullptr); + return numRead; +#else + return pread(this->file, data, length, offset); +#endif } int write(int offset, int length, const void *data) { - return pwrite(this->fd, data, length, offset); +#ifdef _WIN32 + SetFilePointer(this->file, offset, nullptr, FILE_BEGIN); + DWORD numWritten; + WriteFile(this->file, data, length, &numWritten, nullptr); + return numWritten; +#else + return pwrite(this->file, data, length, offset); +#endif } void fill(int offset, int length, uint8_t value) { uint8_t buffer[16] = {value, value, value, value, value, value, value, value, value, value, value, value, value, value, value, value}; int count = length >> 4; for (int i = 0; i < count; ++i) - pwrite(this->fd, buffer, 16, offset + i * 16); + write(offset + i * 16, 16, buffer); int remaining = length & 15; if (remaining > 0) - pwrite(this->fd, buffer, remaining, offset + count * 16); + write(offset + count * 16, remaining, buffer); } protected: - int fd; +#ifdef _WIN32 + HANDLE file; +#else + int file; +#endif }; FLAGS_ENUM(File::Mode) + +inline File::File(const fs::path &path, Mode mode) { +#ifdef _WIN32 + this->file = CreateFileW(path.c_str(), + int(mode & Mode::READ_WRITE), + FILE_SHARE_READ, + nullptr, // security + (mode & Mode::TRUNCATE) != 0 ? CREATE_ALWAYS : OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); +#else + this->file = open(path.c_str(), O_CREAT | int(mode), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); +#endif +} diff --git a/software/system/src/posix/Loop.cpp b/software/system/src/posix/Loop.cpp index 57ace80..a32f9fa 100644 --- a/software/system/src/posix/Loop.cpp +++ b/software/system/src/posix/Loop.cpp @@ -1,79 +1,17 @@ -#include "Loop.hpp" -#include +#include "Loop.inc.hpp" +#include -namespace Loop { +namespace loop { -FileDescriptor::~FileDescriptor() { +void init() { + initTimer(); } -FileDescriptorList fileDescriptors; -Timeout::~Timeout() { -} -TimeoutList timeouts; - -void runOnce(bool wait) { - // activate timeouts - SystemTime time; - bool activated; - do { - time = now(); - activated = false; - auto it = Loop::timeouts.begin(); - while (it != Loop::timeouts.end()) { - // increment iterator beforehand because a timer can remove() itself - auto current = it; - ++it; - - // check if timer needs to be activated - if (current->time <= time) { - current->activate(); - activated = true; - } - } - } while (activated); - - // get next timeout - auto next = time + SystemDuration::max(); - for (auto &timeout : Loop::timeouts) { - if (timeout.time < next) - next = timeout.time; - } - - // fill poll infos - struct pollfd infos[16]; - int count = 0; - for (auto &fileDescriptor : Loop::fileDescriptors) { - infos[count++] = {fileDescriptor.fd, fileDescriptor.events, 0}; - } - assert(count <= array::count(infos)); - - // poll - auto timeout = (next - time).value; - //Terminal::out << "timeout " << dec(timeout) << '\n'; - int r = poll(infos, count, (timeout > 0 && wait) ? timeout : 0); - - // activate file descriptors - if (r > 0) { - int i = 0; - auto it = Loop::fileDescriptors.begin(); - while (it != Loop::fileDescriptors.end()) { - // increment iterator beforehand because a file descriptor can remove() itself - auto current = it; - ++it; - - // check if file descriptor needs to be activated - auto events = infos[i].revents; - if (events != 0) - current->activate(events); - - // "garbage collect" file descriptors that are not interested in events anymore, also after close() was called - if (current->events == 0) - current->remove(); - - ++i; - } +void run() { + while (true) { + handleEvents(); } } -} // namespace Loop +} // namespace loop diff --git a/software/system/src/posix/Loop.hpp b/software/system/src/posix/Loop.hpp index 8d183a1..13e70b2 100644 --- a/software/system/src/posix/Loop.hpp +++ b/software/system/src/posix/Loop.hpp @@ -3,48 +3,36 @@ #include "../Loop.hpp" #include "SystemTime.hpp" #include -#include +#ifdef _WIN32 +using Socket = uint64_t; +#else +using Socket = int; +#endif -namespace Loop { - -// current time -inline SystemTime now() { - timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - return {uint32_t(time.tv_sec * 1000 + time.tv_nsec / 1000000)}; -} - +namespace loop { // list of file descriptors to observe readable/writable events (used in Network.cpp) -class FileDescriptor : public LinkedListNode { +class SocketHandler : public LinkedListNode { public: - virtual ~FileDescriptor(); + virtual ~SocketHandler(); virtual void activate(uint16_t events) = 0; - int fd = -1; + Socket socket = -1; short int events; }; -using FileDescriptorList = LinkedList; -extern FileDescriptorList fileDescriptors; - +using SocketHandlerList = LinkedList; +extern SocketHandlerList socketHandlers; // timeouts for Timer and Calendar -class Timeout : public LinkedListNode { +class TimeHandler : public LinkedListNode { public: - virtual ~Timeout(); + virtual ~TimeHandler(); virtual void activate() = 0; SystemTime time; }; -using TimeoutList = LinkedList; -extern TimeoutList timeouts; - - -/** - * Run the event loop only once - * @param wait wait for an event or timeout. Set to false when using a rendering loop, e.g. using GLFW - */ -void runOnce(bool wait = true); +using TimeHandlerList = LinkedList; +extern TimeHandlerList timeHandlers; -} // namespace Loop +} // namespace loop diff --git a/software/system/src/posix/Loop.inc.hpp b/software/system/src/posix/Loop.inc.hpp new file mode 100644 index 0000000..828b188 --- /dev/null +++ b/software/system/src/posix/Loop.inc.hpp @@ -0,0 +1,159 @@ +#include "Loop.hpp" +#ifdef _WIN32 +#define NOMINMAX +#include +#undef interface +#undef INTERFACE +#undef IN +#undef OUT +#define poll WSAPoll +#else +#include +#include +#endif + + +namespace loop { + +// SocketHandler + +SocketHandler::~SocketHandler() { +} +SocketHandlerList socketHandlers; + + +// TimeHandler + +TimeHandler::~TimeHandler() { +} +TimeHandlerList timeHandlers; + + + +class Timer : public TimeHandler { +public: + void activate() override { + auto time = this->time; + this->time += SystemDuration::max(); + + // resume all coroutines that where timeout occurred + this->waitlist.resumeAll([this, time](SystemTime timeout) { + if (timeout == time) + return true; + + // check if this time is the next to elapse + if (timeout < this->time) + this->time = timeout; + return false; + }); + } + + // waiting coroutines + Waitlist waitlist; +}; +Timer timer; + + +/** + * Platform dependent function: Initialize timer + * @param wait wait for an event or timeout. Set to false when using a rendering loop, e.g. when using GLFW + */ +static void initTimer() { + // check if not yet initialized + assert(!loop::timer.isInList()); + + loop::timer.time = loop::now() + SystemDuration::max(); + loop::timeHandlers.add(loop::timer); +} + +SystemTime now() { +#ifdef _WIN32 + return {timeGetTime()}; +#else + timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + return {uint32_t(time.tv_sec * 1000 + time.tv_nsec / 1000000)}; +#endif +} + +Awaitable sleep(SystemTime time) { + // check if initTimer() was called + assert(loop::timer.isInList()); + + // check if this time is the next to elapse + if (time < loop::timer.time) + loop::timer.time = time; + + return {loop::timer.waitlist, time}; +} + + +/** + * Platform dependent function: Handle events + * @param wait wait for an event or timeout. Set to false when using a rendering loop, e.g. when using GLFW + */ +static void handleEvents(bool wait = true) { + // activate timeouts + SystemTime time; + bool activated; + do { + time = loop::now(); + activated = false; + auto it = loop::timeHandlers.begin(); + while (it != loop::timeHandlers.end()) { + // increment iterator beforehand because a timer can remove() itself + auto current = it; + ++it; + + // check if timer needs to be activated + if (current->time <= time) { + current->activate(); + activated = true; + } + } + } while (activated); + + // get next timeout + auto next = time + SystemDuration::max(); + for (auto &handler : loop::timeHandlers) { + if (handler.time < next) + next = handler.time; + } + + // fill poll infos + struct pollfd infos[16]; + int count = 0; + for (auto &handler : loop::socketHandlers) { + infos[count++] = {handler.socket, handler.events, 0}; + } + assert(count <= array::count(infos)); + + // poll + auto timeout = (next - time).value; + //Terminal::out << "timeout " << dec(timeout) << '\n'; + int r = poll(infos, count, (timeout > 0 && wait) ? timeout : 0); + + // activate file descriptors + if (r > 0) { + int i = 0; + auto it = loop::socketHandlers.begin(); + while (it != loop::socketHandlers.end()) { + // increment iterator beforehand because a socket handler can remove() itself + auto current = it; + ++it; + + // check if file descriptor needs to be activated + auto events = infos[i].revents; + if (events != 0) + current->activate(events); + + // "garbage collect" file descriptors that are not interested in events anymore, also after close() was called + if (current->events == 0) + current->remove(); + + ++i; + } + } +} + +} // namespace loop diff --git a/software/system/src/posix/Loop2.cpp b/software/system/src/posix/Loop2.cpp deleted file mode 100644 index df024bf..0000000 --- a/software/system/src/posix/Loop2.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "Loop.hpp" -#include "../Timer.hpp" -#include -#include -#include -#include - - -namespace Loop { - -bool inited = false; - -void init() { - Loop::inited = true; -} - -void run() { - assert(Loop::inited); - while (true) { - runOnce(); - } -} - -} // namespace Loop diff --git a/software/system/src/posix/Network.cpp b/software/system/src/posix/Network.cpp index 8b10e49..6774c57 100644 --- a/software/system/src/posix/Network.cpp +++ b/software/system/src/posix/Network.cpp @@ -1,14 +1,25 @@ -#include "../Network.hpp" -#include "Loop.hpp" -#include -#include -#include +#ifdef _WIN32 +#define NOMINMAX +#include +#include +#undef interface +#undef INTERFACE +#undef IN +#undef OUT +#else #include #include #include #include #include #include +#include +inline void closesocket(int s) {close(s);} +#endif +#include "../Network.hpp" +#include "Loop.hpp" +#include +#include namespace Network { @@ -27,7 +38,7 @@ Address Address::fromString(String s) { } -class Context : public Loop::FileDescriptor { +class Context : public loop::SocketHandler { public: void activate(uint16_t events) override { if (events & POLLIN) { @@ -35,7 +46,7 @@ class Context : public Loop::FileDescriptor { // receive struct sockaddr_in6 source = {}; socklen_t length = sizeof(source); - auto receivedCount = recvfrom(this->fd, p.data, *p.length, 0, (struct sockaddr*)&source, &length); + auto receivedCount = recvfrom(this->socket, (char*)p.data, *p.length, 0, (struct sockaddr*)&source, &length); if (receivedCount >= 0) { *p.length = receivedCount; @@ -59,7 +70,7 @@ class Context : public Loop::FileDescriptor { destination.sin6_port = htons(p.destination->port); // send - auto sentCount = sendto(this->fd, p.data, p.length, 0, (struct sockaddr*)&destination, sizeof(destination)); + auto sentCount = sendto(this->socket, (const char *)p.data, p.length, 0, (struct sockaddr*)&destination, sizeof(destination)); return sentCount >= 0; }); if (this->sendWaitlist.isEmpty()) @@ -84,26 +95,35 @@ bool open(int index, uint16_t port) { assert(uint(index) < NETWORK_CONTEXT_COUNT); auto &context = Network::contexts[index]; + // assert that socket is not open already + assert(context.socket == -1); + // create socket - assert(context.fd == -1); - int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - fcntl(context.fd, F_SETFL, O_NONBLOCK); + auto s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); +#ifdef _WIN32 + u_long blocking = 0; + ioctlsocket(s, FIONBIO, &blocking); +#else + fcntl(s, F_SETFL, O_NONBLOCK); +#endif // set reuse address and port int reuse = 1; - //setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)); + //setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); + int broadcast = 1; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)); // bind to local port struct sockaddr_in6 address = {.sin6_family = AF_INET6, .sin6_port= htons(port)}; - if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0) { + if (bind(s, (struct sockaddr*)&address, sizeof(address)) < 0) { int e = errno; - ::close(fd); + closesocket(s); return false; } // set file descriptor - context.fd = fd; + context.socket = s; context.events = 0; return true; } @@ -111,13 +131,13 @@ bool open(int index, uint16_t port) { bool join(int index, Address const &multicastGroup) { assert(uint(index) < NETWORK_CONTEXT_COUNT); auto &context = Network::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); // join multicast group struct ipv6_mreq group; array::copy(16, group.ipv6mr_multiaddr.s6_addr, multicastGroup.u8); group.ipv6mr_interface = 0; - int r = setsockopt(context.fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &group, sizeof(group)); + int r = setsockopt(context.socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&group, sizeof(group)); if (r < 0) { int e = errno; return false; @@ -128,10 +148,10 @@ bool join(int index, Address const &multicastGroup) { void close(int index) { assert(uint(index) < NETWORK_CONTEXT_COUNT); auto &context = Network::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); - ::close(context.fd); - context.fd = -1; + closesocket(context.socket); + context.socket = -1; context.events = 0; // resume waiting coroutines @@ -149,13 +169,13 @@ void close(int index) { Awaitable receive(int index, Endpoint& source, int &length, void *data) { assert(uint(index) < NETWORK_CONTEXT_COUNT); auto &context = Network::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); context.events |= POLLIN; // add to event loop if necessary if (!context.isInList()) - Loop::fileDescriptors.add(context); + loop::socketHandlers.add(context); // add to wait list return {context.receiveWaitlist, &source, &length, data}; @@ -164,13 +184,13 @@ Awaitable receive(int index, Endpoint& source, int &length, v Awaitable send(int index, Endpoint const &destination, int length, void const *data) { assert(uint(index) < NETWORK_CONTEXT_COUNT); auto &context = Network::contexts[index]; - assert(context.fd != -1); + assert(context.socket != -1); context.events |= POLLOUT; // add to event loop if necessary if (!context.isInList()) - Loop::fileDescriptors.add(context); + loop::socketHandlers.add(context); // add to wait list return {context.sendWaitlist, &destination, length, data}; diff --git a/software/system/src/posix/Sound.cpp b/software/system/src/posix/Sound.cpp index d13da03..fe8c9da 100644 --- a/software/system/src/posix/Sound.cpp +++ b/software/system/src/posix/Sound.cpp @@ -61,7 +61,7 @@ std::vector types; // timeout to check if audio devices need to be stopped -class Timeout : public Loop::Timeout { +class Timeout : public loop::TimeHandler { public: void activate() override { // next activation in 1s @@ -209,8 +209,8 @@ void init() { Sound::inited = 2; - Sound::timeout.time = Loop::now() + 1s; - Loop::timeouts.add(Sound::timeout); + Sound::timeout.time = loop::now() + 1s; + loop::timeHandlers.add(Sound::timeout); } Array getTypes() { diff --git a/software/system/src/posix/SpiMasterImpl.cpp b/software/system/src/posix/SpiMasterImpl.cpp index 0db4173..9b9d26f 100644 --- a/software/system/src/posix/SpiMasterImpl.cpp +++ b/software/system/src/posix/SpiMasterImpl.cpp @@ -1,13 +1,13 @@ #include "SpiMasterImpl.hpp" -#include +#include "../Terminal.hpp" #include Awaitable SpiMasterImpl::transfer(int writeCount, void const *writeData, int readCount, void *readData) { if (!isInList()) { - this->time = Loop::now() + 100ms; // emulate 100ms transfer time - Loop::timeouts.add(*this); + this->time = loop::now() + 100ms; // emulate 100ms transfer time + loop::timeHandlers.add(*this); } return {this->waitlist, nullptr, writeCount, writeData, readCount, readData}; } diff --git a/software/system/src/posix/SpiMasterImpl.hpp b/software/system/src/posix/SpiMasterImpl.hpp index a07cd8d..d6e232a 100644 --- a/software/system/src/posix/SpiMasterImpl.hpp +++ b/software/system/src/posix/SpiMasterImpl.hpp @@ -6,7 +6,7 @@ /** * Implementation of an SPI master that simply writes info about the transfer operations to Terminal::out */ -class SpiMasterImpl : public SpiMaster, public Loop::Timeout { +class SpiMasterImpl : public SpiMaster, public loop::TimeHandler { public: /** * Constructor @@ -15,7 +15,7 @@ class SpiMasterImpl : public SpiMaster, public Loop::Timeout { explicit SpiMasterImpl(std::string name) : name(std::move(name)) { } - Awaitable transfer(int writeCount, void const *writeData, int readCount, void *readData) override; + [[nodiscard]] Awaitable transfer(int writeCount, void const *writeData, int readCount, void *readData) override; void transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) override; void activate() override; diff --git a/software/system/src/posix/StorageImpl.hpp b/software/system/src/posix/StorageImpl.hpp index d13d8d0..4c533ad 100644 --- a/software/system/src/posix/StorageImpl.hpp +++ b/software/system/src/posix/StorageImpl.hpp @@ -18,13 +18,13 @@ class StorageImpl : public Storage { */ StorageImpl(std::string const &filename, int maxId, int maxDataSize); - [[nodiscard]] virtual Awaitable read(int id, int &size, void *data, Status &status) override; - [[nodiscard]] virtual Awaitable write(int id, int size, void const *data, Status &status) override; - [[nodiscard]] virtual Awaitable clear(Status &status) override; + [[nodiscard]] Awaitable read(int id, int &size, void *data, Status &status) override; + [[nodiscard]] Awaitable write(int id, int size, void const *data, Status &status) override; + [[nodiscard]] Awaitable clear(Status &status) override; - virtual Status readBlocking(int id, int &size, void *data) override; - virtual Status writeBlocking(int id, int size, void const *data) override; - virtual Status clearBlocking() override; + Status readBlocking(int id, int &size, void *data) override; + Status writeBlocking(int id, int size, void const *data) override; + Status clearBlocking() override; protected: void readData(); diff --git a/software/system/src/posix/Terminal.cpp b/software/system/src/posix/Terminal.cpp index f087990..0bb5442 100644 --- a/software/system/src/posix/Terminal.cpp +++ b/software/system/src/posix/Terminal.cpp @@ -1,11 +1,24 @@ #include "../Terminal.hpp" -#include +#include +#include +//#include namespace Terminal { void write(int index, String const &str) { - int size = ::write(index, str.data, str.count()); + std::string s(str.data, str.count()); + switch (index) { + case 1: + std::cout << s; + std::cout.flush(); + break; + case 2: + std::cerr << s; + std::cerr.flush(); + break; + } + //int size = ::write(index, str.data, str.count()); } Stream out{1}; diff --git a/software/system/src/posix/Timer.cpp b/software/system/src/posix/Timer.cpp deleted file mode 100644 index 2bf7f90..0000000 --- a/software/system/src/posix/Timer.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../Timer.hpp" -#include "Loop.hpp" - - -namespace Timer { - -class Context : public Loop::Timeout { -public: - void activate() override { - auto time = this->time; - this->time += SystemDuration::max(); - - // resume all coroutines that where timeout occurred - this->waitlist.resumeAll([this, time](SystemTime timeout) { - if (timeout == time) - return true; - - // check if this time is the next to elapse - if (timeout < this->time) - this->time = timeout; - return false; - }); - } - - // waiting coroutines - Waitlist waitlist; -}; - -bool inited = false; -Context context; - -void init() { - // check if already initialized - if (Timer::inited) - return; - Timer::inited = true; - - Timer::context.time = now() + SystemDuration::max(); - Loop::timeouts.add(Timer::context); -} - -SystemTime now() { - return Loop::now(); -} - -Awaitable sleep(SystemTime time) { - // check if Timer::init() was called - assert(Timer::inited); - - // check if this time is the next to elapse - if (time < Timer::context.time) - Timer::context.time = time; - - return {Timer::context.waitlist, time}; -} - -} // namespace Timer diff --git a/software/system/src/posix/UsbDeviceImpl.cpp b/software/system/src/posix/UsbDeviceImpl.cpp new file mode 100644 index 0000000..ca603db --- /dev/null +++ b/software/system/src/posix/UsbDeviceImpl.cpp @@ -0,0 +1,89 @@ +#include "UsbDeviceImpl.hpp" +#include "../Terminal.hpp" +#include + + +UsbDeviceImpl::UsbDeviceImpl( + std::function const &getDescriptor, + std::function const &onSetConfiguration, + std::function const &onRequest) +{ + // get device descriptor + auto *deviceDescriptor = getDescriptor(usb::DescriptorType::DEVICE).cast(); + this->text = deviceDescriptor->bDeviceProtocol == 1; + + // set configuration + this->onSetConfiguration = onSetConfiguration; + //Loop::context.post([onSetConfiguration]() {onSetConfiguration(1);}); + + // call onSetConfiguration from event loop + this->time = loop::now(); + loop::timeHandlers.add(*this); +} + +void UsbDeviceImpl::enableEndpoints(uint8_t inFlags, uint8_t outFlags) { + Terminal::out << "enable in"; + for (int i = 0; i < 8; ++i) { + if (inFlags & (1 << i)) + Terminal::out << ' ' << dec(i); + } + Terminal::out << " out"; + for (int i = 0; i < 8; ++i) { + if (outFlags & (1 << i)) + Terminal::out << ' ' << dec(i); + } + Terminal::out << '\n'; +} + +Awaitable UsbDeviceImpl::receive(int index, int &length, void *data) { + assert(index == 1); + auto &endpoint = this->endpoints[index - 1]; + if (!isInList()) { + this->time = loop::now() + 10ms; // emulate 10ms transfer time + loop::timeHandlers.add(*this); + } + return {endpoint.receiveWaitlist, length, data}; +} + +Awaitable UsbDeviceImpl::send(int index, int length, void const *data) { + assert(index == 1); + auto &endpoint = this->endpoints[index - 1]; + if (!isInList()) { + this->time = loop::now() + 10ms; // emulate 10ms transfer time + loop::timeHandlers.add(*this); + } + return {endpoint.sendWaitlist, length, data}; +} + +void UsbDeviceImpl::activate() { + this->remove(); + + // call onSetConfiguration once + if (this->onSetConfiguration != nullptr) { + this->onSetConfiguration(*this, 1); + this->onSetConfiguration = nullptr; + } + + for (auto &endpoint : this->endpoints) { + + endpoint.sendWaitlist.resumeFirst([this](SendParameters p) { + if (this->text) { + Terminal::out << String(p.length, reinterpret_cast(p.data)); + } else { + // binary + Terminal::out << dec(p.length) << ": "; + for (int i = 0; i < p.length; ++i) { + if ((i & 15) == 0) { + if (i != 0) + Terminal::out << ",\n"; + } else { + Terminal::out << ", "; + } + Terminal::out << "0x" << hex(reinterpret_cast(p.data)[i]); + } + Terminal::out << '\n'; + } + return true; + }); + } +} diff --git a/software/system/src/posix/UsbDeviceImpl.hpp b/software/system/src/posix/UsbDeviceImpl.hpp new file mode 100644 index 0000000..25d6153 --- /dev/null +++ b/software/system/src/posix/UsbDeviceImpl.hpp @@ -0,0 +1,39 @@ +#include "../UsbDevice.hpp" +#include "Loop.hpp" + + +/** + * Implementation of an USB device that simply writes info about the transfer operations to Terminal::out + */ +class UsbDeviceImpl : public UsbDevice, public loop::TimeHandler { +public: + /** + * Constructor + * @param getDescriptor callback for obtaining descriptors + * @param onSetConfiguration callback for setting the configuration (libusb_set_configuration() on host), always called from event loop + * @param onRequest callback for vendor specific request + */ + UsbDeviceImpl( + std::function const &getDescriptor, + std::function const &onSetConfiguration, + std::function const &onRequest); + + void enableEndpoints(uint8_t inFlags, uint8_t outFlags) override; + [[nodiscard]] Awaitable receive(int index, int &length, void *data) override; + [[nodiscard]] Awaitable send(int index, int length, void const *data) override; + + void activate() override; + +protected: + + bool text; + std::function onSetConfiguration; + + // endpoints 1 - 7 + struct Endpoint { + Waitlist receiveWaitlist; + Waitlist sendWaitlist; + }; + + Endpoint endpoints[1]; +}; diff --git a/software/system/src/posix/libusb.hpp b/software/system/src/posix/libusb.hpp new file mode 100644 index 0000000..51b5826 --- /dev/null +++ b/software/system/src/posix/libusb.hpp @@ -0,0 +1,5 @@ +#define NOMINMAX +#include +#undef IN +#undef OUT +#undef READ_ATTRIBUTES diff --git a/software/system/src/stm32f0/BusNodeImpl.cpp b/software/system/src/stm32f0/BusNodeImpl.cpp index be9fa8c..4e44acb 100644 --- a/software/system/src/stm32f0/BusNodeImpl.cpp +++ b/software/system/src/stm32f0/BusNodeImpl.cpp @@ -1,19 +1,330 @@ #include "BusNodeImpl.hpp" +#include "defs.hpp" +#include "gpio.hpp" +#include "boardConfig.hpp" // CLOCK +#include "Debug.hpp" -BusNodeImpl::BusNodeImpl() { +/* + Reference manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + Data sheet: https://www.st.com/resource/en/datasheet/stm32f042f6.pdf https://www.st.com/resource/en/datasheet/stm32f051r8.pdf + + Resources: + USART1: Bus (reference manual section 27) + TIM16: Timeout (reference manual section 20) + IRQ17: Transfer complete (https://community.arm.com/support-forums/f/architectures-and-processors-forum/4070/using-interrupts-not-implemented-as-software-interrupts) +*/ + +using namespace gpio; + +constexpr int COMPLETE_IRQ = 17; +constexpr int REQUEST_INTERVAL = 50 * 20; // 50ms +constexpr int REQUEST_BYTE = 0xff; + +namespace { + +// for alternate functions, see Table 14 and 15 in data sheet +inline PinFunction rxFunction(int pin) { + assert(pin == PA(10) || pin == PB(7)); + return {pin, pin == PA(10) ? 1 : 0}; +} + +inline PinFunction txFunction(int pin) { + assert(pin == PA(9) || pin == PB(6)); + return {pin, pin == PA(9) ? 1 : 0}; +} + +// instance pointer used in interrupt handlers +BusNodeImpl *busNode; + +} + +BusNodeImpl::BusNodeImpl(int rxPin, int txPin) : rxPin(rxPin) { + const auto uart = USART1; + const auto timer = TIM16; + + // set instance pointer + busNode = this; + + // configure UART pins + configureAlternateOutput(rxFunction(rxPin)); + configureAlternateOutput(txFunction(txPin)); + + // initialize UART + RCC->APB2ENR = RCC->APB2ENR | RCC_APB2ENR_USART1EN; // clock enable + uart->BRR = (CLOCK + 9600) / 19200; // baud rate + //uart->CR2 = USART_CR2_LINEN // LIN enable + // | USART_CR2_LBDIE; // LIN break interrupt enable + uart->CR1 = USART_CR1_UE // enable + | USART_CR1_RE // rx enable + | USART_CR1_TE // tx enable + | USART_CR1_RXNEIE; // rx interrupt enable + enableInterrupt(USART1_IRQn); + + // initialize timer + RCC->APB2ENR = RCC->APB2ENR | RCC_APB2ENR_TIM16EN; // clock enable + timer->PSC = (CLOCK + 9600) / 19200 + 1; // prescaler for bit time (f = CLOCK / (PSC + 1) -> PSC = CLOCK / f - 1) + timer->DIER = TIM_DIER_CC1IE; // interrupt enable for CC1 + timer->CCR1 = REQUEST_INTERVAL; // set timeout, but do not start timer yet + enableInterrupt(TIM16_IRQn); + // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } Awaitable BusNodeImpl::receive(int &length, uint8_t *data) { + // start receiving immediately if no rx is pending and bus is idle + if (this->rxState == RxState::IDLE) { + ReceiveParameters p{&length, data}; + startReceive(p); + } + return {this->receiveWaitlist, &length, data}; } Awaitable BusNodeImpl::send(int length, uint8_t const *data) { + if (length <= 0) + return {}; + + // start sending immediately if no tx is pending and bus is idle + if (this->txState == TxState::IDLE) { + //Debug::toggleRedLed(); + SendParameters p{length, data}; + startSend(p); + } + return {this->sendWaitlist, length, data}; } void BusNodeImpl::handle() { + if (isInterruptPending(COMPLETE_IRQ)) { + // check if tx or rx operation has finished + if (this->txState == TxState::FINISHED) { + // send operation complete + this->txState = TxState::IDLE; + + // check if a next send operation is pending + this->sendWaitlist.visitSecond([this](const SendParameters &p) { + startSend(p); + }); + + // resume first waiting coroutine + this->sendWaitlist.resumeFirst([](const SendParameters &p) { + return true; + }); + } else if (this->rxState == RxState::FINISHED) { + // receive operation complete + int rxLength = this->rxData - this->rxBegin; + this->rxState = RxState::IDLE; + + // check if a next receive operation is pending + this->receiveWaitlist.visitSecond([this](const ReceiveParameters &p) { + startReceive(p); + }); + + // resume first waiting coroutine + this->receiveWaitlist.resumeFirst([rxLength](const ReceiveParameters &p) { + *p.length = rxLength; + return true; + }); + } + + // clear pending interrupt flag at NVIC + clearInterrupt(COMPLETE_IRQ); + } +} + +void BusNodeImpl::startReceive(const ReceiveParameters &p) { + this->rxState = RxState::PENDING; + this->rxBegin = p.data; + this->rxEnd = p.data + *p.length; + + // wait for master to start a transfer +} + +void BusNodeImpl::startSend(const SendParameters &p) { + this->txState = TxState::PENDING; + this->txBegin = p.data; + this->txEnd = p.data + p.length; + + // request a transfer when bus is idle + if (this->state == State::IDLE) + startRequest(); +} + +void BusNodeImpl::startRequest() { + const auto uart = USART1; + const auto timer = TIM16; + + // send a single start bit to inform the master that we want to send data + uart->TDR = REQUEST_BYTE; + + // repeat in case the master does not react + timer->CCR1 = REQUEST_INTERVAL; // set timeout + timer->CR1 = TIM_CR1_CEN; // start imer +} + + +// interrupt handlers + +void USART1_IRQHandler() { + busNode->usartIrqHandler(); +} + +void BusNodeImpl::usartIrqHandler() { + const auto uart = USART1; + const auto timer = TIM16; + + // stop and reset timer + timer->CR1 = 0; // stop timer + timer->EGR = TIM_EGR_UG; // reset timer + + // clear timer interrupt in case it triggered just before stop + timer->SR = ~TIM_SR_CC1IF; + clearInterrupt(TIM16_IRQn); + + // get interrupt status flags + auto isr = uart->ISR; + + if (isr & USART_ISR_LBDF) { + // break detected + uart->ICR = USART_ICR_LBDCF; + + // start receive timeout + timer->CCR1 = 20; // 2 characters = 20 bits + timer->CR1 = TIM_CR1_CEN; // start timer + + // now wait for sync + this->state = State::SYNC; + } + if (isr & USART_ISR_RXNE) { + // received a byte + uint8_t b = uart->RDR; + + switch (this->state) { + case State::IDLE: + // restart timer when tx is pending + if (this->txState >= TxState::PENDING) { + timer->CR1 = TIM_CR1_CEN; // restart timer + } + break; + case State::SYNC: + // check if sync byte was received correctly + if (b != 0x55) { + // go to error state and skip incoming bytes until a timeout occurs + this->state = State::ERROR; + } else { + // sync ok: now start to transfer data + if (this->rxState >= RxState::PENDING) { + this->rxState = RxState::ACTIVE; + this->rxData = this->rxBegin; + } + + // send first byte if tx is pending + if (this->txState >= TxState::PENDING) { + this->txState = TxState::ACTIVE; + auto data = this->txBegin; + this->txData = data + 1; + b = *data; + uart->TDR = b; + this->txByte = b; + } else { + // tx is idle: stop transmit + //uart->TASKS_STOPTX = TRIGGER; + } + + // now in transfer state + this->state = State::TRANSFER; + } + timer->CR1 = TIM_CR1_CEN; // restart timer + break; + case State::TRANSFER: + // received next byte + + // when tx is active, check if received byte is equal to the last sent byte + if (this->txState == TxState::ACTIVE) { + // check if byte was transferred ok and the current state of the bus is high + if (b == this->txByte && gpio::readInput(this->rxPin)) { + // byte was transferred ok + auto data = this->txData; + if (data < this->txEnd) { + // send next byte + this->txData = data + 1; + b = *data; + uart->TDR = b; + this->txByte = b; + } else { + // end: stop transmit and set tx state to success + //uart->TASKS_STOPTX = TRIGGER; + this->txState = TxState::END; + } + } else { + // error: stop transmit as probably another node won the bus arbitration + //uart->TASKS_STOPTX = TRIGGER; + this->txState = TxState::PENDING; + } + } else if (this->txState == TxState::END) { + // error: extra byte received after successful transmission + this->txState = TxState::PENDING; + } + + // check if receive is pending + if (this->rxState == RxState::ACTIVE) { + auto data = this->rxData; + if (data < this->rxEnd) { + // add next byte to receive buffer + this->rxData = data + 1; + *data = b; + } + } + + timer->CR1 = TIM_CR1_CEN; // restart timer + break; + case State::ERROR: + timer->CR1 = TIM_CR1_CEN; // restart timer + break; + } + } +} + +void TIM16_IRQHandler() { + busNode->timerIrqHandler(); +} + +void BusNodeImpl::timerIrqHandler() { + const auto timer = TIM16; + + // clear interrupt flag + timer->SR = ~TIM_SR_CC1IF; + + // stop and reset timer + timer->CR1 = 0; // stop timer + timer->EGR = TIM_EGR_UG; // reset timer + + switch (this->state) { + case State::TRANSFER: + // inform event loop if tx or rx operation has finished + if (this->txState == TxState::END) { + this->txState = TxState::FINISHED; + triggerInterrupt(COMPLETE_IRQ); + } else if (this->rxState == RxState::ACTIVE) { + this->rxState = RxState::FINISHED; + triggerInterrupt(COMPLETE_IRQ); + } + + // return to idle state + this->state = State::IDLE; + break; + default: + // timeout in IDLE, SYNC or ERROR state + debug::toggleGreen(); + + // return to idle state + this->state = State::IDLE; + // request a transfer when tx is pending + if (this->txState >= TxState::PENDING) + startRequest(); + break; + } } diff --git a/software/system/src/stm32f0/BusNodeImpl.hpp b/software/system/src/stm32f0/BusNodeImpl.hpp index 2a19894..acf87f6 100644 --- a/software/system/src/stm32f0/BusNodeImpl.hpp +++ b/software/system/src/stm32f0/BusNodeImpl.hpp @@ -4,12 +4,21 @@ #include "Loop.hpp" -class BusNodeImpl : public BusNode, public Loop::Handler2 { +extern "C" { +void USART1_IRQHandler(); +void TIM16_IRQHandler(); +} + +class BusNodeImpl : public BusNode, public loop::Handler2 { + friend void USART1_IRQHandler(); + friend void TIM16_IRQHandler(); public: /** * Constructor + * @param rxPin receive pin from LIN driver + * @param txPin transmit pin to LIN driver */ - BusNodeImpl(); + BusNodeImpl(int rxPin, int txPin); [[nodiscard]] Awaitable receive(int &length, uint8_t *data) override; [[nodiscard]] Awaitable send(int length, uint8_t const *data) override; @@ -17,6 +26,58 @@ class BusNodeImpl : public BusNode, public Loop::Handler2 { void handle() override; protected: + void startReceive(const ReceiveParameters &p); + void startSend(const SendParameters &p); + void startRequest(); + + void usartIrqHandler(); + void timerIrqHandler(); + + int rxPin; + + // state + enum class State : uint8_t { + // idle, waiting for receive/send by master and sending a request from time to time if send is pending + IDLE, + + // receiving sync byte + SYNC, + + // receive until timeout or send until last character + TRANSFER, + + // error when receiving sync byte + ERROR + }; + volatile State state = State::IDLE; + + // rx + enum class RxState : uint8_t { + IDLE, + FINISHED, + PENDING, + ACTIVE + }; + volatile RxState rxState = RxState::IDLE; + uint8_t *rxBegin; + uint8_t *rxEnd; + uint8_t *rxData; + + // tx + enum class TxState : uint8_t { + IDLE, + FINISHED, + PENDING, + ACTIVE, + END + }; + volatile TxState txState = TxState::IDLE; + const uint8_t *txBegin; + const uint8_t *txEnd; + const uint8_t *txData; + uint8_t txByte; + + // lists for coroutines waiting for receive or send to complete Waitlist receiveWaitlist; Waitlist sendWaitlist; }; diff --git a/software/system/src/stm32f0/FlashImpl.cpp b/software/system/src/stm32f0/FlashImpl.cpp index ad85337..41f3efc 100644 --- a/software/system/src/stm32f0/FlashImpl.cpp +++ b/software/system/src/stm32f0/FlashImpl.cpp @@ -1,13 +1,20 @@ #include "FlashImpl.hpp" #include "defs.hpp" +#include -// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fmemory.html&cp=4_0_0_3_1_1&anchor=flash -// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fnvmc.html&cp=4_0_0_3_2 +/* + Reference manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + + Resources: + FLASH: Flash controller (reference manual section 3) +*/ FlashImpl::FlashImpl(uint32_t baseAddress, int sectorCount, int sectorSize) - : baseAddress(baseAddress), sectorCount(sectorCount), sectorSize(sectorSize & ~(PAGE_SIZE - 1)) + : baseAddress(baseAddress), sectorCount(sectorCount), sectorSize(sectorSize) { + assert((baseAddress & (PAGE_SIZE - 1)) == 0); + assert((sectorSize & (PAGE_SIZE - 1)) == 0); } Flash::Info FlashImpl::getInfo() { @@ -23,6 +30,9 @@ void FlashImpl::eraseSectorBlocking(int sectorIndex) { // erase pages for (int i = 0; i < this->sectorSize; i += PAGE_SIZE) { + // set page erase mode + FLASH->CR = FLASH_CR_PER; + // set address of page to erase FLASH->AR = address + i; diff --git a/software/system/src/stm32f0/FlashImpl.hpp b/software/system/src/stm32f0/FlashImpl.hpp index 8ab59bf..a482e39 100644 --- a/software/system/src/stm32f0/FlashImpl.hpp +++ b/software/system/src/stm32f0/FlashImpl.hpp @@ -5,11 +5,13 @@ /** * Implementation of flash interface for stm32f0 - * See section 3 in reference manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf */ class FlashImpl : public Flash { public: + // size of a page that has to be erased at once static constexpr int PAGE_SIZE = 1024; + + // size of a block that has to be written at once static constexpr int BLOCK_SIZE = 2; /** diff --git a/software/system/src/stm32f0/Input.cpp b/software/system/src/stm32f0/Input.cpp index aef10d0..78a9e84 100644 --- a/software/system/src/stm32f0/Input.cpp +++ b/software/system/src/stm32f0/Input.cpp @@ -1,5 +1,4 @@ #include "../Input.hpp" -#include "../Timer.hpp" #include "Loop.hpp" #include "gpio.hpp" #include @@ -37,12 +36,12 @@ State states[TRIGGER_COUNT]; Waitlist waitlist; // event loop handler chain -Loop::Handler nextHandler = nullptr; +loop::Handler nextHandler = nullptr; void handle() { int PR = EXTI->PR; if ((PR & 0xffff) != 0) { // debounce timeout - auto timeout = Timer::now() + 50ms; + auto timeout = loop::now() + 50ms; for (int index = 0; index < TRIGGER_COUNT; ++index) { auto &input = INPUTS[index]; int pos = input.pin & 15; @@ -111,7 +110,7 @@ void handle() { TIM2->CCR2 = Input::next.value; // repeat until next timeout is in the future - } while (Timer::now() >= Input::next); + } while (loop::now() >= Input::next); } // call next handler in chain @@ -126,14 +125,14 @@ void init() { // configure triggers if (TRIGGER_COUNT > 0) { - Timer::init(); + loop::init(); // check if already initialized if (Input::nextHandler != nullptr) return; // add to event loop handler chain - Input::nextHandler = Loop::addHandler(handle); + Input::nextHandler = loop::addHandler(handle); for (int index = 0; index < TRIGGER_COUNT; ++index) { auto &input = INPUTS[index]; diff --git a/software/system/src/stm32f0/Loop.cpp b/software/system/src/stm32f0/Loop.cpp index 127b8df..3b47932 100644 --- a/software/system/src/stm32f0/Loop.cpp +++ b/software/system/src/stm32f0/Loop.cpp @@ -1,13 +1,34 @@ #include "Loop.hpp" #include "defs.hpp" +#include "../SystemTime.hpp" +#include +/* + refernece manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + + Dependencies: + + Config: + + Resources: + TIM2 + CCR1 +*/ + // called from system/startup_stm32f042x6.s to setup clock and flash before static constructors void SystemInit(void) { // leave clock in default configuration (8MHz) } -namespace Loop { +namespace loop { + +// next timeout of a timer in the list +SystemTime next; + +// waiting coroutines +Waitlist waitlist; + // wait for event // see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHICBGB.html @@ -24,7 +45,7 @@ void waitForEvent() { Handler nextHandler = waitForEvent; Handler addHandler(Handler handler) { Handler h = nextHandler; - Loop::nextHandler = handler; + loop::nextHandler = handler; return h; } @@ -34,13 +55,48 @@ void init() { // disabled interrupts trigger an event and wake up the processor from WFE // see chapter 5.3.3 in reference manual SCB->SCR = SCB->SCR | SCB_SCR_SEVONPEND_Msk; + + // initialize TIM2 + RCC->APB1ENR = RCC->APB1ENR | RCC_APB1ENR_TIM2EN; + loop::next.value = SystemDuration::max().value; + TIM2->CCR1 = loop::next.value; + TIM2->PSC = (CLOCK + 1000 / 2) / 1000 - 1; // prescaler for 1ms timer resolution + TIM2->EGR = TIM_EGR_UG; // update generation so that prescaler takes effect + TIM2->DIER = TIM_DIER_CC1IE; // interrupt enable for CC1 + TIM2->CR1 = TIM_CR1_CEN; // enable, count up } void run() { while (true) { + // check if a sleep time has elapsed + if (TIM2->SR & TIM_SR_CC1IF) { + do { + // clear pending interrupt flags at peripheral and NVIC + TIM2->SR = ~TIM_SR_CC1IF; + clearInterrupt(TIM2_IRQn); + + auto time = loop::next; + loop::next += SystemDuration::max(); + + // resume all coroutines that where timeout occurred + loop::waitlist.resumeAll([time](SystemTime timeout) { + if (timeout == time) + return true; + + // check if this time is the next to elapse + if (timeout < loop::next) + loop::next = timeout; + return false; + }); + TIM2->CCR1 = loop::next.value; + + // repeat until next timeout is in the future + } while (SystemTime(TIM2->CNT) >= loop::next); + } + // call all handlers - auto it = Loop::handlers.begin(); - while (it != Loop::handlers.end()) { + auto it = loop::handlers.begin(); + while (it != loop::handlers.end()) { // increment iterator beforehand because a handler can remove() itself auto current = it; ++it; @@ -51,8 +107,27 @@ void run() { // waitForEvent(); // call handler chain of drivers - Loop::nextHandler(); + loop::nextHandler(); } } -} // namespace Loop + +SystemTime now() { + return {TIM2->CNT}; +} + +Awaitable sleep(SystemTime time) { + // check if this time is the next to elapse + if (time < loop::next) { + loop::next = time; + TIM2->CCR1 = time.value; + } + + // check if timeout already elapsed + if (SystemTime(TIM2->CNT) >= time) + TIM2->EGR = TIM_EGR_CC1G; // trigger compare event + + return {loop::waitlist, time}; +} + +} // namespace loop diff --git a/software/system/src/stm32f0/Loop.hpp b/software/system/src/stm32f0/Loop.hpp index 9cf5f5a..d955d06 100644 --- a/software/system/src/stm32f0/Loop.hpp +++ b/software/system/src/stm32f0/Loop.hpp @@ -4,7 +4,7 @@ #include -namespace Loop { +namespace loop { using Handler = void (*)(); @@ -24,4 +24,4 @@ class Handler2 : public LinkedListNode { using HandlerList = LinkedList; extern HandlerList handlers; -} // namespace Loop +} // namespace loop diff --git a/software/system/src/stm32f0/SpiMasterImpl.cpp b/software/system/src/stm32f0/SpiMasterImpl.cpp index 76c435f..a97fa01 100644 --- a/software/system/src/stm32f0/SpiMasterImpl.cpp +++ b/software/system/src/stm32f0/SpiMasterImpl.cpp @@ -1,17 +1,23 @@ #include "SpiMasterImpl.hpp" #include "defs.hpp" +#include "gpio.hpp" #include /* + Reference manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + Data sheet: https://www.st.com/resource/en/datasheet/stm32f042f6.pdf + Dependencies: Config: SPI_CONTEXTS: Configuration of SPI channels (can share the same SPI peripheral) Resources: - SPI1 - TIM14 + SPI1: SPI master (reference manual section 28) + DMA1 (reference manual section 10) + Channel2: Read (reference manual table 29) + Channel3: Write GPIO CS-pins */ @@ -20,7 +26,7 @@ using namespace gpio; namespace { -// for alternate functions, see STM32F042x4 STM32F042x6 datasheet +// for alternate functions, see Table 14 and 15 in datasheet inline PinFunction sckFunction(int pin) { assert(pin == PA(5) || pin == PB(3)); return {pin, 0}; @@ -79,8 +85,9 @@ constexpr int SPI_CR1 = SPI_CR1_SPE | SPI_CR1_MSB_FIRST | SPI_CR1_DIV8; -constexpr int SPI_CR2 = SPI_CR2_DATA_SIZE(16) - | SPI_CR2_SSOE; +constexpr int SPI_CR2 = SPI_CR2_DATA_SIZE(8) + | SPI_CR2_FRXTH + | SPI_CR2_SSOE; // single master mode } @@ -93,67 +100,141 @@ SpiMasterImpl::SpiMasterImpl(int index, int sckPin, int mosiPin, int misoPin) { configureAlternateOutput(mosiFunction(mosiPin)); configureAlternateOutput(misoFunction(misoPin)); - // initialize TIM14 - RCC->APB1ENR = RCC->APB1ENR | RCC_APB1ENR_TIM14EN; - TIM14->DIER = TIM_DIER_CC1IE; // interrupt enable for CC1 + // initialize DMA + RCC->AHBENR = RCC->AHBENR | RCC_AHBENR_DMAEN; + DMA1_Channel2->CPAR = (uint32_t)&SPI1->DR; + DMA1_Channel3->CPAR = (uint32_t)&SPI1->DR; + RCC->AHBENR = RCC->AHBENR & ~RCC_AHBENR_DMAEN; // add to list of handlers - Loop::handlers.add(*this); + loop::handlers.add(*this); } void SpiMasterImpl::handle() { - if (TIM14->SR & TIM_SR_CC1IF) { - //if (SPI1->SR & SPI_SR_TXE) { - // set CS pin high - gpio::setOutput(this->csPin, true); - - // clear pending interrupt flags at peripheral and NVIC - TIM14->SR = ~TIM_SR_CC1IF; - clearInterrupt(TIM14_IRQn); - - // read - //int x = SPI1->DR; - - // disable timer and SPI - TIM14->CR1 = 0; - SPI1->CR1 = 0; - RCC->APB2ENR = RCC->APB2ENR & ~RCC_APB2ENR_SPI1EN; - - // check for more transfers - this->waitlist.visitSecond([this](const SpiMaster::Parameters &p) { - startTransfer(p); - }); - - // resume first waiting coroutine - this->waitlist.resumeFirst([](const SpiMaster::Parameters &p) { - return true; - }); + // check if read DMA has completed + if ((DMA1->ISR & DMA_ISR_TCIF2) != 0) { + // clear interrupt flags at DMA and NVIC + DMA1->IFCR = DMA_IFCR_CTCIF2; + clearInterrupt(DMA1_Ch2_3_DMA2_Ch1_2_IRQn); + + if (update()) { + // end of transfer + + // disable clocks + RCC->APB2ENR = RCC->APB2ENR & ~RCC_APB2ENR_SPI1EN; + RCC->AHBENR = RCC->AHBENR & ~RCC_AHBENR_DMAEN; + + // check for more transfers + this->waitlist.visitSecond([this](const SpiMaster::Parameters &p) { + startTransfer(p); + }); + + // resume first waiting coroutine + this->waitlist.resumeFirst([](const SpiMaster::Parameters &p) { + return true; + }); + } } } void SpiMasterImpl::startTransfer(const SpiMaster::Parameters &p) { auto &channel = *reinterpret_cast(p.config); - // initialize SPI + // enable clocks RCC->APB2ENR = RCC->APB2ENR | RCC_APB2ENR_SPI1EN; - SPI1->CR2 = SPI_CR2; - SPI1->CR1 = SPI_CR1; + RCC->AHBENR = RCC->AHBENR | RCC_AHBENR_DMAEN; - // set CS pin low and store it - gpio::setOutput(channel.csPin, false); + // set transfer state this->csPin = channel.csPin; + this->readCount = p.readCount; + this->readAddress = (uint32_t)p.readData; + this->writeCount = p.writeCount; + this->writeAddress = (uint32_t)p.writeData; + + // update transfer + update(); + + // now wait for DMA_ISR_TCIF2 flag +} + +bool SpiMasterImpl::update() { + if (this->readCount > 0) { + SPI1->CR1 = 0; + DMA1_Channel2->CCR = 0; + DMA1_Channel3->CCR = 0; + SPI1->CR2 = SPI_CR2 | SPI_CR2_RXDMAEN; // first enable receive DMA according to data sheet + if (this->writeCount > 0) { + // read and write + int count = min(this->readCount, this->writeCount); + DMA1_Channel2->CNDTR = count; + DMA1_Channel2->CMAR = this->readAddress; + DMA1_Channel2->CCR = DMA_CCR_EN // enable read channel + | DMA_CCR_TCIE // transfer complete interrupt enable + | DMA_CCR_MINC; // auto-increment memory + DMA1_Channel3->CNDTR = count; + DMA1_Channel3->CMAR = this->writeAddress; + DMA1_Channel3->CCR = DMA_CCR_EN // enable write channel + | DMA_CCR_DIR // read from memory + | DMA_CCR_MINC; // auto-increment memory + + this->readAddress += count; + this->writeAddress += count; + this->readCount -= count; + this->writeCount -= count; + } else { + // read only + DMA1_Channel2->CNDTR = this->readCount; + DMA1_Channel2->CMAR = this->readAddress; + DMA1_Channel2->CCR = DMA_CCR_EN // enable read channel + | DMA_CCR_TCIE // transfer complete interrupt enable + | DMA_CCR_MINC; // auto-increment memory + DMA1_Channel3->CNDTR = this->readCount; + DMA1_Channel3->CMAR = (uint32_t)&this->writeDummy; // write from dummy + DMA1_Channel3->CCR = DMA_CCR_EN // enable write channel + | DMA_CCR_DIR; // read from memory + + this->readCount = 0; + } + } else if (this->writeCount > 0) { + // write only + SPI1->CR1 = 0; + DMA1_Channel2->CCR = 0; + DMA1_Channel3->CCR = 0; + SPI1->CR2 = SPI_CR2 | SPI_CR2_RXDMAEN; // first enable receive DMA according to data sheet + + DMA1_Channel2->CNDTR = this->writeCount; + DMA1_Channel2->CMAR = (uint32_t)&this->readDummy; // read into dummy + DMA1_Channel2->CCR = DMA_CCR_EN // enable read channel + | DMA_CCR_TCIE; // transfer complete interrupt enable + DMA1_Channel3->CNDTR = this->writeCount; + DMA1_Channel3->CMAR = this->writeAddress; + DMA1_Channel3->CCR = DMA_CCR_EN // enable write channel + | DMA_CCR_DIR // read from memory + | DMA_CCR_MINC; // auto-increment memory + + this->writeCount = 0; + } else { + // end of transfer + + // set CS pin high + gpio::setOutput(this->csPin, true); + + // disable SPI and DMA + SPI1->CR1 = 0; + DMA1_Channel2->CCR = 0; + DMA1_Channel3->CCR = 0; - // prepare timer - TIM14->PSC = (2 << ((SPI_CR1 & SPI_CR1_BR_Msk) >> SPI_CR1_BR_Pos)) * 16 - 1; - TIM14->CCR1 = 1; - TIM14->EGR = TIM_EGR_UG; // clear + return true; + } - // write data - // todo: use DMA, now only the first word is transferred - SPI1->DR = *(uint16_t*)p.writeData; + // set CS pin low + gpio::setOutput(this->csPin, false); - // start timer - TIM14->CR1 = TIM_CR1_CEN; + // start SPI + SPI1->CR2 = SPI_CR2 | SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN; + SPI1->CR1 = SPI_CR1; + + return false; } @@ -171,7 +252,7 @@ Awaitable SpiMasterImpl::Channel::transfer(int writeCount void *readData) { // start transfer immediately if SPI is idle - if ((TIM14->CR1 & TIM_CR1_CEN) == 0) { + if (DMA1_Channel2->CCR == 0) { Parameters parameters{this, writeCount, writeData, readCount, readData}; this->master.startTransfer(parameters); } @@ -181,24 +262,35 @@ Awaitable SpiMasterImpl::Channel::transfer(int writeCount void SpiMasterImpl::Channel::transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) { // check if a transfer is running - bool running = (TIM14->CR1 & TIM_CR1_CEN) != 0; + bool running = DMA1_Channel2->CCR != 0; // wait for end of running transfer if (running) { - while (!(TIM14->SR & TIM_SR_CC1IF)) - __NOP(); + do { + while ((DMA1->ISR & DMA_ISR_TCIF2) == 0) + __NOP(); + } while (!this->master.update()); } + // clear pending interrupt flag and disable SPI and DMA + DMA1->IFCR = DMA_IFCR_CTCIF2; + Parameters parameters{this, writeCount, writeData, readCount, readData}; this->master.startTransfer(parameters); // wait for end of transfer - while (!(TIM14->SR & TIM_SR_CC1IF)) - __NOP(); + do { + while ((DMA1->ISR & DMA_ISR_TCIF2) == 0) + __NOP(); + } while (!this->master.update()); - // clear pending interrupt flags at peripheral and NVIC unless a transfer was running which gets handled in handle() + // clear pending interrupt flags at DMA and NVIC unless a transfer was running which gets handled in handle() if (!running) { - TIM14->SR = ~TIM_SR_CC1IF; - clearInterrupt(TIM14_IRQn); + DMA1->IFCR = DMA_IFCR_CTCIF2; + clearInterrupt(DMA1_Ch2_3_DMA2_Ch1_2_IRQn); + + // disable clocks + RCC->APB2ENR = RCC->APB2ENR & ~RCC_APB2ENR_SPI1EN; + RCC->AHBENR = RCC->AHBENR & ~RCC_AHBENR_DMAEN; } } diff --git a/software/system/src/stm32f0/SpiMasterImpl.hpp b/software/system/src/stm32f0/SpiMasterImpl.hpp index 62c55f4..5321a24 100644 --- a/software/system/src/stm32f0/SpiMasterImpl.hpp +++ b/software/system/src/stm32f0/SpiMasterImpl.hpp @@ -2,13 +2,12 @@ #include "../SpiMaster.hpp" #include "Loop.hpp" -#include "gpio.hpp" /** - * Implementation of SPI hardware interface for nrf52 platform with multiple virtual channels + * Implementation of SPI hardware interface for stm32f0 with multiple virtual channels */ -class SpiMasterImpl : public Loop::Handler2 { +class SpiMasterImpl : public loop::Handler2 { public: /** * Constructor @@ -45,6 +44,14 @@ class SpiMasterImpl : public Loop::Handler2 { protected: void startTransfer(SpiMaster::Parameters const &p); + bool update(); + + uint32_t readAddress; + int readCount; + uint32_t writeAddress; + int writeCount; + uint8_t readDummy; + uint8_t writeDummy = 0; int csPin; Waitlist waitlist; diff --git a/software/system/src/stm32f0/SpiMasterSingle16.cpp b/software/system/src/stm32f0/SpiMasterSingle16.cpp new file mode 100644 index 0000000..c7c1de7 --- /dev/null +++ b/software/system/src/stm32f0/SpiMasterSingle16.cpp @@ -0,0 +1,166 @@ +#include "SpiMasterSingle16.hpp" +#include "defs.hpp" +#include + + +/* + Reference manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + Data sheet: https://www.st.com/resource/en/datasheet/stm32f042f6.pdf + + Dependencies: + + Config: + SPI_CONTEXTS: Configuration of SPI channels (can share the same SPI peripheral) + + Resources: + SPI1: SPI master (reference manual section 28) + GPIO + CS-pin +*/ + +using namespace gpio; + +namespace { + +// for alternate functions, see STM32F042x4 STM32F042x6 datasheet +inline PinFunction sckFunction(int pin) { + assert(pin == PA(5) || pin == PB(3)); + return {pin, 0}; +} + +inline PinFunction mosiFunction(int pin) { + assert(pin == PA(7) || pin == PB(5)); + return {pin, 0}; +} + +inline PinFunction misoFunction(int pin) { + assert(pin == PA(6) || pin == PB(4)); + return {pin, 0}; +} + + +// CR1 register +// ------------ + +// SPI mode +constexpr int SPI_CR1_OFF = 0; +constexpr int SPI_CR1_FULL_DUPLEX_MASTER = SPI_CR1_MSTR; + +// SPI clock phase and polarity +constexpr int SPI_CR1_CPHA_0 = 0; +constexpr int SPI_CR1_CPHA_1 = SPI_CR1_CPHA; +constexpr int SPI_CR1_CPOL_0 = 0; +constexpr int SPI_CR1_CPOL_1 = SPI_CR1_CPOL; + +// SPI bit order +constexpr int SPI_CR1_LSB_FIRST = SPI_CR1_LSBFIRST; +constexpr int SPI_CR1_MSB_FIRST = 0; + +// SPI prescaler +constexpr int SPI_CR1_DIV2 = 0; +constexpr int SPI_CR1_DIV4 = (SPI_CR1_BR_0); +constexpr int SPI_CR1_DIV8 = (SPI_CR1_BR_1); +constexpr int SPI_CR1_DIV16 = (SPI_CR1_BR_1 | SPI_CR1_BR_0); +constexpr int SPI_CR1_DIV32 = (SPI_CR1_BR_2); +constexpr int SPI_CR1_DIV64 = (SPI_CR1_BR_2 | SPI_CR1_BR_0); +constexpr int SPI_CR1_DIV128 = (SPI_CR1_BR_2 | SPI_CR1_BR_1); +constexpr int SPI_CR1_DIV256 = (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0); + +// CR2 register +// ------------ + +// SPI data size (8, 16, 32) +constexpr int SPI_CR2_DATA_SIZE(int s) { return (s - 1) << SPI_CR2_DS_Pos; } + + +// configuration + +constexpr int SPI_CR1 = SPI_CR1_SPE + | SPI_CR1_FULL_DUPLEX_MASTER + | SPI_CR1_CPHA_1 | SPI_CR1_CPOL_0 // shift on rising edge, sample on falling edge + | SPI_CR1_MSB_FIRST + | SPI_CR1_DIV8; + +constexpr int SPI_CR2 = SPI_CR2_DATA_SIZE(16) + | SPI_CR2_RXNEIE // RXNE interrupt enable + | SPI_CR2_SSOE; // single master mode + +} + + +// SpiMasterSingle16 + +SpiMasterSingle16::SpiMasterSingle16(int sckPin, int mosiPin, int misoPin, int csPin) : csPin(csPin) { + // configure SPI pins (driven low when SPI is disabled) + configureAlternateOutput(sckFunction(sckPin)); + configureAlternateOutput(mosiFunction(mosiPin)); + configureAlternateOutput(misoFunction(misoPin)); + + // configure CS pin: output, high on idle + gpio::setOutput(csPin, true); + gpio::configureOutput(csPin); + + // initialize TIM14 + RCC->APB1ENR = RCC->APB1ENR | RCC_APB1ENR_TIM14EN; + + // add to list of handlers + Loop::handlers.add(*this); +} + +void SpiMasterSingle16::handle() { + if (SPI1->SR & SPI_SR_RXNE) { + // set CS pin high + gpio::setOutput(this->csPin, true); + + // read data and clear RXNE flag + uint16_t data = SPI1->DR; + + // clear pending interrupt flags at NVIC + clearInterrupt(SPI1_IRQn); + + // disable timer and SPI + SPI1->CR1 = 0; + RCC->APB2ENR = RCC->APB2ENR & ~RCC_APB2ENR_SPI1EN; + + // check for more transfers + this->waitlist.visitSecond([this](const SpiMaster::Parameters &p) { + startTransfer(p); + }); + + // resume first waiting coroutine + this->waitlist.resumeFirst([data](const SpiMaster::Parameters &p) { + *reinterpret_cast(p.readData) = data; + return true; + }); + } +} + +void SpiMasterSingle16::startTransfer(const SpiMaster::Parameters &p) { + // initialize SPI + RCC->APB2ENR = RCC->APB2ENR | RCC_APB2ENR_SPI1EN; + SPI1->CR2 = SPI_CR2; + SPI1->CR1 = SPI_CR1; + + // set CS pin low and store it + gpio::setOutput(this->csPin, false); + + // write data + SPI1->DR = *reinterpret_cast(p.writeData); + + // now wait for SPI_SR_RXNE flag +} + +Awaitable SpiMasterSingle16::transfer(int writeCount, void const *writeData, int readCount, + void *readData) +{ + // start transfer immediately if SPI is idle + if (SPI1->CR1 == 0) { + Parameters parameters{this, writeCount, writeData, readCount, readData}; + this->startTransfer(parameters); + } + + return {waitlist, this, writeCount, writeData, readCount, readData}; +} + +void SpiMasterSingle16::transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) { +} diff --git a/software/system/src/stm32f0/SpiMasterSingle16.hpp b/software/system/src/stm32f0/SpiMasterSingle16.hpp new file mode 100644 index 0000000..e5d6946 --- /dev/null +++ b/software/system/src/stm32f0/SpiMasterSingle16.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "../SpiMaster.hpp" +#include "Loop.hpp" +#include "gpio.hpp" + + +/** + * Implementation of SPI hardware interface for stm32f0 that transfers a single 16 bit word + */ +class SpiMasterSingle16 : public SpiMaster, public Loop::Handler2 { +public: + /** + * Constructor + * @param index index of device, 1-2 for SPI1 or SPI2 + * @param sckPin clock pin (SPI1: PA5 or PB3) + * @param mosiPin master out slave in pin (SPI1: PA7 or PB5) + * @param misoPin master in slave out pin (SPI1: PA6 or PB4) + */ + SpiMasterSingle16(int sckPin, int mosiPin, int misoPin, int csPin); + + void handle() override; + + // assumes writeCount and readCount to be 2 + Awaitable transfer(int writeCount, void const *writeData, int readCount, void *readData) override; + + // not implemented + void transferBlocking(int writeCount, void const *writeData, int readCount, void *readData) override; + +protected: + void startTransfer(SpiMaster::Parameters const &p); + + + int csPin; + + Waitlist waitlist; +}; diff --git a/software/system/src/stm32f0/Timer.cpp b/software/system/src/stm32f0/Timer.cpp deleted file mode 100644 index 75c6df1..0000000 --- a/software/system/src/stm32f0/Timer.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "../Timer.hpp" -#include "Loop.hpp" -#include -#include -#include - - -/* - refernece manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf - - Dependencies: - - Config: - - Resources: - TIM2 - CCR1 -*/ -namespace Timer { - -// next timeout of a timer in the list -SystemTime next; - -// waiting coroutines -Waitlist waitlist; - - -// event loop handler chain -Loop::Handler nextHandler = nullptr; -void handle() { - if (TIM2->SR & TIM_SR_CC1IF) { - do { - // clear pending interrupt flags at peripheral and NVIC - TIM2->SR = ~TIM_SR_CC1IF; - clearInterrupt(TIM2_IRQn); - - auto time = Timer::next; - Timer::next += SystemDuration::max(); - - // resume all coroutines that where timeout occurred - Timer::waitlist.resumeAll([time](SystemTime timeout) { - if (timeout == time) - return true; - - // check if this time is the next to elapse - if (timeout < Timer::next) - Timer::next = timeout; - return false; - }); - TIM2->CCR1 = Timer::next.value; - - // repeat until next timeout is in the future - } while (SystemTime(TIM2->CNT) >= Timer::next); - } - - // call next handler in chain - Timer::nextHandler(); -} - -void init() { - // check if already initialized - if (Timer::nextHandler != nullptr) - return; - - // add to event loop handler chain - Timer::nextHandler = Loop::addHandler(handle); - - // initialize TIM2 - RCC->APB1ENR = RCC->APB1ENR | RCC_APB1ENR_TIM2EN; - Timer::next.value = SystemDuration::max().value; - TIM2->CCR1 = Timer::next.value; - TIM2->PSC = (CLOCK + 1000 / 2) / 1000 - 1;//(CLOCK + 1024 / 2) / 1024 - 1; // prescaler - TIM2->EGR = TIM_EGR_UG; // update generation so that prescaler takes effect - TIM2->DIER = TIM_DIER_CC1IE; // interrupt enable for CC1 - TIM2->CR1 = TIM_CR1_CEN; // enable, count up -} - -SystemTime now() { - return {TIM2->CNT}; -} - -Awaitable sleep(SystemTime time) { - // check if this time is the next to elapse - if (time < Timer::next) { - Timer::next = time; - TIM2->CCR1 = time.value; - } - - // check if timeout already elapsed - if (SystemTime(TIM2->CNT) >= time) - TIM2->EGR = TIM_EGR_CC1G; // trigger compare event - - return {Timer::waitlist, time}; -} - -} // namespace Timer diff --git a/software/system/src/stm32f0/defs.hpp b/software/system/src/stm32f0/defs.hpp index 8411d86..f5e6a27 100644 --- a/software/system/src/stm32f0/defs.hpp +++ b/software/system/src/stm32f0/defs.hpp @@ -1,6 +1,7 @@ #pragma once #include "system/stm32f042x6.h" +//#include "system/stm32f051x8.h" @@ -18,6 +19,10 @@ inline bool isInterruptPending(int n) { return (NVIC->ISPR[n >> 5] & (1 << (n & 31))) != 0; } +inline void triggerInterrupt(int n) { + NVIC->ISPR[n >> 5] = 1 << (n & 31); +} + inline void clearInterrupt(int n) { NVIC->ICPR[n >> 5] = 1 << (n & 31); } diff --git a/software/system/src/stm32f0/gpio.hpp b/software/system/src/stm32f0/gpio.hpp index 38b33ad..9a65a42 100644 --- a/software/system/src/stm32f0/gpio.hpp +++ b/software/system/src/stm32f0/gpio.hpp @@ -4,8 +4,10 @@ //#include -// data sheet: https://www.st.com/resource/en/datasheet/stm32f042f6.pdf -// refernece manual: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf +/* + Data sheet: https://www.st.com/resource/en/datasheet/stm32f042f6.pdf + Refernece manual section 8: https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf +*/ namespace gpio { @@ -162,6 +164,9 @@ inline void configureAlternateInput(PinFunction pf, Pull pull = Pull::DISABLED) int pos2 = (pf.pin & 15) << 1; int pos4 = (pf.pin & 7) << 2; + // enable peripheral clock for the port + RCC->AHBENR = RCC->AHBENR | (1 << (RCC_AHBENR_GPIOAEN_Pos + (pf.pin >> 4))); + // set alternate function auto &AFR = port->AFR[(pf.pin >> 3) & 1]; AFR = (AFR & ~(15 << pos4)) | (pf.function << pos4); @@ -178,9 +183,12 @@ inline void configureAlternateOutput(PinFunction pf, Pull pull = Pull::DISABLED, { auto port = getPort(pf.pin); int pos = pf.pin & 15; - int pos2 = (pf.pin & 15) << 1; + int pos2 = pos << 1; int pos4 = (pf.pin & 7) << 2; + // enable peripheral clock for the port + RCC->AHBENR = RCC->AHBENR | (1 << (RCC_AHBENR_GPIOAEN_Pos + (pf.pin >> 4))); + // set alternate function auto &AFR = port->AFR[(pf.pin >> 3) & 1]; AFR = (AFR & ~(15 << pos4)) | (pf.function << pos4); diff --git a/software/system/src/stm32f0/system/stm32f042x6.h b/software/system/src/stm32f0/system/stm32f042x6.h index 937df54..94e14a1 100644 --- a/software/system/src/stm32f0/system/stm32f042x6.h +++ b/software/system/src/stm32f0/system/stm32f042x6.h @@ -40,7 +40,7 @@ extern "C" { #endif /* __cplusplus */ - /** @addtogroup Configuration_section_for_CMSIS +/** @addtogroup Configuration_section_for_CMSIS * @{ */ /** @@ -64,7 +64,7 @@ * in @ref Library_configuration_section */ - /*!< Interrupt Number Definition */ +/*!< Interrupt Number Definition */ typedef enum { /****** Cortex-M0 Processor Exceptions Numbers **************************************************************/ @@ -199,7 +199,7 @@ typedef struct uint32_t RESERVED4; /*!< Reserved, 0x218 */ __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ - CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ + CAN_FilterRegister_TypeDef sFilterRegister[14]; /*!< CAN Filter Register, Address offset: 0x240-0x2AC */ }CAN_TypeDef; /** @@ -704,7 +704,16 @@ typedef struct * @{ */ - /** @addtogroup Peripheral_Registers_Bits_Definition +/** @addtogroup Hardware_Constant_Definition + * @{ + */ +#define LSI_STARTUP_TIME 85U /*!< LSI Maximum startup time in us */ + +/** + * @} + */ + +/** @addtogroup Peripheral_Registers_Bits_Definition * @{ */ @@ -1621,48 +1630,6 @@ typedef struct #define CAN_FM1R_FBM13_Pos (13U) #define CAN_FM1R_FBM13_Msk (0x1UL << CAN_FM1R_FBM13_Pos) /*!< 0x00002000 */ #define CAN_FM1R_FBM13 CAN_FM1R_FBM13_Msk /*! #include #include #include @@ -73,7 +72,7 @@ Coroutine scan() { << hex(result.address.u8[3]) << ':' << hex(result.address.u8[2]) << ':' << hex(result.address.u8[1]) << ':' << hex(result.address.u8[0]) << '\n'; - Debug::toggleBlueLed(); + debug::toggleBlue(); Ble::open(0, result.address); @@ -235,13 +234,12 @@ Coroutine scan() { } int main() { - Loop::init(); - Timer::init(); + loop::init(); Ble::init(); Terminal::init(); Output::init(); // for debug led's scan(); - Loop::run(); + loop::run(); } diff --git a/software/system/test/BusMasterTest.cpp b/software/system/test/BusMasterTest.cpp index d05bfef..877ba37 100644 --- a/software/system/test/BusMasterTest.cpp +++ b/software/system/test/BusMasterTest.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -16,19 +15,18 @@ Coroutine transferBus(BusMaster &busMaster) { //int receiveLength = array::count(receive); - co_await Timer::sleep(1s); + co_await loop::sleep(1s); - Debug::toggleBlueLed(); + //Debug::toggleBlueLed(); } } int main() { - Loop::init(); - Timer::init(); + loop::init(); Output::init(); // for debug led's - Drivers drivers; + DriversBusMasterTest drivers; transferBus(drivers.busMaster); - Loop::run(); + loop::run(); } diff --git a/software/system/test/BusNodeTest.cpp b/software/system/test/BusNodeTest.cpp new file mode 100644 index 0000000..a84b70b --- /dev/null +++ b/software/system/test/BusNodeTest.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + + +uint8_t send[] = {0x01, 0x00, 0x0f, 0x33, 0x55, 0xaa, 0xcc, 0xf0}; +uint8_t receive[10]; + + +Coroutine transferBus(BusNode &busNode) { + while (true) { + co_await busNode.send(array::count(send), send); + + //int receiveLength = array::count(receive); + + co_await loop::sleep(1s); + + //Debug::toggleBlueLed(); + } +} + +int main() { + loop::init(); + Output::init(); // for debug led's + DriversBusNodeTest drivers; + + transferBus(drivers.busNode); + + loop::run(); +} diff --git a/software/system/test/CalendarTest.cpp b/software/system/test/CalendarTest.cpp index 323cb35..c6582e5 100644 --- a/software/system/test/CalendarTest.cpp +++ b/software/system/test/CalendarTest.cpp @@ -6,20 +6,20 @@ Coroutine handleSecondTick() { while (true) { co_await Calendar::secondTick(); - Debug::toggleBlueLed(); + debug::toggleBlue(); ClockTime time = Calendar::now(); - Debug::setRedLed((time.getMinutes() & 1) != 0); - Debug::setGreenLed((time.getHours() & 1) != 0); + debug::setRed((time.getMinutes() & 1) != 0); + debug::setGreen((time.getHours() & 1) != 0); } } int main() { - Loop::init(); + loop::init(); Calendar::init(); Output::init(); // for debug led's handleSecondTick(); - Loop::run(); + loop::run(); } diff --git a/software/system/test/FlashTest.cpp b/software/system/test/FlashTest.cpp index 99bb726..87e687d 100644 --- a/software/system/test/FlashTest.cpp +++ b/software/system/test/FlashTest.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -8,21 +7,25 @@ uint8_t writeData[] = {0x12, 0x34, 0x56, 0x78}; uint8_t readData[4]; -int main() { - Loop::init(); - Timer::init(); - Output::init(); // for debug led's +void test() { DriversFlashTest drivers; drivers.flash.readBlocking(0, 4, readData); if (array::equal(4, writeData, readData)) { // blue indicates that the data is there from the last run - Debug::setBlueLed(); + debug::setBlue(); // erase drivers.flash.eraseSectorBlocking(0); - while (true) {} + // also switch on red and green leds in case erase did not work + drivers.flash.readBlocking(0, 4, readData); + if (array::equal(4, writeData, readData)) { + debug::setRed(); + debug::setGreen(); + } + + return; } // erase @@ -35,11 +38,18 @@ int main() { drivers.flash.readBlocking(0, 4, readData); if (array::equal(4, writeData, readData)) { // green indicates that write and read was successful - Debug::setGreenLed(); + debug::setGreen(); } else { // read indicates that write or read failed - Debug::setRedLed(); + debug::setRed(); } +} + +int main() { + loop::init(); + Output::init(); // for debug led's + + test(); - while (true) {} + loop::run(); } diff --git a/software/system/test/InputTest.cpp b/software/system/test/InputTest.cpp index 795f90f..d89aec2 100644 --- a/software/system/test/InputTest.cpp +++ b/software/system/test/InputTest.cpp @@ -13,21 +13,21 @@ Coroutine handleInput() { co_await Input::trigger(1 << INPUT_POTI_BUTTON, 1 << INPUT_PCB_BUTTON, index, value); if (index == 0) { // rising edge on poti button detected - Debug::toggleRedLed(); + debug::toggleRed(); } else { // falling edge on pcb button detected - Debug::toggleGreenLed(); + debug::toggleGreen(); } } } int main() { - Loop::init(); + loop::init(); Output::init(); // for debug signals on pins Input::init(); - Drivers drivers; + //Drivers drivers; handleInput(); - Loop::run(); + loop::run(); } diff --git a/software/system/test/LoopTest.cpp b/software/system/test/LoopTest.cpp new file mode 100644 index 0000000..4ead622 --- /dev/null +++ b/software/system/test/LoopTest.cpp @@ -0,0 +1,48 @@ +#include +#include + + +Coroutine timer1() { + while (true) { + debug::setRed(true); + co_await loop::sleep(100ms); + + debug::setRed(false); + co_await loop::sleep(1900ms); + } +} + +Coroutine timer2() { + while (true) { + debug::toggleGreen(); + + auto timeout = loop::now() + 3s; + co_await loop::sleep(timeout); + + // test if sleep with elapsed timeout works + co_await loop::sleep(timeout); + } +} + +Coroutine timer3() { + while (true) { + debug::toggleBlue(); + + // test if time overflow works on nrf52 + auto time = loop::now(); + int i = int(time.value >> 20) & 3; + + co_await loop::sleep(500ms + i * 1s); + } +} + +int main() { + loop::init(); + Output::init(); // for debug signals on pins + + timer1(); + timer2(); + timer3(); + + loop::run(); +} diff --git a/software/system/test/NetworkTest.cpp b/software/system/test/NetworkTest.cpp index fb6ccf3..22341a6 100644 --- a/software/system/test/NetworkTest.cpp +++ b/software/system/test/NetworkTest.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -20,8 +19,8 @@ Coroutine sender() { uint8_t data[] = {1, 2, 3, 4}; while (true) { co_await Network::send(0, destination, array::count(data), data); - Debug::toggleRedLed(); - co_await Timer::sleep(1s); + debug::toggleRed(); + co_await loop::sleep(1s); } } @@ -31,7 +30,7 @@ Coroutine receiver() { while (true) { int count = 4; co_await Network::receive(0, source, count, data); - Debug::toggleGreenLed(); + debug::toggleGreen(); } } @@ -48,8 +47,7 @@ int main(int argc, char const **argv) { #else int main() { #endif - Loop::init(); - Timer::init(); + loop::init(); Network::init(); Output::init(); // for debug signals on pins @@ -60,5 +58,5 @@ int main() { sender(); receiver(); - Loop::run(); + loop::run(); } diff --git a/software/system/test/QuadratureDecoderTest.cpp b/software/system/test/QuadratureDecoderTest.cpp index b3b9cb9..caa5f29 100644 --- a/software/system/test/QuadratureDecoderTest.cpp +++ b/software/system/test/QuadratureDecoderTest.cpp @@ -25,9 +25,9 @@ Coroutine handleDecoder(QuadratureDecoder &decoder) { #ifdef DEBUG Terminal::out << "delta " << dec(delta) << '\n'; #endif - Debug::setRedLed(delta & 1); - Debug::setGreenLed(delta & 2); - Debug::setBlueLed(delta & 4); + debug::setRed(delta & 1); + debug::setGreen(delta & 2); + debug::setBlue(delta & 4); break; case 2: // button activated @@ -35,23 +35,23 @@ Coroutine handleDecoder(QuadratureDecoder &decoder) { Terminal::out << "activated " << dec(index) << '\n'; #endif if (index == 0) { - Debug::toggleRedLed(); + debug::toggleRed(); } else { - Debug::toggleGreenLed(); + debug::toggleGreen(); } - Debug::setBlueLed(false); + debug::setBlue(false); break; } } } int main() { - Loop::init(); + loop::init(); Output::init(); // for debug signals on pins Input::init(); Drivers drivers; handleDecoder(drivers.quadratureDecoder); - Loop::run(); + loop::run(); } diff --git a/software/system/test/RadioTest.cpp b/software/system/test/RadioTest.cpp index 03753c5..6ec5d17 100644 --- a/software/system/test/RadioTest.cpp +++ b/software/system/test/RadioTest.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -49,15 +48,15 @@ Coroutine send() { while (true) { uint8_t result; - co_await Timer::sleep(1s); + co_await loop::sleep(1s); // send over the air and increment mac counter co_await Radio::send(0, packet1, result); packet1[3]++; bool success = result != 0; - Debug::setRedLed(!success); - Debug::setGreenLed(success); + debug::setRed(!success); + debug::setGreen(success); // change channel /*radio::stop(); @@ -66,7 +65,7 @@ Coroutine send() { radio::enableReceiver(true);*/ - co_await Timer::sleep(1s); + co_await loop::sleep(1s); // send over the air and increment mac counter co_await Radio::send(0, packet2, result); @@ -81,22 +80,21 @@ Coroutine reply() { sendPacket[0] = array::count(sendPacket) - 1 - 1 + 2; while (true) { // wait for receive packet - Debug::setBlueLed(true); + debug::setBlue(true); co_await Radio::receive(0, receivePacket); - Debug::setBlueLed(false); + debug::setBlue(false); // reply - Debug::setRedLed(true); + debug::setRed(true); uint8_t result; co_await Radio::send(0, sendPacket, result); - Debug::setRedLed(false); + debug::setRed(false); } } int main() { - Loop::init(); - Timer::init(); + loop::init(); Radio::init(); Output::init(); @@ -113,6 +111,6 @@ int main() { send(); //reply(); - - Loop::run(); + + loop::run(); } diff --git a/software/system/test/RandomTest.cpp b/software/system/test/RandomTest.cpp index 9fdaa9f..ef83eac 100644 --- a/software/system/test/RandomTest.cpp +++ b/software/system/test/RandomTest.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include // device descriptor @@ -68,25 +68,24 @@ static const UsbConfiguration configurationDescriptor = { uint8_t sendData[16] __attribute__((aligned(4))); // send random numbers to host -Coroutine send() { +Coroutine send(UsbDevice &usb) { while (true) { // generate random numbers for (int i = 0; i < array::count(sendData); ++i) sendData[i] = Random::u8(); // send to host - co_await UsbDevice::send(1, array::count(sendData), sendData); - Debug::toggleBlueLed(); + co_await usb.send(1, array::count(sendData), sendData); + debug::toggleBlue(); - co_await Timer::sleep(1s); + co_await loop::sleep(1s); } } int main() { - Loop::init(); - Timer::init(); + loop::init(); Random::init(); - UsbDevice::init( + UsbDeviceImpl usb( [](usb::DescriptorType descriptorType) { switch (descriptorType) { case usb::DescriptorType::DEVICE: @@ -97,17 +96,17 @@ int main() { return ConstData(); } }, - [](uint8_t bConfigurationValue) { + [](UsbDevice &usb, uint8_t bConfigurationValue) { // enable bulk endpoints in 1 (keep control endpoint 0 enabled) - UsbDevice::enableEndpoints(1 | (1 << 1), 1); + usb.enableEndpoints(1 | (1 << 1), 1); // start to send random numbers to host - send(); + send(usb); }, [](uint8_t bRequest, uint16_t wValue, uint16_t wIndex) { return false; }); Output::init(); // for debug signals on pins - - Loop::run(); + + loop::run(); } diff --git a/software/system/test/SoundTest.cpp b/software/system/test/SoundTest.cpp index 739d7d9..fa5629c 100644 --- a/software/system/test/SoundTest.cpp +++ b/software/system/test/SoundTest.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -12,17 +11,16 @@ Coroutine soundTest() { while (true) { Sound::play(index); do { - co_await Timer::sleep(1s); + co_await loop::sleep(1s); } while (Sound::isPlaying(index)); - Debug::toggleBlueLed(); + debug::toggleBlue(); index = (index + 1) % Sound::getTypes().count(); } } int main() { - Loop::init(); - Timer::init(); + loop::init(); Sound::init(); Output::init(); // for debug led's @@ -37,5 +35,5 @@ int main() { soundTest(); - Loop::run(); + loop::run(); } diff --git a/software/system/test/SpiMasterTest.cpp b/software/system/test/SpiMasterTest.cpp index 04def35..50418a2 100644 --- a/software/system/test/SpiMasterTest.cpp +++ b/software/system/test/SpiMasterTest.cpp @@ -1,39 +1,43 @@ #include -#include #include #include -uint16_t spi0Data[] = {0x0a51, 0x0ff0}; +uint8_t spiWriteData[] = {0x0a, 0x55}; +uint8_t spiReadData[10]; -Coroutine transferSpi0(SpiMaster &spi) { +Coroutine transferSpi(SpiMaster &spi) { while (true) { - co_await spi.transfer(2, spi0Data, 0, nullptr); + co_await spi.transfer(2, spiWriteData, 10, spiReadData); //co_await Timer::sleep(100ms); //Debug::toggleRedLed(); } } -uint16_t spi1Command[] = {0x00ff, 0x3355}; -uint16_t spi1Data[] = {0x3355, 0x00ff}; +uint8_t command[] = {0x00, 0xff}; +uint8_t data[] = {0x33, 0x55}; -Coroutine transferSpi1(SpiMaster &spi) { +struct Spi { + SpiMaster &command; + SpiMaster &data; +}; + +Coroutine writeCommandData(Spi spi) { while (true) { - co_await spi.writeCommand(2, spi1Command); - co_await spi.writeData(2, spi1Data); + co_await spi.command.write(2, command); + co_await spi.data.write(2, data); } } int main() { - Loop::init(); + loop::init(); Output::init(); // for debug led's - Timer::init(); - Drivers drivers; + DriversSpiMasterTest drivers; - transferSpi0(drivers.airSensor); - transferSpi1(drivers.display); + transferSpi(drivers.transfer); + writeCommandData({drivers.command, drivers.data}); - Loop::run(); + loop::run(); } diff --git a/software/system/test/StorageTest.cpp b/software/system/test/StorageTest.cpp index 0165702..7d8c576 100644 --- a/software/system/test/StorageTest.cpp +++ b/software/system/test/StorageTest.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -20,7 +19,7 @@ struct Kiss32Random { c = 7654321; } - uint32_t draw() { + int draw() { // Linear congruence generator x = 69069 * x + 12345; @@ -34,24 +33,25 @@ struct Kiss32Random { c = t >> 32; z = (uint32_t) t; - return x + y + z; + return (x + y + z) & 0x7fffffff; } }; void fail() { - Debug::setRedLed(); - while (true) {} + debug::setRed(); + debug::setBlue(); } -int main() { - Loop::init(); - Timer::init(); - Output::init(); // for debug led's +void test() { + debug::setBlue(); DriversStorageTest drivers; + debug::clearBlue(); + // random generator for random data of random length Kiss32Random random; - auto start = Timer::now(); + // measure time + auto start = loop::now(); // table of currently stored elements int sizes[64]; @@ -60,7 +60,8 @@ int main() { // determine capacity auto info = drivers.flash.getInfo(); - unsigned int capacity = min(((info.sectorCount - 1) * (info.sectorSize - 8)) / (128 + 8), array::count(sizes)) - 1; + int capacity = min(((info.sectorCount - 1) * (info.sectorSize - 8)) / (128 + 8), array::count(sizes)) - 1; + Terminal::out << "capacity: " << dec(capacity) << '\n'; // clear storage drivers.storage.clearBlocking(); @@ -78,10 +79,10 @@ int main() { // check data if (readSize != size) - fail(); + return fail(); for (int j = 0; j < size; ++j) { if (buffer[j] != uint8_t(id + j)) - fail(); + return fail(); } } @@ -100,13 +101,22 @@ int main() { // store if (drivers.storage.writeBlocking(id, size, buffer) != Storage::Status::OK) - fail(); + return fail(); } - auto end = Timer::now(); + auto end = loop::now(); Terminal::out << dec(int((end - start) / 1s)) << "s\n"; - Debug::setGreenLed(); - while (true) {} + // ok + debug::setGreen(); +} + +int main() { + loop::init(); + Output::init(); // for debug led's + + test(); + + loop::run(); } diff --git a/software/system/test/TimerTest.cpp b/software/system/test/TimerTest.cpp deleted file mode 100644 index 9d8ef8f..0000000 --- a/software/system/test/TimerTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - - -Coroutine timer1() { - while (true) { - Debug::setRedLed(true); - co_await Timer::sleep(100ms); - - Debug::setRedLed(false); - co_await Timer::sleep(1900ms); - } -} - -Coroutine timer2() { - while (true) { - Debug::toggleGreenLed(); - - auto timeout = Timer::now() + 3s; - co_await Timer::sleep(timeout); - - // test if sleep with elapsed timeout works - co_await Timer::sleep(timeout); - } -} - -Coroutine timer3() { - while (true) { - Debug::toggleBlueLed(); - - // test if time overflow works on nrf52 - auto time = Timer::now(); - int i = int(time.value >> 20) & 3; - - co_await Timer::sleep(500ms + i * 1s); - } -} - -int main() { - Loop::init(); - Output::init(); // for debug signals on pins - Timer::init(); - - timer1(); - timer2(); - timer3(); - - Loop::run(); -} diff --git a/software/system/test/UsbDeviceTest.cpp b/software/system/test/UsbDeviceTest.cpp index 186ec9c..3f7c19a 100644 --- a/software/system/test/UsbDeviceTest.cpp +++ b/software/system/test/UsbDeviceTest.cpp @@ -1,11 +1,11 @@ #include -#include #include #include #include #include //#include "nrf52/nrf52.hpp" //#include "nrf52/FlashImpl.hpp" +#include // Test for USB device. @@ -42,7 +42,7 @@ struct UsbConfiguration { struct usb::ConfigDescriptor config; struct usb::InterfaceDescriptor interface; struct usb::EndpointDescriptor endpoints[2]; -} __attribute__((packed)); +}; static const UsbConfiguration configurationDescriptor = { .config = { @@ -86,20 +86,20 @@ static const UsbConfiguration configurationDescriptor = { constexpr int bufferSize = 128; -uint8_t buffer[bufferSize] __attribute__((aligned(4))); +uint8_t buffer[bufferSize];// __attribute__((aligned(4))); //FlashImpl flash{0xe0000 - 0x20000, 2, 4096}; //uint8_t writeData[] = {0x12, 0x34, 0x56, 0x78, 0x9a}; // echo data from host -Coroutine echo() { +Coroutine echo(UsbDevice &usb) { while (true) { // receive data from host int length = bufferSize; - co_await UsbDevice::receive(1, length, buffer); + co_await usb.receive(1, length, buffer); // set green led to indicate processing - Debug::setGreenLed(); + debug::setGreen(); // check received data bool error = false; @@ -108,10 +108,10 @@ Coroutine echo() { error = true; } if (error) - Debug::setColor(Debug::RED); + debug::set(debug::RED); // send data back to host - co_await UsbDevice::send(1, length, buffer); + co_await usb.send(1, length, buffer); /* // debug: send nrf52840 chip id @@ -144,15 +144,15 @@ Coroutine echo() { */ // clear green led and toggle blue led to indicate activity - Debug::clearGreenLed(); - Debug::toggleBlueLed(); + debug::clearGreen(); + debug::toggleBlue(); } } int main() { - Loop::init(); - Timer::init(); - UsbDevice::init( + loop::init(); + + UsbDeviceImpl usb( [](usb::DescriptorType descriptorType) { switch (descriptorType) { case usb::DescriptorType::DEVICE: @@ -163,24 +163,28 @@ int main() { return ConstData(); } }, - [](uint8_t bConfigurationValue) { + [](UsbDevice &usb, uint8_t bConfigurationValue) { // enable bulk endpoints 1 in and 1 out (keep control endpoint 0 enabled) - Debug::setGreenLed(true); - UsbDevice::enableEndpoints(1 | (1 << 1), 1 | (1 << 1)); + debug::setGreen(true); + usb.enableEndpoints(1 | (1 << 1), 1 | (1 << 1)); }, [](uint8_t bRequest, uint16_t wValue, uint16_t wIndex) { switch (Request(bRequest)) { case Request::RED: - Debug::setRedLed(wValue != 0); + debug::setRed(wValue != 0); // debug: erase flash and write //flash.eraseSectorBlocking(0); //flash.writeBlocking(0, 4, writeData + 1); break; case Request::GREEN: - Debug::setGreenLed(wIndex != 0); + //Debug::setLeds(wValue); + //Debug::toggleGreenLed(); + debug::setGreen(wIndex != 0); break; case Request::BLUE: - Debug::setBlueLed(wValue == wIndex); + //Debug::setLeds(wIndex); + //Debug::toggleBlueLed(); + debug::setBlue(wValue == wIndex); break; default: return false; @@ -190,7 +194,7 @@ int main() { Output::init(); // for debug led's // start to receive from usb host - echo(); + echo(usb); - Loop::run(); + loop::run(); } diff --git a/software/system/test/UsbTestHost.cpp b/software/system/test/UsbTestHost.cpp index 5f02f5f..7bee60b 100644 --- a/software/system/test/UsbTestHost.cpp +++ b/software/system/test/UsbTestHost.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "StringOperators.hpp" @@ -37,7 +37,7 @@ static void printDevices(libusb_device **devices) { } // https://github.com/libusb/libusb/blob/master/examples/testlibusb.c -/*static int printDevice(libusb_device *dev) { +static void printDevice(libusb_device *dev, int idVendor = 0, int idProduct = 0) { libusb_device_descriptor desc; libusb_device_handle *handle = NULL; unsigned char string[256]; @@ -46,90 +46,89 @@ static void printDevices(libusb_device **devices) { ret = libusb_get_device_descriptor(dev, &desc); if (ret < 0) { Terminal::err << "failed to get device descriptor\n"; - return -1; + return; } - printf("%04x:%04x\n", desc.idVendor, desc.idProduct); - printf("\tBus: %d\n", libusb_get_bus_number(dev)); - printf("\tDevice: %d\n", libusb_get_device_address(dev)); + if ((idVendor | idProduct) != 0 && (desc.idVendor != idVendor || desc.idProduct != idProduct)) + return; + + Terminal::out << hex(desc.idVendor) << ':' << hex(desc.idProduct) << '\n'; + Terminal::out << "\tBus: " << dec(libusb_get_bus_number(dev)) << '\n'; + Terminal::out << "\tDevice: " << dec(libusb_get_device_address(dev)) << '\n'; ret = libusb_open(dev, &handle); if (LIBUSB_SUCCESS == ret) { - printf("\tOpen\n"); + Terminal::out << "\tOpen\n"; // manufacturer if (desc.iManufacturer) { ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); if (ret > 0) - printf("\t\tManufacturer: %s\n", string); + Terminal::out << "\t\tManufacturer: " << str(string) << '\n'; } // product if (desc.iProduct) { ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); if (ret > 0) - printf("\t\tProduct: %s\n", string); + Terminal::out << "\t\tProduct: " << str(string) << '\n'; } libusb_close(handle); } else { - printf("\tOpen error: %d\n", ret); + Terminal::out << "\tOpen error: " << dec(ret) << '\n'; } - // configurations - for (int i = 0; i < desc.bNumConfigurations; i++) { - libusb_config_descriptor *config; - ret = libusb_get_config_descriptor(dev, i, &config); - if (LIBUSB_SUCCESS == ret) { - printf("\tConfiguration[%d]\n", i); - printf("\t\tTotalLength: %d\n", config->wTotalLength); - printf("\t\tNumInterfaces: %d\n", config->bNumInterfaces); - printf("\t\tConfigurationValue: %d\n", config->bConfigurationValue); - printf("\t\tConfiguration: %d\n", config->iConfiguration); - printf("\t\tAttributes: %02xh\n", config->bmAttributes); - printf("\t\tMaxPower: %d\n", config->MaxPower); - - } + // configurations + for (int i = 0; i < desc.bNumConfigurations; i++) { + libusb_config_descriptor *config; + ret = libusb_get_config_descriptor(dev, i, &config); + if (LIBUSB_SUCCESS == ret) { + Terminal::out << "\tConfiguration[" << dec(i) << "]\n"; + Terminal::out << "\t\tTotalLength: " << dec(config->wTotalLength) << '\n'; + Terminal::out << "\t\tNumInterfaces: " << dec(config->bNumInterfaces) << '\n'; + Terminal::out << "\t\tConfigurationValue: " << dec(config->bConfigurationValue) << '\n'; + Terminal::out << "\t\tConfiguration: " << dec(config->iConfiguration) << '\n'; + Terminal::out << "\t\tAttributes: " << hex(config->bmAttributes) << '\n'; + Terminal::out << "\t\tMaxPower: " << dec(config->MaxPower) << '\n'; + } + + // interfaces + for (int j = 0; j < config->bNumInterfaces; j++) { + libusb_interface const & interface = config->interface[j]; - // interfaces - for (int j = 0; j < config->bNumInterfaces; j++) { - libusb_interface const & interface = config->interface[j]; - - // alternate settings - for (int k = 0; k < interface.num_altsetting; k++) { - libusb_interface_descriptor const & descriptor = interface.altsetting[k]; - - printf("\t\tInterface[%d][%d]\n", j, k); - //printf("\t\t\tInterfaceNumber: %d\n", descriptor.bInterfaceNumber); - //printf("\t\t\tAlternateSetting: %d\n", descriptor.bAlternateSetting); - printf("\t\t\tNumEndpoints: %d\n", descriptor.bNumEndpoints); - printf("\t\t\tInterfaceClass: %d\n", descriptor.bInterfaceClass); - printf("\t\t\tInterfaceSubClass: %d\n", descriptor.bInterfaceSubClass); - printf("\t\t\tInterfaceProtocol: %d\n", descriptor.bInterfaceProtocol); - printf("\t\t\tInterface: %d\n", descriptor.iInterface); - - // endpoints - for (int l = 0; l < descriptor.bNumEndpoints; l++) { - libusb_endpoint_descriptor const & endpoint = descriptor.endpoint[l]; - - printf("\t\t\tEndpoint[%d]\n", l); - printf("\t\t\t\tEndpointAddress: %02xh\n", endpoint.bEndpointAddress); - printf("\t\t\t\tAttributes: %02xh\n", endpoint.bmAttributes); - printf("\t\t\t\tMaxPacketSize: %d\n", endpoint.wMaxPacketSize); - printf("\t\t\t\tInterval: %d\n", endpoint.bInterval); - printf("\t\t\t\tRefresh: %d\n", endpoint.bRefresh); - printf("\t\t\t\tSynchAddress: %d\n", endpoint.bSynchAddress); - } + // alternate settings + for (int k = 0; k < interface.num_altsetting; k++) { + libusb_interface_descriptor const & descriptor = interface.altsetting[k]; + + Terminal::out << "\t\tInterface[" << dec(j) << "][" << dec(k) << "]\n"; + //printf("\t\t\tInterfaceNumber: %d\n", descriptor.bInterfaceNumber); + //printf("\t\t\tAlternateSetting: %d\n", descriptor.bAlternateSetting); + Terminal::out << "\t\t\tNumEndpoints: " << dec(descriptor.bNumEndpoints) << '\n'; + Terminal::out << "\t\t\tInterfaceClass: " << dec(descriptor.bInterfaceClass) << '\n'; + Terminal::out << "\t\t\tInterfaceSubClass: " << dec(descriptor.bInterfaceSubClass) << '\n'; + Terminal::out << "\t\t\tInterfaceProtocol: " << dec(descriptor.bInterfaceProtocol) << '\n'; + Terminal::out << "\t\t\tInterface: " << dec(descriptor.iInterface) << '\n'; + + // endpoints + for (int l = 0; l < descriptor.bNumEndpoints; l++) { + libusb_endpoint_descriptor const & endpoint = descriptor.endpoint[l]; + + Terminal::out << "\t\t\tEndpoint[" << dec(l) << "]\n"; + Terminal::out << "\t\t\t\tEndpointAddress: " << hex(endpoint.bEndpointAddress) << '\n'; + Terminal::out << "\t\t\t\tAttributes: " << hex(endpoint.bmAttributes) << '\n'; + Terminal::out << "\t\t\t\tMaxPacketSize: " << dec(endpoint.wMaxPacketSize) << '\n'; + Terminal::out << "\t\t\t\tInterval: " << dec(endpoint.bInterval) << '\n'; + Terminal::out << "\t\t\t\tRefresh: " << dec(endpoint.bRefresh) << '\n'; + Terminal::out << "\t\t\t\tSynchAddress: " << dec(endpoint.bSynchAddress) << '\n'; } - } - - libusb_free_config_descriptor(config); + } - - - return 0; -}*/ + + libusb_free_config_descriptor(config); + } +} // vendor specific control request enum class Request : uint8_t { @@ -141,7 +140,7 @@ enum class Request : uint8_t { // sent vendor specific control request to usb device int controlOut(libusb_device_handle *handle, Request request, uint16_t wValue, uint16_t wIndex) { return libusb_control_transfer(handle, - uint8_t(usb::Request::OUT | usb::Request::TYPE_VENDOR | usb::Request::RECIPIENT_INTERFACE), + uint8_t(usb::Request::VENDOR_DEVICE_OUT), uint8_t(request), wValue, wIndex, @@ -169,9 +168,9 @@ int main() { } // print list of devices - //printDevices(devs); + printDevices(devices); for (int i = 0; devices[i]; ++i) { - //printDevice(devs[i]); + printDevice(devices[i], 0x1915, 0x1337); } // iterate over devices @@ -203,6 +202,15 @@ int main() { Terminal::err << "open error: " << dec(ret) << '\n'; continue; } + + if (libusb_kernel_driver_active(handle, 0) == 1) { + Terminal::out << "detach active kernel driver\n"; + ret = libusb_detach_kernel_driver(handle, 0); + if (ret != LIBUSB_SUCCESS) { + Terminal::out << "detach kernel driver error: " << dec(ret) << '\n'; + continue; + } + } // set configuration (reset alt_setting, reset toggles) ret = libusb_set_configuration(handle, 1); @@ -236,7 +244,7 @@ int main() { // flush out data from last run for (int i = 0; i < 4; ++i) - libusb_bulk_transfer(handle, 1 | usb::IN, buffer, 129, &transferred, 100); + ret = libusb_bulk_transfer(handle, 1 | usb::IN, buffer, 129, &transferred, 100); // echo loop: send data to device and check if we get back the same data int sendLength = 128; diff --git a/software/tools/src/ieeeSniffer.cpp b/software/tools/src/ieeeSniffer.cpp index 10f02ec..3596bfd 100644 --- a/software/tools/src/ieeeSniffer.cpp +++ b/software/tools/src/ieeeSniffer.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include // logs ieee 802.15.4 traffic to a .pcap file @@ -927,7 +927,7 @@ void handleZcl(PacketReader &r, uint8_t destinationEndpoint) { int controlTransfer(libusb_device_handle *handle, Radio::Request request, uint16_t wValue, uint16_t wIndex) { return libusb_control_transfer(handle, - uint8_t(usb::Request::OUT | usb::Request::TYPE_VENDOR | usb::Request::RECIPIENT_INTERFACE), + uint8_t(usb::Request::VENDOR_DEVICE_OUT), uint8_t(request), wValue, wIndex, nullptr, 0, 1000); } diff --git a/software/tools/src/mdnsSniffer.cpp b/software/tools/src/mdnsSniffer.cpp index f0589a5..46ff2b9 100644 --- a/software/tools/src/mdnsSniffer.cpp +++ b/software/tools/src/mdnsSniffer.cpp @@ -243,7 +243,7 @@ Coroutine sniffer(FILE *file) { * See https://wiki.wireshark.org/HowToDissectAnything to dissect DNS application layer in Wireshark */ int main(int argc, char const *argv[]) { - Loop::init(); + loop::init(); Network::init(); Terminal::init(); @@ -284,7 +284,7 @@ int main(int argc, char const *argv[]) { sniffer(file); Terminal::out << "waiting for mDNS packets on port 5353 ...\n"; - Loop::run(); + loop::run(); } } else { // read from file diff --git a/software/tools/src/radioDevice.cpp b/software/tools/src/radioDevice.cpp index 7155b01..90447a4 100644 --- a/software/tools/src/radioDevice.cpp +++ b/software/tools/src/radioDevice.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -143,12 +142,12 @@ static_assert(RADIO_CONTEXT_COUNT * 2 <= array::count(configurationDescriptor.en Barrier<> barriers[RADIO_CONTEXT_COUNT][256]; // receive from radio and send to usb host -Coroutine receive(int index) { +Coroutine receive(UsbDevice &usb, int index) { Radio::Packet packet; while (true) { // receive from radio co_await Radio::receive(index, packet); - Debug::setGreenLed(true); + debug::setGreen(true); // length without crc but with extra data int length = packet[0] - 2 + Radio::RECEIVE_EXTRA_LENGTH; @@ -156,27 +155,27 @@ Coroutine receive(int index) { // check if packet has minimum length (2 bytes frame control and extra data) if (length >= 2 + Radio::RECEIVE_EXTRA_LENGTH) { // send to usb host - co_await UsbDevice::send(1 + index, length, packet + 1); // IN + co_await usb.send(1 + index, length, packet + 1); // IN } - Debug::setGreenLed(false); + debug::setGreen(false); } } // receive from usb host and send to radio -Coroutine send(int index) { +Coroutine send(UsbDevice &usb, int index) { Radio::Packet packet; while (true) { // receive from usb host int length = RADIO_MAX_PAYLOAD_LENGTH + Radio::SEND_EXTRA_LENGTH; - co_await UsbDevice::receive(1 + index, length, packet + 1); // OUT + co_await usb.receive(1 + index, length, packet + 1); // OUT if (length == 1) { // cancel by mac counter uint8_t macCounter = packet[1]; barriers[index][macCounter].resumeAll(); } else if (length >= 2 + Radio::SEND_EXTRA_LENGTH) { - Debug::setRedLed(true); + debug::setRed(true); // set length to first byte (subtract extra data but add space for crc) packet[0] = length - Radio::SEND_EXTRA_LENGTH + 2; @@ -192,19 +191,18 @@ Coroutine send(int index) { // send mac counter and result back to usb host packet[0] = macCounter; packet[1] = result; - co_await UsbDevice::send(1 + index, 2, packet); // IN + co_await usb.send(1 + index, 2, packet); // IN } - Debug::setRedLed(false); + debug::setRed(false); } } } int main(void) { - Loop::init(); - Timer::init(); + loop::init(); Radio::init(); - UsbDevice::init( + UsbDeviceImpl usb( [](usb::DescriptorType descriptorType) { switch (descriptorType) { case usb::DescriptorType::DEVICE: @@ -215,10 +213,10 @@ int main(void) { return ConstData(); } }, - [](uint8_t bConfigurationValue) { + [](UsbDevice &usb, uint8_t bConfigurationValue) { // enable bulk endpoints (keep control endpoint 0 enabled in both directions) int flags = ~(0xffffffff << (1 + RADIO_CONTEXT_COUNT)); - UsbDevice::enableEndpoints(flags, flags); + usb.enableEndpoints(flags, flags); }, [](uint8_t bRequest, uint16_t wValue, uint16_t wIndex) { switch (Radio::Request(bRequest)) { @@ -271,10 +269,10 @@ int main(void) { // start coroutines to send and receive for (int index = 0; index < RADIO_CONTEXT_COUNT; ++index) { for (int i = 0; i < 64; ++i) { - receive(index); - send(index); + receive(usb, index); + send(usb, index); } } - Loop::run(); + loop::run(); } diff --git a/software/tools/src/terminal.cpp b/software/tools/src/terminal.cpp index 5337449..b6a45c2 100644 --- a/software/tools/src/terminal.cpp +++ b/software/tools/src/terminal.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/software/util/src/StringOperators.hpp b/software/util/src/StringOperators.hpp index 83816f0..a9f6540 100644 --- a/software/util/src/StringOperators.hpp +++ b/software/util/src/StringOperators.hpp @@ -100,7 +100,8 @@ constexpr Flt flt(float value, int digitCount, int decimalCount) { // c-string -constexpr String str(char const *value) {return String(value);} +constexpr String str(const char *value) {return String(value);} +inline String str(const unsigned char *value) {return String(reinterpret_cast(value));} // underline node diff --git a/software/util/test/utilTest.cpp b/software/util/test/utilTest.cpp index 791bdf6..258e772 100644 --- a/software/util/test/utilTest.cpp +++ b/software/util/test/utilTest.cpp @@ -15,7 +15,11 @@ #include #include #include +#ifdef _WIN32 +#include // htonl +#else #include // htonl +#endif // test utility functions and classes