diff --git a/.gitignore b/.gitignore index 0b2bbbc..8ed112d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # Ignore .pre-commit-config.yaml -.pre-commit-config.yaml \ No newline at end of file +.vscode/ +.pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5cc5c04..0a26cda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,32 +1,31 @@ -repos: - - - repo: https://github.com/pre-commit/mirrors-clang-format - rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 - hooks: - - id: clang-format - args: [--style=Google] - files: \.(cpp|hpp|c|h)$ - stages: [commit] - - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.5.1 - hooks: - - id: prettier - files: \.(js|ts|jsx|tsx|css|less|html|json|markdown|md|yaml|yml)$ - - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.1 - hooks: - - id: ruff - types_or: [ python, pyi ] - args: [ --fix ] - stages: [commit] - - id: ruff-format - stages: [commit] - - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml \ No newline at end of file +repos: + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 + hooks: + - id: clang-format + args: [--style=Google] + files: \.(cpp|hpp|c|h)$ + stages: [commit] + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.5.1 + hooks: + - id: prettier + files: \.(js|ts|jsx|tsx|css|less|html|json|markdown|md|yaml|yml)$ + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.1 + hooks: + - id: ruff + types_or: [python, pyi] + args: [--fix] + stages: [commit] + - id: ruff-format + stages: [commit] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml diff --git a/QSPI/Inc/QSPI_Command.hpp b/QSPI/Inc/QSPI_Command.hpp new file mode 100644 index 0000000..3aa8ac0 --- /dev/null +++ b/QSPI/Inc/QSPI_Command.hpp @@ -0,0 +1,41 @@ +#ifndef QSPI_COMMAND_HPP +#define QSPI_COMMAND_HPP + +#include +#include + +/** + * @brief Enum class representing the size of the QSPI address. + */ +enum class QSPI_AddressSize { + OneByte = QSPI_ADDRESS_8_BITS, ///< 8-bit address + TwoBytes = QSPI_ADDRESS_16_BITS, ///< 16-bit address + ThreeBytes = QSPI_ADDRESS_24_BITS, ///< 24-bit address + FourBytes = QSPI_ADDRESS_32_BITS ///< 32-bit address +}; + +/** + * @brief Struct representing a QSPI command. + * + * This struct encapsulates the details of a QSPI command, including the + * instruction, address, address size, transfer size, and data buffer. It also + * provides a method to convert the command to a HAL-compatible QSPI command + * structure. + */ +struct QSPI_Command { + uint8_t instruction; ///< The instruction byte for the QSPI command. + uint32_t address; ///< The address for the QSPI command. + QSPI_AddressSize address_size; ///< The size of the address. + size_t transfer_size; ///< The size of the data transfer. + uint8_t *data_buffer = nullptr; ///< Pointer to the data buffer. + + /** + * @brief Converts the QSPI command to a HAL-compatible QSPI command + * structure. + * + * @return QSPI_CommandTypeDef The HAL-compatible QSPI command structure. + */ + QSPI_CommandTypeDef to_hal_cmd() const; +}; + +#endif // QSPI_COMMAND_HPP diff --git a/QSPI/Inc/QSPI_DeviceContext.hpp b/QSPI/Inc/QSPI_DeviceContext.hpp new file mode 100644 index 0000000..591a2ac --- /dev/null +++ b/QSPI/Inc/QSPI_DeviceContext.hpp @@ -0,0 +1,62 @@ +#ifndef QSPI_DEVICE_CTX_HPP +#define QSPI_DEVICE_CTX_HPP + +#include +#include + +/** + * @brief Struct representing the context of a QSPI device. + * + * This struct encapsulates the context required for QSPI communication, + * including the chip select (CS) port and pin, and the maximum timeout for + * operations. + */ +struct QSPI_DeviceContext { + private: + GPIO_TypeDef &cs_port; ///< Reference to the GPIO port for chip select. + uint16_t cs_pin; ///< Pin number for chip select. + uint32_t max_timeout; ///< Maximum timeout for QSPI operations. + + public: + /** + * @brief Constructor for QSPI_DeviceContext. + * + * @param cs_port Reference to the GPIO port for chip select. + * @param cs_pin Pin number for chip select. + * @param max_timeout Maximum timeout for QSPI operations (default is + * HAL_MAX_DELAY). + */ + QSPI_DeviceContext(GPIO_TypeDef &cs_port, uint16_t cs_pin, + uint32_t max_timeout = HAL_MAX_DELAY) + : cs_port(cs_port), cs_pin(cs_pin), max_timeout(max_timeout) {} + + /** + * @brief Get the chip select port as a pointer. + * + * @return GPIO_TypeDef* Pointer to the GPIO port for chip select. + */ + GPIO_TypeDef *get_cs_port_as_ptr() const { return &this->cs_port; } + + /** + * @brief Get the chip select port as a reference. + * + * @return GPIO_TypeDef& Reference to the GPIO port for chip select. + */ + GPIO_TypeDef &get_cs_port_as_ref() const { return this->cs_port; } + + /** + * @brief Get the chip select pin number. + * + * @return uint16_t Pin number for chip select. + */ + uint16_t get_cs_pin() const { return this->cs_pin; } + + /** + * @brief Get the maximum timeout for QSPI operations. + * + * @return uint32_t Maximum timeout for QSPI operations. + */ + uint32_t get_max_timeout() const { return this->max_timeout; } +}; + +#endif // QSPI_DEVICE_CTX_HPP diff --git a/QSPI/Inc/QSPI_Driver.hpp b/QSPI/Inc/QSPI_Driver.hpp new file mode 100644 index 0000000..473f31f --- /dev/null +++ b/QSPI/Inc/QSPI_Driver.hpp @@ -0,0 +1,50 @@ +#ifndef QSPI_DRIVER_HPP +#define QSPI_DRIVER_HPP + +#include + +#include "QSPI_Command.hpp" +#include "QSPI_DeviceContext.hpp" +#include "QSPI_Result.hpp" + +/** + * @brief Class representing a QSPI driver. + * + * This class provides methods to perform read and write operations using QSPI. + * It encapsulates a QSPI handle and provides methods to interact with QSPI + * devices. + */ +class QSPI_Driver { + private: + QSPI_HandleTypeDef &qspi_handle; ///< Reference to the QSPI handle. + + public: + /** + * @brief Constructor for QSPI_Driver. + * + * @param hqspi Reference to the QSPI handle. + */ + QSPI_Driver(QSPI_HandleTypeDef &hqspi) : qspi_handle(hqspi) {} + + /** + * @brief Write data to a QSPI device. + * + * @param ctx The context of the QSPI device. + * @param cmd The QSPI command to be executed. + * @return QSPI_Result The result of the write operation. + */ + QSPI_Result write(const QSPI_DeviceContext &ctx, + const QSPI_Command &cmd) const; + + /** + * @brief Read data from a QSPI device. + * + * @param ctx The context of the QSPI device. + * @param cmd The QSPI command to be executed. + * @return QSPI_Result The result of the read operation. + */ + QSPI_Result read(const QSPI_DeviceContext &ctx, + const QSPI_Command &cmd) const; +}; + +#endif // QSPI_DRIVER_HPP diff --git a/QSPI/Inc/QSPI_Result.hpp b/QSPI/Inc/QSPI_Result.hpp new file mode 100644 index 0000000..5ded6e5 --- /dev/null +++ b/QSPI/Inc/QSPI_Result.hpp @@ -0,0 +1,23 @@ +#ifndef QSPI_RESULT_HPP +#define QSPI_RESULT_HPP + +#include + +/** + * The `Ok` variant is the only truthy variant + */ +enum class QSPI_Result { Ok = 1, Err = -1, Busy = -2, Timeout = -3 }; + +inline bool operator!(QSPI_Result error) { return error != QSPI_Result::Ok; } + +inline bool operator==(QSPI_Result error, bool value) { + return (error == QSPI_Result::Ok) == value; +} + +inline bool operator!=(QSPI_Result error, bool value) { + return !(error == value); +} + +QSPI_Result from_hal_status(HAL_StatusTypeDef status); + +#endif // QSPI_RESULT_HPP diff --git a/QSPI/Src/QSPI_Command.cpp b/QSPI/Src/QSPI_Command.cpp new file mode 100644 index 0000000..b680065 --- /dev/null +++ b/QSPI/Src/QSPI_Command.cpp @@ -0,0 +1,28 @@ +#include "../Inc/QSPI_Command.hpp" + +// If anything causes problems, it probably has to do with this function +// This requires testing along with the rest of the QSPI driver but this is the +// most likely source of issues +QSPI_CommandTypeDef QSPI_Command::to_hal_cmd() const { + QSPI_CommandTypeDef hal_cmd; + + hal_cmd.Address = this->address; + hal_cmd.AddressSize = static_cast(this->address_size); + hal_cmd.AddressMode = QSPI_ADDRESS_NONE; + + hal_cmd.Instruction = this->instruction; + hal_cmd.InstructionMode = QSPI_INSTRUCTION_NONE; + + hal_cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_1_LINE; + hal_cmd.AlternateBytes = 0; + hal_cmd.AlternateBytesSize = 0; + + hal_cmd.DataMode = QSPI_DATA_1_LINE; + hal_cmd.DummyCycles = 0; + hal_cmd.NbData = this->transfer_size; + hal_cmd.DdrMode = QSPI_DDR_MODE_DISABLE; + hal_cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; + hal_cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + + return hal_cmd; +} diff --git a/QSPI/Src/QSPI_Driver.cpp b/QSPI/Src/QSPI_Driver.cpp new file mode 100644 index 0000000..4b8923e --- /dev/null +++ b/QSPI/Src/QSPI_Driver.cpp @@ -0,0 +1,72 @@ +/** + * @author Connell Reffo + */ + +#include "../Inc/QSPI_Driver.hpp" + +#include +#include +#include + +#include "../Inc/QSPI_DeviceContext.hpp" +#include "../Inc/QSPI_Result.hpp" + +QSPI_Result QSPI_Driver::write(const QSPI_DeviceContext &ctx, + const QSPI_Command &cmd) const { + // Pull chip select to low + // Standard communiaction protocol before sending command + HAL_GPIO_WritePin(ctx.get_cs_port_as_ptr(), ctx.get_cs_pin(), GPIO_PIN_RESET); + + // Setup command + QSPI_CommandTypeDef hal_cmd = cmd.to_hal_cmd(); + + const QSPI_Result write_cmd_result = from_hal_status( + HAL_QSPI_Command(&this->qspi_handle, &hal_cmd, ctx.get_max_timeout())); + + if (!write_cmd_result) { + return write_cmd_result; + } + + // Transmit command + const QSPI_Result transmit_cmd_result = from_hal_status(HAL_QSPI_Transmit( + &this->qspi_handle, cmd.data_buffer, ctx.get_max_timeout())); + + if (!transmit_cmd_result) { + return transmit_cmd_result; + } + + // Pull CS back to high + // This signifies the end of the command + HAL_GPIO_WritePin(ctx.get_cs_port_as_ptr(), ctx.get_cs_pin(), GPIO_PIN_SET); + + return QSPI_Result::Ok; +} + +QSPI_Result QSPI_Driver::read(const QSPI_DeviceContext &ctx, + const QSPI_Command &cmd) const { + // Pull CS low + HAL_GPIO_WritePin(ctx.get_cs_port_as_ptr(), ctx.get_cs_pin(), GPIO_PIN_RESET); + + // Setup command + QSPI_CommandTypeDef hal_cmd = cmd.to_hal_cmd(); + + const QSPI_Result read_cmd_result = from_hal_status( + HAL_QSPI_Command(&this->qspi_handle, &hal_cmd, ctx.get_max_timeout())); + + if (!read_cmd_result) { + return read_cmd_result; + } + + // Receive data + const QSPI_Result receive_cmd_result = from_hal_status(HAL_QSPI_Receive( + &this->qspi_handle, cmd.data_buffer, ctx.get_max_timeout())); + + if (!receive_cmd_result) { + return receive_cmd_result; + } + + // Pull CS back high + HAL_GPIO_WritePin(ctx.get_cs_port_as_ptr(), ctx.get_cs_pin(), GPIO_PIN_SET); + + return QSPI_Result::Ok; +} diff --git a/QSPI/Src/QSPI_Result.cpp b/QSPI/Src/QSPI_Result.cpp new file mode 100644 index 0000000..3e2ebdc --- /dev/null +++ b/QSPI/Src/QSPI_Result.cpp @@ -0,0 +1,9 @@ +#include "../Inc/QSPI_Result.hpp" + +QSPI_Result from_hal_status(HAL_StatusTypeDef status) { + if (status == HAL_OK) { + return QSPI_Result::Ok; + } + + return static_cast(static_cast(status) * -1); +} diff --git a/QSPI_DMA/Inc/QSPI_DMA_Context.hpp b/QSPI_DMA/Inc/QSPI_DMA_Context.hpp new file mode 100644 index 0000000..20b0880 --- /dev/null +++ b/QSPI_DMA/Inc/QSPI_DMA_Context.hpp @@ -0,0 +1,44 @@ +#ifndef QSPI_DMA_CONTEXT_HPP +#define QSPI_DMA_CONTEXT_HPP + +#include + +#include "./QSPI_DMA_Result.hpp" + +// https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/STM32H743I-EVAL/Examples/QSPI + +class QSPI_DMA_Context +{ +private: + QSPI_HandleTypeDef &qspi_handle; + DMA_HandleTypeDef &dma_handle; + + uint8_t *src_buffer; + uint8_t *dest_buffer; + + // Total flash capacity in bytes + size_t flash_capacity; + + // Amount of bytes that + size_t amount_used; + +public: + QSPI_DMA_Context( + QSPI_HandleTypeDef *hqspi, + DMA_HandleTypeDef *hdma, + uint8_t *src_buffer, + uint8_t *dest_buffer, + size_t flash_capacity, + ) : + qspi_handle(*hqspi), + dma_handle(*hdma), + src_buffer(src_buffer), + dest_buffer(dest_buffer), + flash_capacity(flash_capacity), + amount_used(0) + {} + + QSPI_DMA_Result transfer(size_t bytes, uint64_t max_delay = HAL_MAX_DELAY); +}; + +#endif // QPSI_DMA_CONTEXT_HPP diff --git a/QSPI_DMA/Inc/QSPI_DMA_Result.hpp b/QSPI_DMA/Inc/QSPI_DMA_Result.hpp new file mode 100644 index 0000000..2010319 --- /dev/null +++ b/QSPI_DMA/Inc/QSPI_DMA_Result.hpp @@ -0,0 +1,21 @@ +#ifndef QSPI_DMA_Result_RESULT_HPP +#define QSPI_DMA_Result_RESULT_HPP + +#include + +/** + * The `Ok` variant is the only truthy variant + */ +enum class QSPI_DMA_Result { Ok = 1, Err = -1, Busy = -2, Timeout = -3, Overflow = -4 }; + +inline bool operator!(QSPI_DMA_Result error) { return error != QSPI_DMA_Result::Ok; } + +inline bool operator==(QSPI_DMA_Result error, bool value) { + return (error == QSPI_DMA_Result::Ok) == value; +} + +inline bool operator!=(QSPI_DMA_Result error, bool value) { + return !(error == value); +} + +#endif // QSPI_DMA_Result_HPP diff --git a/QSPI_DMA/Src/QSPI_DMA_Context.cpp b/QSPI_DMA/Src/QSPI_DMA_Context.cpp new file mode 100644 index 0000000..d0d3f7f --- /dev/null +++ b/QSPI_DMA/Src/QSPI_DMA_Context.cpp @@ -0,0 +1,26 @@ +#include "../Inc/QSPI_DMA_Context.hpp" + +QSPI_DMA_Result QSPI_DMA_Context::transfer(size_t bytes, uint64_t max_delay = HAL_MAX_DELAY) { + const size_t new_bytes_used = this->amount_used + bytes; + + if (new_bytes_used > this->flash_capacity) { + return QSPI_DMA_Result::Overflow; + } + + HAL_DMA_Start(&this->dma_handle, (uint32_t) this->src_buffer, (uint32_t) this->dest_buffer, bytes); + + HAL_StatusTypeDef status = HAL_BUSY; + + while (status != HAL_OK) { + status = HAL_DMA_PollForTransfer(&this->dma_handle, HAL_DMA_FULL_TRANSFER, max_delay); + __NOP(); + } + + if (!status) { + return QSPI_DMA_Result::Err; + } + + this->amount_used = new_bytes_used; + + return QSPI_DMA_Result::Ok; +} diff --git a/README.md b/README.md index 75072ad..15592f0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ -# CommunicationSystemsSubmodule -This repository will host drivers for communication protocols such as UART drivers. +# Communication Systems Submodule + +> This repository will host drivers for communication protocols such as UART drivers. + +### Setting up VSCode + +Add the following to `c_cpp_properties.json`: + +```json +"includePath": [ + "${workspaceFolder}/**", + "/AvionicsTemplateRepository/Drivers/CMSIS/Device/ST/STM32H7xx/Include/**", + "/AvionicsTemplateRepository/Drivers/CMSIS/Include/**", + "/AvionicsTemplateRepository/Drivers/STM32H7xx_HAL_Driver/Inc/**", + "/AvionicsTemplateRepository/Core/Inc/**" +], +"defines": ["QUADSPI"] +``` + +replace `` with the path to `AvionicsTemplateRepository`.