Una nova interpretació del QRP? (I)

La transmissió utilitzant baixes potències dona un camp d’experimentació ja conegut en l’àmbit dels radioaficionats. Potser no tothom està a favor del ús de baixes potències amb al·legacions tant absurdes com “la vida és massa curta per QRP”, no obstant ofereix un gran atractiu per molts membres de la nostra comunitat.

Sigue leyendo

Anuncios

Noves vocacions en la radioafició

El passat mes de febrer, des de la Associació Radioaficionats de Catalunya (ARCAT), em van demanar de participar en la tercera agermanada que tindria lloc a Santpedor. Volien que preparés alguna xerrada tècnica com a part de les activitats que tenien previstes per aquest esdeveniment.

Sigue leyendo

DDSBee, un oscilador DDS controlado por comandos série

El DDSBee es un pequeño dispositivo diseñado para ser de utilidad para makers, estudiantes y profesionales en el desarrollo e implementación de circuitos electrónicos que necesiten disponer de algún tipo de oscilador.

DDSBee proporciona actualmente tres tipos diferentes de oscilación, hasta 12.5MHz.

  • Senoidal.
  • Triangular.
  • Cuadrada.

El oscilador DDS microcontrolado

El uso y programación de este módulo se realiza simplemente con una conexión série y usando un juego de mas de 30 comandos programados a modo de CLI. El uso de estos comando permite establecer tanto las formas de onda de salida como la frecuéncia (con ajuste de 1Hz.) o bién los modos de funcionamiento como modulador (OOK, FSK y PSK).

Compatibilidad de formato

El módulo DDSBee está diseñado partiendo del factor de forma de los módulos XBee ™, fabricados por Digi. Este formato proporciona un estándard de facto que diversos fabricantes han adoptado.

Además del factor de forma, el módulo persigue la compatibilidad entre terminales, entendiendo que responden a conceptos tecnológicos diferentes. Entre los terminales que guardan esta relativa compatibilidad encontramos:

  • Alimentación (1 y 10).
  • TXD/RXD (2 y 3).
  • DTR (9).
  • Reset (5)
  • GPIO’s y ADC.

Esta compatibilidad permite la conexión directa a circuitos diseñados para alojar dispositivos con este formato, como USB XBee Xplorer o Arduino XBee Shield, por mencionar alguno.

Descripción física

El módulo tiene un formato externo del tipo XBee, el cual lo hace apto para utilizarlo con interfaces de conexión de este tipo.

Descrito brevemente, el DDSBee es un oscilador DDS microcontrolado. Por lo tanto tiene dos parte claramente definidas, cada uno de estos tiene su circuitería asociada ubicada en una de las caras del módulo.

  • El microcontrolador (AVR Atmega328)
  • El oscilador DDS (AD9833)

En el módulo existen dos niveles de comunicación diferentes:

Externa UART:
La comunicación entre el exterior y el módulo se hace a través de la conexión UART del microcontrolador. Esta es accesible desde los terminales 2 y 3 del módulo, al igual que lo son de manera estándar en los módulos XBee.

Interna SPI:
Entre el microcontrolador y el DDS la comunicación es unidireccional y se realiza por medio de un bus de comunicaciones SPI. A través de este bus el microcontrolador programa los diferentes registros existentes dentro del DDS y produce los cambios en la salida.

La parte propia del DDS incorpora un cristal oscilador de referencia con una frecuencia de 25MHz. Esto produce que el valor máximo de frecuencia programable del DDSBee sea de la mitad (12. 5MHz). En realidad de un Hertz menos (12.499.999 Hz.). Sin embargo hay que tener en cuenta que cada vez que deseamos aumentar la frecuencia de salida en la que debe trabajar el oscilador, la resolución de la forma de onda disminuye. Es decir, por 12. 5MHz sólo disponemos de dos bits de resolución, uno en cada semiperiodo. Con lo que cuando utilizamos el oscilador para frecuencias elevadas las formas de onda triangular y senoidal, no pueden ser muy exactos. Al menos sin disponer de una recuperación eficiente de la forma de onda de forma externa.

