Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SPI Library #103

Merged
merged 4 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions cores/arduino/zephyrCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

#include <Arduino.h>
#include "zephyrInternal.h"

namespace {

Expand Down Expand Up @@ -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};
Expand All @@ -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;
}
}

Expand All @@ -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();
}
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
}
20 changes: 20 additions & 0 deletions cores/arduino/zephyrInternal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <Arduino.h>

#ifdef __cplusplus
extern "C" {
#endif

void enableInterrupt(pin_size_t);
void disableInterrupt(pin_size_t);

#ifdef __cplusplus
} // extern "C"
#endif
1 change: 1 addition & 0 deletions libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: Apache-2.0

add_subdirectory(Wire)
add_subdirectory(SPI)
6 changes: 6 additions & 0 deletions libraries/SPI/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
125 changes: 125 additions & 0 deletions libraries/SPI/SPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "SPI.h"
#include "zephyrInternal.h"
#include <zephyr/kernel.h>

/* 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,
};

Ayush1325 marked this conversation as resolved.
Show resolved Hide resolved
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)));
67 changes: 67 additions & 0 deletions libraries/SPI/SPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <Arduino.h>
#include <api/HardwareSPI.h>
#include <zephyr/drivers/spi.h>

#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;
13 changes: 13 additions & 0 deletions samples/spi_controller/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 9 additions & 0 deletions samples/spi_controller/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _spi_controller:

SPI Controller
###############

Overview
********

A simple sample that sends incrementing byte to SPI peripheral.
6 changes: 6 additions & 0 deletions samples/spi_controller/prj.conf
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions samples/spi_controller/src/app.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "SPI.h"
#include <Arduino.h>

#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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
builtin-led-gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>; /* 2.4GHz TX/RX */
serials = <&uart0 &uart1>;
i2cs = <&i2c0>;
spis = <&spi0>;
};
};

Expand Down
Loading