Leonardo Port Mapping Issue

Hello Arduino Forum!

My project is to parallel program the module AD9850.
I have chosen parallel connection for speed reasons, since I am experimenting with Frequency modulation of this cheap DAC's output. I tried serial programming but shiftingOut 40bits for a single change is quite time consuming with a 16MHz clock if we consider an audio signal sampled at 40kHz.

So after tried serial with shiftout() and digitalWrite() worked but the audio quality was not audiable :disappointed_relieved:.
Then i decided to go parallel with my Leonardo and found out that PORTD is not the same as UNO that all tutorials are working with.

Ok i don't bother that the pin numbers are not in series but PD5 is used by the TX led. So Leonardo does not have any 8bit ports available.

Is there a solution to have a complete 8bit PORT on leonardo or it is OK to desolder the TX led?

offTopic:
I would also use an opinion about SPI programming the AD9850. I know that SPI is faster than simple serial but never got in it.

Thanks in advance,
Zissis, Greece :slight_smile:

Sorry about the wrong topic, thank you for moderating!

Anyone having a clue with this issue???
Maybe parallel programming is not a good idea?
What are my other choices for loading the 40bit tuning word to this dac?

Thanks again!

I see two possible solutions for your problem.

  1. Use two half ports for the parallel operation: PD0-PD3 and PB4-PB7:
uint8_t value = 0x3F; // example
PORTD = PORTD & 0xF0 | value & 0x0F;
PORTB = PORTB & 0x0F | value & 0xF0;
  1. Use the SPI hardware to send the serial data. SPI in standard mode is 4MHz, so should almost get 100kHz frequency updates if you don't have to do expensive calculations in between. Using the hardware is quite simple with the SPI library.

Thank you very much for the response.!!

Well, I am trying to go on with the SPI Library.
This is my code so far but it doesn't output any frequency

pins connected as:

from ICSP
MOSI == Serial Data
SCK == W_CLK
RESET == RESET

D2 = FQ_UD (used digitalWrite at first to see if it is working)

#include <SPI.h>
#define TIMECTL_MAXTICKS 4294967295
#define DDS_CLOCK 125000000  

// Define ADC prescaler
const unsigned char PS_16 = (1 << ADPS2);
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

 long int tword;                            //the tuning word for the module 
 long int freq = 0;                         //a value for frequency 
 int mic = 0;                               //a valur for the input signal to use for modulation
 
 void setup(){
  SPI.begin();
  SPI.setDataMode(SPI_MODE1);
   pinMode (2,OUTPUT); 
  pinMode (A0,INPUT);                       //here comes the audio frequency in
                                            // set up the ADC
  ADCSRA &= ~PS_128;                        // remove bits set by Arduino library
  ADCSRA |= PS_32;                          // setting the ADC sampling frequency to 16mHz/32
}


void loop(){
  
//---------This has to be inside an interrupt to keep the audio sampling rate constant-----------  
  mic = analogRead(A0);                     // this analog read takes about 20?s to complete
  if (mic<512)                               //the microphone preamplifier bias is set with offset for ~2.5V idle output
 {freq = 27115000+70*mic;}
 else                                       //this can frequency modulate our carrier at about 35khz back and forth
 {freq = 27115000-70*mic;}
//---------------------------------------------------------------------------------------------- 
//freq = 27115000;
  tword = (freq * TIMECTL_MAXTICKS) / DDS_CLOCK;    //tuning word calculation
  digitalWrite(2,LOW);                                                         //fq_ud low
  SPI.setBitOrder(LSBFIRST); 
  SPI.transfer(tword);
  SPI.transfer(tword >> 8);
  SPI.transfer(tword >> 16);
  SPI.transfer(tword >> 24);
  SPI.transfer(0x00);
  digitalWrite(2,HIGH);
  
}

What I was using for simple serial communication before, was this function:

void SetFrequency(unsigned long frequency)
{
  unsigned long tuning_word = (frequency * pow(2, 32)) / DDS_CLOCK;
  digitalWrite (LOAD, LOW); 

  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 8);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 16);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 24);
  shiftOut(DATA, CLOCK, LSBFIRST, 0x00);
  digitalWrite (LOAD, HIGH); 
}

Is my code right?? I doubt that I send the tuning word correctly...

