diff --git a/src/shared/radio/CC3135/CC3135.cpp b/src/shared/radio/CC3135/CC3135.cpp index 9d8da293966a8e0bea83b7c8fd613aeeea3b7c71..a6f9985686208454e313ccd1e0627704f64b8816 100644 --- a/src/shared/radio/CC3135/CC3135.cpp +++ b/src/shared/radio/CC3135/CC3135.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2022 Skyward Experimental Rocketry * Author: Davide Mor * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,62 +22,226 @@ #include "CC3135.h" +#include <kernel/scheduler/scheduler.h> +#include <utils/Debug.h> + +using namespace Boardcore::CC3135Defs; +using namespace miosix; + namespace Boardcore { -using namespace CC3135Defs; +CC3135::CC3135(std::unique_ptr<ICC3135Iface> &&iface) : iface(std::move(iface)) +{ + // Start internal thread + this->start(); + irq_wait_thread = thread; +} + +CC3135Defs::DeviceVersion CC3135::getVersion() +{ + DeviceSetGet tx_command = {}; + tx_command.device_set_id = 1; // Device get general + tx_command.option = 12; // Device get general version + + DeviceSetGet rx_command = {}; + DeviceVersion rx_payload = {}; + + inoutPacketSync(OPCODE_DEVICE_DEVICEGET, Buffer::from(&tx_command), + Buffer::null(), OPCODE_DEVICE_DEVICEGETRESPONSE, + Buffer::from(&rx_command), Buffer::from(&rx_payload)); + + return rx_payload; +} + +void CC3135::handleIrq() +{ + irq_count++; + + if (irq_wait_thread) + { + irq_wait_thread->IRQwakeup(); + if (irq_wait_thread->IRQgetPriority() > + Thread::IRQgetCurrentThread()->IRQgetPriority()) + { + Scheduler::IRQfindNextThread(); + } + } +} + +void CC3135::waitForIrq() +{ + Thread *cur = Thread::getCurrentThread(); + + FastInterruptDisableLock dLock; + while (irq_wait_thread != cur || irq_count == 0) + { + irq_wait_thread->IRQwait(); + { + FastInterruptEnableLock eLock(dLock); + Thread::yield(); + } + } + + irq_count--; +} + +void CC3135::installAsServiceThread() +{ + FastInterruptDisableLock dLock; + irq_wait_thread = Thread::getCurrentThread(); +} + +void CC3135::restoreDefaultServiceThread() +{ + FastInterruptDisableLock dLock; + irq_wait_thread = thread; + + // Wakeup just in case + irq_wait_thread->IRQwakeup(); + if (irq_wait_thread->IRQgetPriority() > + Thread::IRQgetCurrentThread()->IRQgetPriority()) + { + Scheduler::IRQfindNextThread(); + } +} + +void CC3135::defaultPacketHandler(CC3135Defs::ResponseHeader header) +{ + TRACE( + "[cc3135] Received packet:\n" + "- Opcode: %s (%4x)\n" + "- Status: %u\n" + "- Socket tx failures: %u\n" + "- Socket non blocking: %u\n", + opToStr(header.inner.opcode), header.inner.opcode, header.dev_status, + header.socket_tx_failure, header.socket_non_blocking); + + // Dummy read rest of the data + // TODO: Add async commands + dummyRead(header.inner.len); +} + +void CC3135::run() +{ + // TODO: Implement a way to stop this thread + + while (true) + { + waitForIrq(); + + { + // Lock the device interface + Lock<FastMutex> lock(iface_mutex); + + ResponseHeader header; + readHeader(&header); + + defaultPacketHandler(header); + } + } +} -void CC3135Proto::handleIntr() +void CC3135::inoutPacketSync(CC3135Defs::OpCode tx_opcode, + CC3135::Buffer tx_command, + CC3135::Buffer tx_payload, + CC3135Defs::OpCode rx_opcode, + CC3135::Buffer rx_command, + CC3135::Buffer rx_payload) { - // TODO: + installAsServiceThread(); + + // Lock the device interface + Lock<FastMutex> lock(iface_mutex); + writePacket(tx_opcode, tx_command, tx_payload); + + readPacket(rx_opcode, rx_command, rx_payload); + + restoreDefaultServiceThread(); } -void CC3135::dummyRead() +void CC3135::readPacketSync(CC3135Defs::OpCode opcode, CC3135::Buffer command, + CC3135::Buffer payload) { - uint8_t buf[220]; - proto.readPacket(buf); + installAsServiceThread(); - ResponseHeader header; - memcpy(&header, &buf[0], sizeof(ResponseHeader)); + // Lock the device interface + Lock<FastMutex> lock(iface_mutex); + readPacket(opcode, command, payload); - // TRACE("status: %d\n", header.dev_status); - // TRACE("opcode: %s\n", opToStr(header.gen_header.opcode)); - // TRACE("len: %d\n", header.gen_header.len); - // TRACE("\n"); + restoreDefaultServiceThread(); +} - // if(header.gen_header.opcode == OPCODE_DEVICE_DEVICEGETRESPONSE) { - // for(int i = 0; i < header.gen_header.len + 4; i++) { - // TRACE("%2x\n", buf[i]); - // } - // } +void CC3135::writePacketSync(CC3135Defs::OpCode opcode, CC3135::Buffer command, + CC3135::Buffer payload) +{ + // Lock the device interface + Lock<FastMutex> lock(iface_mutex); + writePacket(opcode, command, payload); } -DeviceVersion CC3135::getVersion() +void CC3135::readPacket(OpCode opcode, CC3135::Buffer command, + CC3135::Buffer payload) { - DeviceSetGet packet; - packet.gen_header.opcode = OPCODE_DEVICE_DEVICEGET; - packet.device_set_id = 1; // Device get general - packet.option = 12; // Device get version + while (true) + { + waitForIrq(); + + // Locking the interface is not needed + + ResponseHeader header; + readHeader(&header); - proto.writePacket((uint8_t *)&packet, sizeof(packet)); + if (header.inner.opcode != opcode) + { + defaultPacketHandler(header); + } + else + { + // Read the rest of the packet + size_t len = header.inner.len; - return {}; + iface->read(command.ptr, std::min(len, command.len)); + len -= std::min(len, command.len); + + iface->read(payload.ptr, std::min(len, payload.len)); + len -= std::min(len, payload.len); + + // Read tail of remanining data + if (len > 0) + dummyRead(len); + + break; + } + } } -//! Get a generic header out of a buffer. -GenericHeader *getHeader(uint8_t *buf) { return (GenericHeader *)(buf); } +void CC3135::writePacket(OpCode opcode, CC3135::Buffer command, + CC3135::Buffer payload) +{ + RequestHeader header{opcode, + static_cast<uint16_t>(command.len + payload.len)}; + + writeHeader(&header); -void CC3135Proto::readPacket(uint8_t *buf) + iface->write(command.ptr, command.len); + iface->write(payload.ptr, payload.len); +} + +void CC3135::readHeader(ResponseHeader *header) { // 1. Write CNYS pattern (only if SPI) if (iface->is_spi()) { SyncPattern sync = H2N_CNYS_PATTERN; - iface->write((uint8_t *)(&sync.short1), SYNC_PATTERN_LEN); + iface->write(reinterpret_cast<uint8_t *>(&sync.short1), + SYNC_PATTERN_LEN); } + uint8_t buf[4]; + // 2. Read initial data from the device - iface->read(&buf[0], 8); + iface->read(&buf[0], 4); /* Here the TI driver does some weird stuff. @@ -92,70 +256,52 @@ void CC3135Proto::readPacket(uint8_t *buf) memcpy(&sync, &buf[0], 4); if (n2hSyncPatternMatch(sync, tx_seq_num)) - { - // Copy the bytes after the sync to the start - memcpy(&buf[0], &buf[4], 4); break; - } // TODO: The TI driver reads 4 bytes at a time, is this also good? // Shift everything - memmove(&buf[0], &buf[1], 7); - iface->read(&buf[7], 1); - } - - // 4. Scan for double syncs - while (true) - { - uint32_t sync; - memcpy(&sync, &buf[0], 4); - - if (!n2hSyncPatternMatch(sync, tx_seq_num)) - { - break; - } - - iface->read(&buf[0], 4); + memmove(&buf[0], &buf[1], 3); + iface->read(&buf[3], 1); } tx_seq_num++; - // 5. Parse generic header - GenericHeader *header = getHeader(&buf[0]); + // TODO: Is skipping double sync detection good? - // 6. Finalize and read rest of the data - if (header->len > 0) - { - // TODO: The TI driver reads a ResponseHeader, violating zero size + // 4. Read initial header + iface->read(reinterpret_cast<uint8_t *>(header), sizeof(ResponseHeader)); - size_t aligned_len = alignSize(header->len); - iface->read(&buf[sizeof(GenericHeader)], aligned_len); - } + // 5. Adjust for bigger response header + header->inner.len -= sizeof(ResponseHeader) - sizeof(GenericHeader); } -void CC3135Proto::writePacket(uint8_t *buf, size_t size) +void CC3135::writeHeader(RequestHeader *header) { // 1. Write SYNC pattern if (iface->is_spi()) { // Short pattern for SPI SyncPattern sync = H2N_SYNC_PATTERN; - iface->write((uint8_t *)&sync.short1, SYNC_PATTERN_LEN); + iface->write(reinterpret_cast<uint8_t *>(&sync.short1), + SYNC_PATTERN_LEN); } else { // Long pattern for UART SyncPattern sync = H2N_SYNC_PATTERN; - iface->write((uint8_t *)&sync.long1, SYNC_PATTERN_LEN * 2); + iface->write(reinterpret_cast<uint8_t *>(&sync.long1), + SYNC_PATTERN_LEN * 2); } - // 2. Setup header length - GenericHeader *header = getHeader(&buf[0]); - header->len = size - sizeof(GenericHeader); + // 2. Write body + iface->write(reinterpret_cast<uint8_t *>(header), sizeof(RequestHeader)); +} - // 3. Write message - iface->write(buf, size); +void CC3135::dummyRead(size_t n) +{ + uint8_t dummy[n]; + iface->read(dummy, n); } } // namespace Boardcore diff --git a/src/shared/radio/CC3135/CC3135.h b/src/shared/radio/CC3135/CC3135.h index 64629e3bba0313359f36e70a3d961fa35e618dd7..1925d1959733613d7c4c5b145e8e174c77e6df2d 100644 --- a/src/shared/radio/CC3135/CC3135.h +++ b/src/shared/radio/CC3135/CC3135.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2022 Skyward Experimental Rocketry * Author: Davide Mor * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,6 +22,10 @@ #pragma once +#include <ActiveObject.h> +#include <miosix.h> + +#include <functional> #include <memory> #include "CC3135Defs.h" @@ -29,50 +33,86 @@ /* TODO(davide.mor): Write a small description of the CC3135 + +TODO(davide.mor): Write about scatter/gather IO */ namespace Boardcore { -/** - * @brief Abstraction over the comunication protocol. - */ -class CC3135Proto +class CC3135 : ActiveObject { public: - explicit CC3135Proto(std::unique_ptr<ICC3135Iface> &&iface) - : iface(std::move(iface)), tx_seq_num(0) - { - } + explicit CC3135(std::unique_ptr<ICC3135Iface> &&iface); - void handleIntr(); + void handleIrq(); - void readPacket(uint8_t *buf); - void writePacket(uint8_t *buf, size_t size); + CC3135Defs::DeviceVersion getVersion(); private: - std::unique_ptr<ICC3135Iface> iface; - uint8_t tx_seq_num; -}; - -class CC3135 -{ -public: - explicit CC3135(std::unique_ptr<ICC3135Iface> &&iface) - : proto(std::move(iface)) + //! Simple buffer for scatter/gather IO + struct Buffer { - } + uint8_t *ptr; + size_t len; - void handleIntr() { proto.handleIntr(); } + template <typename T> + static Buffer from(T *data) + { + return {reinterpret_cast<uint8_t *>(data), sizeof(T)}; + } - //! Dummy packet read, used to test comunication. - void dummyRead(); + static Buffer null() { return {nullptr, 0}; } + }; - //! Retrieve information about the device - CC3135Defs::DeviceVersion getVersion(); + //! Function for servicing async messages. + void run() override; -private: - CC3135Proto proto; + void defaultPacketHandler(CC3135Defs::ResponseHeader header); + + // Functions dedicated to interrupt servicing + + //! Wait for an incoming interrupt (only callable in service thread). + void waitForIrq(); + //! Install this thread as the service thread. + void installAsServiceThread(); + //! Restore default service thread. + void restoreDefaultServiceThread(); + + // Functions for high level IO + + //! Write a packet in output and wait for a packet in input + void inoutPacketSync(CC3135Defs::OpCode tx_opcode, Buffer tx_command, + Buffer tx_payload, CC3135Defs::OpCode rx_opcode, + Buffer rx_command, Buffer rx_payload); + //! Read packet in input, with proper synchronization. + void readPacketSync(CC3135Defs::OpCode opcode, Buffer command, + Buffer payload); + //! Write a apcket in output, with proper synchronization. + void writePacketSync(CC3135Defs::OpCode opcode, Buffer command, + Buffer payload); + + // Functions for low level IO + + //! Read a single packet. + void readPacket(CC3135Defs::OpCode opcode, Buffer command, Buffer payload); + //! Write a single packet. + void writePacket(CC3135Defs::OpCode opcode, Buffer command, Buffer payload); + //! Read a packet header. + void readHeader(CC3135Defs::ResponseHeader *header); + //! Write a packet header. + void writeHeader(CC3135Defs::RequestHeader *header); + + //! Read dummy n bytes. + void dummyRead(size_t n); + + miosix::Thread *irq_wait_thread = nullptr; //< Thread waiting on IRQ + size_t irq_count = 0; //< Number of interrupts + + miosix::FastMutex iface_mutex; + std::unique_ptr<ICC3135Iface> iface; + + uint8_t tx_seq_num = 0; }; } // namespace Boardcore diff --git a/src/shared/radio/CC3135/CC3135Defs.h b/src/shared/radio/CC3135/CC3135Defs.h index 09031e0ea5795a11220a13083049a31ef20af827..affcb713ae3ebad92bdb2e4d2d3edbe9bdab0a5a 100644 --- a/src/shared/radio/CC3135/CC3135Defs.h +++ b/src/shared/radio/CC3135/CC3135Defs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2022 Skyward Experimental Rocketry * Author: Davide Mor * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -53,6 +53,9 @@ enum OpCode : uint16_t OPCODE_DEVICE_DEVICESET = 0x84B7, }; +//! Is this message synchronous? +inline bool isSync(OpCode op) { return op & OPCODE_SYNC; } + struct SyncPattern { uint32_t long1; @@ -79,7 +82,7 @@ struct GenericHeader struct ResponseHeader { - GenericHeader gen_header; + GenericHeader inner; uint8_t tx_pool_count; uint8_t dev_status; uint16_t min_max_payload; @@ -87,9 +90,10 @@ struct ResponseHeader uint16_t socket_non_blocking; }; +typedef GenericHeader RequestHeader; + struct DeviceSetGet { - GenericHeader gen_header; uint16_t status; uint16_t device_set_id; uint16_t option; @@ -114,9 +118,6 @@ inline bool n2hSyncPatternMatch(uint32_t sync, uint8_t seq_num) (N2H_SYNC_PATTERN & N2H_SYNC_SPI_BUGS_MASK & N2H_SYNC_PATTERN_MASK); } -//! Is this message synchronous? -inline bool isSync(OpCode op) { return op & OPCODE_SYNC; } - //! Align message size. inline size_t alignSize(size_t size) { return (size + 3) & (~3); } diff --git a/src/shared/radio/CC3135/CC3135Iface.h b/src/shared/radio/CC3135/CC3135Iface.h index ef468ebe3447f99da5e06c0dd87a072e2b6c1559..96c127f93c2f9dae56281e734d9e2db67a14e615 100644 --- a/src/shared/radio/CC3135/CC3135Iface.h +++ b/src/shared/radio/CC3135/CC3135Iface.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2022 Skyward Experimental Rocketry * Author: Davide Mor * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -37,6 +37,7 @@ public: virtual void read(uint8_t *buffer, size_t size) = 0; virtual void write(uint8_t *buffer, size_t size) = 0; virtual bool is_spi() = 0; + virtual void reset() = 0; }; /** @@ -65,6 +66,8 @@ public: bool is_spi() override { return true; } + void reset() override {} + private: SPISlave slave; }; @@ -92,6 +95,8 @@ public: bool is_spi() override { return false; } + void reset() override { usart.clearQueue(); } + private: static constexpr USART::Baudrate DEFAULT_BAUD = USART::Baudrate::B115200; diff --git a/src/tests/drivers/CC3135/test-cc3135.cpp b/src/tests/drivers/CC3135/test-cc3135.cpp index 8d115fe3d2947f62c5d8e6fe3a7ce05c7acbd7a0..5f717f801138e9936ffc6b73116dbd2d860bf235 100644 --- a/src/tests/drivers/CC3135/test-cc3135.cpp +++ b/src/tests/drivers/CC3135/test-cc3135.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2022 Skyward Experimental Rocketry * Author: Davide Mor * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -23,6 +23,8 @@ #include <drivers/interrupt/external_interrupts.h> #include <radio/CC3135/CC3135.h> +#include <thread> + using namespace Boardcore; using namespace miosix; @@ -46,15 +48,17 @@ using hib = Gpio<GPIOA_BASE, 5>; CC3135 *cc3135 = nullptr; +volatile size_t IRQ_COUNT = 0; + void __attribute__((used)) EXTI4_IRQHandlerImpl() { + IRQ_COUNT += 1; if (cc3135) - cc3135->handleIntr(); + cc3135->handleIrq(); } void initBoard() { - #ifdef CC3135_HIB { miosix::FastInterruptDisableLock dLock; @@ -84,19 +88,60 @@ void initBoard() int main() { + // IRQ watcher thread + /*std::thread _watcher( + []() + { + size_t last = -1; + while (1) + { + if (last != IRQ_COUNT) + { + printf("[cc3135] IRQ: %d\n", IRQ_COUNT); + last = IRQ_COUNT; + } + + // Sleep to avoid CPU hogging + Thread::sleep(10); + } + });*/ + initBoard(); +#ifdef CC3135_HIB + // Reset CC3135 + hib::low(); + Thread::sleep(10); + hib::high(); + + // Wait for the device to fully initialize. + // The device is very chatty at the beginning, + // but it's also in a weird state where the IRQ + // pin doesn't trigger properly. Just wait for it to calm down + Thread::sleep(2000); +#endif + #ifdef CC3135_UART std::unique_ptr<ICC3135Iface> iface(new CC3135Uart(CC3135_UART)); #endif + printf("[cc3135] Initializing...\n"); cc3135 = new CC3135(std::move(iface)); + printf("[cc3135] Initialization complete!\n"); + + auto version = cc3135->getVersion(); + printf( + "[cc3135] Chip Id: %lx\n" + "[cc3135] Fw version: %u.%u.%u.%u\n" + "[cc3135] Phy version: %u.%u.%u.%u\n" + "[cc3135] Nwp version: %u.%u.%u.%u\n" + "[cc3135] Rom version: %x\n", + version.chip_id, version.fw_version[0], version.fw_version[1], + version.fw_version[2], version.fw_version[3], version.phy_version[0], + version.phy_version[1], version.phy_version[2], version.phy_version[3], + version.nwp_version[0], version.nwp_version[1], version.nwp_version[2], + version.nwp_version[3], version.rom_version); - Thread::sleep(200); - - cc3135->getVersion(); while (true) - { - cc3135->dummyRead(); - } + ; }