You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.0 KiB
155 lines
4.0 KiB
/* |
|
* Copyright (c) 2024 Astrolight |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <errno.h> |
|
#include <stdbool.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
#include <zephyr/drivers/spi.h> |
|
#include <zephyr/shell/shell.h> |
|
#include <zephyr/sys/util.h> |
|
|
|
#define TXRX_ARGV_BYTES (1) |
|
#define CONF_ARGV_DEV (1) |
|
#define CONF_ARGV_FREQUENCY (2) |
|
#define CONF_ARGV_SETTINGS (3) |
|
|
|
/* Maximum bytes we can write and read at once */ |
|
#define MAX_SPI_BYTES MIN((CONFIG_SHELL_ARGC_MAX - TXRX_ARGV_BYTES), 32) |
|
|
|
static struct device *spi_device; |
|
static struct spi_config config = {.frequency = 1000000, |
|
.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)}; |
|
|
|
static void device_name_get(size_t idx, struct shell_static_entry *entry) |
|
{ |
|
const struct device *dev = shell_device_lookup(idx, "spi"); |
|
|
|
entry->syntax = (dev != NULL) ? dev->name : NULL; |
|
entry->handler = NULL; |
|
entry->help = NULL; |
|
entry->subcmd = NULL; |
|
} |
|
|
|
SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); |
|
|
|
static int cmd_spi_transceive(const struct shell *ctx, size_t argc, char **argv) |
|
{ |
|
uint8_t rx_buffer[MAX_SPI_BYTES] = {0}; |
|
uint8_t tx_buffer[MAX_SPI_BYTES] = {0}; |
|
|
|
if (spi_device == NULL) { |
|
shell_error(ctx, "SPI device isn't configured. Use `spi conf`"); |
|
return -ENODEV; |
|
} |
|
|
|
int bytes_to_send = argc - TXRX_ARGV_BYTES; |
|
|
|
for (int i = 0; i < bytes_to_send; i++) { |
|
tx_buffer[i] = strtol(argv[TXRX_ARGV_BYTES + i], NULL, 16); |
|
} |
|
|
|
const struct spi_buf tx_buffers = {.buf = tx_buffer, .len = bytes_to_send}; |
|
const struct spi_buf rx_buffers = {.buf = rx_buffer, .len = bytes_to_send}; |
|
|
|
const struct spi_buf_set tx_buf_set = {.buffers = &tx_buffers, .count = 1}; |
|
const struct spi_buf_set rx_buf_set = {.buffers = &rx_buffers, .count = 1}; |
|
|
|
int ret = spi_transceive(spi_device, &config, &tx_buf_set, &rx_buf_set); |
|
|
|
if (ret < 0) { |
|
shell_error(ctx, "spi_transceive returned %d", ret); |
|
return ret; |
|
} |
|
|
|
shell_print(ctx, "TX:"); |
|
shell_hexdump(ctx, tx_buffer, bytes_to_send); |
|
|
|
shell_print(ctx, "RX:"); |
|
shell_hexdump(ctx, rx_buffer, bytes_to_send); |
|
|
|
return ret; |
|
} |
|
|
|
static int cmd_spi_conf(const struct shell *ctx, size_t argc, char **argv) |
|
{ |
|
spi_operation_t operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER; |
|
|
|
/* warning: initialization discards 'const' qualifier from pointer */ |
|
/* target type */ |
|
struct device *dev = (struct device *)device_get_binding(argv[CONF_ARGV_DEV]); |
|
|
|
if (dev == NULL) { |
|
shell_error(ctx, "device %s not found.", argv[CONF_ARGV_DEV]); |
|
return -ENODEV; |
|
} |
|
|
|
uint32_t frequency = strtol(argv[CONF_ARGV_FREQUENCY], NULL, 10); |
|
|
|
if (!IN_RANGE(frequency, 100 * 1000, 80 * 1000 * 1000)) { |
|
shell_error(ctx, "frequency must be between 100000 and 80000000"); |
|
return -EINVAL; |
|
} |
|
|
|
/* no settings */ |
|
if (argc == (CONF_ARGV_FREQUENCY + 1)) { |
|
goto out; |
|
} |
|
|
|
char *opts = argv[CONF_ARGV_SETTINGS]; |
|
bool all_opts_is_valid = true; |
|
|
|
while (*opts != '\0') { |
|
switch (*opts) { |
|
case 'o': |
|
operation |= SPI_MODE_CPOL; |
|
break; |
|
case 'h': |
|
operation |= SPI_MODE_CPHA; |
|
break; |
|
case 'l': |
|
operation |= SPI_TRANSFER_LSB; |
|
break; |
|
case 'T': |
|
operation |= SPI_FRAME_FORMAT_TI; |
|
break; |
|
default: |
|
all_opts_is_valid = false; |
|
shell_error(ctx, "invalid setting %c", *opts); |
|
} |
|
opts++; |
|
} |
|
|
|
if (!all_opts_is_valid) { |
|
return -EINVAL; |
|
} |
|
|
|
out: |
|
config.frequency = frequency; |
|
config.operation = operation; |
|
spi_device = dev; |
|
|
|
return 0; |
|
} |
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_spi_cmds, |
|
SHELL_CMD_ARG(conf, &dsub_device_name, |
|
"Configure SPI\n" |
|
"Usage: spi conf <device> <frequency> [<settings>]\n" |
|
"<settings> - any sequence of letters:\n" |
|
"o - SPI_MODE_CPOL\n" |
|
"h - SPI_MODE_CPHA\n" |
|
"l - SPI_TRANSFER_LSB\n" |
|
"T - SPI_FRAME_FORMAT_TI\n" |
|
"example: spi conf spi1 1000000 ol", |
|
cmd_spi_conf, 3, 1), |
|
SHELL_CMD_ARG(transceive, NULL, |
|
"Transceive data to and from an SPI device\n" |
|
"Usage: spi transceive <TX byte 1> [<TX byte 2> ...]", |
|
cmd_spi_transceive, 2, MAX_SPI_BYTES - 1), |
|
SHELL_SUBCMD_SET_END); |
|
|
|
SHELL_CMD_REGISTER(spi, &sub_spi_cmds, "SPI commands", NULL);
|
|
|