AD9850 datasheet: http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf

Is my code right?? I doubt that I send the tuning word correctly...

I'm not sure because I currently don't understand what exactly you're trying to achieve with your project.

But the following code doesn't really make sense:

#define TIMECTL_MAXTICKS 4294967295
long int tword;                            //the tuning word for the module 
tword = (freq * TIMECTL_MAXTICKS) / DDS_CLOCK;    //tuning word calculation

TIMECTL_MAXTICKS is defined bigger than the variable can hold values. Do you really now that a long int in Arduino has 32 bits, where one is used for the sign?

This code looks also wrong to me:

  mic = analogRead(A0);                     // this analog read takes about 20?s to complete
  if (mic<512)                               //the microphone preamplifier bias is set with offset for ~2.5V idle output
 {freq = 27115000+70*mic;}
 else                                       //this can frequency modulate our carrier at about 35khz back and forth
 {freq = 27115000-70*mic;}

From 0-511 you'll get an increasing frequency output while above 512 it's decreasing and from 511 to 512 there's a huge jump. If your applications is not very, very special, this is probably not meant so.

pylon:
I'm not sure because I currently don't understand what exactly you're trying to achieve with your project.

I am experimenting in frequency modulating the AD9850's output, using an audio signal sampled at around 40kHz.
However the simple serial communication is too slow to program this DAC in my case, so I am asking for faster ways to do it. Parallel is a bit difficult for my leonardo so I liked your opinion about using the SPI Library.

TIMECTL_MAXTICKS is defined bigger than the variable can hold values. Do you really now that a long int in Arduino has 32 bits, where one is used for the sign?

You are right. I already replaced the variable type to unsigned long which can hold the 32bit value (pow(2^32)).
so we have
unsigned long tword;
unsigned long freq;
but I am still having no output on the AD9850.

This code looks also wrong to me:

  mic = analogRead(A0);                     // this analog read takes about 20μs to complete

if (mic<512)                              //the microphone preamplifier bias is set with offset for ~2.5V idle output
{freq = 27115000+70mic;}
else                                      //this can frequency modulate our carrier at about 35khz back and forth
{freq = 27115000-70
mic;}




From 0-511 you'll get an increasing frequency output while above 512 it's decreasing and from 511 to 512 there's a huge jump. If your applications is not very, very special, this is probably not meant so.

Well it is wrong.!! Ok I need to add the carrier frequency with the difference between the 2.5v offset and the signal voltage at the A0.
But forget this part. For now (until we make the AD9850 work on SPI) we can use a stable frequency.

You are right. I already replaced the variable type to unsigned long which can hold the 32bit value (pow(2^32)).
so we have
unsigned long tword;
unsigned long freq;
but I am still having no output on the AD9850.

"unsigned long" is the same as "unsigned long int", so this change won't help. Do you really need numbers in that range? If your calculating in the 16 bit area and scaling up at the end? Simplified it would read:

uint32_t tword = freq * 3436 / 100;

This should fit in the 32 bits and is probably accurate enough.

Well thanks again for your response pylon. You have been very helpful to me!

After some thinking, reading and experimenting I tried the SPI library successfully but the proccessing time for each frequency change was something more than 300?sec.
So i came up and achieved parallel tuning of the AD9850 in around 8?S ( :astonished: :astonished: ) that is far better than everything else. Working fine with this code.:

float frequency = 45000000;  //initialize to 45MHz Listening to 90MHz fm radio 
uint64_t CLOCKFREQ = 125.000000e6; // 125MHz tweakable the DDS clock
uint64_t TWO_E32 = pow(2,32);
unsigned long tword = (unsigned long) frequency*TWO_E32/CLOCKFREQ;
byte W[5] = {0,0,0,0,0};
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //these are the prescalers for setting up the ADC sampling rate
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
int mic = 512;

void setup()
          {
            DDRD = 0xFF;                                   //The pins PD0 - PD7(leonardo pins 0,1,2,3,4,6,12,TXLED) are connected to D0 - D7 on AD9850
            DDRB = B01100000;                          //The pins PB5, PB6 (leonardo pins 9,10) are used for W_CLK and FQ_UD pins
            Serial.begin(115200);                          // only serial output works  since PORTD is all output (DDRD = 0xFF)
            pinMode (A0,INPUT);                       //here comes the audio frequency in
                                                       // set up the ADC
             ADCSRA &= ~PS_128;                        // remove bits set by Arduino library
            ADCSRA |= PS_32;  
            
          }

