Showing posts with label adc. Show all posts
Showing posts with label adc. Show all posts

Monday

AutoAnalogAudio Library - nRF52 Now supports Successive Approximation Analog-to-Digital Converter for Audio Input

 AutoAnalogAudio Library - nRF52 Now supports SAADC

Support for Successive Approximation Analog-to-Digital Converter for Audio Input

I've been doing a lot of work on my AAAudio library lately in regards to nRF52 devices like the XIAO BLE SENSE 52840 or the Feather Express 52840, and just added support for the onboard SAADC. This allows users to capture input audio from a regular analog microphone instead of requiring I2S or PDM. 

After having developed support for PDM, PWM and I2S, this wasn't too complicated. I've grown to understand the nRF52 interface, which is really nice and user friendly, and the documentation is great.



To use the SAADC interface start up analog audio by calling the following:

aaAudio.dinPin = 1; //Where 1 represents the AIN0, 2 represents AIN1, etc...
aaAudio.begin(3, 1); // Where 3 represents enabling of SAADC, 1 is for PWM output



All in all the SAADC seems to work very well! I am still doing initial testing & code review, but the changes have been pushed to GitHub as usual. Users can install from ZIP to test out the new code, or wait until the next release.

AutoAnalogAudio Arduino library updated with ESP32 Support

 AutoAnalogAudio Arduino library updated with ESP32 Support

ESP32 DAC/ADC Output/Input via the I2S peripheral

Previously this year, I received some ESP32 based MCUs with OLED displays from DigitSpace, and used these devices to add ESP32 support to the AutoAnalogAudio library. It is a bit different from previous iterations, since instead of AVR interrupts and PWM, the ESP32 uses much more advanced peripherals. The I2S capabilities of the ESP32 provide a fairly seamless interaction when in/outputting audio signals, since it is just a matter of configuring the I2S, DAC and/or ADC and feeding and/or drawing data from the device. The main challenge in this case was combining the I2S functionality with RTOS tasks to provide a simple, asynchronous audio interface as is the case with the AutoAnalogAudio library.

The capabilities are much the same, but the ESP32 portion is still a bit in development, although working nicely at this point. The ESP32 example is specifically for the ESP32 device, since some minor API changes were required to manage RTOS tasks in combination with the I2S peripheral. This allows asynchronous handling of audio, so users can start playback and manage other tasks however desired while audio playback occurs.

Hardware:

1 x ESP32 OLED Wifi Kit
1 x MAX9815 Microphone Preamp
1 x TDA7297 PA Module

The MAX9815 will provide some audio to sample, and the TDA7297 was used to amplify the output of the onboard DAC. The above hardware as mentioned is provided by DigitSpace.

Connecting the Hardware:

With the ESP32, the AAAudio library samples the ADC on analog channel 4 by default, and sound output is on DAC1 and/or DAC2 (See previous post for pinout) The ADC channel cannot currently be changed, but that is on the to-do list.

Limitations:

The ESP32 onboard ADC will provide relatively good quality audio sampling, at 12-bits. The onboard DAC however, is only 8-bits, so the output will not be quite as high quality as with the Arduino Due. Using a higher sample rate can make up a for the low bit-rate of the DAC, as higher sample-rate * bit-rate = quality.

Overview:

Updating the library for ESP32 support has been fairly interesting, as it required a fair bit of learning and development time to understand I2S and how to control the related peripherals, while providing the same or similar behavior as with currently supported devices like the Due or Uno.

All in all it seems to perform pretty well. Audio input or output from virtually any source can be managed via the AAAudio library.

The updated library has been released and should be available via the Arduino Library Manager.






Saturday

New Sampling/Audio library for Arduino : AutoAnalogAudio
Easy access to the internal DAC(or PWM), ADC, Timers & DMA for sound generation

I've been playing around with the Arduino Due for a while now, and finally got to looking over the datasheet and playing around with some of the internals because of ongoing requests regarding audio functionality and advanced timer usage etc.

As a result of my dabbling, I've created a sampling library to simplify access to the onboard Analog-to-Digital (ADC), Digital-to-Analog (DAC), Timer and DMA peripherals.

