This is an automated email from the ASF dual-hosted git repository.
cederom pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 62894b9ba examples/spislave_test: Add user data receive
62894b9ba is described below
commit 62894b9baf2978815ceec454e53118e6d83c84f6
Author: Felipe Moura <[email protected]>
AuthorDate: Sun Feb 16 20:57:45 2025 -0300
examples/spislave_test: Add user data receive
Improve example, now it can receive commands / data from user;
Fixed indentation issues.
Signed-off-by: Felipe Moura <[email protected]>
---
examples/spislv_test/spislv_test.c | 563 ++++++++++++++++++++++++++++++++++---
1 file changed, 520 insertions(+), 43 deletions(-)
diff --git a/examples/spislv_test/spislv_test.c
b/examples/spislv_test/spislv_test.c
index 763bee76f..2ad13c236 100644
--- a/examples/spislv_test/spislv_test.c
+++ b/examples/spislv_test/spislv_test.c
@@ -24,81 +24,558 @@
* Included Files
****************************************************************************/
-#include <nuttx/config.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <fcntl.h>
-#include <unistd.h>
#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/select.h>
-#define SOURCE_FILE "/dev/spislv2"
-#define BUFFER_SIZE 256
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Define buffer sizes */
+#define RX_BUFFER_SIZE 64
+#define TX_BUFFER_SIZE 64
+#define SOURCE_FILE "dev/spislv2" /* SPI device path */
+
+/* Enumeration for operation modes */
+
+typedef enum
+{
+ MODE_WRITE,
+ MODE_LISTEN,
+ MODE_ECHO,
+ MODE_INVALID
+} operation_mode_t;
/****************************************************************************
- * Public Functions
+ * Structure to hold program configurations
****************************************************************************/
+typedef struct
+{
+ operation_mode_t mode;
+ int num_bytes; /* Applicable for write mode */
+ int timeout_seconds; /* Applicable for all modes */
+ unsigned char tx_buffer[TX_BUFFER_SIZE];
+} program_config_t;
+
/****************************************************************************
- * spislv_test
+ * Private Functions
****************************************************************************/
-int main(int argc, FAR char *argv[])
+/**
+ * @brief Converts a single hexadecimal character to its byte value.
+ *
+ * @param c The hexadecimal character.
+ * @return The byte value of the hexadecimal character, or -1 if invalid.
+ */
+
+static int hexchar_to_byte(char c)
{
- int fd;
- char buffer[BUFFER_SIZE];
- ssize_t bytes_read;
- ssize_t i;
+ if (c >= '0' && c <= '9')
+ {
+ return c - '0';
+ }
- printf("Slave started!!\n");
- fd = open(SOURCE_FILE, O_RDWR);
+ c = tolower(c);
- if (fd < 0)
+ if (c >= 'a' && c <= 'f')
{
- printf("Failed to open %s: %s\n", SOURCE_FILE, strerror(errno));
- return 0;
+ return c - 'a' + 10;
}
- while (1)
+ return -1;
+}
+
+/**
+ * @brief Converts a hexadecimal string to a byte array.
+ *
+ * @param hexstr The input hexadecimal string.
+ * @param bytes The output byte array.
+ * @param max_bytes The maximum number of bytes to convert.
+ * @return The number of bytes converted, or -1 on error.
+ */
+
+static int hexstr_to_bytes(const char *hexstr, unsigned char *bytes,
+ size_t max_bytes)
+{
+ int len;
+ int i;
+
+ len = strlen(hexstr);
+ if (len % 2 != 0 || len / 2 > max_bytes)
+ {
+ return -1;
+ }
+
+ for (i = 0; i < len / 2; i++)
+ {
+ int high = hexchar_to_byte(hexstr[2 * i]);
+ int low = hexchar_to_byte(hexstr[2 * i + 1]);
+
+ if (high == -1 || low == -1)
+ {
+ return -1;
+ }
+
+ bytes[i] = (high << 4) | low;
+ }
+
+ return len / 2;
+}
+
+/**
+ * @brief Parses and validates command-line arguments.
+ *
+ * @param argc Argument count.
+ * @param argv Argument vector.
+ * @param config Pointer to the program configuration structure.
+ * @return 0 on success, -1 on failure.
+ */
+
+static int parse_arguments(int argc, char *argv[],
+ program_config_t *config)
+{
+ int opt;
+
+ /* Set default configurations */
+
+ config->mode = MODE_INVALID;
+ config->num_bytes = 0;
+ config->timeout_seconds = 10; /* Default timeout */
+
+ /* Parse command-line options */
+
+ while ((opt = getopt(argc, argv, "x:t:le")) != -1)
+ {
+ switch (opt)
+ {
+ case 'x':
+ if (config->mode != MODE_INVALID)
+ {
+ fprintf(stderr,
+ "Error: Multiple operation modes specified.\n");
+ return -1;
+ }
+
+ config->mode = MODE_WRITE;
+ config->num_bytes = atoi(optarg);
+ if (config->num_bytes <= 0 ||
+ config->num_bytes > TX_BUFFER_SIZE)
+ {
+ fprintf(stderr,
+ "Error: Invalid number of bytes for write mode.\n");
+ return -1;
+ }
+
+ break;
+
+ case 't':
+ config->timeout_seconds = atoi(optarg);
+ if (config->timeout_seconds <= 0)
+ {
+ fprintf(stderr,
+ "Error: Timeout must be a positive integer.\n");
+ return -1;
+ }
+ break;
+
+ case 'l':
+ if (config->mode != MODE_INVALID)
+ {
+ fprintf(stderr,
+ "Error: Multiple operation modes specified.\n");
+ return -1;
+ }
+
+ config->mode = MODE_LISTEN;
+ break;
+
+ case 'e':
+ if (config->mode != MODE_INVALID)
+ {
+ fprintf(stderr,
+ "Error: Multiple operation modes specified.\n");
+ return -1;
+ }
+
+ config->mode = MODE_ECHO;
+ break;
+
+ default:
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s -x <num_bytes> [-t <timeout_seconds>] <hex_bytes>\n",
+ argv[0]);
+ fprintf(stderr,
+ " %s -l [-t <timeout_seconds>]\n", argv[0]);
+ fprintf(stderr,
+ " %s -e [-t <timeout_seconds>]\n", argv[0]);
+ printf("Examples:\n");
+ printf(" spislv -x 2 abba\n");
+ printf(" spislv -l -t 5\n");
+ printf(" spislv -e -t 10\n\n");
+ return -1;
+ }
+ }
+
+ /* Validate mutual exclusivity and required arguments */
+
+ if (config->mode == MODE_WRITE)
{
- /* Read the number from the source file */
+ if (optind >= argc)
+ {
+ fprintf(stderr,
+ "Error: Missing hexadecimal bytes to send.\n");
+ fprintf(stderr,
+ "Usage: %s -x <num_bytes> [-t <timeout_seconds>] <hex_bytes>\n",
+ argv[0]);
+ return -1;
+ }
+
+ char *hex_input = argv[optind];
- printf("Slave: Reading from %s\n", SOURCE_FILE);
- bytes_read = read(fd, buffer, BUFFER_SIZE - 1);
+ /* Verify the hexadecimal string length */
- if (bytes_read < 0)
+ if (strlen(hex_input) != (size_t)(config->num_bytes * 2))
{
- printf("Failed to read from %s: %s\n",
- SOURCE_FILE, strerror(errno));
- close(fd);
- return 0;
+ fprintf(stderr,
+ "Error: Hex string length must be %d characters\n"
+ "for %d bytes.\n",
+ config->num_bytes * 2, config->num_bytes);
+ return -1;
}
- else if (bytes_read > 0)
+
+ /* Convert hexadecimal string to byte array */
+
+ int converted = hexstr_to_bytes(hex_input, config->tx_buffer,
+ TX_BUFFER_SIZE);
+ if (converted != config->num_bytes)
{
- buffer[bytes_read] = '\0';
+ fprintf(stderr, "Error: Invalid hexadecimal string.\n");
+ return -1;
+ }
+ }
- /* Print buffer in hexadecimal format */
+ else if (config->mode == MODE_INVALID)
+ {
+ fprintf(stderr, "Error: No operation mode specified.\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s -x <num_bytes> [-t <timeout_seconds>] <hex_bytes>\n",
+ argv[0]);
+ fprintf(stderr,
+ " %s -l [-t <timeout_seconds>]\n", argv[0]);
+ fprintf(stderr,
+ " %s -e [-t <timeout_seconds>]\n", argv[0]);
+ printf("Examples:\n");
+ printf(" spislv -x 2 abba\n");
+ printf(" spislv -l -t 5\n");
+ printf(" spislv -e -t 10\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Executes the write mode: sends specified bytes to the master.
+ *
+ * @param config Pointer to the program configuration structure.
+ * @param fd File descriptor for the SPI device.
+ * @return 0 on success, -1 on failure.
+ */
+
+static int write_mode(program_config_t *config, int fd)
+{
+ ssize_t bytes_written;
+ char data_str[3 * TX_BUFFER_SIZE + 1]; /* Buffer for debug string */
+ char *ptr = data_str;
+ int len;
+ int i;
+
+ for (i = 0; i < config->num_bytes; i++)
+ {
+ len = snprintf(ptr, sizeof(data_str) - (ptr - data_str),
+ "%02X ", config->tx_buffer[i]);
+ if (len < 0 || len >= (int)(sizeof(data_str) - (ptr - data_str)))
+ {
+ break;
+ }
+
+ ptr += len;
+ }
- printf("Slave: Read value in hex: ");
- for (i = 0; i < bytes_read; ++i)
+ *ptr = '\0';
+
+ printf("Slave: Queuing %d bytes for sending to master: %s\n",
+ config->num_bytes, data_str);
+
+ bytes_written = write(fd, config->tx_buffer, config->num_bytes);
+ if (bytes_written < 0)
+ {
+ fprintf(stderr, "Error: Failed to write to %s: %s\n",
+ SOURCE_FILE, strerror(errno));
+ return -1;
+ }
+
+ else if (bytes_written != config->num_bytes)
+ {
+ fprintf(stderr, "Error: Incomplete write. Expected %d, got %zd\n",
+ config->num_bytes, bytes_written);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Executes the listen-only mode: waits for data from the master.
+ *
+ * @param config Pointer to the program configuration structure.
+ * @param fd File descriptor for the SPI device.
+ * @return 0 on success, -1 on failure.
+ */
+
+static int listen_mode(program_config_t *config, int fd)
+{
+ printf("Slave: Listen-only mode activated. Waiting for data\n"
+ " from master.\n");
+
+ return 0;
+}
+
+/**
+ * @brief Executes the echo mode: continuously echoes received data to
+ * the master.
+ *
+ * @param config Pointer to the program configuration structure.
+ * @param fd File descriptor for the SPI device.
+ * @return 0 on success, -1 on failure.
+ */
+
+static int echo_mode_func(program_config_t *config, int fd)
+{
+ printf("Slave: Echo mode activated. Will echo received data until\n"
+ " timeout.\n");
+
+ return 0;
+}
+
+/**
+ * @brief Reads data from the SPI device with a specified timeout.
+ * Depending on the mode, it either exits after the first read or
+ * continues (echo mode).
+ *
+ * @param config Pointer to the program configuration structure.
+ * @param fd File descriptor for the SPI device.
+ * @return 0 on success, -1 on failure or timeout.
+ */
+
+static int read_with_timeout(program_config_t *config, int fd)
+{
+ unsigned char buffer_rx[RX_BUFFER_SIZE];
+ ssize_t bytes_read;
+ ssize_t bytes_written;
+ int select_ret;
+
+ while (1)
+ {
+ fd_set read_fds;
+ struct timeval timeout;
+
+ FD_ZERO(&read_fds);
+ FD_SET(fd, &read_fds);
+
+ timeout.tv_sec = config->timeout_seconds;
+ timeout.tv_usec = 0;
+
+ select_ret = select(fd + 1, &read_fds,
+ NULL, NULL, &timeout);
+ if (select_ret == -1)
+ {
+ fprintf(stderr, "Error: Select failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ else if (select_ret == 0)
+ {
+ if (config->mode == MODE_ECHO)
+ {
+ printf("Communication timeout after %d seconds. No more\n"
+ "data received from master.\n",
+ config->timeout_seconds);
+ }
+ else
{
- printf("%02x ", (unsigned char)buffer[i]);
+ printf("Communication timeout after %d seconds. No data\n"
+ "received from master.\n",
+ config->timeout_seconds);
}
- printf("\n");
+ return -1;
+ }
+ else
+ {
+ bytes_read = read(fd, buffer_rx, RX_BUFFER_SIZE);
+ if (bytes_read < 0)
+ {
+ fprintf(stderr, "Error: Failed to read from %s: %s\n",
+ SOURCE_FILE, strerror(errno));
+ return -1;
+ }
- /* Write the same value back */
+ else if (bytes_read == 0)
+ {
+ printf("No data received from master.\n");
+ }
- printf("Slave: Writing %d bytes back to %s\n",
- bytes_read, SOURCE_FILE);
- ssize_t bytes_written = write(fd, buffer, bytes_read);
- if (bytes_written < 0)
+ else
{
- printf("Failed to write to %s: %s\n",
- SOURCE_FILE, strerror(errno));
- close(fd);
- return 0;
+ printf("Data received from master (%zd bytes): ",
+ bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ {
+ printf("%02X ", buffer_rx[i]);
+ }
+
+ printf("\n");
+ if (config->mode == MODE_ECHO)
+ {
+ bytes_written = write(fd, buffer_rx,
+ bytes_read);
+ if (bytes_written < 0)
+ {
+ fprintf(stderr,
+ "Error: Failed to write to %s: %s\n",
+ SOURCE_FILE, strerror(errno));
+ return -1;
+ }
+ else if (bytes_written != bytes_read)
+ {
+ printf("Error: Incomplete write during echo. ");
+ fprintf(stderr, "Expected %zd, got %zd\n",
+ bytes_read, bytes_written);
+ return -1;
+ }
+
+ printf("Echoed back %zd bytes to master.\n",
+ bytes_written);
+ }
+
+ if (config->mode != MODE_ECHO)
+ {
+ break;
+ }
}
}
}
+
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/**
+ * @brief Main function orchestrating the SPI slave operations based on
+ * user input.
+ *
+ * @param argc Argument count.
+ * @param argv Argument vector.
+ * @return 0 on success, -1 on failure.
+ */
+
+int main(int argc, char *argv[])
+{
+ program_config_t config;
+ int fd;
+ int ret = 0;
+
+ if (parse_arguments(argc, argv, &config) != 0)
+ {
+ return -1;
+ }
+
+ fd = open(SOURCE_FILE, O_RDWR);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Error: Failed to open %s: %s\n",
+ SOURCE_FILE, strerror(errno));
+ return -1;
+ }
+
+ /* Ensure the file descriptor is in blocking mode */
+
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1)
+ {
+ fprintf(stderr, "Error: Failed to get file flags: %s\n",
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ {
+ fprintf(stderr, "Error: Failed to set blocking mode: %s\n",
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ /* Execute the selected mode */
+
+ switch (config.mode)
+ {
+ case MODE_WRITE:
+ ret = write_mode(&config, fd);
+ if (ret != 0)
+ {
+ close(fd);
+ return -1;
+ }
+ break;
+
+ case MODE_LISTEN:
+ ret = listen_mode(&config, fd);
+ if (ret != 0)
+ {
+ close(fd);
+ return -1;
+ }
+ break;
+
+ case MODE_ECHO:
+ ret = echo_mode_func(&config, fd);
+ if (ret != 0)
+ {
+ close(fd);
+ return -1;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Error: Invalid operation mode.\n");
+ close(fd);
+ return -1;
+ }
+
+ ret = read_with_timeout(&config, fd);
+ if (ret != 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ printf("\n");
+ close(fd);
+ return 0;
}