El aspecto de la placa es la que se muestra en las siguientes figuras, donde se pueden ver los diseños de las caras superior e inferior (top y bottom, respectivamente).

DDSBee proporciona dos salidas diferentes.

  • La salida principal está situada en un conector SMA en la parte frontal del módulo. Esta salida está desacoplada en continua a través de un condensador. La salida es apta para ser usada con etapas de amplificación de audio o RF que requieran un nivel cero de tensión continua a la entrada. Debido a la presencia del condensador de desacoplamiento esta salida podría distorsionar algunas señal de muy baja frecuencia, por ejemplo una señal cuadrada por debajo de 10Hz.
  • La salida secundaria está situada en el pin número 20 del módulo y provee la salida directa desde el DDS interno. Esta salida tiene un nivel medio de tensión alrededor de 310mV. La salida secundaria no está desacoplada en continua, por lo tanto es válida para señales de transición lenta o bien para mantener un nivel de tensión constante a la salida, si se detiene el DDS en un determinado momento.

Descripción de los terminales

Los terminales del dispositivo se describen a continuación, en la figura [fig:pinout]

pinout

Pin DDSBee Denominación Pin DDSBee Denominación
1 VCC 20 F_OUT
2 DOUT 19 AD1/DIO1
3 DIN 18 AD2/DIO2
4 N.C. 17 AD3/DIO3
5 ​​RESET 16 DIO6/SDA
6 SCK 15 DIO5/SCL
7 MISO 14 N.C.
8 MOSI 13 REG_CHG
9 EN_OUT 12 DIO7
10 GND 11 N.C.
  • Alimentación: El DDSBee está alimentado entre los terminales 1 (VCC) y 10 (GND). La alimentación puede tener valores entre 3 y 5V (Típicamente alimentado a 3.3V).
  • DOUT: Salida de datos desde el DDSBee. Los datos se envían a 115200 bds. con el formato de 8 bits de datos un bit de stop y sin paridad.
  • DIN: Entrada de datos hacia el DDSBee. Hay que finalizar cada línea con la retorno de línea (\n), sin retorno de carro.
  • F_OUT: Salida del DDSBee directa. Esta salida incorpora la tensión continua (300 mV) al contrario de la salida principal a través del conector SMA
  • EN_OUT: Terminal de control externo para activar o desactivar la salida.
  • REG_CHG: Terminal de intercambio de registros. Según el estado de este terminal se escoge internamente entre los dos registros de fase o los dos registros de frecuencia, dependiendo del modo de trabajo en cada caso.
  • RESET: Poniendo este terminal en estado bajo se hace un reset del sistema y éste se reiniciado. También se utiliza para programar el firmware.
  • Programación de Firmware: El dispositivo se programa con un ISP, que está distribuido en el terminales del DDSBee 6 (SCK), 7 (MISO), 8 (MOSI) y el RESET del terminal 5. También es necesario proveer el sistema de alimentación (VCC y GND).
  • Terminales de reserva:Son terminales que en la presente versión de Firmware no tienen definida ninguna aplicación pero están conectados internamente, a fin de proveer futuras ampliaciones. Estos terminales son el 12 (DIO7), el 15 (DIO5), el 16 (DIO6), el 17 (DIO3), el 18 (DIO2) y el 19 (DIO1).
  • Terminales no conectados (NC):Estos terminales no tienen ninguna conexión interna con el DDSBee. Son el 4, el 11 y el 14.

Manipulador electrónico para telegrafia EB3EWQ

Este es un pequeño proyecto destinado a radioaficionados que quieran construirse su propio manipulador de telegrafía. Eso es algo que hace años que tenia en cabeza, pero mi poca habilidad con las piezas mecánicas me ha hecho desisitir en muchas ocasiones.

Así que un buen día decidí hacer un manipulador telegráfico que no necesite de piezas mecánicas. Algo que sea puramente electrónico y simple a la vez.

