diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index c023e7ad..b98c24da 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -5,6 +5,7 @@ */ #include +#include "zephyrInternal.h" namespace { @@ -68,9 +69,14 @@ const int max_ngpios = max_in_list( * GPIO callback implementation */ +struct arduino_callback { + voidFuncPtr handler; + bool enabled; +}; + struct gpio_port_callback { struct gpio_callback callback; - voidFuncPtr handlers[max_ngpios]; + struct arduino_callback handlers[max_ngpios]; gpio_port_pins_t pins; const struct device *dev; } port_callback[port_num] = {0}; @@ -95,7 +101,7 @@ void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); if (pcb) { - pcb->handlers[BIT(arduino_pins[pinNumber].pin)] = func; + pcb->handlers[BIT(arduino_pins[pinNumber].pin)].handler = func; } } @@ -104,8 +110,8 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; for (uint32_t i = 0; i < max_ngpios; i++) { - if (pins & BIT(i)) { - pcb->handlers[BIT(i)](); + if (pins & BIT(i) && pcb->handlers[BIT(i)].enabled) { + pcb->handlers[BIT(i)].handler(); } } } @@ -321,6 +327,7 @@ void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinSt pcb->pins |= BIT(arduino_pins[pinNumber].pin); setInterruptHandler(pinNumber, callback); + enableInterrupt(pinNumber); gpio_pin_interrupt_configure(arduino_pins[pinNumber].port, arduino_pins[pinNumber].pin, intmode); gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); @@ -330,6 +337,7 @@ void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinSt void detachInterrupt(pin_size_t pinNumber) { setInterruptHandler(pinNumber, nullptr); + disableInterrupt(pinNumber); } #ifndef CONFIG_MINIMAL_LIBC_RAND @@ -393,3 +401,19 @@ unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout } #endif // CONFIG_GPIO_GET_DIRECTION + +void enableInterrupt(pin_size_t pinNumber) { + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + if (pcb) { + pcb->handlers[BIT(arduino_pins[pinNumber].pin)].enabled = true; + } +} + +void disableInterrupt(pin_size_t pinNumber) { + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + if (pcb) { + pcb->handlers[BIT(arduino_pins[pinNumber].pin)].enabled = false; + } +} diff --git a/cores/arduino/zephyrInternal.h b/cores/arduino/zephyrInternal.h new file mode 100644 index 00000000..b07e1bd9 --- /dev/null +++ b/cores/arduino/zephyrInternal.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Ayush Singh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void enableInterrupt(pin_size_t); +void disableInterrupt(pin_size_t); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index f1f486c7..5600b327 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory(Wire) +add_subdirectory(SPI) diff --git a/libraries/SPI/CMakeLists.txt b/libraries/SPI/CMakeLists.txt new file mode 100644 index 00000000..d3ce7538 --- /dev/null +++ b/libraries/SPI/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 +zephyr_include_directories(.) + +if(NOT DEFINED ARDUINO_BUILD_PATH) + zephyr_sources_ifdef(CONFIG_SPI SPI.cpp) +endif() diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp new file mode 100644 index 00000000..81b42481 --- /dev/null +++ b/libraries/SPI/SPI.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024 Ayush Singh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "SPI.h" +#include "zephyrInternal.h" +#include + +/* Serial Peripheral Control Register */ +uint8_t SPCR; + +arduino::ZephyrSPI::ZephyrSPI(const struct device *spi) : spi_dev(spi) {} + +uint8_t arduino::ZephyrSPI::transfer(uint8_t data) { + int ret; + uint8_t rx; + const struct spi_buf tx_buf = {.buf = &data, .len = sizeof(data)}; + const struct spi_buf_set tx_buf_set = { + .buffers = &tx_buf, + .count = 1, + }; + const struct spi_buf rx_buf = {.buf = &rx, .len = sizeof(rx)}; + const struct spi_buf_set rx_buf_set = { + .buffers = &rx_buf, + .count = 1, + }; + + ret = spi_transceive(spi_dev, &config, &tx_buf_set, &rx_buf_set); + if (ret < 0) { + return 0; + } + + return rx; +} + +uint16_t arduino::ZephyrSPI::transfer16(uint16_t data) { + int ret; + uint16_t rx; + const struct spi_buf tx_buf = {.buf = &data, .len = sizeof(data)}; + const struct spi_buf_set tx_buf_set = { + .buffers = &tx_buf, + .count = 1, + }; + const struct spi_buf rx_buf = {.buf = &rx, .len = sizeof(rx)}; + const struct spi_buf_set rx_buf_set = { + .buffers = &rx_buf, + .count = 1, + }; + + ret = spi_transceive(spi_dev, &config, &tx_buf_set, &rx_buf_set); + if (ret < 0) { + return 0; + } + + return rx; +} + +void arduino::ZephyrSPI::transfer(void *buf, size_t count) { + int ret; + const struct spi_buf tx_buf = {.buf = buf, .len = count}; + const struct spi_buf_set tx_buf_set = { + .buffers = &tx_buf, + .count = 1, + }; + + ret = spi_write(spi_dev, &config, &tx_buf_set); + if (ret < 0) { + return; + } + + ret = spi_read(spi_dev, &config, &tx_buf_set); + if (ret < 0) { + return; + } +} + +void arduino::ZephyrSPI::usingInterrupt(int interruptNumber) { + interrupt[interrupt_pos++] = interruptNumber; +} + +void arduino::ZephyrSPI::notUsingInterrupt(int interruptNumber) { + for (size_t i = 0; i < interrupt_pos; ++i) { + if (interrupt[i] == interruptNumber) { + memmove(&interrupt[i], &interrupt[i + 1], interrupt_pos - i - 1); + interrupt_pos--; + break; + } + } +} + +void arduino::ZephyrSPI::beginTransaction(SPISettings settings) { + memset(&config, 0, sizeof(config)); + config.frequency = settings.getClockFreq(); + config.operation = ((settings.getBitOrder() ^ 1) << 4) | + (settings.getDataMode() << 1) | ((SPCR >> MSTR) & 1) | + SPI_WORD_SET(8); + + detachInterrupt(); +} + +void arduino::ZephyrSPI::endTransaction(void) { + spi_release(spi_dev, &config); + attachInterrupt(); +} + +void arduino::ZephyrSPI::attachInterrupt() { + for (size_t i = 0; i < interrupt_pos; ++i) { + enableInterrupt(interrupt[i]); + } +} + +void arduino::ZephyrSPI::detachInterrupt() { + for (size_t i = 0; i < interrupt_pos; ++i) { + disableInterrupt(interrupt[i]); + } +} + +void arduino::ZephyrSPI::begin() {} + +void arduino::ZephyrSPI::end() {} + +arduino::ZephyrSPI + SPI(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), spis, 0))); diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h new file mode 100644 index 00000000..ff9d96ba --- /dev/null +++ b/libraries/SPI/SPI.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Ayush Singh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#define SPR0 0 +#define SPR1 1 +#define CPHA 2 +#define CPOL 3 +#define MSTR 4 +#define DORD 5 +#define SPE 6 +#define SPIE 7 + +/* Count the number of GPIOs for limit of number of interrupts */ +#define INTERRUPT_HELPER(n, p, i) 1 +#define INTERRUPT_COUNT \ + DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, \ + INTERRUPT_HELPER, (+)) + +namespace arduino { +class ZephyrSPI : public HardwareSPI { +public: + ZephyrSPI(const struct device *spi); + + virtual uint8_t transfer(uint8_t data); + virtual uint16_t transfer16(uint16_t data); + virtual void transfer(void *buf, size_t count); + + // Transaction Functions + virtual void usingInterrupt(int interruptNumber); + virtual void notUsingInterrupt(int interruptNumber); + virtual void beginTransaction(SPISettings settings); + virtual void endTransaction(void); + + // SPI Configuration methods + virtual void attachInterrupt(); + virtual void detachInterrupt(); + + virtual void begin(); + virtual void end(); + +private: + const struct device *spi_dev; + struct spi_config config; + int interrupt[INTERRUPT_COUNT]; + size_t interrupt_pos = 0; +}; + +} // namespace arduino + +extern arduino::ZephyrSPI SPI; +/* Serial Peripheral Control Register */ +extern uint8_t SPCR; + +using arduino::SPI_MODE0; +using arduino::SPI_MODE1; +using arduino::SPI_MODE2; +using arduino::SPI_MODE3; +using arduino::SPISettings; diff --git a/samples/spi_controller/CMakeLists.txt b/samples/spi_controller/CMakeLists.txt new file mode 100644 index 00000000..824634e5 --- /dev/null +++ b/samples/spi_controller/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +cmake_path(SET ZephyrBase $ENV{ZEPHYR_BASE}) +set(DTC_OVERLAY_FILE ${ZephyrBase}/../modules/lib/Arduino-Zephyr-API/variants/${BOARD}/${BOARD}.overlay) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(spi_controller) + +target_sources(app PRIVATE src/app.cpp) + +zephyr_compile_options(-Wno-unused-variable -Wno-comment) diff --git a/samples/spi_controller/README.rst b/samples/spi_controller/README.rst new file mode 100644 index 00000000..8d2aad33 --- /dev/null +++ b/samples/spi_controller/README.rst @@ -0,0 +1,9 @@ +.. _spi_controller: + +SPI Controller +############### + +Overview +******** + +A simple sample that sends incrementing byte to SPI peripheral. diff --git a/samples/spi_controller/prj.conf b/samples/spi_controller/prj.conf new file mode 100644 index 00000000..661fb228 --- /dev/null +++ b/samples/spi_controller/prj.conf @@ -0,0 +1,6 @@ +CONFIG_CPLUSPLUS=y +CONFIG_ARDUINO_API=y +CONFIG_SPI=y +CONFIG_LOG=y +CONFIG_LOG_OUTPUT=y +CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/samples/spi_controller/src/app.cpp b/samples/spi_controller/src/app.cpp new file mode 100644 index 00000000..d0306512 --- /dev/null +++ b/samples/spi_controller/src/app.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Ayush Singh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "SPI.h" +#include + +#define CHIPSELECT 3 + +static uint8_t data = 0; + +void setup() { + SPI.begin(); + pinMode(CHIPSELECT, OUTPUT); + digitalWrite(CHIPSELECT, HIGH); +} + +void loop() { + SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); + digitalWrite(CHIPSELECT, LOW); + SPI.transfer(data++); + digitalWrite(CHIPSELECT, HIGH); + SPI.endTransaction(); + delay(1000); +} diff --git a/variants/beagleconnect_freedom/beagleconnect_freedom.overlay b/variants/beagleconnect_freedom/beagleconnect_freedom.overlay index d9a66c7f..848aaf8c 100644 --- a/variants/beagleconnect_freedom/beagleconnect_freedom.overlay +++ b/variants/beagleconnect_freedom/beagleconnect_freedom.overlay @@ -50,6 +50,7 @@ builtin-led-gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>; /* 2.4GHz TX/RX */ serials = <&uart0 &uart1>; i2cs = <&i2c0>; + spis = <&spi0>; }; };