Question about a code I didnt understand I2C / SPI

Status
Not open for further replies.

Gary

Active member
Good Morning

I'm working on a project since a long time. I need a changeable frequency about 0 - 300 hz.

For a short discription. This is not the first project. The first one was very easy, works also with a signal generator, called AD9833. It was an Arduino Nano with the AD9833 and a SPI display. I used the following code for this project.

Code:
#include "Wire.h"

int FSYNC = 6;
int SDATA = 7;
int SCLK = 5;
unsigned long freq;     
byte wave=3;

void setup() {
  pinMode(FSYNC, OUTPUT);
  pinMode(SDATA, OUTPUT);
  pinMode(SCLK, OUTPUT);
  digitalWrite(FSYNC, HIGH);
  digitalWrite(SDATA, LOW);
  digitalWrite(SCLK, HIGH);
  Wire.begin();
  Serial.begin(38400);
  UpdateRegister(0x2100);  // Nach Application Note AN-1070 von Analog Devices
  UpdateRegister(0x50C7);
  UpdateRegister(0x4000);
  UpdateRegister(0xC000);
  UpdateRegister(0x2000);
  freq=10;
  
  UpdateFreq(freq, 0x2020); // Sinus 1000 Hz
}

void loop() {
int ser, len;
char buf[10];

 
UpdateFreq(freq, 0x2020);           // Rechteck
     

void UpdateFreq(long freq, int form){
// Die Frequenz Register schreiben.
long FreqReg;
unsigned int MSB, LSB;
   
  FreqReg = (freq * pow(2, 28)) / 25000000; // 25MHz Quarz
  if (form == 0x2020) FreqReg = FreqReg << 1;  // Rechteck 1/2 Frequenz
  MSB = (int)((FreqReg & 0xFFFC000) >> 14);
  LSB = (int)(FreqReg & 0x3FFF);
  LSB |= 0x4000;
  MSB |= 0x4000;
  UpdateRegister(0x2100); // Control Register, Reset Bit DB8 gesetzt
  UpdateRegister(LSB);    // Frequency Register 0 LSB
  UpdateRegister(MSB);    // Frequency Register 0 MSB
  UpdateRegister(0xC000); // Phase Register
  UpdateRegister(form);   // Exit Reset : Wellenform nach dem Reset
}

void UpdateRegister(unsigned int data){
// Ein DDS Register schreiben
unsigned int pointer = 0x8000;

  digitalWrite(FSYNC, LOW);
  for (int i=0; i<16; i++){
    if ((data & pointer) > 0) { 
      digitalWrite(SDATA, HIGH); 
    }
    else { 
      digitalWrite(SDATA, LOW); 
    }
    digitalWrite(SCLK, LOW);
    digitalWrite(SCLK, HIGH);
    pointer = pointer >> 1 ;
  }
  digitalWrite(FSYNC, HIGH);
}

This code was shortened…..


I tried to understand this code but I have a problem. The code included the Wire.h. But this AD9833 is for a SPI port. The pins are also not SPI (6, 5, 7). How is it possible that this code runs???


Now I want to take this part of this code to use it with an Teensy 4.0. But the code dont run on the Teensy….

The SPI port from the Teensy was already used. I cant use him for the AD9833.


Someone who could explain the code for me or could help to write this code for the Teensy 4.0


Sorry for my english. It's not my first language ;-)


Greetings

Gary
 
Quick forum search for the AD9833 led to this post and thread : Function-generator-using-teensy

That might be a working example to start with.

SPI is designed for multiple devices - the CS ChipSelect pin must be unique - but SCK (clock) and MOSI and MISO can be common.
 
Thanks defragster.

I saw this thread while searching for a way to program the project.

Do you have an explanation why the code runs? I dont understand why a code for I2c (wire.h) will start a tool for SPI.

I asked because this is an easy way to use three free pins of the Teensy for the AD9833.

Greetings
 
Hello Gary,

from my point of view:
- your shortened code will probably not run due typos
- wire lib is not used in code, the include at begin is useless
- the code (without typos) might run because communication to AD9833 seems to be hard coded in function UpdateRegister()
 
Ah. Ok.
This code was copied from the net. But only shortened a bit.

But why didnt it runs on Teensy 4.0? Is the Teensy to fast for this kind of programming without a clk?

Is there an option to adapt this code to the Teensy?

Thanks
 
The issue is probably timings:
That is if you look at the code:
Code:
void UpdateRegister(unsigned int data){
// Ein DDS Register schreiben
unsigned int pointer = 0x8000;

  digitalWrite(FSYNC, LOW);
  for (int i=0; i<16; i++){
    if ((data & pointer) > 0) { 
      digitalWrite(SDATA, HIGH); 
    }
    else { 
      digitalWrite(SDATA, LOW); 
    }
    digitalWrite(SCLK, LOW);
    digitalWrite(SCLK, HIGH);
    pointer = pointer >> 1 ;
  }
  digitalWrite(FSYNC, HIGH);
}
This code is a simple bitbang code that does a simple I2C like output, with the only timing here is how fast the code runs.
So needless to say the timing will be very different when you switch over from an 8 bit processor running at probably 16mhz to run it on the T4 running at 600mhz.

One way to fix it is to move the SCL/SDA pins over to pins that can do hardware I2C, like 18, 19 and use the wire library.

The other way would to be to figure out what speed you need these signals to run at, and insert some delay and or more likely delayMicroseconds in at key locations and see what it does. Something like:
Code:
void UpdateRegister(unsigned int data){
// Ein DDS Register schreiben
unsigned int pointer = 0x8000;

  digitalWrite(FSYNC, LOW);
  for (int i=0; i<16; i++){
    if ((data & pointer) > 0) { 
      digitalWrite(SDATA, HIGH); 
    }
    else { 
      digitalWrite(SDATA, LOW); 
    }
    digitalWrite(SCLK, LOW);
    delayMicroseconds(10);  // make sure there is a gap
    digitalWrite(SCLK, HIGH);
    delayMicroseconds(250);  // Adjust to get the timing you need... 
    pointer = pointer >> 1 ;
  }
  digitalWrite(FSYNC, HIGH);
}
 
This sounds like that my guess was right. Now I'm a step in front.

The programming is very difficult (but very interesting). I'm very fast at the end of my knowledge. ;-)

Thanks to all....
 
If you are bit banging SPI, it's all about the timing. Kurt showed how fix the existing code. Following on that, I would find the data sheet(s) for the device(s) you are working with. There should be some timing diagrams which will tell you how much time each pulse will need. Pay attention to minimums. In your case, I'm pretty sure it's the clock timing and there will be a diagram in the datasheet that shows how long the clock pulses need to be. Then, I would modify the delays (delayMicroseconds(10); ) in the code above to meet the datasheet requirements.

I think it's an important point to understand that all these protocols - SPI, I2C, OneWire, ... can be emulated ("bit banged") with just pins and code. The advantage of the hardware versions is you don't have to worry about timing as much. With bit banging, you have to be reasonably precise and have a solid understanding of the device. Frankly, I think an engineer/hobbyist/... should bit bang every protocol they use at least once to truly understand how it works. The hardware versions can be mystical black boxes and trying to figure out how they work can be an exercise in confusion, especially if you don't understand the basic protocol.
 
Last edited:
At this time I dont know what I'm doing. I never heard from things like bit banging and so on.
But I think I will learn a lot while programming my tool.

I have load the datasheet from the AD9833 but I only found out that there is a time about 10 nanosecond time.

In the evening I will try to play with the micros to get the AD9833 running.

But I'm happy to get such good ideas and tips from you. You are great. Thanks a lot....
 
Page 4 of the datasheet shows exactly what I was talking about. Sclk clock period is 25 nS minimum. Could be longer, no minimum is called out. You lower fsync, clock in 16 bits of data and raise fsync. The diagram shows the relationship of clock, fsync and data.

By the way, 10 microseconds looks to be ok. could be a lot less.
 
Thanks Phil.

I took a look at this site but I didnt see it. I'm blind. ;-)

This could be the way. I will try it later and will give a reply.
 
Good morning.

Yesterday I have tested a lot but there wasn't a result. I have taken the I2C ports 18 and 19. But the Oszi didn't Show any correct line.

Today I have checked the AD9833. This tool was ok.

My last plan was to take other digital Pins. And... The AD9833 runs on PIN 14 and 15. I took the delayMicroseconds(10).

But I don't understand what is different between 18, 19 and 14, 15. Maybe someone could give an realy short explaination. You've already helped enough.

Thanks….
 
Post #2 linked to SPI code with the same device name - and the post #13 data sheet says it is an SPI device. As noted the WIRE.h code is not used - it seems if it is the same device what worked on the linked thread should offer a working solution.
 
Thanks defragster.

I have a running solution on pin 14 and 15. My questions was why it doesn't work an 18 and 19. This are digital pins too.

But the answer isn't important for my project. It's only for understanding the Teensy.
 
It is hard to say what is going on without seeing your actual code you are running in both cases... Example maybe you have a stray line of code that was not edited to work with the same IO pin.

Or maybe someone does a call like Wire.begin()... Which then takes over pins 18 and 19 for I2C communications or ???
Note that was in your original code above, so would be my guess!

Or maybe picture showing setup. Like maybe IO pins are not soldered correctly or there is a solder jumper or many other possibilities.
 
Status
Not open for further replies.
Back
Top