Sigue leyendo

¿Este blog está muerto?

Han pasado algunos dias desde la última vez que se editó este blog. Bueno, vale. No solamente algunos dias, sino mas bien algún año. Casí cinco.

Los motivos son los que sean. Tal vez una colección de escusas que no interesan a nadie. No me voy a andar por las ramas, así que confieso que he tenido otras prioridades antes que mantener vivo otro blog en inglés sobre arduino.

Por otra parte creo que es una lástima dejar morir de inanición este espacio en la red, que me ofrece la posibilidad de publicar algunos proyectos y alguna prueba que, tal vez, pueda ayudar a otros. Si por una casulidad se tropieza en la red con este.

Así que desde este momento intento retomar el camino, modificando la filosofia que en un principio le dió vida, pero que finalmente dejó este blog en un estado de hibernación. Voy a intentar no encorsetarlo, no limitarlo ni en la forma, ni en el fondo. Tal vez así se mantenga vivo algún tiempo mas.

A partir de ahora este blog va a ser algo mas personal y no tan ligado al mundo ‘Arduino’. Quiero dar espacio a todo aquello que me interesa y crea que pueda ser de interés para cualquier otro. Y si no lo es, al menos que sirva de recordatorio para mi mismo. Un diario técnico, un bloc de notas para poner negro sobre blanco, todo aquello que experimente con Arduino y otros microcontroladores. Pero también experimentación con radio frecuéncia, con circuitos discretos, programación en python, scratch, snap o cualquier otra. Y en un futuro quien sabe qué mas.

Otro de los cambios que va a sufrir va a ser la lengua usada. El inglés no es mi lengua. Para mí es un gran esfuerzo escribir artículos en inglés. Hacerlo de manera ágil es ahora mismo un freno a la publicación de ideas. No descarto utilizar la lengua inglesa, pero no quiero que esto sea una carrera de obstáculos. Así que no voy a limitar la redacción a una lengua. Ahora estoy redactando este texto en castellano, pero no quiere decir que a partir de ahora todos los artículos vayan a estar escritos de esta forma. Soy catalán, hablo catalán, castellano e inglés. El francés lo tengo algo olvidado y cualquier dia empiezo a aprender èlfico.

Esta es mi declaración de intenciones. Espero que el contenido sea mas importante que la forma de este. Todo sea por la difusión técnica.

Using Arduino platform to build a subsampling SSB demodulator

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.

A Library for LTC1298, 12-bit Analog To Digital Converter

After the previous posts I wanted to do something more using Arduino and SPI Bus. Actually I’m working on a project in which I’m going to need an ADC and I need a little more precision than 10-bit ADC from Atmega328.

Looking for  a convenient ADC I found very interesting products in Linear Technology site. Mainly I was interested in LTC1298 and LTC1286 ADC. Both are similar products. Both are 12-bit, successive approximation sampling A/D converter and both are provided with SPI bus. But I like very much that there aren’t external components. Just the IC-to-IC connection.

I asked LT for free samples and a week later I get two LTC1298 in my home (Many thanks to Linear Technology). I tested with Bus Pirate and they just works fine.

The LTC1298 has two channel inputs (ch0 and ch1) that can be selected for the measure you want to. There are four options available:

  • Channel 0
  • Channel 1
  • Differential (Channel 0 – Channel 1)
  • Differential (Channel 1 – Channel 0)

Additionally, it’s possible to select the bit order in which the A/D converter sends the read value through SPI.

The Library

I wrote a simple library to use in Arduino with LTC1298. Basically I used the SPI functions I already wrote inside a new library with two new functions: One to initialize the communications (just calls the SPI_Init() function) and other to read the data from ADC.

The ltc1298_Read() function needs two parameters:

Mode:

  • CH0
  • CH1
  • DF0: Differential CH0-CH1
  • DF1: Differential CH1-CH0

Bit Order:

  • LSB: LSB First
  • MSB: MSB First

