This project has born during a conversation with the DSP teacher in the UPC. We were talking about to build some exercise around arduino platform using digital signal demodulation. Mainly whether arduino would achieve to do this job using subsampling techniques.

We already know that arduino isn’t the best tool so far, but is a well known platform and very afordable. So, we decided to go forward in despite the limitations.

Subsampling

Is not my aim write a lot about subsampling techniques. There’s a lot of information out there, starting from Wikipedia’s article. But I’m trying to sumerise what’s all about.

When sample data from a source is wanted, the Nyquist criteria claims that the sample speed must be higher than twice the speed from data to be sampled. Understanding that speed means freqüency, of course. If data is a sine signal with 1kHz of freqüency, the proper freqüency must be higher than 2kHz.

But, what happens when the sampling freqüency is lower than the signal freqüency?

Take a look to this picture (The image belongs to skywired.net. Please, visit this interesting blog)

Let’s give to the red signal the rol of the signal data. This signal has a freqüency of 8Hz (eight whole periods in one second). We use a sampling speed of 7Hz. You can see the 7 blue lines (counting the blue dot in 1) sampling the value of the signal. The  shape that is build from the data sampled isn’t 8Hz anymore!  Instead of 8Hz signal, once downsampled, we see the original signal has 1Hz signal. That’s downsampling effect.

What do should happen if we get a 10Hz signal freqüency and the same 7Hz sampling freqüency? The sampled data would take then a clone-image as a 3Hz signal.

SSB Siignal

SSB means Single Sided Band. The SSB modulation is hard to go deep inside the theory that involves, but the  result is easy to explain. There’s two diferent types of Single Sided Band modulation, The Upper Sided Band (USB) and the Lower Sided Band (LSB). The result of SSB modulation can be the carrier freqüency plus signal freqüency for USB, and the carrier freqüency less signal freqüency for LSB.  If we consider, for instance USB,  we get a new freqüency some Hz above the carrier freqüency. The amplitude of the SSB Modulated signal is proportional to the one the original signal had before modulating.

For demodulating this signal a freqüency convertion should be enought. That is: driving the modulated signal to the original  freqüency range. This can be achieved analogically and also using digital techniques.

 The Atmega328 doing the job.

According the previous sections, it’s possible lower a freqüency signal using downsampling. Let’s try to do the job with Arduino.

Basically we need to use the ADC, taken some samples in a freqüency lower than sampled signal. Then this samples has to be returned to the analogic world. For that purpose I’m going to use PWM. Using PWM with the Arduino default libraries is easy, but is not convenient, because the PWM freqüency is fixed around 500Hz. That is a limitation for recovering signals with higher freqüency.

Due that, I decided to write the C code in a low level, near assembler code.

The Code

Let’s take a look at the main program:

#include <stdbool.h>
#include <avr/interrupt.h>
#include "control_adc.h"
#include "control_timer.h"
#include "control_PWM.h"

/********************************************
                  subsampling
---------------------------------------------
The following program take samples from analog
input A5 in Arduino board and sends this value,
8 bit coded, to the PWM output at pin11.

An Interruption Request is raised every 125us,
but the speed rate to the output is 250us (4000Hz). 

Joan Martínez (c) 2012
LGPL v3.0  
(http://www.gnu.org/licenses/lgpl-3.0.en.html)

*********************************************/

bool sampling=false;

void setup(){
  setup_ADC();
  setup_TMR();
  setup_PWM();
  /* Enabling Interruptions */
  sei();
}

void loop()
{
  /* Nothing is done in here. Just waiting 
     for timer 0 interruptions   */
}

int main(void){
  setup();      /* Calling the program setup  */
  while(true){
    loop();   /* Calling the an empty loop */
  }
  return 0;
}

ISR(TIMER0_COMPA_vect){
  sampling= !sampling;   /* This allows a kind of pseudo pre-escaler */ 
  if (sampling){
    uint8_t value=read_ADC();
    set_PWM(value);
    start_ADC();
  }
}

This Code is compossed basically by a setup section, that sets how the ADC, Timer0 and PWM will work, and the ISR, responding to the Interruption request launched by the coincidence of Comparator A in Tiomer0.