void loop()
{         int startime = micros();                          //start measuring time
          mic = analogRead(A0);                           // read the audio signal
         ** frequency = 45000000 + (50*(mic-512));          // calculating the modulation frequency 
         *** tword = frequency*TWO_E32/CLOCKFREQ;  //not sure if this is needed here 
         
              W[4] = (byte) tword;                             //converting tword to bytes
              W[3] = (byte) (tword >> 8);
              W[2] = (byte) (tword >> 16);
              W[1] = (byte) (tword >> 24);
              W[0] = 0;                                                   //phase zero
                 PORTB = B01000000;                     //pulse fu_ud
                 PORTB = 0x00;
                 PORTD = W[0];                                    //send w0
                 PORTB = B00100000;                     // pulse w_clk
                 PORTB = 0x00;
                 PORTD = W[1];                                     //send w1
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[2];                                     //send w2
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[3];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[4];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTB = B01000000;                    //pulse fu_ud to load the frequency
                 PORTB = 0x00;
      
          int stoptime = micros();                          //stop measuring time
          startime = stoptime - startime;           //calculate ?t
          Serial.println(startime);                          //send elapsed time to serial monitor
          //delay(10);
}

**concerning to a previous post, I think this calculation should be fine for the input signal to be converted to FM.
*** this line costs the program about 70 microseconds of time. Is it necessary? Is it possible to become faster or can be avoided because of the signing at the 4th line of the code?

These are better news this time,
Thanks again!

After some thinking, reading and experimenting I tried the SPI library successfully but the proccessing time for each frequency change was something more than 300?sec.

Then you've done something wrong. With standard speed (4Mbit/s) the transfer of 40bit needs a bit more than 10µs so if you managed to get it out parallel in 8µs the serial version should need 20µs at most.

his line costs the program about 70 microseconds of time. Is it necessary? Is it possible to become faster or can be avoided because of the signing at the 4th line of the code?

That's because of this:

float frequency = 45000000;  //initialize to 45MHz Listening to 90MHz fm radio

If you want to go for speed never use a float. Especially not if your calculation is in integer and you can cancel by some power of ten easily. Did you read my previous post? Did you understand it?

If you want to go for speed never use a float. Especially not if your calculation is in integer and you can cancel by some power of ten easily. Did you read my previous post? Did you understand it?

Yes, i read the previous post but did not understand a lot...
I assume that if i sign frequency as a uint32_t the calculation may be even faster?? Sorry about misunderstanding but i am a mech engineer so i don't get much about bits...
What do you mean by cancelling some power of ten??

I also made a small video to show you what i achieved.

Thanks again!

Yes, i read the previous post but did not understand a lot...

In this case you should ask and not just ignore it.

I assume that if i sign frequency as a uint32_t the calculation may be even faster?

Yes. The ATmega328p processor of an Arduino is an 8bit microcontroller, most of it's commands only handle one byte of information. If you make a calculation with 32bit integers the processor has to do 4-16 internal calculations to get the result. If it has to do the same with a float (which also needs 32bits in memory) it's a lot more because the floating point number calculation has to be emulated using simple 8bit integer calculations.

What do you mean by cancelling some power of ten??

That's what I have done in that post. If you write down what calculation has to be done:

#define DDS_CLOCK 125000000
#define TIMECTL_MAXTICKS 4294967295
tword = (freq * TIMECTL_MAXTICKS) / DDS_CLOCK;

or just directly: tword = freq * 4294967295 / 125000000 (the braces aren't needed). These numbers are to big for 32bit integers (the max number there is about 2 billion), so we try to make the smaller. If we calculate 4294967295 / 125000000 we get 34.35973836. So you could have written the formula as tword = freq * 34.35973836 but that would be a float calculation and that's very slow as you now know. So I thought that tword = freq * 3436 / 100 (which is the same as freq * 34.36 but in integer arithmetics) you get a result that is exact to about 0.03 percent, which is probably fine in your application. This calculation should fit into 32bits and is much faster than a float calculation. "twords" needs to be a uint32_t as well as "freq" does.

Regarding the serial transfer, please post the code you used for the SPI version and show or describe how you measured the 300µs.

pylon:
Yes. The ATmega328p processor of an Arduino is an 8bit microcontroller, most of it's commands only handle one byte of information. If you make a calculation with 32bit integers the processor has to do 4-16 internal calculations to get the result. If it has to do the same with a float (which also needs 32bits in memory) it's a lot more because the floating point number calculation has to be emulated using simple 8bit integer calculations.

got that!! 8)