The function returns a 16bit value (But A/D is just 12-bit: 0x000~0xFFF).

To use the ltc1298 library after include it in your program you must to initialize the communication, placing ltc1298_Init() function somewhere into setup().

void setup(){
  ...
  ...
  ltc1298_Init();      /* Inicialize the comunication with LTC1298 */
  ...
  ...
}

Then, in your program you can read the Analog Data, specifying which channel are you reading from or how are you processing a differential channel measure. Give that data to a convinient variable space (at least 16bit wide).

void loop(){
   ...
   ...
   uint16_t Data_0=ltc1298_Read(CH0, MSB);
   uint16_t Data_1=ltc1298_Read(CH1, MSB);
   ...
   ...
}

Important note: In that library, is possible to specify the bit order (MSB or LSB). Is important using the same bit order in Master and Slave device.  At this time inside the Library the SPI configuration for Arduino is set to MSB. Due that, if you’re not changing the value in SPI configuration you must use MSB always.

You can Download the Library here.

Connecting

The connection between Arduino and LTC1298 uses a 4-wire SPI.

Arduino LTC1298
Pin Name Pin Name
10 SS 1 CS/SHDN
11 MOSI 5 DIN
12 MISO 6 DOUT
13 SCK 7 CLK

In LTC1298 Pin 4 and 8 are for supply connection (GND and +5V), and Pins 2 and 3 are the two analog channel inputs (CH0 and CH1).

Is very inportant to notice that Arduino pins from 10 to 13 are defined and used by Library to hold SPI Communication. So is not possible to give other use to those pins if your including the LTC1298 Library.

The Exemple Code

The following code can be found in Exemples folder, inside the Library.

/*
 *******************************************************
 *  Name:          LTC1298 Exemple    
 *
 *  Author:        Joan Martínez
 *
 *  Start Date:    1 Sept 2012        
 *
 *  Version Date:  1 Sept 2012 
 *
 *  Description:   Arduino as SPI Master controlling
 *                 an ADC LTC1298 from Linear Technology.
 *                 The library includes SPI driver. Take 
 *                 that in acount.
 *  Platform:      Arduino (Any...)
 *
 *******************************************************/

#include <ltc1298.h>

#define MEASURES 100

long int value;         /* Stores the read value */

void setup(){
  Serial.begin(9600);  /* Inicialize serial port */
  ltc1298_Init();      /* Inicialize the comunication with LTC1298 */
}

void loop(){
    /* Make 100 measures. Every read value 
    is added to the former one and then the 
    total is divided by 100. The result is 
    mapped and presented in a convenient way */

  value=0;
  for(int i=0; i<MEASURES; i++) value+=ltc1298_Read(CH0, MSB); /* ltc1298_Read() sends the 4-bit
                                   programming word for LTC1298
                                   and reads the Data from it */
  value=value/MEASURES;
  uint16_t newValue=map(value,0,4095,0,4899);
  uint8_t integerValue=newValue/1000;
  uint16_t decimalValue=newValue-1000*integerValue;
  Serial.print(integerValue);
  Serial.print('.');
  if(decimalValue<100) Serial.print('0');
  if(decimalValue<10) Serial.print('0');
  Serial.print(decimalValue);
  Serial.println(" V");  
  delay(1000);
}

The program measures CH0 from ADC 100 times and gives a mean value from it. That is a simple voltmeter.

Download the Library here.

Download LTC1298/LTC1286 Datasheet here.

Several Tests with Bus Pirate, Arduino and the DDS AD9837 (and IV)

In this post finally I’m going to design an oscillator using the DDS AD9837, digitally controlled by Arduino.

What I have done until now?

