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 <moura....@gmail.com>
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 <moura....@gmail.com>
---
 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;
 }

Reply via email to