So I thought that tword = freq * 3436 / 100 (which is the same as freq * 34.36 but in integer arithmetics) you get a result that is exact to about 0.03 percent, which is probably fine in your application. This calculation should fit into 32bits and is much faster than a float calculation. "twords" needs to be a uint32_t as well as "freq" does.

Well very clever thought.!! My two cents.
But as I compiled with my variables as uint32_t, didn't work as expected. The program didn't send the tword correctly until i changed the freq (only) to uint64_t. Why is that? I am pasting again the whole code:

uint64_t frequency = 45000000;  //initialize to 7MHz 
uint32_t tword = frequency * 3436 / 100;
byte W[5] = {0,0,0,0,0};
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
int mic = 512;

void setup()
          {
            DDRD = 0xFF;
            DDRB = B01100000;
            //Serial.begin(115200);
             pinMode (A0,INPUT);                       //here comes the audio frequency in
                                                       // set up the ADC
            ADCSRA &= ~PS_128;                        // remove bits set by Arduino library
            ADCSRA |= PS_32;  
          }

void loop()
{       //  int startime = micros();
         mic = analogRead(A0);
        frequency = 45000000 + (50*(mic-512));
        tword = frequency * 3436 / 100;
              W[4] = (byte) tword;
              W[3] = (byte) (tword >> 8);
              W[2] = (byte) (tword >> 16);
              W[1] = (byte) (tword >> 24);
              W[0] = 0; //phase zero
                 PORTB = B01000000;
                 PORTB = 0x00;
                 PORTD = W[0];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[1];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[2];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[3];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTD = W[4];
                 PORTB = B00100000;
                 PORTB = 0x00;
                 PORTB = B01000000;
                 PORTB = 0x00;    
          // int stoptime = micros();
          //startime = stoptime - startime;
          //Serial.println(startime);
          //delay(10);
}

Regarding the serial transfer, please post the code you used for the SPI version and show or describe how you measured the 300µs.

This is the old code. I haven't corrected the variables issue but this is the way i measured it. Reading micros() before, after, and substract them, then print the ?t to serial.

#include <SPI.h> 
#define FQ_UD 10

float frequency = 7000000.0;  //initialize to 7MHz 
uint64_t DDS_CLOCK= 125.000000e6; //tweaked in comparison to my HF rig, nominally 125MHz.
uint64_t TWO_E32 = pow(2,32);
unsigned long tword= frequency * TWO_E32/DDS_CLOCK;

void setup()
          {
            pinMode(FQ_UD,OUTPUT);      
            digitalWrite(FQ_UD,LOW);
            SPI.setDataMode(SPI_MODE0);   
            SPI.setClockDivider(SPI_CLOCK_DIV4);
            SPI.setBitOrder(LSBFIRST);
            SPI.begin();
            Serial.begin(115200);
          }
void loop()
{
  int startime = micros();
 writefreq(frequency,FQ_UD);
 int stoptime = micros();
startime = stoptime - startime;
Serial.println(startime);
delay(1000);
}
void writefreq(long freq, int chip_select) 
     { 
       tword= (unsigned long) freq*TWO_E32/CLOCKFREQ; 
       byte W[5] = {0,0,0,0,0};
       W[0] = (byte) tword;
       W[1] = (byte) (tword>> 8);
       W[2] = (byte) (tword>> 16);
       W[3] = (byte) (tword>> 24);
       W[4] = 0; //phase zero
       digitalWrite(chip_select,HIGH);
       digitalWrite(chip_select,LOW);
       for (int j = 0; j<5;j++)
           {
             SPI.transfer(W[j]);
           }
       digitalWrite(chip_select,HIGH);
       digitalWrite(chip_select,LOW);
      }