It is a simplified API that allows users to create a wide range of audio or related applications in short order.

Auto Analog Audio (Automatic DAC, ADC & Timer) library

Goals:
Extremely low-latency digital audio recording, playback, communication and relaying using a simple API

Features:
  • New: Now supports AVR devices (Uno,Nano,Mega,etc)
  • Designed with low-latency radio/wireless communication in mind
  • Very simple user interface/API to Arduino DUE DAC, ADC, Timer and DMA
  • PCM/WAV Audio/Analog Data playback using Arduino Due DAC
  • PCM/WAV Audio/Analog Data recording using Arduino Due ADC
  • Onboard timers drive the DAC & ADC automatically
  • Automatic sample rate/timer adjustment based on rate of user-driven data requests/input
  • Uses DMA (Direct Memory Access) to buffer DAC & ADC data
  • ADC & DAC: 8, 10 or 12-bit sampling
  • Single channel or stereo output
  • Multi-channel ADC sampling
  • AVR devices with no DAC or DMA use pseudo DAC(PWM) & DMA(Timer + Memory Buffer)

Testing:

The results so far have been very good. Using the nrf24l01+ radio modules, I was able to stream a standard format *.wav file of high quality (48khz, 16-bit, Stereo) from a Raspberry Pi to the Arduino Due using a slightly modified version of the included Wireless Speaker example.

To put that in perspective: Data Rate = Sample Rate * Channels * BytesPerSample

Streaming over Radio (RF24):
Data Rate = 48,000 * 2 * 2 = 192.0KB/s 

Streaming from SD Card:
Data Rate = 44,100 * 2 * 1 = 88.2KB/s (maximum rate with noticeable slowdown)

Using an SD card is a bit less exciting, as the audio starts to slow down noticeably with audio of 44.1khz, 8-bit, Stereo. Unfortunately, the SD speed is currently a bit limited, since the HSMCI is not available on stock Arduino Due, and I haven't found a faster working library like sdFat for Due.

SdFat Lib:
The sdFat library works with the due, I just had to initialize it at SPI_DIV6_SPEED (10.25Mhz SPI), and it worked using a value of 5 (17mhz SPI) as well. Initial tests show the max read speeds with the stock SD library at about 113KB/s, and 182KB/s with sdFat lib. Adjusting the SdFatConfig.h file to set ARDUINO_FILE_USES_STREAM = 0 results in speeds of 204KB/s with my current card & module.

These numbers indicate that the Due can handle quite a bit of punishment, and is easily up to the task at hand. The main limitation seems to be the throughput of whatever device is providing the audio or recording it etc.

Initial testing on AVR devices seems to indicate functionality similar to my RF24Audio and TMRpcm libraries.

If using a faster device, or generating audio from tables stored in memory, it seems that the Due with the AAAudio library will most likely handle it.

Notes:

If using the RF24 library in combination with an SD card, I highly recommend using a separate SPI BUS for the radio and SD card. I experienced a number of problems with high speed transfers etc when attempting to extend the length of wires used or connect an SD card module as well.

See the SPI_UART library section of the RF24 docs to enable a secondary SPI BUS for the radio.
Due Pins - TX1: MOSI, RX1: MISO, SDA1: SCK, CS&CE: User selected



Installation: AutoAnalogAudio is available via the Arduino Library Manager


I've also included a number of examples that demonstrate usage:
Click: File -> Examples -> AutoAnalogAudio in the Arduino IDE

SDAudio Examples: Demonstrate how to play back and record WAV/PCM format audio from SD card using the AAAudio library

Wireless Examples: Demonstrate receiving and sending streams of audio data via nrf24l01+ radio modules and are easily modified to work with other radios or devices. Some matching examples and audio samples are included for Raspberry Pi.

Audio Generation Examples: Demonstrate playback of simple audio tones or synthesized audio.

ADC/Audio Capture Examples: Demonstrate how to capture data/audio streams from the ADC on one or more pins/channels.