Before continue I feel that is interesting to look back and sumarize the previous posts.

  1. In the first part of the tests, I take the Bus Pirate and it was plugged together with the evaluation Board of DDS AD9837. I didn’t use arduino, because I wanted to focus on the DDS, the SPI protocol and how to configure Bus Pirate to hold SPI comunication with DDS.
  2. Then, in the second post of the serie, I implemented a slave device with the Arduino. I connected to Bus Pirate and I was able to sniffing data from it. In that way I build a tool to check the reliability of a future Master Device implemented with Arduino. Also improve my acknowledge about SPI.
  3. In the third part, I build the Master device to hold SPI communication. I discovered a problem regarding the pins definition in the arduino platform and I solved in a convenient way. Using the slave device designed before, I check the communication between both devices.

The complete system

So in this post I’m going to program a Master device to control and program DDS, while the slave sniffer will check the correctness of the data. A potentiometer will set the outputfrequency that canbe measured using a frequencymeter.
The following draft shows how everything must be connected.

The potentiometer is connected to the Analog Input 0 to read a voltage value. This value will be suitably converted into data sended for the SPI interface lines and this data will be processed for DDS and Sniffed by the Slave Arduino at once.

Both Arduinos provide information to user with the USB connected to terminal serial monitor. I usually do this job with Moserial.

The Code

The code is organiized in three different levels:

  1. The SPI Interface.
  2. The DDS control interface.
  3. The main control program.
/*
 *******************************************************
 *  Name:            DDS Oscillator Controller V2    
 *
 *  Author:        Joan Martínez
 *
 *  Start Date:         2 ago 2012        
 *
 *  Version Date:       3 ago 2012
 *
 *  Description:    Use of SPI Mode in ATmega as master
 *                      SCK: PB5(D13), MISO: PB4(D12),
 *                      MOSI: PB3(D11), SS: PB2(D10).
 *  Platform:           Arduino UNO, 2009,...
 *
 *******************************************************/

#define POT A0
#define F_MIN 1800
#define F_MAX 2200

/* SPI Pin Config.
 ---------------
 These are the pin configuration for Arduino with ATMEGA328, 
 ATMEGA168. To be sure about what the pins to use check the 
 documentation of your MCU */
#define DD_SS (1<<DDB2)
#define DD_SCK (1<<DDB5)
#define DD_MOSI (1<<DDB3)
#define DD_MISO (1<<DDB4)

uint32_t frequency, old_freq;
boolean out_on=false;

void setup(){
  Serial.begin(9600);
  DDS_Init();
  delay(1000);
  DDS_Reset(); 
  Serial.println("DDS Master Ready!!"); 
}

void loop(){
  frequency=Read_Pot();
  if(old_freq!=frequency){
    Serial.print(frequency);
    Serial.print("Hz : ");
    Set_frequency(frequency);
    old_freq=frequency;
    delay(200);
  }
}

uint16_t Read_Pot()
{
  /* Reads a vale form POT, changes the 
   value an return the value */
  int value;
  value=analogRead(POT);
  return map(value, 0, 1023, F_MIN, F_MAX);
}

void Set_frequency(uint32_t freq){
  PORTB &= ~DD_SS;
  Serial.print("[");  
  //DDS_Reset();
  //DDS_SetFrequency(freq);
  DDS_SetDirectFrequency(freq);
  //DDS_SetPhase(0);
  if(!out_on) {
    DDS_Start();
    out_on=true;
  }
  PORTB |= DD_SS;
  Serial.println("]");
}

/***********************
 *
 *          DDS 
 *
 ************************/

#define FREQ_MUL 16.777216f
#define PHSE_MUL 651.8986469f

#define RESET_WD 0x0100
#define START_WD 0x2000
#define SET_F_WD 0x2100
#define PHSE_MSK 0xC000
#define FREQ_MSK 0x4000

void DDS_Reset(){
  /* Resets output (not registers)
   output is held in middle value */
  SPI_Master16bTransmit(RESET_WD);
}

void DDS_Init(){
  /* Prepares Master Device 
   and stops DDS */
  SPI_MasterInit();
  DDS_Reset();
}

void DDS_Start(){
  /* Enables DDS Output */
  SPI_Master16bTransmit(START_WD);
}

