diff --git a/.github/workflows/build-tests_TestBoard_TPS27SA08-Q1.yml b/.github/workflows/build-tests_TestBoard_TPS27SA08-Q1.yml new file mode 100644 index 0000000..f139267 --- /dev/null +++ b/.github/workflows/build-tests_TestBoard_TPS27SA08-Q1.yml @@ -0,0 +1,35 @@ +name: Build Tests (TestBoard_TPS27SA08-Q1) + +on: + push: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize Submodules + run: git submodule update --init ./firmware/Embedded-Sharepoint + + - name: Cache Nix + uses: actions/cache@v3 + with: + path: | + ~/.cache/nix + key: nix-${{ runner.os }}-${{ hashFiles('flake.lock') }} + restore-keys: | + nix-${{ runner.os }}- + + - name: Install Nix Action + uses: cachix/install-nix-action@v31 + + - name: Clean & Build tests + run: | + nix develop ./firmware/Embedded-Sharepoint/ --accept-flake-config --command bash -c " + cd firmware + cd TestBoard_TPS27SA08-Q1 + make clean + python test/MakeAllTests_TestBoard_TPS27SA08-Q1.py + " diff --git a/.gitignore b/.gitignore index e86edc6..648c66f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# ----------------------------------------------------------------------------- +# HARDWARE (KICAD) +# ----------------------------------------------------------------------------- # For PCBs designed using KiCad: https://www.kicad.org/ # Format documentation: https://kicad.org/help/file-formats/ @@ -37,4 +40,36 @@ fp-info-cache **/*-backups/*.zip # Local project settings -*.kicad_prl \ No newline at end of file +*.kicad_prl + +# ----------------------------------------------------------------------------- +# FIRMWARE +# ----------------------------------------------------------------------------- + +# VS Code +.vscode/ +compile_commands.json + +# Build files +build +.dockerfile.hash +.cache +/can/CANMetaData.* +Madefile + +# Python +*venv +__pycache__/ +.env + +# Nix +flake.lock + +# Documentation toolset +docs/doxygen/ +site/* +!site/empty.a +Zone.Identifier + +# Mac specific files +.DS_Store diff --git a/.gitmodules b/.gitmodules index 4c7852b..a8595dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "hardware/lib/KiCAD-Sharepoint"] path = hardware/lib/KiCAD-Sharepoint url = git@github.com:lhr-solar/KiCAD-Sharepoint.git +[submodule "firmware/Embedded-Sharepoint"] + path = firmware/Embedded-Sharepoint + url = https://github.com/lhr-solar/Embedded-Sharepoint.git diff --git a/datasheets/MCP23017_MCP23S17.pdf b/datasheets/MCP23017_MCP23S17.pdf new file mode 100644 index 0000000..5880f3d Binary files /dev/null and b/datasheets/MCP23017_MCP23S17.pdf differ diff --git a/firmware/Embedded-Sharepoint b/firmware/Embedded-Sharepoint new file mode 160000 index 0000000..2bdaa5b --- /dev/null +++ b/firmware/Embedded-Sharepoint @@ -0,0 +1 @@ +Subproject commit 2bdaa5be38e0ac249f0972ad1469ed4fa36dcb04 diff --git a/firmware/TestBoard_TPS27SA08-Q1/Makefile b/firmware/TestBoard_TPS27SA08-Q1/Makefile new file mode 100644 index 0000000..943a04b --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/Makefile @@ -0,0 +1,157 @@ +# CRAZY COLORS ---------------------------------------------------------------- +RED=\033[0;31m +GREEN=\033[0;32m +ORANGE=\033[0;33m +BLUE=\033[0;34m +PURPLE=\033[0;35m +CYAN=\033[0;36m +LIGHTGRAY=\033[0;37m +DARKGRAY=\033[1;30m +YELLOW=\033[0;33m +NC=\033[0m # No Color +# ----------------------------------------------------------------------------- + +# generate paths +MAKEFILE_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + +# PROJECT CONFIGURATION ------------------------------------------------------- +TEST ?= main + +# COMMON TARGETS +# LSOM: stm32g473xx +# PSOM: stm32l431cbt +# FAT NUCLEO: stm32g474xx +# BAD NUCLEO: stm32l432kcu +PROJECT_TARGET ?= stm32l432kcu + +# SOURCE AND INCLUDE DIRECTORIES +PROJECT_C_INCLUDES = $(wildcard core/inc) $(wildcard ../driver/inc) +PROJECT_C_SOURCES = $(wildcard core/src/*.c) $(wildcard ../driver/src/*.c) +PROJECT_MAIN_DIR ?= core/src + +# BUILD DIRECTORIES +PROJECT_BUILD_DIR = build +BUILD_MAKEFILE_DIR ?= ../Embedded-Sharepoint + +MADEFILE ?= true +MADEFILE_DIR = ${MAKEFILE_DIR} +# ----------------------------------------------------------------------------- + + +# remove main and add test folders if running test +ifneq ($(TEST), "main") + PROJECT_C_SOURCES := $(filter-out $(PROJECT_MAIN_DIR)/main.c, $(PROJECT_C_SOURCES)) + PROJECT_C_SOURCES += $(wildcard test/src/${TEST}.c) + + PROJECT_C_INCLUDES += $(wildcard test/inc) +endif + +# Convert relative paths to absolute paths for passing to Embedded-Sharepoint build +PROJECT_C_SOURCES := $(addprefix $(MAKEFILE_DIR)/, $(PROJECT_C_SOURCES)) +PROJECT_C_INCLUDES := $(addprefix $(MAKEFILE_DIR)/, $(PROJECT_C_INCLUDES)) + +# Debug +PRINT_DEBUG ?= true + +ifeq ($(PRINT_DEBUG), true) +$(info SOURCES: $(PROJECT_C_SOURCES)) +$(info INCLUDES: $(PROJECT_C_INCLUDES)) +endif + +# generate paths (cont.d) +PROJECT_BUILD_DIR := $(addprefix $(MAKEFILE_DIR)/, $(PROJECT_BUILD_DIR)) + +# export variables +export PROJECT_TARGET +export PROJECT_C_SOURCES +export PROJECT_C_INCLUDES +export PROJECT_BUILD_DIR + + +# ----------------------------------------------------------------------------- +# Clang +BEAR_PREFIX := +# check if bear is installed +BEAR_INSTALLED := $(shell command -v bear >/dev/null 2>&1 && echo yes || echo no) + +# define path of .vscode (create if missing) +VS_CODE_DIR := $(MAKEFILE_DIR)/.vscode +$(shell mkdir -p $(VS_CODE_DIR)) + +ifeq ($(BEAR_INSTALLED),yes) +BEAR_PREFIX := bear --output $(VS_CODE_DIR)/compile_commands.json --append -- +endif +# ----------------------------------------------------------------------------- + + +# BUILD ----------------------------------------------------------------------- +ifeq ($(MAKECMDGOALS),) +default: build_code +else ifeq ($(MAKECMDGOALS), all) +all: build_code +else +%: + $(BEAR_PREFIX) $(MAKE) -C $(BUILD_MAKEFILE_DIR) $(MAKECMDGOALS) +endif + + +build_code: +ifeq ($(MADEFILE), true) + @echo -e "Make initiated at $(shell date)\n" > ${MADEFILE_DIR}/Madefile +endif + +ifneq ($(TEST), main) + @echo -e "Making ${PURPLE}$(PROJECT_TARGET)${NC}build for ${BLUE}TEST=${PURPLE}${TEST}${NC}" + ifeq ($(MADEFILE), true) + @echo -e "Making $(PROJECT_TARGET) build for TEST=${TEST}\n" >> ${MADEFILE_DIR}/Madefile + endif +else + @echo -e "Making ${PURPLE}$(PROJECT_TARGET)${NC}build with ${ORANGE}main.${NC}" + ifeq ($(MADEFILE), true) + @echo -e "Making $(PROJECT_TARGET) build with main.\n" >> ${MADEFILE_DIR}/Madefile + endif +endif + +ifeq ($(MADEFILE), true) + @set -o pipefail; if $(BEAR_PREFIX) $(MAKE) -C $(BUILD_MAKEFILE_DIR) all -j | tee -a ${MADEFILE_DIR}/Madefile; then \ + echo -e "${GREEN}Compiled! Splendid! Jolly Good!!!\n${NC}"; \ + echo -e "\nCompiled! Splendid! Jolly Good!!\n" >> ${MADEFILE_DIR}/Madefile; \ + else \ + echo -e "${RED}Build failure.\n${NC}"; \ + echo -e "\nBuild failure.\n" >> ${MADEFILE_DIR}/Madefile; \ + exit 1; \ + fi +else + @set -o pipefail; if $(BEAR_PREFIX) $(MAKE) -C $(BUILD_MAKEFILE_DIR) all -j; then \ + echo -e "${GREEN}Compiled! Splendid! Jolly Good!!!\n${NC}"; \ + else \ + echo -e "${RED}Build failure.\n${NC}"; \ + exit 1; \ + fi +endif +# ----------------------------------------------------------------------------- + + +# FLASH ----------------------------------------------------------------------- +flash: + @set -o pipefail; if $(MAKE) -C $(BUILD_MAKEFILE_DIR) flash; then \ + echo -e "${GREEN}Flash successful.\n${NC}"; \ + else \ + echo -e "${RED}Flash failure.\n${NC}"; \ + exit 1; \ + fi +# ----------------------------------------------------------------------------- + + +# CLEAN ----------------------------------------------------------------------- +clean: + $(MAKE) -C $(BUILD_MAKEFILE_DIR) clean + -rm -fR $(MADEFILE_DIR)/Madefile +# ----------------------------------------------------------------------------- + + +# DOCUMENTATION --------------------------------------------------------------- +# .PHONY: docs +# docs: +# cd $(BUILD_MAKEFILE_DIR) && mkdocs serve +# ----------------------------------------------------------------------------- diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_Pins.h b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_Pins.h new file mode 100644 index 0000000..33cf43f --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_Pins.h @@ -0,0 +1,14 @@ +#define LED_PIN GPIO_PIN_3 +#define LED_PORT GPIOB +#define LED_CLOCK_INIT() __HAL_RCC_GPIOB_CLK_ENABLE(); + +#define VCP_TX_PIN GPIO_PIN_2 +#define VCP_TX_PORT GPIOA +#define VCP_RX_PIN GPIO_PIN_15 +#define VCP_RX_PORT GPIOA + +#define GPIOEXP_CS_PIN GPIO_PIN_3 +#define GPIOEXP_CS_PORT GPIOA + +#define GPIOEXP_INTA_PIN GPIO_PIN_5 +#define GPIOEXP_INTA_PORT GPIOA diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_SPI.h b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_SPI.h new file mode 100644 index 0000000..ee95c45 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_SPI.h @@ -0,0 +1,32 @@ +#include "stm32xx_hal.h" + +#define SPI1_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY+1 + +extern SPI_HandleTypeDef hspi1; + +/** + * @brief SPI1 Initialization Function + * @param None + * @retval None + */ +void MX_SPI1_Init(void); + +/** + * @brief SPI MSP Initialization + * This function configures the hardware resources used in this example + * @param hspi: SPI handle pointer + * @retval None + */ +void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi); + +/** + * @brief SPI MSP De-Initialization + * This function freeze the hardware resources used in this example + * @param hspi: SPI handle pointer + * @retval None + */ +void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi); + +bool SPI_Init_Dummy_Send(SPI_HandleTypeDef* spi, SemaphoreHandle_t spi2_mutex, SemaphoreHandle_t spi2_done_sem); + +bool SPI_RTOS_Mutex_Semaphore_Setup(SemaphoreHandle_t* SPIx_mutex, StaticSemaphore_t* SPIx_mutex_buffer, SemaphoreHandle_t* SPIx_done_sem, StaticSemaphore_t* SPIx_done_sem_buffer); diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_UART.h b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_UART.h new file mode 100644 index 0000000..179c162 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/inc/TestBoard_TPS27SA08-Q1_UART.h @@ -0,0 +1,27 @@ +#include "stm32xx_hal.h" +#include "TestBoard_TPS27SA08-Q1_Pins.h" + +extern UART_HandleTypeDef huart2; + +/** + * @brief USART2 Initialization Function + * @param None + * @retval None + */ +void MX_USART2_UART_Init(void); + +/** + * @brief UART MSP Initialization + * This function configures the hardware resources used in this example + * @param huart: UART handle pointer + * @retval None + */ +void HAL_UART_MspGPIOInit(UART_HandleTypeDef* huart); + +/** + * @brief UART MSP De-Initialization + * This function freeze the hardware resources used in this example + * @param huart: UART handle pointer + * @retval None + */ +void HAL_UART_MspGPIODeInit(UART_HandleTypeDef* huart); diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_SPI.c b/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_SPI.c new file mode 100644 index 0000000..146fac4 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_SPI.c @@ -0,0 +1,171 @@ +#include "TestBoard_TPS27SA08-Q1_SPI.h" + +void MX_SPI1_Init(void) +{ + /* SPI1 parameter configuration*/ + hspi1.Instance = SPI1; + hspi1.Init.Mode = SPI_MODE_MASTER; + hspi1.Init.Direction = SPI_DIRECTION_2LINES; + hspi1.Init.DataSize = SPI_DATASIZE_8BIT; + hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; + hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; + hspi1.Init.NSS = SPI_NSS_SOFT; + hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; + hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; + hspi1.Init.TIMode = SPI_TIMODE_DISABLE; + hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hspi1.Init.CRCPolynomial = 7; + hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE; + hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE; + if (HAL_SPI_Init(&hspi1) != HAL_OK) + { + Error_Handler(); + } +} + +void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + if(hspi->Instance==SPI1) + { + /* USER CODE BEGIN SPI1_MspInit 0 */ + + /* USER CODE END SPI1_MspInit 0 */ + /* Peripheral clock enable */ + __HAL_RCC_SPI1_CLK_ENABLE(); + + __HAL_RCC_GPIOA_CLK_ENABLE(); + /**SPI1 GPIO Configuration + PA1 ------> SPI1_SCK + PA11 ------> SPI1_MISO + PA12 ------> SPI1_MOSI + */ + GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /* SPI1 interrupt Init */ + HAL_NVIC_SetPriority(SPI1_IRQn, SPI1_INTERRUPT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(SPI1_IRQn); + } +} + +void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) +{ + if(hspi->Instance==SPI1) + { + /* Peripheral clock disable */ + __HAL_RCC_SPI1_CLK_DISABLE(); + + /**SPI1 GPIO Configuration + PA1 ------> SPI1_SCK + PA11 ------> SPI1_MISO + PA12 ------> SPI1_MOSI + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12); + } +} + +bool SPI_Init_Dummy_Send(SPI_HandleTypeDef* spi, SemaphoreHandle_t spi2_mutex, SemaphoreHandle_t spi2_done_sem) +{ + if(xSemaphoreTake(spi2_mutex, pdMS_TO_TICKS(100)) != pdTRUE) + { + return false; + } + + // SPI dummy send because CLK pin initializes high even when configured low for some reason + uint8_t dummy_spi_send = 0; + if(HAL_SPI_Transmit_IT(spi, &dummy_spi_send, 1) != HAL_OK) + { + // release SPI mutex + xSemaphoreGive(spi2_mutex); + + return false; + } + + if(xSemaphoreTake(spi2_done_sem, pdMS_TO_TICKS(100)) != pdTRUE) + { + HAL_SPI_Abort(spi); + + // release SPI mutex + xSemaphoreGive(spi2_mutex); + + return false; + } + + xSemaphoreGive(spi2_mutex); + + return true; +} + +bool SPI_RTOS_Mutex_Semaphore_Setup(SemaphoreHandle_t* SPIx_mutex, StaticSemaphore_t* SPIx_mutex_buffer, SemaphoreHandle_t* SPIx_done_sem, StaticSemaphore_t* SPIx_done_sem_buffer) +{ + // create mutex (prevent simultaneous access to SPIx) + *SPIx_mutex = xSemaphoreCreateMutexStatic(SPIx_mutex_buffer); + //creates semaphore (tells when SPIx hardware has finished transmission) + *SPIx_done_sem = xSemaphoreCreateBinaryStatic(SPIx_done_sem_buffer); + + // check mutex and semaphore creation + if((*SPIx_mutex == NULL) || (*SPIx_done_sem == NULL)) + { + return false; + } + else + { + return true; + } +} + +// INTERRUPT STUFF ------------------------------------------------------------ + +extern SemaphoreHandle_t spi1_done_sem; + +void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(spi1_done_sem != NULL) + { + xSemaphoreGiveFromISR(spi1_done_sem, &xHigherPriorityTaskWoken); + } + + // Context switch if a higher priority task was woken up + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef* hspi) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(spi1_done_sem != NULL) + { + xSemaphoreGiveFromISR(spi1_done_sem, &xHigherPriorityTaskWoken); + } + + // Context switch if a higher priority task was woken up + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef* hspi) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(spi1_done_sem != NULL) + { + xSemaphoreGiveFromISR(spi1_done_sem, &xHigherPriorityTaskWoken); + } + + // Context switch if a higher priority task was woken up + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +/** + * @brief This function handles SPI1 global interrupt. + */ +void SPI1_IRQHandler(void) +{ + HAL_SPI_IRQHandler(&hspi1); +} diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_UART.c b/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_UART.c new file mode 100644 index 0000000..dd13c57 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/src/TestBoard_TPS27SA08-Q1_UART.c @@ -0,0 +1,72 @@ +#include "TestBoard_TPS27SA08-Q1_UART.h" + +void MX_USART2_UART_Init(void) +{ + huart2.Instance = USART2; + huart2.Init.BaudRate = 115200; + huart2.Init.WordLength = UART_WORDLENGTH_8B; + huart2.Init.StopBits = UART_STOPBITS_1; + huart2.Init.Parity = UART_PARITY_NONE; + huart2.Init.Mode = UART_MODE_TX_RX; + huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart2.Init.OverSampling = UART_OVERSAMPLING_16; + huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; + huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; + if (HAL_UART_Init(&huart2) != HAL_OK) + { + Error_Handler(); + } +} + +void HAL_UART_MspGPIOInit(UART_HandleTypeDef* huart) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + if(huart->Instance==USART2) + { + /** Initializes the peripherals clock */ + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2; + PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + { + Error_Handler(); + } + + /* Peripheral clock enable */ + __HAL_RCC_USART2_CLK_ENABLE(); + + __HAL_RCC_GPIOA_CLK_ENABLE(); + /**USART2 GPIO Configuration + PA2 ------> USART2_TX + PA15 (JTDI) ------> USART2_RX + */ + GPIO_InitStruct.Pin = VCP_TX_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF7_USART2; + HAL_GPIO_Init(VCP_TX_PORT, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = VCP_RX_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF3_USART2; + HAL_GPIO_Init(VCP_RX_PORT, &GPIO_InitStruct); + } +} + +void HAL_UART_MspGPIODeInit(UART_HandleTypeDef* huart) +{ + if(huart->Instance==USART2) + { + /* Peripheral clock disable */ + __HAL_RCC_USART2_CLK_DISABLE(); + + /**USART2 GPIO Configuration + PA2 ------> USART2_TX + PA15 (JTDI) ------> USART2_RX + */ + HAL_GPIO_DeInit(GPIOA, VCP_TX_PIN|VCP_RX_PIN); + } +} diff --git a/firmware/TestBoard_TPS27SA08-Q1/core/src/main.c b/firmware/TestBoard_TPS27SA08-Q1/core/src/main.c new file mode 100644 index 0000000..75af454 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/core/src/main.c @@ -0,0 +1,21 @@ +// main.c + +// INCLUDES ------------------------------------------------------------------- + + +// DEFINES -------------------------------------------------------------------- + + +// CONFIGURATION HANDLES/STRUCTS ---------------------------------------------- + + +// FUNCTION DECLARATIONS ------------------------------------------------------ + + +// GLOBAL VARIABLES ----------------------------------------------------------- + + +int main() +{ + return 0; +} diff --git a/software/TestBoard_TPS27SA08-Q1_CubeMX/TestBoard_TPS27SA08-Q1_CubeMX.ioc b/firmware/TestBoard_TPS27SA08-Q1/cubemx/TestBoard_TPS27SA08-Q1_CubeMX.ioc similarity index 100% rename from software/TestBoard_TPS27SA08-Q1_CubeMX/TestBoard_TPS27SA08-Q1_CubeMX.ioc rename to firmware/TestBoard_TPS27SA08-Q1/cubemx/TestBoard_TPS27SA08-Q1_CubeMX.ioc diff --git a/firmware/TestBoard_TPS27SA08-Q1/test/MakeAllTests_TestBoard_TPS27SA08-Q1.py b/firmware/TestBoard_TPS27SA08-Q1/test/MakeAllTests_TestBoard_TPS27SA08-Q1.py new file mode 100644 index 0000000..5988a05 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/test/MakeAllTests_TestBoard_TPS27SA08-Q1.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +import argparse +import os +import sys +import subprocess +from pathlib import Path +from multiprocessing import cpu_count +from concurrent.futures import ThreadPoolExecutor, as_completed + +# ANSI color codes +RED = "\033[0;31m" +GREEN = "\033[0;32m" +YELLOW = "\033[0;33m" +BLUE = "\033[0;34m" +NC = "\033[0m" + +def error(msg, exit_code=1): + print(f"{RED}[ERROR] {msg}{NC}", file=sys.stderr) + sys.exit(exit_code) + +def info(msg, color=BLUE): + print(f"{color}[INFO] {msg}{NC}") + +def parse_args(): + parser = argparse.ArgumentParser( + description="Compile all tests for all STM ports", + add_help=False + ) + parser.add_argument("-v", "--verbose", action="store_true", + help="Enable verbose output (show commands as they're run)") + parser.add_argument("-h", "--help", action="help", + help="Show this help message and exit") + return parser.parse_args() + +def find_tests(tests_dir: Path): + print(f"Tests directory: {tests_dir}") + if not tests_dir.is_dir(): + error("Something is horribly wrong. No tests directory found.") + + tests = [] + for cfile in tests_dir.glob("*.c"): + tests.append(cfile.stem) + + if not tests: + error("Something is horribly wrong. No test files found in the tests directory.") + + return tests + +def compile_test(script_dir: Path, port: str, test_name: str, + make_flags: list[str], verbose: bool): + info(f"Compiling the test - {test_name} for {port}") + + build_dir = script_dir.parent / "TestBoard_TPS27SA08-Q1/autobuild" / port / test_name + + cmd = [ + "make", + "-C", str(script_dir), + f"TEST={test_name}", + f"PROJECT_TARGET={port}", + "BEAR_ENABLE=0", + f"PROJECT_BUILD_DIR={build_dir}", + "MADEFILE=false", + *make_flags, + ] + + try: + proc = subprocess.run( + cmd, + capture_output=True, + text=True, + check=False, + ) + except Exception as e: + return False, f"Failed to invoke make: {e}" + + output = proc.stdout + proc.stderr + + if proc.returncode != 0: + print(f"{RED}[{port}:{test_name}] {output}{NC}") + if "[CONFIG]" in output: + print(f"{YELLOW}[WARNING] CONFIG issue. Continuing...{NC}") + return True, None + return False, f"Errors occurred while compiling {test_name}.c using {port}" + + info(f"Successfully compiled {test_name}.c : {port}", GREEN) + if verbose and output.strip(): + print(f"{GREEN}[INFO] Output:{NC}\n{output}") + + return True, None + +def main(): + args = parse_args() + git_dir = Path( + os.popen("git rev-parse --show-toplevel").read().strip() + ) + + make_flags = ["-B", "-s"] + if args.verbose: + make_flags = ["-B"] + + ports = ["stm32l432kcu"] + + tests_dir = Path("firmware/TestBoard_TPS27SA08-Q1/test/src") + tests = find_tests(git_dir / tests_dir) + + makefile_dir = Path("firmware/TestBoard_TPS27SA08-Q1") + script_dir = git_dir / makefile_dir + + info("Compiling all tests for the following ports:") + for p in ports: + print(p) + print(f"{BLUE}{'-' * 40}{NC}") + + failures = [] + + max_workers = cpu_count() + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = [] + for port in ports: + for test in tests: + futures.append( + executor.submit( + compile_test, + script_dir, + port, + test, + make_flags, + args.verbose, + ) + ) + + for fut in as_completed(futures): + ok, err = fut.result() + if not ok and err: + failures.append(err) + + if failures: + error("Error: Some builds failed") + + info("Jolly Good! All tests compiled successfully", GREEN) + +if __name__ == "__main__": + main() diff --git a/firmware/TestBoard_TPS27SA08-Q1/test/inc/Test_Utilities.h b/firmware/TestBoard_TPS27SA08-Q1/test/inc/Test_Utilities.h new file mode 100644 index 0000000..55b65cd --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/test/inc/Test_Utilities.h @@ -0,0 +1,19 @@ +#include "stm32xx_hal.h" + +/** + * @brief Converts uint8_t bits into ASCII characters for printouts. + * @param val Value for bits to be converted to ASCII. + * @param str Pointer to starting location for ASCII bits (can be in the middle of a char array) with MSB first. + * Warning: ensure this does not overflow the array length. + */ +static void uint8_to_binary_str(uint8_t val, char* str) +{ + str += 7; + + for(uint8_t i = 0; i < 8; i++) + { + *str = '0' + (val & 0x01); + val = val >> 1; + str--; + } +} diff --git a/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_in_test.c b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_in_test.c new file mode 100644 index 0000000..6bfc326 --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_in_test.c @@ -0,0 +1,396 @@ +// MCP23S17_in_test.c +// Tests input capability of MCP23S17 GPIO expander on TestBoard TPS27SA08-Q1 +// ---------------------------------------------------------------------------- +// Polls MCP23S17 GPIO Expander state every 100 ms. +// Prints out the GPIO state to serial monitor with baud rate 115200 bits/s. +// Format: [A7] 10100000 [A0] [B7] 00000011 [B0] +// Note that if LEDs are soldered on, can affect readings (specifically when +// testing internal pull-up resistors). + +#include "stm32xx_hal.h" +// stm32xx_hal.h contains includes for RTOS stuff. +// #include "printf.h" +#include "TestBoard_TPS27SA08-Q1_Pins.h" +#include "TestBoard_TPS27SA08-Q1_SPI.h" +#include "TestBoard_TPS27SA08-Q1_UART.h" +#include "Test_Utilities.h" +#include "MCP23S17.h" +#include +#include + +// DECLARATIONS --------------------------------------------------------------- + +// MCU Peripheral Handles +SPI_HandleTypeDef hspi1; +UART_HandleTypeDef huart2; + +// Tasks +TaskHandle_t blink_task_handle; +TaskHandle_t gpioexp_in_task_handle; + +StaticTask_t initTaskBuffer; +StackType_t initTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t blinkTaskBuffer; +StackType_t blinkTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t gpioexpInTaskBuffer; +StackType_t gpioexpInTaskStack[configMINIMAL_STACK_SIZE]; + +// RTOS crap +SemaphoreHandle_t spi1_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi1_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi1_done_sem; // Semaphore to signal SPI DMA/IT completion +StaticSemaphore_t spi1_done_sem_buffer; // Static buffer for completion semaphore + +// Driver Handles +MCP23S17_HandleTypeDef gpioexp; + +// Variables +uint8_t gpioexp_state[MCP23S17_NUM_PORTS] = {0}; + +char dashed_line[] = "----------------\n"; +char newline[] = "\n"; +char gpio_state_msg[] = "GPIO State: [A7] XXXXXXXX [A0] [B7] XXXXXXXX [B0]\n"; + +// TASKS ---------------------------------------------------------------------- + +// Initialize MCU peripherals and external device drivers +void Init_Task(void *argument) +{ + GPIO_InitTypeDef led_config = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = LED_PIN + }; + + // initialize LED pin + LED_CLOCK_INIT(); + HAL_GPIO_Init(LED_PORT, &led_config); + HAL_GPIO_WritePin(LED_PORT, LED_PIN, 0); + + GPIO_InitTypeDef gpioexp_cs = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = GPIOEXP_CS_PIN + }; + + // initialize GPIO expander CS pin + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOEXP_CS_PORT, &gpioexp_cs); + HAL_GPIO_WritePin(GPIOEXP_CS_PORT, GPIOEXP_CS_PIN, 1); + + // UART2 init + MX_USART2_UART_Init(); + + // SPI1 init + MX_SPI1_Init(); + + // set up SPI mutex and done semaphore + if(SPI_RTOS_Mutex_Semaphore_Setup(&spi1_mutex, &spi1_mutex_buffer, &spi1_done_sem, &spi1_done_sem_buffer) != true) + { + char fail_spi_mutex_semaphore_init_msg[] = "FAIL:SPI_MUTEX_SEMAPHORE_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_mutex_semaphore_init_msg, strlen(fail_spi_mutex_semaphore_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // SPI dummy send because MCU starts with CLK idling high (despite configuration + // to idle low) until first transmission + if(SPI_Init_Dummy_Send(&hspi1, spi1_mutex, spi1_done_sem) != true) + { + char fail_spi_init_dummy_send_msg[] = "FAIL:SPI_INIT_DUMMY_SEND\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_init_dummy_send_msg, strlen(fail_spi_init_dummy_send_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander configuration + gpioexp = (MCP23S17_HandleTypeDef) { + .spi = &hspi1, + .cs_port = GPIOEXP_CS_PORT, + .cs_pin = GPIOEXP_CS_PIN, + .spi_mutex = spi1_mutex, + .spi_done_sem = spi1_done_sem, + + .address_en = MCP23S17_CONFIG_INT_MIRRORED, + .int_mirror = MCP23S17_CONFIG_ADDRESSING_DISABLED, + .int_drive = MCP23S17_CONFIG_INTDRIVE_PP, + .int_pol = MCP23S17_CONFIG_INTPOL_ACTIVE_HIGH, + }; + + if(MCP23S17_Init(&gpioexp) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_init_msg[] = "FAIL:MCP23S17_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_init_msg, strlen(fail_mcp23s17_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander pin configuration + MCP23S17_PinConfigInput_t gpioexp_pin_configs[MCP23S17_NUM_GPIO_ALL] = { + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN0, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN1, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN2, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN3, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN4, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN5, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN6, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN7, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN0, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN1, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN2, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN3, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN4, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN5, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN6, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + { + .port = MCP23S17_GPIOB, + .pin = MCP23S17_PIN7, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_DISABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1 + }, + }; + + for(uint8_t i = 0; i < MCP23S17_NUM_GPIO_ALL; i++) + { + if(MCP23S17_GetAllOfYourSingleInputGPIOInitSetUpWithThisOneFunctionCallThatDoesEverythingForYourInstantly(&gpioexp, gpioexp_pin_configs[i]) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_pin_init_msg[] = "FAIL:MCP23S17_PIN_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_pin_init_msg, strlen(fail_mcp23s17_pin_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + } + + // celebrate! + char initialization_complete_msg[] = "Initialization complete.\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) initialization_complete_msg, strlen(initialization_complete_msg), HAL_MAX_DELAY); + // let's get this party started... + vTaskResume(blink_task_handle); + vTaskResume(gpioexp_in_task_handle); + + // Init task kills itself + vTaskDelete(NULL); +} + +// Heartbeat LED +void Blink_Task(void *argument) +{ + for(;;) + { + char blink_task_runs_msg[] = "Blink_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) blink_task_runs_msg, strlen(blink_task_runs_msg), HAL_MAX_DELAY); + + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +// Poll GPIO expander inputs +void GpioExp_In_Task(void *argument) +{ + for(;;) + { + char gpioexp_in_task_runs_msg[] = "GpioExp_In_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) gpioexp_in_task_runs_msg, strlen(gpioexp_in_task_runs_msg), HAL_MAX_DELAY); + + // poll GPIO expander status + if(MCP23S17_ReadGPIO_All(&gpioexp, gpioexp_state) != MCP23S17_πŸ™‚) + { + char fail_readgpio_all_msg[] = "FAIL:READGPIO_ALL\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_readgpio_all_msg, strlen(fail_readgpio_all_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // print status + HAL_UART_Transmit(&huart2, (uint8_t*) &dashed_line, strlen(dashed_line), HAL_MAX_DELAY); + uint8_to_binary_str(gpioexp_state[0], gpio_state_msg+17); + uint8_to_binary_str(gpioexp_state[1], gpio_state_msg+38); + HAL_UART_Transmit(&huart2, (uint8_t*) &gpio_state_msg, strlen(gpio_state_msg), HAL_MAX_DELAY); + HAL_UART_Transmit(&huart2, (uint8_t*) &dashed_line, strlen(dashed_line), HAL_MAX_DELAY); + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +// MAIN ----------------------------------------------------------------------- + +int main() +{ + HAL_Init(); + SystemClock_Config(); + + xTaskCreateStatic(Init_Task, + "Init Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 2, + initTaskStack, + &initTaskBuffer + ); + + blink_task_handle = xTaskCreateStatic(Blink_Task, + "Blink Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + blinkTaskStack, + &blinkTaskBuffer + ); + + gpioexp_in_task_handle = xTaskCreateStatic(GpioExp_In_Task, + "GPIO Exp In Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + gpioexpInTaskStack, + &gpioexpInTaskBuffer + ); + + vTaskSuspend(blink_task_handle); + vTaskSuspend(gpioexp_in_task_handle); + + vTaskStartScheduler(); + + while(1) {} +} diff --git a/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_int_test.c b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_int_test.c new file mode 100644 index 0000000..a8e4cfa --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_int_test.c @@ -0,0 +1,280 @@ +// MCP23S17_in_test.c +// Tests input capability of MCP23S17 GPIO expander on TestBoard TPS27SA08-Q1 +// ---------------------------------------------------------------------------- +// Polls MCP23S17 GPIO Expander state every 100 ms. +// Prints out the GPIO state to serial monitor with baud rate 115200 bits/s. +// Format: [A7] 10100000 [A0] [B7] 00000011 [B0] +// Note that if LEDs are soldered on, can affect readings (specifically when +// testing internal pull-up resistors). + +#include "stm32xx_hal.h" +// stm32xx_hal.h contains includes for RTOS stuff. +// #include "printf.h" +#include "TestBoard_TPS27SA08-Q1_Pins.h" +#include "TestBoard_TPS27SA08-Q1_SPI.h" +#include "TestBoard_TPS27SA08-Q1_UART.h" +#include "Test_Utilities.h" +#include "MCP23S17.h" +#include +#include + +// DECLARATIONS --------------------------------------------------------------- + +// MCU Peripheral Handles +SPI_HandleTypeDef hspi1; +UART_HandleTypeDef huart2; + +// Tasks +TaskHandle_t blink_task_handle; +TaskHandle_t gpioexp_int_task_handle; + +StaticTask_t initTaskBuffer; +StackType_t initTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t blinkTaskBuffer; +StackType_t blinkTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t gpioexpIntTaskBuffer; +StackType_t gpioexpIntTaskStack[configMINIMAL_STACK_SIZE]; + +// RTOS crap +SemaphoreHandle_t spi1_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi1_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi1_done_sem; // Semaphore to signal SPI DMA/IT completion +StaticSemaphore_t spi1_done_sem_buffer; // Static buffer for completion semaphore + +// Driver Handles +MCP23S17_HandleTypeDef gpioexp; + +// Variables +uint8_t int_state[2] = {0, 0}; +uint8_t gpio_state[2] = {0, 0}; +uint8_t cap_state[2] = {0, 0}; + +char dashed_line[] = "----------------\n"; +char newline[] = "\n"; +char interrupt_msg[] = "Interrupt happened!\n"; +char int_state_msg[] = "INT State: [A7] XXXXXXXX [A0] [B7] XXXXXXXX [B0]\n"; +char gpio_state_msg[] = "GPIO State: [A7] XXXXXXXX [A0] [B7] XXXXXXXX [B0]\n"; +char cap_state_msg[] = "CAP State: [A7] XXXXXXXX [A0] [B7] XXXXXXXX [B0]\n"; +char interest_indicator[] = " ^\n"; + +// TASKS ---------------------------------------------------------------------- + +// Initialize MCU peripherals and external device drivers +void Init_Task(void *argument) +{ + GPIO_InitTypeDef led_config = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = LED_PIN + }; + + // initialize LED pin + LED_CLOCK_INIT(); + HAL_GPIO_Init(LED_PORT, &led_config); + HAL_GPIO_WritePin(LED_PORT, LED_PIN, 0); + + GPIO_InitTypeDef gpioexp_cs = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = GPIOEXP_CS_PIN + }; + + // initialize GPIO expander CS pin + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOEXP_CS_PORT, &gpioexp_cs); + HAL_GPIO_WritePin(GPIOEXP_CS_PORT, GPIOEXP_CS_PIN, 1); + + GPIO_InitTypeDef gpioexp_intA = { + .Mode = GPIO_MODE_INPUT, + .Pull = GPIO_NOPULL, + .Pin = GPIOEXP_INTA_PIN + }; + + // initialize GPIO expander CS pin + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOEXP_INTA_PORT, &gpioexp_intA); + + // UART2 init + MX_USART2_UART_Init(); + + // SPI1 init + MX_SPI1_Init(); + + // set up SPI mutex and done semaphore + if(SPI_RTOS_Mutex_Semaphore_Setup(&spi1_mutex, &spi1_mutex_buffer, &spi1_done_sem, &spi1_done_sem_buffer) != true) + { + char fail_spi_mutex_semaphore_init_msg[] = "FAIL:SPI_MUTEX_SEMAPHORE_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_mutex_semaphore_init_msg, strlen(fail_spi_mutex_semaphore_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // SPI dummy send because MCU starts with CLK idling high (despite configuration + // to idle low) until first transmission + if(SPI_Init_Dummy_Send(&hspi1, spi1_mutex, spi1_done_sem) != true) + { + char fail_spi_init_dummy_send_msg[] = "FAIL:SPI_INIT_DUMMY_SEND\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_init_dummy_send_msg, strlen(fail_spi_init_dummy_send_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander configuration + gpioexp = (MCP23S17_HandleTypeDef) { + .spi = &hspi1, + .cs_port = GPIOEXP_CS_PORT, + .cs_pin = GPIOEXP_CS_PIN, + .spi_mutex = spi1_mutex, + .spi_done_sem = spi1_done_sem, + + .address_en = MCP23S17_CONFIG_INT_MIRRORED, + .int_mirror = MCP23S17_CONFIG_ADDRESSING_DISABLED, + .int_drive = MCP23S17_CONFIG_INTDRIVE_PP, + .int_pol = MCP23S17_CONFIG_INTPOL_ACTIVE_HIGH, + }; + + if(MCP23S17_Init(&gpioexp) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_init_msg[] = "FAIL:MCP23S17_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_init_msg, strlen(fail_mcp23s17_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander pin configuration + MCP23S17_PinConfigInput_t hss_st = { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN5, + .pullup = MCP23S17_PULLUP_DISABLED, + .inpol = MCP23S17_INPUTPOLARITY_SAME, + .inten = MCP23S17_INTEN_ENABLED, + .intmode = MCP23S17_INTMODE_ON_CHANGE, + .default_value = 1, + }; + + if(MCP23S17_GetAllOfYourSingleInputGPIOInitSetUpWithThisOneFunctionCallThatDoesEverythingForYourInstantly(&gpioexp, hss_st) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_pin_init_msg[] = "FAIL:MCP23S17_PIN_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_pin_init_msg, strlen(fail_mcp23s17_pin_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // celebrate! + char initialization_complete_msg[] = "Initialization complete.\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) initialization_complete_msg, strlen(initialization_complete_msg), HAL_MAX_DELAY); + // let's get this party started... + vTaskResume(blink_task_handle); + vTaskResume(gpioexp_int_task_handle); + + // Init task kills itself + vTaskDelete(NULL); +} + +// Heartbeat LED +void Blink_Task(void *argument) +{ + for(;;) + { + char blink_task_runs_msg[] = "Blink_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) blink_task_runs_msg, strlen(blink_task_runs_msg), HAL_MAX_DELAY); + + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +// Poll GPIO expander inputs +void GpioExp_Int_Task(void *argument) +{ + for(;;) + { + char gpioexp_int_task_runs_msg[] = "GpioExp_Int_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) gpioexp_int_task_runs_msg, strlen(gpioexp_int_task_runs_msg), HAL_MAX_DELAY); + + if(HAL_GPIO_ReadPin(GPIOEXP_INTA_PORT, GPIOEXP_INTA_PIN)) + { + // get states and acknowledge interrupt + MCP23S17_ReadInterruptStatus_All(&gpioexp, int_state); + MCP23S17_ReadGPIO_All(&gpioexp, gpio_state); + MCP23S17_ReadInterruptGPIOState_All(&gpioexp, cap_state); + + // print notification to serial monitor + HAL_UART_Transmit(&huart2, (uint8_t*) &dashed_line, strlen(dashed_line), HAL_MAX_DELAY); + HAL_UART_Transmit(&huart2, (uint8_t*) &interrupt_msg, strlen(interrupt_msg), HAL_MAX_DELAY); + + uint8_to_binary_str(int_state[0], int_state_msg+17); + uint8_to_binary_str(int_state[1], int_state_msg+38); + HAL_UART_Transmit(&huart2, (uint8_t*) &int_state_msg, strlen(int_state_msg), HAL_MAX_DELAY); + + uint8_to_binary_str(gpio_state[0], gpio_state_msg+17); + uint8_to_binary_str(gpio_state[1], gpio_state_msg+38); + HAL_UART_Transmit(&huart2, (uint8_t*) &gpio_state_msg, strlen(gpio_state_msg), HAL_MAX_DELAY); + + uint8_to_binary_str(cap_state[0], cap_state_msg+17); + uint8_to_binary_str(cap_state[1], cap_state_msg+38); + HAL_UART_Transmit(&huart2, (uint8_t*) &cap_state_msg, strlen(cap_state_msg), HAL_MAX_DELAY); + HAL_UART_Transmit(&huart2, (uint8_t*) &interest_indicator, strlen(interest_indicator), HAL_MAX_DELAY); + HAL_UART_Transmit(&huart2, (uint8_t*) &dashed_line, strlen(dashed_line), HAL_MAX_DELAY); + HAL_UART_Transmit(&huart2, (uint8_t*) &newline, strlen(newline), HAL_MAX_DELAY); + } + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +// MAIN ----------------------------------------------------------------------- + +int main() +{ + HAL_Init(); + SystemClock_Config(); + + xTaskCreateStatic(Init_Task, + "Init Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 2, + initTaskStack, + &initTaskBuffer + ); + + blink_task_handle = xTaskCreateStatic(Blink_Task, + "Blink Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + blinkTaskStack, + &blinkTaskBuffer + ); + + gpioexp_int_task_handle = xTaskCreateStatic(GpioExp_Int_Task, + "GPIO Exp Int Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + gpioexpIntTaskStack, + &gpioexpIntTaskBuffer + ); + + vTaskSuspend(blink_task_handle); + vTaskSuspend(gpioexp_int_task_handle); + + vTaskStartScheduler(); + + while(1) {} +} diff --git a/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_out_test.c b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_out_test.c new file mode 100644 index 0000000..bed02fd --- /dev/null +++ b/firmware/TestBoard_TPS27SA08-Q1/test/src/MCP23S17_out_test.c @@ -0,0 +1,254 @@ +// MCP23S17_out_test.c +// Tests output capability of MCP23S17 GPIO expander on TestBoard TPS27SA08-Q1 +// ---------------------------------------------------------------------------- +// Configures MCP23S17 GPIO A0, A1, and A2 as outputs. Initializes with all +// LEDs on, then cycling through each LED, before returning to all LEDs on +// (and repeating) with state change every 100 ms. + +#include "stm32xx_hal.h" +// stm32xx_hal.h contains includes for RTOS stuff. +// #include "printf.h" +#include "TestBoard_TPS27SA08-Q1_Pins.h" +#include "TestBoard_TPS27SA08-Q1_SPI.h" +#include "TestBoard_TPS27SA08-Q1_UART.h" +#include "MCP23S17.h" +#include +#include + +// DECLARATIONS --------------------------------------------------------------- + +// MCU Peripheral Handles +SPI_HandleTypeDef hspi1; +UART_HandleTypeDef huart2; + +// Tasks +TaskHandle_t blink_task_handle; +TaskHandle_t gpioexp_out_task_handle; + +StaticTask_t initTaskBuffer; +StackType_t initTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t blinkTaskBuffer; +StackType_t blinkTaskStack[configMINIMAL_STACK_SIZE]; +StaticTask_t gpioexpOutTaskBuffer; +StackType_t gpioexpOutTaskStack[configMINIMAL_STACK_SIZE]; + +// RTOS crap +SemaphoreHandle_t spi1_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi1_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi1_done_sem; // Semaphore to signal SPI DMA/IT completion +StaticSemaphore_t spi1_done_sem_buffer; // Static buffer for completion semaphore + +// Driver Handles +MCP23S17_HandleTypeDef gpioexp; + +// Variables +const bool led_dance[4][3] = {{1, 1, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; +uint8_t led_dance_state = 0; + +// TASKS ---------------------------------------------------------------------- + +// Initialize MCU peripherals and external device drivers +void Init_Task(void *argument) +{ + GPIO_InitTypeDef led_config = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = LED_PIN + }; + + // initialize LED pin + LED_CLOCK_INIT(); + HAL_GPIO_Init(LED_PORT, &led_config); + HAL_GPIO_WritePin(LED_PORT, LED_PIN, 0); + + GPIO_InitTypeDef gpioexp_cs = { + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Pin = GPIOEXP_CS_PIN + }; + + // initialize GPIO expander CS pin + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOEXP_CS_PORT, &gpioexp_cs); + HAL_GPIO_WritePin(GPIOEXP_CS_PORT, GPIOEXP_CS_PIN, 1); + + // UART2 init + MX_USART2_UART_Init(); + + // SPI1 init + MX_SPI1_Init(); + + // set up SPI mutex and done semaphore + if(SPI_RTOS_Mutex_Semaphore_Setup(&spi1_mutex, &spi1_mutex_buffer, &spi1_done_sem, &spi1_done_sem_buffer) != true) + { + char fail_spi_mutex_semaphore_init_msg[] = "FAIL:SPI_MUTEX_SEMAPHORE_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_mutex_semaphore_init_msg, strlen(fail_spi_mutex_semaphore_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // SPI dummy send because MCU starts with CLK idling high (despite configuration + // to idle low) until first transmission + if(SPI_Init_Dummy_Send(&hspi1, spi1_mutex, spi1_done_sem) != true) + { + char fail_spi_init_dummy_send_msg[] = "FAIL:SPI_INIT_DUMMY_SEND\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_spi_init_dummy_send_msg, strlen(fail_spi_init_dummy_send_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander configuration + gpioexp = (MCP23S17_HandleTypeDef) { + .spi = &hspi1, + .cs_port = GPIOEXP_CS_PORT, + .cs_pin = GPIOEXP_CS_PIN, + .spi_mutex = spi1_mutex, + .spi_done_sem = spi1_done_sem, + + .address_en = MCP23S17_CONFIG_INT_MIRRORED, + .int_mirror = MCP23S17_CONFIG_ADDRESSING_DISABLED, + .int_drive = MCP23S17_CONFIG_INTDRIVE_PP, + .int_pol = MCP23S17_CONFIG_INTPOL_ACTIVE_HIGH, + }; + + if(MCP23S17_Init(&gpioexp) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_init_msg[] = "FAIL:MCP23S17_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_init_msg, strlen(fail_mcp23s17_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + + // GPIO expander pin configuration + MCP23S17_PinConfigOutput_t gpioexp_pin_configs[3] = { + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN0, + .initial_state = 1, + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN1, + .initial_state = 1, + }, + { + .port = MCP23S17_GPIOA, + .pin = MCP23S17_PIN2, + .initial_state = 1, + } + }; + + for(uint8_t i = 0; i < 3; i++) + { + if(MCP23S17_TheOneStopShopForAllYourOutputGPIOInitNeedsOfOneSpecificPin_DoneInOneLineOrYourMoneyBack(&gpioexp, gpioexp_pin_configs[i]) != MCP23S17_πŸ™‚) + { + char fail_mcp23s17_pin_init_msg[] = "FAIL:MCP23S17_PIN_INIT\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) fail_mcp23s17_pin_init_msg, strlen(fail_mcp23s17_pin_init_msg), HAL_MAX_DELAY); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + HAL_Delay(50); + } + } + } + + // celebrate! + char initialization_complete_msg[] = "Initialization complete.\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) initialization_complete_msg, strlen(initialization_complete_msg), HAL_MAX_DELAY); + // let's get this party started... + vTaskResume(blink_task_handle); + vTaskResume(gpioexp_out_task_handle); + + // Init task kills itself + vTaskDelete(NULL); +} + +// Heartbeat LED +void Blink_Task(void *argument) +{ + for(;;) + { + char blink_task_runs_msg[] = "Blink_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) blink_task_runs_msg, strlen(blink_task_runs_msg), HAL_MAX_DELAY); + + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +// Update GPIO expander outputs +void GpioExp_Out_Task(void *argument) +{ + for(;;) + { + char gpioexp_in_task_runs_msg[] = "GpioExp_Out_Task runs...\n"; + HAL_UART_Transmit(&huart2, (uint8_t*) gpioexp_in_task_runs_msg, strlen(gpioexp_in_task_runs_msg), HAL_MAX_DELAY); + + // update LED outputs + MCP23S17_WriteGPIO_Pin(&gpioexp, MCP23S17_GPIOA, MCP23S17_PIN0, led_dance[led_dance_state][0]); + MCP23S17_WriteGPIO_Pin(&gpioexp, MCP23S17_GPIOA, MCP23S17_PIN1, led_dance[led_dance_state][1]); + MCP23S17_WriteGPIO_Pin(&gpioexp, MCP23S17_GPIOA, MCP23S17_PIN2, led_dance[led_dance_state][2]); + + // cycle through LED state + led_dance_state++; + if(led_dance_state > 3) + { + led_dance_state = 0; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +// MAIN ----------------------------------------------------------------------- + +int main() +{ + HAL_Init(); + SystemClock_Config(); + + xTaskCreateStatic(Init_Task, + "Init Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 2, + initTaskStack, + &initTaskBuffer + ); + + blink_task_handle = xTaskCreateStatic(Blink_Task, + "Blink Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + blinkTaskStack, + &blinkTaskBuffer + ); + + gpioexp_out_task_handle = xTaskCreateStatic(GpioExp_Out_Task, + "GPIO Exp Out Task", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 1, + gpioexpOutTaskStack, + &gpioexpOutTaskBuffer + ); + + vTaskSuspend(blink_task_handle); + vTaskSuspend(gpioexp_out_task_handle); + + vTaskStartScheduler(); + + while(1) {} +} diff --git a/firmware/driver/inc/MCP23S17.h b/firmware/driver/inc/MCP23S17.h new file mode 100644 index 0000000..d8881b3 --- /dev/null +++ b/firmware/driver/inc/MCP23S17.h @@ -0,0 +1,384 @@ +#pragma once + +#include "stm32xx_hal.h" +#include + +// DRIVER FOR MCP23S17 SPI GPIO Expander +// Datasheet: datasheets/MCP23017_MCP23S17.pdf (from repo root) + +// RTOS PARAMETERS ------------------------------------------------------------ + +#define MCP23S17_SPI_MUTEX_DELAY_TICKS portMAX_DELAY // ticks +#define MCP23S17_SPI_TRANSMISSION_DELAY_TICKS pdMS_TO_TICKS(100) // ticks + +// DEVICE PARAMETERS ---------------------------------------------------------- + +#define MCP23S17_NUM_PORTS 2 +#define MCP23S17_NUM_GPIO_PORT 8 +#define MCP23S17_NUM_GPIO_ALL MCP23S17_NUM_PORTS*MCP23S17_NUM_GPIO_PORT + +// REGISTERS ------------------------------------------------------------------ + +#define MCP23S17_OPCODE_READ 0x41 // needs address if hardware addressing is enabled +#define MCP23S17_OPCODE_WRITE 0x40 // needs address if hardware addressing is enabled + + // For each port (A, B)... +#define MCP23S17_REG_IODIRA 0x00 // GPIO Direction Registers +#define MCP23S17_REG_IODIRB 0x01 +#define MCP23S17_REG_IPOLA 0x02 // GPIO Input Polarity Registers +#define MCP23S17_REG_IPOLB 0x03 +#define MCP23S17_REG_GPINTENA 0x04 // GPIO Interrupt Enable Registers +#define MCP23S17_REG_GPINTENB 0x05 +#define MCP23S17_REG_DEFVALA 0x06 // GPIO Default Value Registers (for interrupt use) +#define MCP23S17_REG_DEFVALB 0x07 +#define MCP23S17_REG_INTCONA 0x08 // GPIO Interrupt Mode Registers +#define MCP23S17_REG_INTCONB 0x09 +#define MCP23S17_REG_IOCON 0x0A // Configuration Registers +#define MCP23S17_REG_IOCON2 0x0B +#define MCP23S17_REG_GPPUA 0x0C // GPIO Pull-up Enable/Disable Registers +#define MCP23S17_REG_GPPUB 0x0D +#define MCP23S17_REG_INTFA 0x0E // Interrupt Flag Registers +#define MCP23S17_REG_INTFB 0x0F +#define MCP23S17_REG_INTCAPA 0x10 // Interrupt Captured GPIO State Registers +#define MCP23S17_REG_INTCAPB 0x11 +#define MCP23S17_REG_GPIOA 0x12 // GPIO Registers +#define MCP23S17_REG_GPIOB 0x13 +#define MCP23S17_REG_OLATA 0x14 // Output Latch Registers +#define MCP23S17_REG_OLATB 0x15 + +#define MCP23S17_REG_INVALID 0x16 +#define MCP23S17_REG_INVALID_CHECK (reg_addr+num_regs-1) >= MCP23S17_REG_INVALID + +#define MCP23S17_IOCON_TEMPLATE 0x00 +#define MCP23S17_IOCON_MIRROR_LSHIFT 6 +#define MCP23S17_IOCON_HAEN_LSHIFT 3 +#define MCP23S17_IOCON_ODR_LSHIFT 2 +#define MCP23S17_IOCON_INTPOL_LSHIFT 1 + +// DEVICE --------------------------------------------------------------------- + +typedef enum { + MCP23S17_😒, // MCP23S17 sad + MCP23S17_πŸ™‚, // MCP23S17 happy + MCP23S17_πŸ•·οΈ, // MCP23S17 SPI mutex timeout + MCP23S17_πŸ•ΈοΈ, // MCP23S17 SPI done semaphore timeout +} MCP23S17_Status_t; + +// PORTS/PINS ----------------------------------------------------------------- + +typedef enum { + MCP23S17_GPIOA, + MCP23S17_GPIOB, + MCP23S17_PORT_INVALID, +} MCP23S17_Port_t; + +typedef enum { + MCP23S17_PIN0, + MCP23S17_PIN1, + MCP23S17_PIN2, + MCP23S17_PIN3, + MCP23S17_PIN4, + MCP23S17_PIN5, + MCP23S17_PIN6, + MCP23S17_PIN7, + MCP23S17_PIN_INVALID, +} MCP23S17_Pin_t; + +#define MCP23S17_PORT_PIN_INVALID_CHECK port >= MCP23S17_PORT_INVALID || pin >= MCP23S17_PIN_INVALID + +// DEVICE CONFIGURATION ------------------------------------------------------- + +// Device Addressing Configuration +typedef enum { + MCP23S17_CONFIG_ADDRESSING_DISABLED, // Device addressing disabled. + MCP23S17_CONFIG_ADDRESSING_ENABLED, // Device addressing enabled. +} MCP23S17_Config_Addressing_t; +#define MCP23S17_CONFIG_ADDRESSING_DEFAULT MCP23S17_CONFIG_ADDRESSING_DISABLED + +// INT Pins Mirroring +typedef enum { + MCP23S17_CONFIG_INT_SEPARATE, // INTA and INTB pins are separate. + MCP23S17_CONFIG_INT_MIRRORED, // INTA and INTB pins are tied internally. +} MCP23S17_Config_IntMirror_t; +#define MCP23S17_CONFIG_INTMIRROR_DEFAULT MCP23S17_CONFIG_INT_SEPARATE + +// INT Pins Operating Mode +typedef enum { + MCP23S17_CONFIG_INTDRIVE_PP, // INT pins are push-pull. + MCP23S17_CONFIG_INTDRIVE_OD, // INT pins are open-drain. +} MCP23S17_Config_IntDrive_t; +#define MCP23S17_CONFIG_INTDRIVE_DEFAULT MCP23S17_CONFIG_INTDRIVE_PP + +// INT Pins Polarity (only applicable if configured as push-pull) +typedef enum { + MCP23S17_CONFIG_INTPOL_ACTIVE_LOW, // INT pins are active low. + MCP23S17_CONFIG_INTPOL_ACTIVE_HIGH, // INT pins are active high. +} MCP23S17_Config_IntPol_t; +#define MCP23S17_CONFIG_INTPOL_DEFAULT MCP23S17_CONFIG_INTPOL_ACTIVE_LOW + +// PIN CONFIGURATION ---------------------------------------------------------- + +// GPIO Direction +typedef enum { + MCP23S17_DIR_OUTPUT, // GPIO is configured as output. + MCP23S17_DIR_INPUT, // GPIO is configured as input. +} MCP23S17_Dir_t; + +// GPIO Pullup Enabled/Disabled +typedef enum { + MCP23S17_PULLUP_DISABLED, // Internal GPIO pullup disabled. + MCP23S17_PULLUP_ENABLED, // Internal GPIO pullup enabled. +} MCP23S17_Pullup_t; +#define MCP23S17_PULLUP_DEFAULT MCP23S17_PULLUP_DISABLED + +// GPIO Input Polarity (only affects inputs) +typedef enum { + MCP23S17_INPUTPOLARITY_SAME, // GPIO polarity as-is (active-high). + MCP23S17_INPUTPOLARITY_INVERT, // GPIO polarity inverted (active-low). +} MCP23S17_InputPolarity_t; +#define MCP23S17_INPUTPOLARITY_DEFAULT MCP23S17_INPUTPOLARITY_SAME + +// GPIO Interrupt Enabled/Disabled +typedef enum { + MCP23S17_INTEN_DISABLED, // Interrupts disabled on GPIO pin. + MCP23S17_INTEN_ENABLED, // Interrupts enabled on GPIO pin. +} MCP23S17_InterruptEnable_t; +#define MCP23S17_INTEN_DEFAULT MCP23S17_INTEN_DISABLED + +typedef enum { + MCP23S17_INTMODE_ON_CHANGE, // Interrupt occurs on pin state change. + MCP23S17_INTMODE_DEFVAL_COMPARE, // Interrupt occurs on deviation from default pin state. + // Default pin state is set in DEFVAL register. +} MCP23S17_InterruptMode_t; +#define MCP23S17_INTMODE_DEFAULT MCP23S17_INTMODE_ON_CHANGE + +// GPIO Output Pin Configuration Struct +typedef struct { + // GPIO config + MCP23S17_Port_t port; + MCP23S17_Pin_t pin; + bool initial_state; +} MCP23S17_PinConfigOutput_t; + +// GPIO Input Pin Configuration Struct +typedef struct { + // GPIO config + MCP23S17_Port_t port; + MCP23S17_Pin_t pin; + MCP23S17_Pullup_t pullup; + MCP23S17_InputPolarity_t inpol; + // If interrupts disabled (inten == MCP23S17_INT_DISABLED), intmode and default_value have no effect. + MCP23S17_InterruptEnable_t inten; + MCP23S17_InterruptMode_t intmode; + bool default_value; +} MCP23S17_PinConfigInput_t; + +// HANDLE --------------------------------------------------------------------- + +typedef struct { + SPI_HandleTypeDef* spi; // STM32 HAL SPI handle + GPIO_TypeDef* cs_port; + uint16_t cs_pin; + uint8_t addr; // 3-bit hardware address << 1 + // A2, A1, A0 (set on IC pins). + // If hardware addressing is disabled, set to 0. + // Do not left-shift. Value is left-shifted by the init function when used. + + SemaphoreHandle_t spi_mutex; // Mutex to prevent simultaenous SPI access + SemaphoreHandle_t spi_done_sem; // Semaphore to signal SPI transmission complete + + MCP23S17_Config_Addressing_t address_en; // Device hardware addressing configuration (MCP23S17_ADDRESSING_DISABLED: hardware addressing disabled, MCP23S17_ADDRESSING_ENABLED: hardware addressing enabled) + MCP23S17_Config_IntMirror_t int_mirror; // Device INT pin mirroring configuration (MCP23S17_CONFIG_INT_SEPARATE: interrupt pins independent, MCP23S17_CONFIG_INT_MIRRORED: Interrupt pins tied internally) + MCP23S17_Config_IntDrive_t int_drive; // Device INT pin mode configuration (MCP23S17_CONFIG_INTDRIVE_PP: INT pin is push-pull, MCP23S17_CONFIG_INTDRIVE_OD: INT pin is open-drain) + MCP23S17_Config_IntPol_t int_pol; // Device INT pin polarity (MCP23S17_CONFIG_INTPOL_ACTIVE_LOW: INT pin is active-low, MCP23S17_CONFIG_INTPOL_ACTIVE_HIGH: INT pin is active-high) +} MCP23S17_HandleTypeDef; + +// DEVICE FUNCTIONS ----------------------------------------------------------- +// meant to be used interally, but exposed for outside use if CAREFUL + +/** + * @brief Writes to register(s) on MCP23S17 (primarily meant to be used internally). + * @param device MCP23S17 Device Handle + * @param reg_addr Starting Register Address + * @param data Pointer (uint8_t array) to data to write to register(s) + * @param num_regs Number of sequential register(s) to write + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_WriteRegs(MCP23S17_HandleTypeDef* device, uint8_t reg_addr, uint8_t* data, uint16_t num_regs); + +/** + * @brief Reads register(s) on MCP23S17 (primarily meant to be used internally). + * @param device MCP23S17 Device Handle + * @param reg_addr Starting register address + * @param data Pointer (uint8_t array) to store data read from register(s) + * @param num_regs Number of sequential register(s) to read + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadRegs(MCP23S17_HandleTypeDef* device, uint8_t reg_addr, uint8_t* data, uint16_t num_regs); + +// GENERAL GPIO FUNCTIONS ----------------------------------------------------- + +/** + * @brief Initializes MCP23S17 driver and device for use. + * @param device MCP23S17 Device Handle + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_Init(MCP23S17_HandleTypeDef* device); + +/** + * @brief Sets a GPIO pin's direction. + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param dir Pin direction: MCP23S17_DIR_OUTPUT or MCP23S17_DIR_INPUT + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetDirection_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_Dir_t dir); + +/** + * @brief Enables or disables a GPIO pin's pull-up resistor. + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param pu Pull up resistor configuration: MCP23S17_PULLUP_DISABLED or MCP23S17_PULLUP_ENABLED + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetPullup_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_Pullup_t pu); + +/** + * @brief Sets a GPIO pin's input polarity. + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param pol GPIO input polarity: MCP23S17_POLARITY_SAME or MCP23S17_POLARITY_INVERT + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetInputPolarity_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InputPolarity_t pol); + +/** + * @brief Writes to a GPIO pin. (friendly) + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param state GPIO pin state + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_WriteGPIO_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool state); + +/** + * @brief Writes to all GPIO pins. (NON-friendly) + * @param device MCP23S17 Device Handle + * @param state Pointer (uint8_t array) to GPIO pin states (16 bits), from port A7 (MSB) to port B0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_WriteGPIO_All(MCP23S17_HandleTypeDef* device, uint8_t* state); + +/** + * @brief Reads a GPIO pin. + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param state Pointer (bool) to store GPIO pin state + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadGPIO_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool* state); + +/** + * @brief Reads all GPIO pins. + * @param device MCP23S17 Device Handle + * @param state Pointer (uint8_t array) to store GPIO pin states (16 bits), from port A7 (MSB) to port B0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadGPIO_All(MCP23S17_HandleTypeDef* device, uint8_t* state); + +// INTERRUPT FUNCTIONS -------------------------------------------------------- + +/** + * @brief Enables or disables interrupts for a GPIO pin. + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param inten GPIO interrupt configuration: MCP23S17_INT_DISABLED or MCP23S17_INT_ENABLED + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetInterruptEnable_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InterruptEnable_t inten); + +/** + * @brief Sets a GPIO pin's interrupt mode + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param intmode GPIO interrupt mode: MCP23S17_INT_ON_CHANGE (interrupt on-change) or MCP23S17_INT_DEFVAL_COMPARE (default value comparison) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetInterruptMode_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InterruptMode_t intmode); + +/** + * @brief Sets a GPIO pin's default value (for default value comparison interrupt mode) + * @param device MCP23S17 Device Handle + * @param port Device GPIO Port + * @param pin Device GPIO Pin + * @param defval GPIO default state + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetInterruptDefaultValue_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool defval); + +/** + * @brief Sets all GPIO pins' default value (for default value comparison interrupt mode) + * @param device MCP23S17 Device Handle + * @param defval Pointer (uint8_t array) to store GPIO pin default states (16 bits), from port A7 (MSB) to port B0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_SetInterruptDefaultValue_All(MCP23S17_HandleTypeDef* device, uint8_t* defval); + +/** + * @brief Reads one GPIO port's interrupt status + * @param device MCP23S17 Device Handle + * @param port GPIO port to read interrupt status of + * @param state Pointer (uint8_t) to store interrupt status of GPIO port, from GPIOx7 (MSB) to GPIOx0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadInterruptStatus_Port(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, uint8_t* state); + +/** + * @brief Reads all GPIO pins' interrupt status. + * @param device MCP23S17 Device Handle + * @param state Pointer (uint8_t array) to store GPIO pin interrupt status' (16 bits), from port A7 (MSB) to port B0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadInterruptStatus_All(MCP23S17_HandleTypeDef* device, uint8_t* state); + +/** + * @brief Reads one GPIO port's captured state when interrupt occurred + * @param device MCP23S17 Device Handle + * @param port GPIO port to read interrupt status of + * @param state Pointer (uint8_t) to store captured state of GPIO port during interrupt, from GPIOx7 (MSB) to GPIOx0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadInterruptGPIOState_Port(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, uint8_t* state); + +/** + * @brief Reads all GPIO pins' captured state when interrupt occurred. + * @param device MCP23S17 Device Handle + * @param state Pointer (uint8_t array) to store GPIO pin captured states' (16 bits), from port A7 (MSB) to port B0 (LSB) + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_ReadInterruptGPIOState_All(MCP23S17_HandleTypeDef* device, uint8_t* state); + +// SETUP COMBO FUNCTIONS ------------------------------------------------------ + +/** + * @brief Setup function to quickly initialize an output GPIO pin's direction and state in one call. + * @param device MCP23S17 Device Handle + * @param pin_config GPIO Output Pin Configuration Struct + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_TheOneStopShopForAllYourOutputGPIOInitNeedsOfOneSpecificPin_DoneInOneLineOrYourMoneyBack(MCP23S17_HandleTypeDef* device, MCP23S17_PinConfigOutput_t pin_config); + +/** + * @brief Setup function to quickly initialize an input GPIO pin's direction, pull-up resistor, polarity, and interrupts in one call. + * @param device MCP23S17 Device Handle + * @param state GPIO Input Pin Configuration Struct + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful) + */ +MCP23S17_Status_t MCP23S17_GetAllOfYourSingleInputGPIOInitSetUpWithThisOneFunctionCallThatDoesEverythingForYourInstantly(MCP23S17_HandleTypeDef* device, MCP23S17_PinConfigInput_t pin_config); diff --git a/firmware/driver/src/MCP23S17.c b/firmware/driver/src/MCP23S17.c new file mode 100644 index 0000000..085132c --- /dev/null +++ b/firmware/driver/src/MCP23S17.c @@ -0,0 +1,396 @@ +#include "MCP23S17.h" + +// (PRIVATE) HELPER FUNCTIONS ------------------------------------------------- + +static inline void MCP23S17_SPI_Select(MCP23S17_HandleTypeDef* device) +{ + // bring CS pin low + HAL_GPIO_WritePin(device->cs_port, device->cs_pin, GPIO_PIN_RESET); + vTaskDelay(1); +} + +static inline void MCP23S17_SPI_DeSelect(MCP23S17_HandleTypeDef* device) +{ + // bring CS pin high + HAL_GPIO_WritePin(device->cs_port, device->cs_pin, GPIO_PIN_SET); +} + +// FUNCTION DEFINITIONS ------------------------------------------------------- + +MCP23S17_Status_t MCP23S17_WriteRegs(MCP23S17_HandleTypeDef* device, uint8_t reg_addr, uint8_t* data, uint16_t num_regs) +{ + if(MCP23S17_REG_INVALID_CHECK || num_regs == 0){return MCP23S17_😒;} + if(device == NULL || data == NULL){return MCP23S17_😒;} + if(device->spi_mutex == NULL || device->spi_done_sem == NULL){return MCP23S17_😒;} + + // command/device address word + starting register address word + num_regs + uint8_t msg_len = 2+num_regs; + uint8_t msg[msg_len]; + memset(msg, 0, msg_len); + + msg[0] = MCP23S17_OPCODE_WRITE | (device->addr); // command/device address + msg[1] = reg_addr; // starting register address + memcpy(msg+2, data, num_regs); // register data to write + + // get SPI mutex / wait for SPI mutex to free (to prevent simulatenous SPI access) + if(xSemaphoreTake(device->spi_mutex, MCP23S17_SPI_MUTEX_DELAY_TICKS) != pdTRUE) + { + return MCP23S17_πŸ•·οΈ; + } + + // select SPI device + MCP23S17_SPI_Select(device); + + // send SPI transmission + if(HAL_SPI_Transmit_IT(device->spi, msg, msg_len) != HAL_OK) + { + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + return MCP23S17_😒; + } + + // wait for SPI completion + if(xSemaphoreTake(device->spi_done_sem, MCP23S17_SPI_TRANSMISSION_DELAY_TICKS) != pdTRUE) + { + HAL_SPI_Abort(device->spi); + + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + return MCP23S17_πŸ•ΈοΈ; + } + + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + return MCP23S17_πŸ™‚; +} + +MCP23S17_Status_t MCP23S17_ReadRegs(MCP23S17_HandleTypeDef* device, uint8_t reg_addr, uint8_t* data, uint16_t num_regs) +{ + if(MCP23S17_REG_INVALID_CHECK || num_regs == 0){return MCP23S17_😒;} + if(device == NULL || data == NULL){return MCP23S17_😒;} + if(device->spi_mutex == NULL || device->spi_done_sem == NULL){return MCP23S17_😒;} + + // command/device address word + starting register address word + num_regs + // HAL_SPI_Receive_x will transmit prexisting data in buffer, which takes care + // of command / address words (while the rest are filled with data) + uint8_t msg_len = 2+num_regs; + uint8_t msg[msg_len]; + memset(msg, 0, msg_len); + + msg[0] = MCP23S17_OPCODE_READ | (device->addr); // command/device address + msg[1] = reg_addr; // starting register address + + // get SPI mutex / wait for SPI mutex to free (to prevent simulatenous SPI access) + if(xSemaphoreTake(device->spi_mutex, MCP23S17_SPI_MUTEX_DELAY_TICKS) != pdTRUE) + { + return MCP23S17_πŸ•·οΈ; + } + + // select SPI device + MCP23S17_SPI_Select(device); + + // SPI transmission + if(HAL_SPI_Receive_IT(device->spi, msg, msg_len) != HAL_OK) + { + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + return MCP23S17_😒; + } + + // wait for SPI completion + if(xSemaphoreTake(device->spi_done_sem, MCP23S17_SPI_TRANSMISSION_DELAY_TICKS) != pdTRUE) + { + HAL_SPI_Abort(device->spi); + + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + return MCP23S17_πŸ•ΈοΈ; + } + + // deselect SPI device + MCP23S17_SPI_DeSelect(device); + + // release SPI mutex + xSemaphoreGive(device->spi_mutex); + + // copy register data from msg buffer + memcpy(data, msg+2, num_regs); + + return MCP23S17_πŸ™‚; +} + +/** + * @brief Writes to a specific register bit (for a specific GPIO pin) on MCP23S17 in FRIENDLY manner (performs register read before write). + * @param device MCP23S17 Device Handle + * @param reg_addr Register Address + * @param pin Device GPIO Pin + * @param val Value to write in + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful, MCP23S17_😒 otherwise) + */ +static inline MCP23S17_Status_t MCP23S17_WriteBitFriendly(MCP23S17_HandleTypeDef* device, uint8_t reg, MCP23S17_Pin_t pin, bool val) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + uint8_t reg_state = 0; + + status = MCP23S17_ReadRegs(device, reg, ®_state, 1); + if(status != MCP23S17_πŸ™‚){return status;} + + if(val) + { + reg_state |= (0x01 << pin); + } + else + { + reg_state &= ~(0x01 << pin); + } + + status = MCP23S17_WriteRegs(device, reg, ®_state, 1); + // skipping status πŸ™‚ check for last part of function + + return status; +} + +/** + * @brief Reads a specific register bit (for a specific GPIO pin) on MCP23S17. + * @param device MCP23S17 Device Handle + * @param reg_addr Register Address + * @param pin Device GPIO Pin + * @param bool Pointer (bool) to store bit read + * @returns MCP23S17 Status (MCP23S17_πŸ™‚ if successful, MCP23S17_😒 otherwise) + */ +static inline MCP23S17_Status_t MCP23S17_ReadBit(MCP23S17_HandleTypeDef* device, uint8_t reg, MCP23S17_Pin_t pin, bool* state) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + uint8_t reg_state = 0; + + status = MCP23S17_ReadRegs(device, reg, ®_state, 1); + if(status != MCP23S17_πŸ™‚){return status;} + + *state = (reg_state >> pin) & 0x01; + + return status; +} + +MCP23S17_Status_t MCP23S17_Init(MCP23S17_HandleTypeDef* device) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + device->addr = device->address_en == MCP23S17_CONFIG_ADDRESSING_ENABLED ? device->addr << 1 : 0; + + // validate SPI configured correctly + if(device->spi == NULL) + { + return MCP23S17_😒; + } + + // validate SPI mutex and semaphore configured correctly + if(device->spi_mutex == NULL || device->spi_done_sem == NULL) + { + return MCP23S17_😒; + } + + // set up configuration + uint8_t configuration = MCP23S17_IOCON_TEMPLATE + // IOCON.BANK = 0 | REGISTER ADDRESSING (This driver is designed ONLY for IOCON.BANK=0 register addresses.) + | (device->int_mirror << MCP23S17_IOCON_MIRROR_LSHIFT) // IOCON.MIRROR | INT PIN MIRRORING + // IOCON.SEQOP = 0 | SEQUENTIAL OPERATION MODE (This driver is designed to utilize sequential operations.) + // IOCON.DISSLW = 0 | SDA SLEW RATE CONTROL (I don't know why this is here or what it's for.) + | (device->address_en << MCP23S17_IOCON_HAEN_LSHIFT) // IOCON.HAEN | HARDWARE ADDRESS ENABLE + | (device->int_drive << MCP23S17_IOCON_ODR_LSHIFT) // IOCON.ODR | INT PIN OPEN-DRAIN + | (device->int_pol << MCP23S17_IOCON_INTPOL_LSHIFT); // IOCON.INTPOL | INT PIN POLARITY + + // write configuration + status = MCP23S17_WriteRegs(device, MCP23S17_REG_IOCON, &configuration, 1); + if(status != MCP23S17_πŸ™‚){return status;} + + // verify configuration via readback + uint8_t configuration_readback = 0; + status = MCP23S17_ReadRegs(device, MCP23S17_REG_IOCON, &configuration_readback, 1); + if(status != MCP23S17_πŸ™‚){return status;} + + if(configuration == configuration_readback) + { + return MCP23S17_πŸ™‚; + } + else + { + return MCP23S17_😒; + } +} + +MCP23S17_Status_t MCP23S17_SetDirection_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_Dir_t dir) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_IODIRA+port), pin, dir); +} + +MCP23S17_Status_t MCP23S17_SetPullup_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_Pullup_t pu) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_GPPUA+port), pin, pu); +} + +MCP23S17_Status_t MCP23S17_SetInputPolarity_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InputPolarity_t pol) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_IPOLA+port), pin, pol); +} + +MCP23S17_Status_t MCP23S17_WriteGPIO_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool state) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_GPIOA+port), pin, state); +} + +MCP23S17_Status_t MCP23S17_WriteGPIO_All(MCP23S17_HandleTypeDef* device, uint8_t* state) +{ + return MCP23S17_WriteRegs(device, MCP23S17_REG_GPIOA, state, 2); +} + +MCP23S17_Status_t MCP23S17_ReadGPIO_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool* state) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_ReadBit(device, (MCP23S17_REG_GPIOA+port), pin, state); +} + +MCP23S17_Status_t MCP23S17_ReadGPIO_All(MCP23S17_HandleTypeDef* device, uint8_t* state) +{ + return MCP23S17_ReadRegs(device, MCP23S17_REG_GPIOA, state, 2); +} + +MCP23S17_Status_t MCP23S17_SetInterruptEnable_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InterruptEnable_t inten) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_GPINTENA+port), pin, inten); +} + +MCP23S17_Status_t MCP23S17_SetInterruptMode_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InterruptMode_t intmode) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_INTCONA+port), pin, intmode); +} + +MCP23S17_Status_t MCP23S17_SetInterruptDefaultValue_Pin(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, bool defval) +{ + if(MCP23S17_PORT_PIN_INVALID_CHECK){return MCP23S17_😒;} + + return MCP23S17_WriteBitFriendly(device, (MCP23S17_REG_DEFVALA+port), pin, defval); +} + +MCP23S17_Status_t MCP23S17_SetInterruptDefaultValue_All(MCP23S17_HandleTypeDef* device, uint8_t* defval) +{ + return MCP23S17_WriteRegs(device, MCP23S17_REG_DEFVALA, defval, 2); +} + +MCP23S17_Status_t MCP23S17_ReadInterruptStatus_Port(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, uint8_t* state) +{ + return MCP23S17_ReadRegs(device, (MCP23S17_REG_INTFA+port), state, 1); +} + +MCP23S17_Status_t MCP23S17_ReadInterruptStatus_All(MCP23S17_HandleTypeDef* device, uint8_t* state) +{ + return MCP23S17_ReadRegs(device, MCP23S17_REG_INTFA, state, 2); +} + +MCP23S17_Status_t MCP23S17_ReadInterruptGPIOState_Port(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, uint8_t* state) +{ + return MCP23S17_ReadRegs(device, (MCP23S17_REG_INTCAPA+port), state, 1); +} + +MCP23S17_Status_t MCP23S17_ReadInterruptGPIOState_All(MCP23S17_HandleTypeDef* device, uint8_t* state) +{ + return MCP23S17_ReadRegs(device, MCP23S17_REG_INTCAPA, state, 2); +} + +// set up struct for pin information instead? +MCP23S17_Status_t MCP23S17_TheOneStopShopForAllYourOutputGPIOInitNeedsOfOneSpecificPin_DoneInOneLineOrYourMoneyBack(MCP23S17_HandleTypeDef* device, MCP23S17_PinConfigOutput_t pin_config) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + // pin setup + status = MCP23S17_SetDirection_Pin(device, pin_config.port, pin_config.pin, MCP23S17_DIR_OUTPUT); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetPullup_Pin(device, pin_config.port, pin_config.pin, MCP23S17_PULLUP_DISABLED); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_WriteGPIO_Pin(device, pin_config.port, pin_config.pin, pin_config.initial_state); + if(status != MCP23S17_πŸ™‚){return status;} + + return status; +} + +MCP23S17_Status_t MCP23S17_GetAllOfYourSingleInputGPIOInitSetUpWithThisOneFunctionCallThatDoesEverythingForYourInstantly(MCP23S17_HandleTypeDef* device, MCP23S17_PinConfigInput_t pin_config) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + // pin setup + status = MCP23S17_SetDirection_Pin(device, pin_config.port, pin_config.pin, MCP23S17_DIR_INPUT); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetPullup_Pin(device, pin_config.port, pin_config.pin, pin_config.pullup); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetInputPolarity_Pin(device, pin_config.port, pin_config.pin, pin_config.inpol); + if(status != MCP23S17_πŸ™‚){return status;} + + // interrupt setup + if(pin_config.inten == MCP23S17_INTEN_ENABLED) + { + status = MCP23S17_SetInterruptEnable_Pin(device, pin_config.port, pin_config.pin, pin_config.inten); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetInterruptMode_Pin(device, pin_config.port, pin_config.pin, pin_config.intmode); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetInterruptDefaultValue_Pin(device, pin_config.port, pin_config.pin, pin_config.default_value); + // skipping status πŸ™‚ check for last part of function + } + + return status; +} + +MCP23S17_Status_t MCP23S17_TheBestGPIOInterruptSetupThatYoullEverSeeAnywhereInTheSolarSystem_CallNowToSeeItHappen(MCP23S17_HandleTypeDef* device, MCP23S17_Port_t port, MCP23S17_Pin_t pin, MCP23S17_InterruptEnable_t inten, MCP23S17_InterruptMode_t intmode, bool defval) +{ + MCP23S17_Status_t status = MCP23S17_πŸ™‚; + + status = MCP23S17_SetInterruptEnable_Pin(device, port, pin, inten); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetInterruptMode_Pin(device, port, pin, intmode); + if(status != MCP23S17_πŸ™‚){return status;} + + status = MCP23S17_SetInterruptDefaultValue_Pin(device, port, pin, defval); + // skipping status πŸ™‚ check for last part of function + + return status; +} diff --git a/firmware/nix_thing.sh b/firmware/nix_thing.sh new file mode 100755 index 0000000..388a707 --- /dev/null +++ b/firmware/nix_thing.sh @@ -0,0 +1 @@ +nix develop ./Embedded-Sharepoint