diff --git a/CMakeLists.txt b/CMakeLists.txt index 53273bb8481f7b3ac5c2385fd4505fd29a0f90fe..5623c9375bd54d014e1e31f5fe8109ff40650ff2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,6 +276,9 @@ add_executable(test-sx1278-serial ) sbs_target(test-sx1278-serial stm32f429zi_stm32f4discovery) +add_executable(test-cc3135 src/tests/drivers/CC3135/test-cc3135.cpp) +sbs_target(test-cc3135 stm32f407vg_stm32f4discovery) + #-----------------------------------------------------------------------------# # Tests - Sensors # #-----------------------------------------------------------------------------# diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 2fdea04d4a7b2628845b1bcfc62952433eee4e2b..7d521942145deac97f0c9b58b127de8b93b420e8 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -62,6 +62,7 @@ foreach(OPT_BOARD ${BOARDS}) ${SBS_BASE}/src/shared/radio/Xbee/APIFrameParser.cpp ${SBS_BASE}/src/shared/radio/Xbee/Xbee.cpp ${SBS_BASE}/src/shared/radio/SX1278/SX1278.cpp + ${SBS_BASE}/src/shared/radio/CC3135/CC3135.cpp # Scheduler ${SBS_BASE}/src/shared/scheduler/TaskScheduler.cpp diff --git a/src/shared/radio/CC3135/CC3135.cpp b/src/shared/radio/CC3135/CC3135.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d8da293966a8e0bea83b7c8fd613aeeea3b7c71 --- /dev/null +++ b/src/shared/radio/CC3135/CC3135.cpp @@ -0,0 +1,161 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "CC3135.h" + +namespace Boardcore +{ + +using namespace CC3135Defs; + +void CC3135Proto::handleIntr() +{ + // TODO: +} + +void CC3135::dummyRead() +{ + uint8_t buf[220]; + proto.readPacket(buf); + + ResponseHeader header; + memcpy(&header, &buf[0], sizeof(ResponseHeader)); + + // 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"); + + // if(header.gen_header.opcode == OPCODE_DEVICE_DEVICEGETRESPONSE) { + // for(int i = 0; i < header.gen_header.len + 4; i++) { + // TRACE("%2x\n", buf[i]); + // } + // } +} + +DeviceVersion CC3135::getVersion() +{ + DeviceSetGet packet; + packet.gen_header.opcode = OPCODE_DEVICE_DEVICEGET; + packet.device_set_id = 1; // Device get general + packet.option = 12; // Device get version + + proto.writePacket((uint8_t *)&packet, sizeof(packet)); + + return {}; +} + +//! Get a generic header out of a buffer. +GenericHeader *getHeader(uint8_t *buf) { return (GenericHeader *)(buf); } + +void CC3135Proto::readPacket(uint8_t *buf) +{ + // 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); + } + + // 2. Read initial data from the device + iface->read(&buf[0], 8); + + /* + Here the TI driver does some weird stuff. + Also the TI comment is wrong, soooo, I'll just skip that part + */ + + // 3. Scan for device SYNC + while (true) + { + // Try and find the SYNC here + uint32_t sync; + 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); + } + + tx_seq_num++; + + // 5. Parse generic header + GenericHeader *header = getHeader(&buf[0]); + + // 6. Finalize and read rest of the data + if (header->len > 0) + { + // TODO: The TI driver reads a ResponseHeader, violating zero size + + size_t aligned_len = alignSize(header->len); + iface->read(&buf[sizeof(GenericHeader)], aligned_len); + } +} + +void CC3135Proto::writePacket(uint8_t *buf, size_t size) +{ + // 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); + } + else + { + // Long pattern for UART + SyncPattern sync = H2N_SYNC_PATTERN; + iface->write((uint8_t *)&sync.long1, SYNC_PATTERN_LEN * 2); + } + + // 2. Setup header length + GenericHeader *header = getHeader(&buf[0]); + header->len = size - sizeof(GenericHeader); + + // 3. Write message + iface->write(buf, size); +} + +} // namespace Boardcore diff --git a/src/shared/radio/CC3135/CC3135.h b/src/shared/radio/CC3135/CC3135.h new file mode 100644 index 0000000000000000000000000000000000000000..64629e3bba0313359f36e70a3d961fa35e618dd7 --- /dev/null +++ b/src/shared/radio/CC3135/CC3135.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <memory> + +#include "CC3135Defs.h" +#include "CC3135Iface.h" + +/* +TODO(davide.mor): Write a small description of the CC3135 +*/ + +namespace Boardcore +{ + +/** + * @brief Abstraction over the comunication protocol. + */ +class CC3135Proto +{ +public: + explicit CC3135Proto(std::unique_ptr<ICC3135Iface> &&iface) + : iface(std::move(iface)), tx_seq_num(0) + { + } + + void handleIntr(); + + void readPacket(uint8_t *buf); + void writePacket(uint8_t *buf, size_t size); + +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)) + { + } + + void handleIntr() { proto.handleIntr(); } + + //! Dummy packet read, used to test comunication. + void dummyRead(); + + //! Retrieve information about the device + CC3135Defs::DeviceVersion getVersion(); + +private: + CC3135Proto proto; +}; + +} // namespace Boardcore diff --git a/src/shared/radio/CC3135/CC3135Defs.h b/src/shared/radio/CC3135/CC3135Defs.h new file mode 100644 index 0000000000000000000000000000000000000000..09031e0ea5795a11220a13083049a31ef20af827 --- /dev/null +++ b/src/shared/radio/CC3135/CC3135Defs.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <cstdint> +#include <cstdlib> + +/* +CC3135 Protocol definitions + +This definitions come mostly from the TI driver and a bit of RE +*/ + +namespace Boardcore +{ + +namespace CC3135Defs +{ + +//! Synchronous message mask. +constexpr uint16_t OPCODE_SYNC = 1 << 10; + +//! Device command opcodes. +enum OpCode : uint16_t +{ + OPCODE_DEVICE_INITCOMPLETE = 0x0008, + OPCODE_DEVICE_DEVICEASYNCDUMMY = 0x0063, + OPCODE_DEVICE_DEVICEGETRESPONSE = 0x0466, + OPCODE_DEVICE_DEVICESETRESPONSE = 0x04B7, + OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT = 0x089A, //< ???? + OPCODE_NETAPP_IPACQUIRED = 0x1825, //< ???? + OPCODE_DEVICE_DEVICEGET = 0x8466, + OPCODE_DEVICE_DEVICESET = 0x84B7, +}; + +struct SyncPattern +{ + uint32_t long1; + uint16_t short1; + uint8_t byte1; + uint8_t byte2; +}; + +struct DeviceVersion +{ + uint32_t chip_id; + uint8_t fw_version[4]; + uint8_t phy_version[4]; + uint8_t nwp_version[4]; + uint16_t rom_version; + uint16_t _pad; +}; + +struct GenericHeader +{ + OpCode opcode; + uint16_t len; +}; + +struct ResponseHeader +{ + GenericHeader gen_header; + uint8_t tx_pool_count; + uint8_t dev_status; + uint16_t min_max_payload; + uint16_t socket_tx_failure; + uint16_t socket_non_blocking; +}; + +struct DeviceSetGet +{ + GenericHeader gen_header; + uint16_t status; + uint16_t device_set_id; + uint16_t option; + uint16_t config_len; +}; + +constexpr SyncPattern H2N_SYNC_PATTERN = {0xBBDDEEFF, 0x4321, 0x34, 0x12}; +constexpr SyncPattern H2N_CNYS_PATTERN = {0xBBDDEEFF, 0x8765, 0x78, 0x56}; + +constexpr size_t SYNC_PATTERN_LEN = sizeof(uint32_t); + +constexpr uint32_t N2H_SYNC_PATTERN = 0xABCDDCBA; +constexpr uint32_t N2H_SYNC_PATTERN_SEQ_NUM_BITS = 0x00000003; +constexpr uint32_t N2H_SYNC_PATTERN_SEQ_NUM_EXISTS = 0x00000004; +constexpr uint32_t N2H_SYNC_PATTERN_MASK = 0xFFFFFFF8; +constexpr uint32_t N2H_SYNC_SPI_BUGS_MASK = 0x7FFF7F7F; // What? + +inline bool n2hSyncPatternMatch(uint32_t sync, uint8_t seq_num) +{ + // TODO: Add sequence number + return (sync & N2H_SYNC_SPI_BUGS_MASK & N2H_SYNC_PATTERN_MASK) == + (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); } + +inline const char *opToStr(OpCode op) +{ + switch (op) + { + case OpCode::OPCODE_DEVICE_INITCOMPLETE: + return "OPCODE_DEVICE_INITCOMPLETE"; + case OpCode::OPCODE_DEVICE_DEVICEASYNCDUMMY: + return "OPCODE_DEVICE_DEVICEASYNCDUMMY"; + case OpCode::OPCODE_DEVICE_DEVICEGETRESPONSE: + return "OPCODE_DEVICE_DEVICEGETRESPONSE"; + case OpCode::OPCODE_DEVICE_DEVICESETRESPONSE: + return "OPCODE_DEVICE_DEVICESETRESPONSE"; + case OpCode::OPCODE_NETAPP_IPACQUIRED: + return "OPCODE_NETAPP_IPACQUIRED"; + case OpCode::OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT: + return "OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT"; + case OpCode::OPCODE_DEVICE_DEVICEGET: + return "OPCODE_DEVICE_DEVICEGET"; + case OpCode::OPCODE_DEVICE_DEVICESET: + return "OPCODE_DEVICE_DEVICESET"; + default: + return "<unknown>"; + } +} + +} // namespace CC3135Defs + +} // namespace Boardcore diff --git a/src/shared/radio/CC3135/CC3135Iface.h b/src/shared/radio/CC3135/CC3135Iface.h new file mode 100644 index 0000000000000000000000000000000000000000..ef468ebe3447f99da5e06c0dd87a072e2b6c1559 --- /dev/null +++ b/src/shared/radio/CC3135/CC3135Iface.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <drivers/spi/SPIDriver.h> +#include <drivers/usart/USART.h> + +namespace Boardcore +{ + +/** + * @brief Abstraction over the kinds of interface of the CC3135 + */ +class ICC3135Iface +{ +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; +}; + +/** + * @brief SPI CC3135 implementation. + */ +class CC3135Spi : public ICC3135Iface +{ +public: + explicit CC3135Spi(SPIBusInterface &bus, GpioType cs, + SPIBusConfig config = {}) + : slave(bus, cs, config) + { + } + + void read(uint8_t *buffer, size_t size) override + { + SPITransaction spi(slave); + spi.read(buffer, size); + } + + void write(uint8_t *buffer, size_t size) override + { + SPITransaction spi(slave); + spi.write(buffer, size); + } + + bool is_spi() override { return true; } + +private: + SPISlave slave; +}; + +/** + * @brief UART CC3135 implementation. + */ +class CC3135Uart : public ICC3135Iface +{ +public: + explicit CC3135Uart(USARTType *usart) : usart(usart, DEFAULT_BAUD) + { + this->usart.init(); + } + + void read(uint8_t *buffer, size_t size) override + { + usart.read(buffer, size); + } + + void write(uint8_t *buffer, size_t size) override + { + usart.write(buffer, size); + } + + bool is_spi() override { return false; } + +private: + static constexpr USART::Baudrate DEFAULT_BAUD = USART::Baudrate::B115200; + + USART usart; +}; + +} // namespace Boardcore diff --git a/src/tests/drivers/CC3135/test-cc3135.cpp b/src/tests/drivers/CC3135/test-cc3135.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d115fe3d2947f62c5d8e6fe3a7ce05c7acbd7a0 --- /dev/null +++ b/src/tests/drivers/CC3135/test-cc3135.cpp @@ -0,0 +1,102 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <drivers/interrupt/external_interrupts.h> +#include <radio/CC3135/CC3135.h> + +using namespace Boardcore; +using namespace miosix; + +/* +Connection diagram: +cc3135:CC_SPI_CS -> stm32:pa1 +cc3135:CC_nHIB -> stm32:pc14 +cc3135:CC_IRQ -> stm32:pc15 +cc3135:CC_SPI_DIN -> stm32:pc12 (SPI3_MOSI) +cc3135:CC_SPI_DOUT -> stm32:pc11 (SPI3_MISO) +cc3135:CC_SPI_CLK -> stm32:pc10 (SPI3_SCK) +*/ + +using tx = Gpio<GPIOA_BASE, 2>; +using rx = Gpio<GPIOA_BASE, 3>; +using irq = Gpio<GPIOA_BASE, 4>; +using hib = Gpio<GPIOA_BASE, 5>; + +#define CC3135_UART USART2 +#define CC3135_HIB + +CC3135 *cc3135 = nullptr; + +void __attribute__((used)) EXTI4_IRQHandlerImpl() +{ + if (cc3135) + cc3135->handleIntr(); +} + +void initBoard() +{ + +#ifdef CC3135_HIB + { + miosix::FastInterruptDisableLock dLock; + + hib::mode(miosix::Mode::OUTPUT); + } + + hib::high(); +#endif + +#ifdef CC3135_UART + { + miosix::FastInterruptDisableLock dLock; + + irq::mode(miosix::Mode::INPUT); + tx::mode(miosix::Mode::ALTERNATE); + tx::alternateFunction(7); + rx::mode(miosix::Mode::ALTERNATE); + rx::alternateFunction(7); + } + + auto irq_pin = irq::getPin(); + enableExternalInterrupt(irq_pin.getPort(), irq_pin.getNumber(), + InterruptTrigger::RISING_EDGE); +#endif +} + +int main() +{ + initBoard(); + +#ifdef CC3135_UART + std::unique_ptr<ICC3135Iface> iface(new CC3135Uart(CC3135_UART)); +#endif + + cc3135 = new CC3135(std::move(iface)); + + Thread::sleep(200); + + cc3135->getVersion(); + while (true) + { + cc3135->dummyRead(); + } +}