1.0 Introduction
Following my recent RSL10-SENSE-GEVK road test, I got myself a J-Link Lite CortexM debugger and so I decided to create a blog documenting my “noob” experiences as I dive into the deep end and explore some of the other examples available in the RSL10-SENSE SDK.
I will start by unpacking the other example mentioned in the User Guide (EVBUM2614), which is On−board Sensor Tests. This example enables microphone functionality and returns sensors data into the Eclipse, or Segger J-Link RTT Viewer, console.
I then had a quick look at the other examples provided in the CMSIS Pack and noticed that they appear to be subsets of this example, as in, they would only use one sensor type etc. So I'm not reviewing other examples here. As such, I will tweak various bits of code and see what happens. You will see in the demo video below, I started by delaying the LED colour changes for better visuals on video (except that I forgot my desktop lamp would mask the blue light). Anyhow...
So, that sort of thing.
2.0 The "On−board Sensor Tests" Example
2.1 Loading project into IDE
The procedure is fairly standardised, so we follow instructions as per user guide, starting with selecting our example via the CMSIS Packs perspective.
Then, as before, we right-click on mouse and copy the project into our workspace.
Then, once the project is loaded in the Project Explorer folder we can choose our build configuration. This time we use the DEBUG option.
Once the build is done, the code is ready to be flashed to the board.
But before we do that, I found that the Readme.html file included in the project is essential reading. For a start, it provides a nice overview of what this example does.
2.2 Flashing the RSL10-SENSE board
I soldered a 10-pin header onto my RSL10−SENSE−GEVK board to allow me to use the Segger J-Link Lite CortexM debugger.
To flash this board, we follow the User Manual instructions:
[1] Connect the J-Link Debugger SWD ribbon cable to the board as shown in the picture.
[2] Make sure the CR2032 battery is inserted into the board – alternatively, you can power using 3V3, or less, via the pinout (but then make sure to remove the battery beforehand), as shown here. Powering the board is a mandatory step as it creates the voltage reference for SWD logic signals. In my opinion, when developing code, it is better to power without battery.
[3] Go to IDE's top menu and select Run → Debug Configurations...
[4] We can now click the "Debug" button.
This will usually trigger a dialog popup asking us to confirm a perspective switch when we click "Yes". If you've run debugger before this request (assuming you did not select the "Remember my decision") will happen after the debugger launched and before the "resume" option comes up.
Launching debugger can sometimes take awhile. You can watch progress on bottom right of screen.
2.3 Executing the project
When perspective has changed, you then click on "Resume (F8)" to execute the code on the board.
To view the debugger output we need to launch the terminal console.
This is where I found the documentation to be a little confusing. For a start, the project readme.html instructions differs somewhat from the User Guide instructions. Anyhow, I suspect that part of the problem is my lack of experience using the Segger debugger.
Here is a video demo capturing my experiences executing this program and running through the various tests.
2.3 Reviewing the project configuration settings
Before I start experimenting with the code, I thought it worthwhile having a look at some of the project configuration settings that are described at the back of the EVBUM2614-D User Guide.
2.3.1 sense_production_tests.rteconfig
According to the User Guide, the *.rteconfig file lists the software components within the CMSIS pack.
So for this project we have:
{gallery:autoplay=false} rteconfig |
---|
As you may have noticed, many of the Descriptions given are hyperlinked. So, if we click on one of those links it opens up further documentation.
Let's look at some of those links, starting with the one provided in the User Guide for the Device BDK:
The document is divided into a number of bookmarks or subsections, namely: Modules, Macros, Functions, and Variables. Then, on the left, there are plenty of drill down options available. The items listed on the left are the same as those shown in the Modules section.
So, for example, if we clicked on “COMPONENTS”, we get the following:
Then if we drill down further, into say the I2C Eeprom Library we get this page, which then also has a number of subsections (as shown on the top right), etc.:
So, the documentation looks pretty good once you get familiar with it.
However, one thing I found was that the Search box does not seem to work. I tried putting in the term “INMP522” and it returned “No Matches” even though that term was listed on the page I was on.
2.3.2 The “RTE” folder
The User Guide also mentions that System settings can be configured directly from within the CMSIS pack via the RTE folder. It mentions that “Each example is equipped with basic system
configuration that covers three main categories”. Let's try and make sense of this.
First, we know from the User Guide that each system configuration starts with “RTE_”.
Then, we can use the CMSIS configuration wizard (right click on the header file), to display the configuration table.
There are also configuration options found in the Device->RSL10 folder. This key one here is the GPIO configuration:
The one that does not appear to provide configuration settings options is “RTE_Components.h”
And that is basically it, regarding what I know about documentation and configuration settings via the IDE.
It's time to experiment...

