This is an automated email from the ASF dual-hosted git repository. xiaoxiang 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 61e82925d examples/i2schar: Implement loopback mode check 61e82925d is described below commit 61e82925dc8dd8e9cb9544ef8b0b0f218a6d68c7 Author: Tiago Medicci <tiago.medi...@espressif.com> AuthorDate: Mon Aug 25 13:28:35 2025 -0300 examples/i2schar: Implement loopback mode check This commit implements the loopback mode for the i2schar example application. This mode - available only when both RX and TX are enabled - allows the user to test the I2S buses when the TX pin is connected to the RX pin. This is done by pre-filling a buffer with known data (additionally, it checks the peripheral's data width to format the data in the buffer accordingly) and passing it for both the transmitter and the receiver threads. This buffer is written to the TX and the received buffer is compared to the known buffer. Signed-off-by: Tiago Medicci <tiago.medi...@espressif.com> --- examples/i2schar/i2schar.h | 3 +- examples/i2schar/i2schar_main.c | 295 ++++++++++++++++++++++++++++++++- examples/i2schar/i2schar_receiver.c | 61 ++++++- examples/i2schar/i2schar_transmitter.c | 35 +--- 4 files changed, 358 insertions(+), 36 deletions(-) diff --git a/examples/i2schar/i2schar.h b/examples/i2schar/i2schar.h index fb73a6c40..73e9f6ea3 100644 --- a/examples/i2schar/i2schar.h +++ b/examples/i2schar/i2schar.h @@ -73,7 +73,7 @@ #endif #ifndef CONFIG_EXAMPLES_I2SCHAR_BUFSIZE -# define CONFIG_EXAMPLES_I2SCHAR_BUFSIZE 256 +# define CONFIG_EXAMPLES_I2SCHAR_BUFSIZE 256 #endif /**************************************************************************** @@ -83,6 +83,7 @@ struct i2schar_state_s { bool initialized; + bool loopback; FAR char *devpath; #ifdef CONFIG_EXAMPLES_I2SCHAR_TX int txcount; diff --git a/examples/i2schar/i2schar_main.c b/examples/i2schar/i2schar_main.c index ea5d6850f..dec7b70c1 100644 --- a/examples/i2schar/i2schar_main.c +++ b/examples/i2schar/i2schar_main.c @@ -34,6 +34,10 @@ #include <pthread.h> #include <errno.h> #include <debug.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <nuttx/audio/audio.h> +#include <nuttx/audio/i2s.h> #include "i2schar.h" @@ -91,12 +95,18 @@ static void i2schar_help(FAR struct i2schar_state_s *i2schar) printf("Usage: i2schar [OPTIONS]\n"); printf("\nArguments are \"sticky\".\n"); printf("For example, once the I2C character device is\n"); - printf("specified, that device will be re-used until it is changed.\n"); + printf("specified, that device will be reused until it is changed.\n"); printf("\n\"sticky\" OPTIONS include:\n"); printf(" [-p devpath] selects the I2C character device path. " "Default: %s Current: %s\n", CONFIG_EXAMPLES_I2SCHAR_DEVPATH, g_i2schar.devpath ? g_i2schar.devpath : "NONE"); +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + printf(" [-l [0|1]] enables/disables loopback mode " + "(0=disable, 1=enable, no value=enable). " + "Default: disabled Current: %s\n", + g_i2schar.loopback ? "enabled" : "disabled"); +#endif #ifdef CONFIG_EXAMPLES_I2SCHAR_TX printf(" [-t count] selects the number of audio buffers to send. " "Default: %d Current: %d\n", @@ -175,8 +185,81 @@ static void parse_args(FAR struct i2schar_state_s *i2schar, index += nargs; break; +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + case 'l': + + /* Check if a value is provided */ + + if (ptr[2] == '\0' && index + 1 < argc && + argv[index + 1][0] != '-') + { + /* Value provided as next argument */ + + long loopback = strtol(argv[index + 1], NULL, 10); + + if (loopback == 0) + { + i2schar->loopback = false; + } + else if (loopback == 1) + { + i2schar->loopback = true; + } + else + { + printf("Invalid loopback value: %ld (must be 0 or 1)\n", + loopback); + exit(1); + } + + index += 2; + } + else if (ptr[2] == '\0') + { + /* No value provided, default to enable */ + + i2schar->loopback = true; + index += 1; + } + + else + { + /* Value provided as part of the same argument */ + + long loopback = strtol(&ptr[2], NULL, 10); + + if (loopback == 0) + { + i2schar->loopback = false; + } + else if (loopback == 1) + { + i2schar->loopback = true; + } + else + { + printf("Invalid loopback value: %ld (must be 0 or 1)\n", + loopback); + exit(1); + } + + index += 1; + } + break; +#endif + #ifdef CONFIG_EXAMPLES_I2SCHAR_RX case 'r': +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + if (i2schar->loopback) + { + printf("i2schar_main: Warning: -r argument " + "ignored in loopback mode\n"); + index += 1; + break; + } +#endif + nargs = arg_decimal(&argv[index], &value); if (value < 0) { @@ -191,6 +274,16 @@ static void parse_args(FAR struct i2schar_state_s *i2schar, #ifdef CONFIG_EXAMPLES_I2SCHAR_TX case 't': +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + if (i2schar->loopback) + { + printf("i2schar_main: Warning: -t argument " + "ignored in loopback mode\n"); + index += 1; + break; + } +#endif + nargs = arg_decimal(&argv[index], &value); if (value < 0) { @@ -215,6 +308,107 @@ static void parse_args(FAR struct i2schar_state_s *i2schar, } } +#ifdef CONFIG_EXAMPLES_I2SCHAR_TX + +/**************************************************************************** + * Name: i2schar_prepare_tx_buffer + ****************************************************************************/ + +static struct ap_buffer_s *i2schar_prepare_tx_buffer(uint32_t data_width) +{ + FAR struct ap_buffer_s *apb; + struct audio_buf_desc_s desc; + int ret; + int j; + uint32_t crap = 0; + + /* Allocate an audio buffer of the configured size */ + + desc.numbytes = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; + desc.u.pbuffer = &apb; + + ret = apb_alloc(&desc); + if (ret < 0) + { + printf("i2schar_prepare_tx_buffer: ERROR: failed to " + "allocate buffer: %d\n", ret); + return NULL; + } + + /* Fill the audio buffer with sequential data based on data width */ + + if (data_width == 8) + { + uint8_t *ptr8 = (uint8_t *)apb->samp; + + /* 8-bit data: fill with sequential bytes */ + + for (j = 0; j < CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; j++) + { + *ptr8++ = crap++; + } + } + else if (data_width == 16) + { + /* 16-bit data: fill with sequential 16-bit values */ + + uint16_t *ptr16 = (uint16_t *)apb->samp; + int samples_16bit = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE / 2; + for (j = 0; j < samples_16bit; j++) + { + *ptr16++ = crap++; + } + } + else if (data_width == 24) + { + /* 24-bit data: fill with sequential 24-bit values (3 bytes each) */ + + uint8_t *ptr8 = apb->samp; + int samples_24bit = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE / 3; + for (j = 0; j < samples_24bit; j++) + { + /* Store 24-bit value in little-endian format */ + + *ptr8++ = crap & 0xff; + *ptr8++ = (crap >> 8) & 0xff; + *ptr8++ = (crap >> 16) & 0xff; + crap++; + } + } + else if (data_width == 32) + { + /* 32-bit data: fill with sequential 32-bit values */ + + uint32_t *ptr32 = (uint32_t *)apb->samp; + int samples_32bit = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE / 4; + for (j = 0; j < samples_32bit; j++) + { + *ptr32++ = crap++; + } + } + else + { + uint8_t *ptr = (uint8_t *)apb->samp; + + /* Unknown data width, fall back to byte-by-byte */ + + printf("i2schar_prepare_tx_buffer: Unknown data width %"PRIu32", " + "using byte mode\n", data_width); + for (j = 0, ptr = apb->samp; j < CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; j++) + { + *ptr++ = crap++; + } + } + + apb->nbytes = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; + + printf("i2schar_prepare_tx_buffer: Prepared transmitter buffer " + "with %"PRIu32"-bit data\n", data_width); + + return apb; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -229,6 +423,7 @@ int main(int argc, FAR char *argv[]) pthread_addr_t result; #ifdef CONFIG_EXAMPLES_I2SCHAR_TX pthread_t transmitter; + int fd; #endif #ifdef CONFIG_EXAMPLES_I2SCHAR_RX pthread_t receiver; @@ -236,6 +431,8 @@ int main(int argc, FAR char *argv[]) #if defined(CONFIG_EXAMPLES_I2SCHAR_RX) & defined(CONFIG_EXAMPLES_I2SCHAR_TX) struct sched_param param; #endif + FAR struct ap_buffer_s *transmitter_apb = NULL; + uint32_t data_width = 16; /* Default data width */ int ret; UNUSED(ret); @@ -269,6 +466,10 @@ int main(int argc, FAR char *argv[]) g_i2schar.rxcount = CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS; #endif +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + g_i2schar.loopback = false; /* Default to disabled */ +#endif + g_i2schar.initialized = true; } @@ -276,6 +477,66 @@ int main(int argc, FAR char *argv[]) parse_args(&g_i2schar, argc, argv); +#ifdef CONFIG_EXAMPLES_I2SCHAR_TX + + /* Get I2S configuration parameters to set data width */ + + fd = open(g_i2schar.devpath, O_RDONLY); + if (fd >= 0) + { + ret = ioctl(fd, I2SIOC_GTXDATAWIDTH, (unsigned long)&data_width); + if (ret >= 0) + { + printf("i2schar_main: TX data width: %" PRIu32 " bits\n", + data_width); + } + else + { + printf("i2schar_main: Failed to get TX data width: %d\n", ret); + printf("i2schar_main: Using default data width: %" PRIu32 " " + "bits\n", data_width); + } + + close(fd); + } + else + { + printf("i2schar_main: Failed to open device for data width query: " + "%d\n", errno); + printf("i2schar_main: Using default data width: %" PRIu32 " bits\n", + data_width); + } + + /* Always prepare the transmitter buffer when transmitter is enabled */ + + transmitter_apb = i2schar_prepare_tx_buffer(data_width); + + if (transmitter_apb == NULL) + { + printf("i2schar_main: ERROR: failed to prepare transmitter buffer\n"); + return EXIT_FAILURE; + } +#endif + +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + + /* Handle loopback mode */ + + if (g_i2schar.loopback) + { + printf("i2schar_main: Loopback mode enabled\n"); + + /* In loopback mode, we only send/receive one buffer each */ + + g_i2schar.txcount = 1; + g_i2schar.rxcount = 1; + } + else + { + printf("i2schar_main: Loopback mode disabled\n"); + } +#endif + sched_lock(); #ifdef CONFIG_EXAMPLES_I2SCHAR_RX /* Start the receiver thread */ @@ -301,7 +562,9 @@ int main(int argc, FAR char *argv[]) /* Start the receiver */ - ret = pthread_create(&receiver, &attr, i2schar_receiver, NULL); + ret = pthread_create(&receiver, &attr, i2schar_receiver, + g_i2schar.loopback ? \ + (pthread_addr_t)transmitter_apb : NULL); if (ret != OK) { sched_unlock(); @@ -325,7 +588,8 @@ int main(int argc, FAR char *argv[]) /* Start the transmitter */ - ret = pthread_create(&transmitter, &attr, i2schar_transmitter, NULL); + ret = pthread_create(&transmitter, &attr, i2schar_transmitter, + (pthread_addr_t)transmitter_apb); if (ret != OK) { sched_unlock(); @@ -349,6 +613,13 @@ int main(int argc, FAR char *argv[]) { printf("i2schar_main: ERROR: pthread_join failed: %d\n", ret); } +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + else if (g_i2schar.loopback && result != NULL) + { + printf("i2schar_main: ERROR: Loopback verification failed\n"); + ret = EXIT_FAILURE; + } +#endif #endif #ifdef CONFIG_EXAMPLES_I2SCHAR_RX @@ -358,7 +629,23 @@ int main(int argc, FAR char *argv[]) { printf("i2schar_main: ERROR: pthread_join failed: %d\n", ret); } +#if defined(CONFIG_EXAMPLES_I2SCHAR_TX) && defined(CONFIG_EXAMPLES_I2SCHAR_RX) + else if (g_i2schar.loopback && result != NULL) + { + printf("i2schar_main: ERROR: Loopback verification failed\n"); + ret = EXIT_FAILURE; + } +#endif +#endif + +#ifdef CONFIG_EXAMPLES_I2SCHAR_TX + /* Clean up transmitter buffer if it was allocated */ + + if (transmitter_apb != NULL) + { + apb_free(transmitter_apb); + } #endif - return EXIT_SUCCESS; + return ret; } diff --git a/examples/i2schar/i2schar_receiver.c b/examples/i2schar/i2schar_receiver.c index 843381ccc..fdcc0c8ba 100644 --- a/examples/i2schar/i2schar_receiver.c +++ b/examples/i2schar/i2schar_receiver.c @@ -33,6 +33,7 @@ #include <errno.h> #include <debug.h> #include <unistd.h> +#include <string.h> #include <nuttx/audio/audio.h> @@ -79,12 +80,15 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) { FAR struct ap_buffer_s *apb; + FAR struct ap_buffer_s *transmitter_apb = (FAR struct ap_buffer_s *)arg; struct audio_buf_desc_s desc; int bufsize; int nread; int ret; int fd; int i; + int j; + bool loopback = (transmitter_apb != NULL); /* Open the I2C character device */ @@ -101,7 +105,7 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) for (i = 0; i < g_i2schar.rxcount; i++) { - /* Allocate an audio buffer of the configured size */ + /* Allocate an audio buffer of the configured size for receiving data */ desc.numbytes = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; desc.u.pbuffer = &apb; @@ -118,7 +122,13 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) bufsize = sizeof(struct ap_buffer_s) + CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; - /* Then receifve into the buffer */ + if (loopback) + { + printf("i2schar_receiver: Using allocated buffer for " + "loopback verification\n"); + } + + /* Then receive into the buffer */ do { @@ -137,6 +147,7 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) printf("i2schar_receiver: ERROR: read failed: %d\n", errcode); close(fd); + apb_free(apb); pthread_exit(NULL); } } @@ -145,6 +156,7 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) printf("i2schar_receiver: ERROR: partial read: %d\n", nread); close(fd); + apb_free(apb); pthread_exit(NULL); } else @@ -154,6 +166,51 @@ pthread_addr_t i2schar_receiver(pthread_addr_t arg) } while (nread != bufsize); + /* Print received buffer data (first 16 bytes) regardless of mode */ + + printf("i2schar_receiver: Received data (first 16 bytes): "); + for (j = 0; j < 16 && j < CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; j++) + { + printf("0x%02x ", ((uint8_t *)apb->samp)[j]); + } + + printf("\n"); + + /* Verify the received data in loopback mode */ + + if (loopback) + { + /* Compare the received data with the expected transmitter buffer */ + + if (memcmp(apb->samp, transmitter_apb->samp, + CONFIG_EXAMPLES_I2SCHAR_BUFSIZE) == 0) + { + printf("i2schar_receiver: Loopback verification PASSED - " + "data matches expected\n"); + } + else + { + printf("i2schar_receiver: Loopback verification FAILED - " + "data mismatch!\n"); + printf("i2schar_receiver: Expected data (first 16 bytes): "); + for (j = 0; + j < 16 && j < CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; j++) + { + printf("0x%02x ", + ((uint8_t *)transmitter_apb->samp)[j]); + } + + printf("\n"); + close(fd); + apb_free(apb); + pthread_exit((pthread_addr_t)1); /* Return error */ + } + } + + /* Free the buffer after use */ + + apb_free(apb); + /* Make sure that the transmitter thread has a chance to run */ pthread_yield(); diff --git a/examples/i2schar/i2schar_transmitter.c b/examples/i2schar/i2schar_transmitter.c index 2fb9ded0f..df1560ac6 100644 --- a/examples/i2schar/i2schar_transmitter.c +++ b/examples/i2schar/i2schar_transmitter.c @@ -33,6 +33,7 @@ #include <errno.h> #include <debug.h> #include <unistd.h> +#include <sys/ioctl.h> #include <nuttx/audio/audio.h> @@ -79,17 +80,13 @@ pthread_addr_t i2schar_transmitter(pthread_addr_t arg) { FAR struct ap_buffer_s *apb; - struct audio_buf_desc_s desc; - uint8_t crap; - uint8_t *ptr; + FAR struct ap_buffer_s *transmitter_apb = (FAR struct ap_buffer_s *)arg; int bufsize; int nwritten; - int ret; int fd; int i; - int j; - /* Open the I2C character device */ + /* Open the I2S character device */ fd = open(g_i2schar.devpath, O_WRONLY); if (fd < 0) @@ -102,33 +99,13 @@ pthread_addr_t i2schar_transmitter(pthread_addr_t arg) /* Loop for the requested number of times */ - for (i = 0, crap = 0; i < g_i2schar.txcount; i++) + for (i = 0; i < g_i2schar.txcount; i++) { - /* Allocate an audio buffer of the configured size */ - - desc.numbytes = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; - desc.u.pbuffer = &apb; - - ret = apb_alloc(&desc); - if (ret < 0) - { - printf("i2schar_transmitter:" - "ERROR: failed to allocate buffer %d: %d\n", i + 1, ret); - close(fd); - pthread_exit(NULL); - } + /* Use the provided transmitter buffer */ + apb = transmitter_apb; bufsize = sizeof(struct ap_buffer_s) + CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; - /* Fill the audio buffer with crap */ - - for (j = 0, ptr = apb->samp; j < CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; j++) - { - *ptr++ = crap++; - } - - apb->nbytes = CONFIG_EXAMPLES_I2SCHAR_BUFSIZE; - /* Then send the buffer */ do