void DDS_SetFrequency(uint32_t frequency){
  /* Sets a new frequency reseting 
   previously the output */
  uint32_t divisor=frequency*FREQ_MUL;
  SPI_Master16bTransmit(SET_F_WD);
  SPI_Master16bTransmit((divisor & 0x3fff) | FREQ_MSK);
  SPI_Master16bTransmit((divisor >> 14) | FREQ_MSK);
}

void DDS_SetDirectFrequency(uint32_t frequency){
  /* Sets a new frequency without 
   reseting the output */
  uint32_t divisor=frequency*FREQ_MUL;
  SPI_Master16bTransmit((divisor & 0x3fff) | FREQ_MSK);
  SPI_Master16bTransmit((divisor >> 14) | FREQ_MSK);
}

void DDS_SetPhase(uint16_t phase){
  /* Sets a new phase */
  /* NOT CHECKED: just zero phase */
  SPI_Master16bTransmit((uint16_t)(phase*PHSE_MUL) | PHSE_MSK);
}

/***********************
 *
 *          SPI 
 *
 ************************/

/* Master */

void SPI_MasterInit(void){
  /* Set SCK, MOSI & SS as outputs, left unchanged others
   (MISO must!!! be set as input as default of SPI master mode) */
  DDRB = DD_MOSI | DD_SCK | DD_SS;
  PORTB |= ((1<<DD_SS)|(1<<DD_MOSI) | (1<<DD_SCK));

  /* Set mode 2, Enable SPI, set clock, master mode and order */
  SPSR &= ~(1<<SPI2X);
  SPCR &= ~((1<<SPIE) | (1<<CPHA) | (1<<DORD)) ;  
  SPCR |=  (1<<SPE) | (1<<MSTR) | (1<<CPOL) | (1<<SPR1) | (1<<SPR0);
}

uint8_t SPI_MasterTransmit(uint8_t cData){
  SPDR=cData;
  Serial.print(" 0x");
  Serial.print(cData,HEX);  
  while(!(SPSR & (1<<SPIF)))
    ;
  return SPDR;
}

void SPI_Master16bTransmit(uint16_t wData){
  SPI_MasterTransmit(wData>>8);
  SPI_MasterTransmit(wData & 0xff);
}

The SPI part only has the functions that holds the SPI communication protocol. This functions are used by next level.

The functions od DDS are in charge of convert freqüency information in data to send by SPI.

With this structure the previous two parts can be used in diferent and more complex projects, because this is only a tinny part of whant can be done using the same devices .

In the top of the code is the main part that reads the potentiometerr value and gives a freqúency value to the other levels.

Changing values of F_MAX and F_MIN is possible to change the freqúency range that can be adjusted by the potentiometer connected to A0.

Conclussion

This series of posts has shown the method to use Arduino with SPI, learning in every step from the previuos step. Collecting the aknowledge from the begining I learn about Bus Pirate, a great tool despite some limitations, but a Must to get in the home lab. Also I discovered the DDS technology, something I already heard to talk about but never before face by my self.

Using the tools descrived it’s easy to get new ideas, new project. Now, it looks to me easy to design and build a function generator, controlled by PC, or a FSK/PSK Modem.

I hope these posts may help you to get inspiration in new projects.

Several Tests with Bus Pirate, Arduino and the DDS AD9837 (III)

The purpose in this post is to describe how to build a Master Device with the same configuration than the one we setup on the receiver side and described in the previous post. So, let’s continue this Serie!

Facing the problem

As I did before, I’m going to use the code used in the AVR Reference Manual for inizialize and perform a transmission in SPI. The original code looks like this:

