forked from nrfconnect/sdk-zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inspired by I2C shell. Useful during SPI device driver development or for debugging. Usage example (read JEDEC ID): ``` uart:~$ spi conf spi4 1000000 oh uart:~$ spi transceive 9f 00 00 00 TX: 00000000: 9f 00 00 00 |.... | RX: 00000000: 00 ef 40 19 |.... | ``` Signed-off-by: Nerijus Bendžiūnas <[email protected]>
- Loading branch information
1 parent
0e617a3
commit b394664
Showing
3 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Copyright (c) 2024 Astroligt | ||
* | ||
* 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:" | ||
"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); |