Documentation: http://tmrh20.github.io/AutoAnalogAudio
Source Code: https://github.com/TMRh20/AutoAnalogAudio

Wednesday

HB-100 / GH1420 10.25GHz Microwave/Doppler Radar Motion Sensor w/Arduino


I picked up some of these modules out of curiosity, as these modules can detect the motion and speed of objects through walls or other non-metal obstacles.


Overview:

The modules themselves are fairly simple in use, since they output a signal with the frequency/pulse width indicating the speed, and the amplitude/voltage indicating the strength of the reflected signal.

The speed is very simple to calculate once the frequency of the incoming signal is detected:

km/h = hz / 19.49
mph = hz / 31.36

These HB-100 modules require a pre-amp circuit to boost the IF signal, so I used an LM358N and the circuit at https://hackaday.io/project/371-hb100-radar-shield to boost the intermediate signal.

The output of the circuit sits at about 2.5v when quiet, and moves up & down to represent the received AC waveform.

In pure digital form, this would be very easy to detect using interrupts, but with an amplified analog signal, weaker signals may be missed.

Problem:

Tests with this radar shield using standard frequency counters/libraries seemed to show that interrupts and edge-detection have limited sensitivity to weak signals. They also provide no means for detecting the amplitude of the signal. It seemed that using the on-board ADC may provide better results in this frequency range. (0 to 4000 Hz or 0 to 205km/h)




Goal: 

Detect the frequency and amplitude of low frequency (0-4khz) signals from HB-100 modules, while potentially increasing the sensitivity of waveform detection using the HB-100 & circuit as linked above.

Result:

The resulting code uses the Arduino ADC to sample incoming signals using very simple peak-to-peak detection.

Signals are detected when the waveform transitions above or below a set "midPoint" which represents the 0v value of the AC waveform. Typically, it will be ADC_RESOLUTION / 2.

This is very similar to the edge detection used for interrupts and digital signal detection, but using the ADC to detect weaker transitions.



ADC Resolution / VCC == Volts per Increment ( 1023/5v = 0.005v )
Sensitivity of 10 == peak-to-peak detection of +/- 0.05v signals


Technical Info:

The ADC is driven in free running mode, taking samples at about 38.5khz while looking for specific deviations in the measured voltage that correlate with a peak-to-trough waveform.

The results are summed in a single variable, and returned either as a single result, or an average, depending on how often the results are read in relation to the received signal.

Information about the signal, including frequency, amplitude and number of samples allows more complex analysis.

Conclusion:

The code was developed in a very short time, and can still be considered a work in progress, although it seems good as-is. It tests well up to about 2-4khz with another Arduino producing square wave output. When used with the radar module, it is very sensitive, and allows the sensitivity of the device to be defined in software, and/or results to be filtered.

Code & Examples:

https://github.com/TMRh20/AnalogFrequency



Thursday

Arduino AVR Timers and Waveform Generation

I've had a number of questions regarding Arduino timers and the generation of signals and waveforms, so here is (hopefully) a simple tutorial on using Arduino timers.

This tutorial requires a basic understanding of the difference between bits and bytes, along what it means to read or write bits and bytes to AVR registers.

Overview:
Timers can be seen as additional components used to offload work from the processor. A timer running on its own can create a signal or waveform without any interaction from the CPU, allowing the CPU to do things like interact with the timer to modify the waveform or handle other tasks.

The most basic function of a timer is to count from 0 to a specified number, lets say 255, and then start all over again and count from 0 to 255 again. Using a standard Arduino board running at 16 million cycles/second, the timer will count from 0 to 255 at a set frequency, which can be determined using a simple calculation:

CPU Freq / TIMER TOP  =   Timer Frequency (cycles/second)
16mHz     /         255          =   62,745 cycles/second  ( 62.75kHz )

The UNO has both 8-bit timers (TIMER0, TIMER2) and a 16-bit timer (TIMER1) which can all be used to perform various functions. This means that timer0 and timer2 can handle or count in increments ranging from 0 to 255, and TIMER1 will handle values from 0 to 65,535.


Timer Parameters & How to generate a signal:

Timer TOP:
Basic timer functionality is simple, we set a TOP value, and the timer will count from 0 to TOP, then start counting at 0 again.

Timer COMPARE:
Timers can also be set to toggle an output or trigger an interrupt when the timer value reaches a set value. Lets say the TOP value has been set to 255, the compare value is 127, and an LED is connected to pin 9 on an Arduino UNO. The following will take place:

a: The timer will increment its value by 1 every 16-millionth of a second, starting at 0
b: The LED stays lit until the counter value reaches 127, then is turned off.
c: The LED turns back on once the counter value is reset to 0 (after reaching its defined TOP value)
d: GoTo b:

Result: This produces an approximately 50% duty cycle PWM signal at 62.75kHz

Example Code:

void setup(){

    pinMode(9,OUTPUT);
 
   //This sets the timer1 mode, prescaler and enables timer PWM output on Uno pin 9 (not covered in this tutorial)
    TCCR1A = _BV(WGM11) | _BV(COM1A1);
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

    //This sets the TOP value, or the value that the timer will count up to before resetting to 0
    ICR1 = 255;
 
   // This sets the OUTPUT COMPARE value A ( Compare A for pin 9, Compare B is linked to pin 10)
   OCR1A = 127;

}

Changing the Duty Cycle (how long the pin output is HIGH vs LOW):

Changing the duty cycle is as simple as changing the value of the OCR1A register. Since these examples use timer1, 16-bit (0-65535) values could be used, but since the TOP value is set at 255, only those values can be used for this demonstration.

Example Code (Uses the above setup() code) :

void loop(){
    OCR1A = OCR1A + 1;
    delay(15);
    if(OCR1A == 255){
         OCR1A = 0;
    }
}

Result: The example above will increment the duty cycle by 1/255 (0.4%) every 15 milliseconds until it has reached 100%, then it will reset to 0 and continue to increment. A connected LED will be seen to get brighter and brighter until the timer compare register reaches the TOP value, and the duty cycle (OCR1A) will be reset to 0.

Changing the Frequency:

To change the frequency, simply adjust the timer TOP value in the above example code:
ICR1 = 30000;

Calculation:  16,000,000 / 30000 = 533.33... HZ (cycles/second)

Note: The compare register (OCR1A) can be set to any value between 0 and 30,000 in this example.

Prescaling:

The frequency can be made lower by adjusting the prescaler. The system clock signal will be divided by the prescaler before being made available to the timer. This is not required to be understood for this tutorial.

Calculation: CPU Freq. / Pre-Scaler / TIMER TOP = frequency


Basic Theory of Creating Audio:

One of the simplest and most common forms of uncompressed audio is the WAV or PCM format. PCM stands for Pulse-Code-Modulation, which can be replicated using the Arduino timers, and the information provided above.

In the case of recording 8-bit audio, the voltage of an audio signal is converted to a number ranging from 0 to 255. The higher the voltage, the higher the number. This is done at a set frequency, say 16kHz or 16,000 times a second.

Recreating an audio signal can get fairly complicated and in-depth, but basically just needs to meet the following requirements:

1. Frequency of the timer generally should equal the frequency that the audio was sampled at (sample rate)
2. Sample values need to be fed into the timer every cycle

To create audio of 16khz sample rate, the timer needs to run at 16,000 cycles/second, and a new sample value needs to be fed into the timer every time it reaches its TOP value (every 62.5 microseconds) This generally requires the use of interrupts, which is out of the scope of this tutorial.

The above example code can be used to create simple audio tones by varying the frequency from 0 to about 10,000 hz max, and volume controlled by adjusting the duty cycle.

Summary:

This overview is intended to cover very basic timer usage in Fast PWM mode via TIMER1. Additional timers, modes, details, usage, etc can be determined by referencing the datasheet, which is really the best reference for any kind of advanced timer usage. All of the timers are accessed in very similar ways, but some settings like prescaling are different between timers.








RF24Gateway - What Does It Do And How Do I Use It?

 RF24Gateway - What Does It Do And How Do I Use It?  A simplified explanation...  So maybe you've taken a look at the RF24 communication...