void SPI_MasterInit(void)
{
   /* Set MOSI and SCK output, all others input */
   DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
   /* Enable SPI, Master, set clock rate fck/16 */
   SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

void SPI_MasterTransmit(char cData)
{
   /* Start transmission */
   SPDR = cData;
   /* Wait for transmission complete */
   while(!(SPSR & (1<<SPIF)))
;
}

If you try to use this code in your program you will see that doesn’t looks to run, because the progrom doesn’t recognizes neither DDR_SPI, DD_MOSI nor DD_SCK. Reading the manual page 168 you will find a important point:

DDR_SPI in the examples must be replaced by the actual Data Direction Register controlling the SPI pins. DD_MOSI, DD_MISO and DD_SCK must be replaced by the actual data direction bits for these pins. E.g. if MOSI is placed on pin PB5, replace DD_MOSI with DDB5 and DDR_SPI with DDRB.

That’s very important!! Even trying to use SCK and MOSI instead of DD_SCK and DD_MOSI it’s a problem using Arduino IDE. If you try, your program will be compiled and loaded into your board. But you cannot be succesfull with the comunication. Why? The answer is inside the pin definition used by Arduino. In pins_arduino.h file you can find something similar to this:

static const uint8_t SS   = 10;
static const uint8_t MOSI = 11;
static const uint8_t MISO = 12;
static const uint8_t SCK  = 13;

We cannot configure the Arduino in that way, due that pin definition.

The working code

To solve the problem there are many sollutions. The one I found more usefull is using the Atmel’s advice. That is, use the labels DD_SCK, DD_SS and DD_MOSI with a right pin definition.

/*
 *******************************************************
 *  Name:            SPI Master Test    
 *
 *  Author:        Joan Martínez
 *
 *  Start Date:         19 jul 2012        
 *
 *  Version Date:       1 ago 2012
 *
 *  Description:    Use of SPI Mode in ATmega as master
 *                      SCK: PB5(D13), MISO: PB4(D12),
 *                      MOSI: PB3(D11), SS: PB2(D10).
 *  Platform:           Arduino UNO, 2009,...
 *
 *******************************************************/

#define DD_SS (1<<DDB2)
#define DD_SCK (1<<DDB5)
#define DD_MOSI (1<<DDB3)
#define DD_MISO (1<<DDB4)

byte bData;

void setup(){
  Serial.begin(9600);
  SPI_MasterInit();
  Serial.println("Master Ready!!"); 
}

void loop(){
  if(Serial.available()){
   Serial.print('.'); 
   bData=Serial.read(); 
   Serial.print('.');

   //Drive the SS low.
   PORTB &= ~DD_SS;
   Serial.print("[0x");

   SPI_MasterTransmit(bData); 
   Serial.print(bData,HEX);
   //Drive the SS high
   PORTB |= DD_SS;
   Serial.println(']');
  }
}

void SPI_MasterInit(void){
  /* Set SCK, MOSI & SS as outputs, left unchanged others
     (MISO is set as input as default of SPI master mode) */
  DDRB = DD_MOSI | DD_SCK | DD_SS;
  PORTB |= ((1<<DD_SS)|(1<<DD_MOSI) | (1<<DD_SCK));

  /* Set mode 2, Enable SPI, set clock, master mode and bit order */
  SPSR &= ~(1<<SPI2X);
  SPCR &= ~((1<<SPIE) | (1<<CPHA) | (1<<DORD)) ;  
  SPCR |=  (1<<SPE) | (1<<MSTR) | (1<<CPOL) | (1<<SPR1) | (1<<SPR0);
}

byte SPI_MasterTransmit(byte cData){
  SPDR=cData;
  while(!(SPSR & (1<<SPIF)))
  ;
  return SPDR;
}

Of course, the code has been completed with a serial data monitoring.

Doing:

#define DD_SCK (1<<DDB5)

I’m creating a mask that notes the SCK pin overriden in PORTB. The same is done for the rest of SPI lines.

The scattered Serial.print() instructions inside the void loop() has been a good helping tool to find where bugs were. So I keep it this way, even if they look uggly inside the code.

Connecting Master and Slave

To check that everything is running let’s connect both, the Master and the slave Arduino, as is shown in the image

Black wire: GND
White wire: SCK
Yellow wire: MOSI
Blue wire: SS

Using any serial monitor is possible to send data from both sides. Master echoes data in HEX mode. Also slave shows SPI received data as HEX.