aboutsummaryrefslogtreecommitdiffstats
path: root/src/spi_software.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spi_software.c')
-rw-r--r--src/spi_software.c81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/spi_software.c b/src/spi_software.c
new file mode 100644
index 00000000..385b4f87
--- /dev/null
+++ b/src/spi_software.c
@@ -0,0 +1,81 @@
+// Software SPI emulation
+//
+// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "board/irq.h" // gpio_out_setup
+#include "board/gpio.h" // gpio_out_setup
+#include "basecmd.h" // oid_alloc
+#include "command.h" // DECL_COMMAND
+#include "sched.h" // sched_shutdown
+
+struct spi_software {
+ struct gpio_out sclk, mosi;
+ struct gpio_in miso;
+ uint8_t mode;
+};
+
+void
+command_config_software_spi(uint32_t *args)
+{
+ uint8_t oid = args[0], sclk_pin = args[1], mosi_pin = args[2];
+ uint8_t miso_pin = args[3], mode = args[4];
+ if (mode > 3)
+ shutdown("Invalid spi mode");
+
+ struct spi_software *spi = oid_alloc(oid, command_config_software_spi
+ , sizeof(*spi));
+
+ spi->sclk = gpio_out_setup(sclk_pin, 0);
+ spi->mosi = gpio_out_setup(mosi_pin, 0);
+ spi->miso = gpio_in_setup(miso_pin, 1);
+}
+DECL_COMMAND(command_config_software_spi,
+ "config_software_spi oid=%c sclk_pin=%u mosi_pin=%u miso_pin=%u"
+ " mode=%u rate=%u");
+
+struct spi_software *
+spi_software_oid_lookup(uint8_t oid)
+{
+ return oid_lookup(oid, command_config_software_spi);
+}
+
+void
+spi_software_prepare(struct spi_software *ss)
+{
+ gpio_out_write(ss->sclk, ss->mode < 2 ? 0 : 1);
+}
+
+void
+spi_software_transfer(struct spi_software *ss, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ while (len--) {
+ uint8_t outbuf = *data;
+ uint8_t inbuf = 0;
+ for (uint_fast8_t i = 0; i < 8; i++) {
+ if (ss->mode & 0x01) {
+ // MODE 1 & 3
+ gpio_out_toggle(ss->sclk);
+ gpio_out_write(ss->mosi, outbuf & 0x80);
+ outbuf <<= 1;
+ gpio_out_toggle(ss->sclk);
+ inbuf <<= 1;
+ inbuf |= gpio_in_read(ss->miso);
+ } else {
+ // MODE 0 & 2
+ gpio_out_write(ss->mosi, outbuf & 0x80);
+ outbuf <<= 1;
+ gpio_out_toggle(ss->sclk);
+ inbuf <<= 1;
+ inbuf |= gpio_in_read(ss->miso);
+ gpio_out_toggle(ss->sclk);
+ }
+ }
+
+ if (receive_data)
+ *data = inbuf;
+ data++;
+ }
+}