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; }