As we can see the ISR is doing the job that we described before. That is, reading a ADC value and writting it back over PWM. But there’s something unusual doing with a bolean variable called sampling. This variable provide us a way to add a pseudo pre-escaler for timmer 0, because the period of time in wich we are measuring is doubled.

The complete code with the control_adc, control_TMR and control_ADC files can be found here.  Maybe the code you can see there is a little bit diferent that the one described here, because it’s an alive SVN repository.

Inside the Code

The code looks simply and clear, because the functions that are doing the dirty jobs are inside the included modules.

control ADC module

#include <inttypes.h>
#include <avr/io.h>
#include "control_adc.h"

/*******************
 control_ADC module
*********************/

void  setup_ADC(){
  /* Enables ADC, sets the pre-escaler */
  ADCSRA = AD_EN|PRESCALER_16;
  /* Sets the reference as internal reference , selects A5 as input,
     Left aligment of the result */
  ADMUX  = R_INT|ADC5|AD_LAR;
}

uint8_t read_ADC(){
  /* Waits for the convertion is completed */
  while (bit_is_set(ADCSRA, ADSC));
  return ADCH;
}

void start_ADC(){
  /* starts running convertion */ 
  ADCSRA|=(1<<ADSC);
}

The only secret inside the A/D Convertion is the setting up. The selecction of pre-escaler and Left Adjusting  of the result provide us a high speed measure, uisng 8 bit resolution.

control PWM module

#include <stdio.h>
#include <stdint.h>
#include <avr/io.h>
#include "control_PWM.h"

/*******************
 control_PWM module
*********************/

void setup_PWM(){
  DDRB |= (1 << DDB3);   //Output  Pin 11 arduino
  TCCR2A=0b10000011;   //OC2A: Clear on compare match, set at bottom. Fast PWM
  TCCR2B=0x01;         //No pre-escaling. freq=clk/256 = 62500  
}

void set_PWM(uint8_t value){
  OCR2A=value;
}

That is quite clear. The most important point is to use the fastest PWM possible frequency.

Maybe there’s something to improve about the selected Mode of PWM, because this mode would be noisy.

control TMR module

#include <inttypes.h>
#include <avr/io.h>
#include "control_timer.h"

/*******************
 control_TMR module
*********************/

/*
Formula to calculate frequency of the Interruption Requests:

Ftimmer=Fclk/(TCCR0B*(1+OCR0A))
*/

void setup_TMR(){
  TCCR0A=0x02;          //Pin timer desconnected form output, mode CTC
  TCCR0B=TMR_PRESC_8;   //Pre-escaler 8
  OCR0A=61;             //TOP value
  TIMSK0=0x02;          //Bit 1 – OCIE0A: Timer/Counter0 
                        //Output Compare Match A Interrupt Enable
}

As shown, only the timer setup is done there, because when this jo is done the timmer will be in charge to raise up an Interrruption to a constant period of time.

There’s a short variety of internal pre-escaler for timmers (x1, x8, x64, x256 and x1024), due that the flag variable named sampling has been used, This help me to find some values more convenient.

The key values are the the pre-escaler set in TCCR0B and the top value for comparator, in OCR0A.

Using it

This code has been checked in University (UPC), in the lab of Digital Signal Processing, as an easy way to get a signal decoded from an ultrasonic transmission (around 40kHz). The results has been a little noisy, but the voice is understandable. The demodulation results is like listening and AM transmission.

Subsampling connected to an ultrasonic trasducer, an amplifier and a filter.

Final notes

For optimal demodulation result a good signal must be generated. That is, we have setup the pre-escaler set in TCCR0B and the top value OCR0A, closer to desired sampling freqüency. After that we have been using external function generator to adjust the carrier to the target value fixed by the limitations of Arduino.

The noisy signal demodulated is something that must be found out. There are three diferent theories about it:

  • Maybe is because the PWM mode that has been selected: This mode would produce noise around the PWM frequency (62.5kHz.) because there are transitions at a fixed period of time. But we thing this should be far enough from our 40kHz.
  • Maybe is because Arduino is refreshing PWM output without consider in which axact moment is doing it. Maybe there are some glitches. This would be avoided oing some checking before to place a new value to the PWM.
  • Is there an amount of noise down-converted several times?

In despite of the noise, and the limitations from the platform, this has been a good application that can give a basic vision of downsampling techniques, using a simple and afordable tool.

Anuncios