3.0 Experimentation
3.1 Looking at the ISO15963 Tag (via N24RF64 EEPROM)
The code snippets found within main.c for the N24RF64 EEPROM is as follows:
#define EEPROM_I2C_ADDR (0x50) #define EEPROM_PAGE_SIZE (4) int32_t eeprom_status = -1; I2CEeprom eeprom; const uint8_t eeprom_ref[] = "This is for my Element14 blog"; const uint8_t eeprom_ref_length = sizeof(eeprom_ref); int main(void) { int32_t retval = 0; /** DO R/W test for N24RF64 EEPROM memory. */ retval = I2CEeprom_Initialize(EEPROM_I2C_ADDR, EEPROM_PAGE_SIZE, &eeprom); if (retval == I2C_EEPROM_OK) { retval = I2CEeprom_Write(0, (uint8_t*)eeprom_ref, eeprom_ref_length, &eeprom); if (retval == I2C_EEPROM_OK) { uint8_t eeprom_readback[sizeof(eeprom_ref)]; retval = I2CEeprom_Read(0, eeprom_readback, eeprom_ref_length, &eeprom); if (retval == I2C_EEPROM_OK) { if (memcmp(eeprom_ref, eeprom_readback, eeprom_ref_length) == 0) { memset(eeprom_readback, 0, eeprom_ref_length); I2CEeprom_Write(0, eeprom_readback, eeprom_ref_length, &eeprom); eeprom_status = 0; } else eeprom_status = 4; } else eeprom_status = 3; } else eeprom_status = 2; } else eeprom_status = 1; uint32_t timestamp = 0; while (1) { /* Execute any events that have occurred & refresh Watchdog timer. */ BDK_Schedule(); /* Print status information every second */ if (HAL_TIME_ELAPSED_SINCE(timestamp) >= 1000) { timestamp = HAL_Time(); printf("N24RF64 read back test: %s\r\n\n", eeprom_status == 0 ? COLORIZE("OK", GREEN) : COLORIZE("ERROR", RED)); } } }
However, because the EEPROM is not formatted for NDEF or NCI Tag, I could read the information when I presented the RSL10-SENSE antenna to my PN7150 NFC controller. This is the output I get:
When reading via a phone app we also get the relevant information as per the PN7150 controller:
As such, if I wanted to read any NFC data, I would need to modify this code. I was thinking that probably the easier method, or at least the method I would start with, would be NDEF and I will be looking at my previous blog for guidance:
Decoding different NDEF message types via NXP's NTAG I2C Plus and an Arduino MKR
To start I think I would need to configure the EEPROM correct to match the spec:
3.2 Looking at the DMIC
Let's extract out the code:
#define AUDIO_DMIC0_GAIN 0x800 #define AUDIO_DMIC1_GAIN 0x800 #define AUDIO_OD_GAIN 0x800 #define AUDIO_CONFIG (OD_AUDIOSLOWCLK | \ DMIC_AUDIOCLK | \ DECIMATE_BY_64 | \ OD_UNDERRUN_PROTECT_ENABLE | \ OD_DATA_MSB_ALIGNED | \ DMIC0_DATA_LSB_ALIGNED | \ DMIC1_DATA_LSB_ALIGNED | \ OD_DMA_REQ_DISABLE | \ DMIC0_DMA_REQ_DISABLE | \ DMIC1_DMA_REQ_DISABLE | \ OD_INT_GEN_DISABLE | \ DMIC0_INT_GEN_ENABLE | \ DMIC1_INT_GEN_DISABLE | \ OD_DISABLE | \ DMIC0_ENABLE | \ DMIC1_DISABLE) int32_t dmic_value = 0; int32_t dmic_max = 0; int32_t dmic_min = INT32_MAX; int main(void) { int32_t retval = 0; /** Configure DMIC input to test INMP522 microphone. */ /* Configure AUDIOCLK to 2 MHz and AUDIOSLOWCLK to 1 MHz. */ CLK->DIV_CFG1 &= ~(AUDIOCLK_PRESCALE_64 | AUDIOSLOWCLK_PRESCALE_4); CLK->DIV_CFG1 |= AUDIOCLK_PRESCALE_4 | AUDIOSLOWCLK_PRESCALE_2; /* Configure OD, DMIC0 and DMIC1 */ Sys_Audio_Set_Config(AUDIO_CONFIG); Sys_Audio_Set_DMICConfig(DMIC0_DCRM_CUTOFF_20HZ | DMIC1_DCRM_CUTOFF_20HZ | DMIC1_DELAY_DISABLE | DMIC0_FALLING_EDGE | DMIC1_RISING_EDGE, 0); Sys_Audio_DMICDIOConfig(DIO_6X_DRIVE | DIO_LPF_DISABLE | DIO_NO_PULL, 10, 6, DIO_MODE_AUDIOCLK); /* Configure Gains for DMIC0, DMIC1 and OD */ AUDIO->DMIC0_GAIN = AUDIO_DMIC0_GAIN; NVIC_EnableIRQ(DMIC_OUT_OD_IN_IRQn); uint32_t timestamp = 0; while (1) { /* Execute any events that have occurred & refresh Watchdog timer. */ BDK_Schedule(); /* Print status information every second */ if (HAL_TIME_ELAPSED_SINCE(timestamp) >= 1000) { timestamp = HAL_Time(); printf("INMP522: Use J-Scope to see waveform\r\n"); printf("INMP522: dmic_min=" COLORIZE("%ld", YELLOW) ", dmic_max=" COLORIZE("%ld", YELLOW) "\r\n", dmic_min, dmic_max); } SYS_WAIT_FOR_INTERRUPT; } return 0; } void DMIC_OUT_OD_IN_IRQHandler(void) { dmic_value = (int32_t)AUDIO->DMIC0_DATA; if (dmic_max < dmic_value) { dmic_max = dmic_value; } else { if (dmic_min > dmic_value) { dmic_min = dmic_value; } } }
When trying to understand the DMIC code, I found the Adafruit learning guide for their I2S mems microphone breakout to be very informative, especially for someone who has never experimented with digital microphones before. Here they mention the Arduino Serial Plotter and show a time series chart which looks pretty similar to the one we get via J-Scope. It also makes mention of a very interesting Arduino library called ArduinoSound. This library provides code for plotting frequency (ftt) and has code for a clap detector and a whistle detector. I certainly plan to experiment here.
... And experiment I did.
Having digested the Adafruit learning guide, I decided to add in a Sound Pressure Level calculation, this time in dB's, and then I also took these measurements and added in some more logic to determine if a clap or loud knock is detected, which is similar to the Arduino clap detector example found in the ArduinoSound library.
To calculate Sound Pressure Level we need to know the amplitude, which can be determined with the minimum and maximum values of a sample of values.
If you look at the original code (as shown above) you will notice that there are two variables dmic_min and dmic_max. In the above video you would have seen that these are shown in the Segger RTT Viewer terminal view. I have a screen shot of that view here:
But, I noticed something odd with the max value. It always shows a max value as the maximum value of an INT16 data type and this value never changes. Then another thing I noticed is that the min and max values are never reset during runtime in the code, so it just provides us with a man/max for the period the device is powered on, which is not what we need for determining a dynamic sound pressure level.
We can make simple modification though.
printf("INMP522: dmic_min=" COLORIZE("%ld", YELLOW) ", dmic_max=" COLORIZE("%ld", YELLOW) "\r\n", dmic_min, dmic_max); dmic_min = 0; dmic_max = 0;
By resetting the values after the print statement we will get a min and max value for every second the printf is updated. I worked out that the sample rate via the interrupt is approximately 30kHz.
Anyway, you will notice that I set them at a zero value. The reason for this is that I when using J-Scope to monitor these values we can get spurious values because of the way J-Scope extracts the value at a specified frequency. I managed to capture this in another test I did. We will get back to that test.
So, by resetting the min and max we get much better results - so, it appears to me that the spurious values are caused at device power up, for some reason.void DMIC_OUT_OD_IN_IRQHandler(void) { dmic_value = (int32_t)AUDIO->DMIC0_DATA; if (dmic_max < dmic_value) dmic_max = dmic_value; if (dmic_min > dmic_value) dmic_min = dmic_value; if (dmic_smplCntr >= DMIC_SAMPLEINT) { if (dmic_arrCntr < DMIC_ARRAYSIZE) { dmic_array[dmic_arrCntr] = dmic_value; dmic_arrCntr++; if (dmic_arrCntr == DMIC_ARRAYSIZE) { if (!dmicBuffull) dmicBuffull = true; dmic_arrCntr = 0; } } } else dmic_smplCntr++; }
Now I can normalise and determine a sample min and max value. Then once we have those values we can calculate the sound pressure level using the formula
SPL_dB = 20.0 x log10(max - min). Note that this is a float or double. For J-Scope I wanted an integer so I mulitply this value by 10 to get better precision. In the code shown you will also notice some logic to handle clap detection and a clap counter. More on that later.
if (dmic_smplCntr >= DMIC_SAMPLEINT) { // Calculate Sound Pressure Level... if (dmicBuffull) { meanval = 0; for (x = 0; x < DMIC_ARRAYSIZE; x++) { meanval += dmic_array[x]; } meanval /= DMIC_ARRAYSIZE; // Normalise the values for (x = 0; x < DMIC_ARRAYSIZE; x++) { dmic_array[x] -= meanval; } // Now determine Min and Max values for (x = 0; x < DMIC_ARRAYSIZE; x++) { if (dmic_smplMax < dmic_array[x]) dmic_smplMax = dmic_array[x]; if (dmic_smplMin > dmic_array[x]) dmic_smplMin = dmic_array[x]; } if (prevDMICupdated) prev_dB10val = dmic_dB10val; dmic_dB10val = (int16_t)(200.0 * log10((dmic_smplMax-dmic_smplMin))); if (prevDMICupdated) { prev_SPLdelta = dmic_SPLdelta; dmic_SPLdelta = dmic_dB10val - prev_dB10val; if (dmic_SPLdelta > DMIC_CLAPTHRESHOLD) { if (!checkClapDelta) checkClapDelta = true; else checkClapDelta = false; } else { if (checkClapDelta) { checkClapDelta = false; if ((prev_SPLdelta - dmic_SPLdelta) > prev_SPLdelta) clapCounter++; } } } // reset the values dmic_smplMax = 0; dmic_smplMin = INT32_MAX; } dmic_smplCntr = 0; }
I created another video capturing the output from the Sound Pressure Level measurements. Note that this video is via my phone as camera, as the laptop struggles to video capture and use J-Scope sampling at 10us at the same time:
Then I create another video to shows the clap detection in action:
Finally, I created a music test video to see what my all my parameters we doing using J-Scope. It was here that I spotted some anomolies with using this technique (as shown in that chart for the min and max values). But anyhow, the great thing with J-Scope is that you can export the data. I have created some charts from this data.
{gallery:autoplay=false} J-Scope |
---|
So, hopefully you agree, from this audio analytics that there is plenty of scope for further experimentation.
To be continued...
Top Comments