But as I compiled with my variables as uint32_t, didn't work as expected. The program didn't send the tword correctly until i changed the freq (only) to uint64_t. Why is that? I am pasting again the whole code:

I misread the base frequency, I thought it to be in the thousands not millions.
But you can cancel even more, so you will stay within 32bit:

        frequency = 900000 + (mic-512);
        tword = frequency * 1718;

Then you can use uint32_t as the type of frequency and tword.

BTW, I think you still have comments that are not correct anymore:

uint64_t frequency = 45000000;  //initialize to 7MHz

In your SPI code the four calls to digitalWrite() is what costs you too much. Replace it with the direct port manipulation code you use in the parallel version you should see much better timings.

pylon:
I misread the base frequency, I thought it to be in the thousands not millions.
But you can cancel even more, so you will stay within 32bit:

        frequency = 900000 + (mic-512);

tword = frequency * 1718;



Then you can use uint32_t as the type of frequency and tword.

Yes, but since i am going to use this set as a VFO and radio applications accurate frequencies may need to be calculated. Whouldn't this make my frequency changing step wider.?

BTW, I think you still have comments that are not correct anymore:

uint64_t frequency = 45000000;  //initialize to 7MHz

:grin: sorry about that...

In your SPI code the four calls to digitalWrite() is what costs you too much. Replace it with the direct port manipulation code you use in the parallel version you should see much better timings.

No. I tried with port manipulating there too but i only won around 30-40μs. Overall timing for each load after that was ~300μs. Thus there were faults on sending the frequency to AD9850. With parallel, the output signal is clearer than ever!

Yes, but since i am going to use this set as a VFO and radio applications accurate frequencies may need to be calculated. Whouldn't this make my frequency changing step wider.?

The result of the calculation should be exactly the same, I just calculated parts of it before writing the code, the calculation is even faster without loosing accuracy.

The calculation is as follows:
tword = (45000000 + 50 *(mic-512)) * 3436 / 100
now we cancel by 50:
tword = (900000 + 1 * (mic-512)) * 3436 / 2
calculate the last part:
tword = (900000 + mic - 512) * 1718

As you can see you never loose precision.

No. I tried with port manipulating there too but i only won around 30-40?s. Overall timing for each load after that was ~300?s. Thus there were faults on sending the frequency to AD9850.

I see that 300µs is too long but I cannot see where that time is lost. The only idea I had was that a timer interrupt was handled during this calls but should also not last over a hundred microseconds. The hardware needs 10µs to shift out the bits. A few microseconds are lost in the calls to the library but the rest is identical to the parallel version (or should be).

pylon:
The result of the calculation should be exactly the same, I just calculated parts of it before writing the code, the calculation is even faster without loosing accuracy.
The calculation is as follows:
tword = (45000000 + 50 *(mic-512)) * 3436 / 100
now we cancel by 50:
tword = (900000 + 1 * (mic-512)) * 3436 / 2
calculate the last part:
tword = (900000 + mic - 512) * 1718
As you can see you never loose precision.

Well thank you very very much pylon!
by this way the whole modulating takes around 44?s after all. Now we have a nice software defined FM transmitter for under 5$ that works perfect inside my room. However the project is not just that. AM modulation works really well by following this paper: http://www.analog.com/static/imported-files/application_notes/AN-423.pdf
There we have an excellent SSB exiter for radio frequencies up to 50mhz.

So what I need to concern now is the reciever part. I am thinking of this chip for FM recieving, http://www.discriminator.nl/ic/mc3362.pdf
which has also an FSK detector built-in.
And AM/SSB recieving is also simple with analog electronics. Then processing the incoming signal with software might be an other matter.
I really need to thank you again for your time spent. You are helping me so much!

I see that 300µs is too long but I cannot see where that time is lost. The only idea I had was that a timer interrupt was handled during this calls but should also not last over a hundred microseconds. The hardware needs 10µs to shift out the bits. A few microseconds are lost in the calls to the library but the rest is identical to the parallel version (or should be).

I will try again the SPI interface but this time moving the function inside the loop. I think that the signing of the W[5] table and the unsigned long takes too much time for each function process. I will post back my findings.

I like your parallel programming very much, actually seeking for a clean
routine. Have a look at: http://qrz.com/db/PE1MXP
Have there also some DDS 9850 C++ software linked for beacon usage.