AD9833 Success With TFT-Touch And T4.1

Arctic_Eddie

Well-known member
I have a curve tracer project that uses the T4.1, TFT-touch, and the AD9833 board. The library is the ILI9341-t3n. Everything worked OK until the AD9833 was added. Hardware SPI did not work as the board wanted mode 2. Also, neither of the AD9833 libraries would work. The software SPI was tried on various pins but with no success. Close examination of the AD9833.cpp file reveals that it did not actually use SPI but a straight forward bit-bang technique. There was no attempt to create any kind of timing of output so it ran as fast as possible. That probably worked fine on the Uno and related boards with 16MHz clocks. However, that's not the case with the T4.1 as it's way too fast. The solution was to slow down the output rate by inserting a time delay into the last line of six for{} loops. This essentially makes the rate 1MHz.

At the end of the file, there are two procedures that need an addition, AD9833::writeData(uint16_t data), and AD9833::writeData28(uint16_t LSB, uint16_t MSB). The first has two for{} loops and the second has four. Into each of these six loops, insert as the last line, delayMicroseconds(1); The first one is shown below. Once this is done then the board is compatible with the other objects.

Code:
    //  MSBFIRST
    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      uint8_t oldSREG = SREG;
      noInterrupts();
      if (data & mask) *_dataOutRegister |= outmask1;
      else             *_dataOutRegister &= outmask2;
      *_clockRegister &= cbmask2;
      *_clockRegister |= cbmask1;
      SREG = oldSREG;
      delayMicroseconds(1); // Slow down for T41 ************************
    }
 
Eddie:
Any chance you can show your complete project as I have an AD9833 board and was planning on using it for the same type of project?

Regards,
Ed
 
Unfortunately, the code is proprietary to the company for which it's written. I can describe in general terms anything you would like to know. At present, it's about 1200 lines but contains numerous serial.print statements as debug info. Most are commented out but remain for future work.

In general, the screen is numerically divided into an 8x8 grid of potential menu buttons with each one being 40w x 30h pixels. A pair of numbers describes the effective column(X) and row(Y) of a button. A button print statement will display any text that fits into that space with a border, background color, and text color. The main menu is a column of buttons down the left side. The remainder of the screen, 280x240 is the graph area with about 10 grid spaces in each axis but varies according to the data range. A configuration menu is selected off of main and printed as numerous horizontal rows with values in each column. The choices are saved in a ConfData structure and stored in EEPROM for recall at next boot. It also uses the internal SD card reader to store graph screenshots in the BMP format for product test documentation.

The driving signal for the curve trace comes from the AD9833 at several selectable frequencies. Data capture comes from the dual synced ADC capture where one samples DUT voltage and the other samples DUT current. The sampling rate is adjusted so the 280 points are captured in one cycle of the driving frequency. The analog part is on a separate board. The DUT trace can be saved to memory by finding the X/Y position of all pixels of a particular color. A recall function will put that data back on the screen in a different color so that two DUT results can be compared. That's when the screenshot is typically saved to the SD card for documentation.

This is the general construction of the project and gives you an idea as to what it does. I will be glad to answer any other questions but cannot divulge the actual code.
 
I am only interested in the AS9833 driver, nothing else.
Not really a driver but this is the code I used successfully an a Teensy LC:
C++:
#include <SPI.h>                      // pin 13 (SCK), pin 11 (MOSI), AD9833 generator
#define FSYNC 10                      // pin 10 (SS)
#define SPI_CLOCK_SPEED 12000000      // 12MHz SPI clock
unsigned long MCLK = 25000000;        // AD9833 onboard crystal reference frequency
unsigned long freq = 1000;            // set initial frequency

void setup() {
 
  pinMode (FSYNC, OUTPUT);
  digitalWrite(FSYNC, HIGH);
  SPI.begin();
  AD9833setFrequency(freq);           // set frequency
}

void loop() {
}

void AD9833setFrequency(long frequency) {
  long FreqReg = (frequency * pow(2, 28)) / MCLK;
  int MSB = (int)((FreqReg & 0xFFFC000) >> 14);    // only lower 14 bits are used for data
  int LSB = (int)(FreqReg & 0x3FFF);

  LSB |= 0x4000;                      // DB 15=0, DB14=1
  MSB |= 0x4000;                      // DB 15=0, DB14=1

  WriteRegister(0x2100);              // put AD9833 into reset and tell it to accept 14bit words (DB13=1, DB8=1)
  WriteRegister(LSB);                 // write lower 16 bits to AD9833 registers
  WriteRegister(MSB);                 // write upper 16 bits to AD9833 registers
  WriteRegister(0xC000);              // write phase register
  WriteRegister(0x2000);              // take AD9833 out of reset and output sinewave (DB8=0)
}

void WriteRegister(int data) {
  SPI.beginTransaction(SPISettings(SPI_CLOCK_SPEED, MSBFIRST, SPI_MODE2));
  digitalWrite(FSYNC, LOW);           // set FSYNC low before writing to AD9833 registers
  SPI.transfer16(data);
  digitalWrite(FSYNC, HIGH);          // write done, set FSYNC high
  SPI.endTransaction();
}

Paul
 
If the AD9833 is the only object on the SPI bus or all others are using mode 2 then the hardware version in Rob Tillaart's V0.4.2 library works fine. If there are other objects likely in mode 0 then the software approach is needed using a different set of pins. In addition, the changes to the library are also needed. Either way, the Tillaart library is fine.
 
Can you explain why the AD9833 library doesn't work if there are other SPI devices? It seems like as long as SPI.begin/endTransaction are used, it shouldn't matter if one device uses Mode 0 and another uses Mode 2. As long as only one device is selected at a time, as far as I know, what happens on the clock and data lines shouldn't matter to the other devices.
 
Paul or Kurt are certainly better at explaining this but it's related the idle level of the various SPI signals and there direction of switching. The following link shows the difference between the four modes. In my case, the AD9833.begin() statement left these signals in a state that nothing else beyond that point would work. Essentially, the sketch stopped because the next object was confused about what to with signal levels not compatible with it's own needs. The library author recognized this to be a problem so used the bit-bang method to make it work on separate pins. At that time, he was not aware that the future T4.1 would be too fast. However, when the AD9833 is the only object used, it works as the clock rates are controlled by the SPI driver. My first attempt to fix the problem was to set up SPI1 and put it in mode 2 but I was unsure of how to make the device use that bus and did not want to make extensive changes to the library. When I found the solution presently used, I added numerous comments above the #include statement explaining the situation. I'll still investigate the SPI1 solution on a different set of pins as I don't like modifying libraries.

SPI Details
 
OK folks, help needed. After looking at the library again, it appears that a custom hardware SPI can be declared. The problem is that I don't see a way of telling it to use SPI1. Here's what I have that compiles but does not work. I would really like to eliminate library modifications.

Code:
// Before setup()
AD9833 fgb( FGB_CS_PIN, &SPI1 );

// In setup()
SPI1.setMOSI( FGB_DA_PIN );
SPI1.setSCK( FGB_CK_PIN );
SPI1.setCS( FGB_CS_PIN );
SPI1.setDataMode( SPI_MODE2 );
SPI1.begin();
fgb.begin();

// Also needed but where
SPISettings fgb_settings = SPISettings( 8000000, MSBFIRST, SPI_MODE2 );
 
I tried the hardware version again and now it works. No idea why the first attempt was a failure in that the board would not respond. This code appears to be enough to get it running and eliminate the need for a modified AD9833.cpp file. I used the default settings for SPI1 from the T41 pinout card.

Code:
// Before setup()
#define FGB_MOSI_PIN  A12              // Function generator SDATA out pin, A12, MOSI1
#define FGB_SCK_PIN    A13              // Function generator SCLK pin, A13, SCK1
#define FGB_CS_PIN      A14              // Function generator FSYNC pin, A14, CS1
#define FGB_MISO_PIN A15               // Data in SDATA_IN in pin, A15, MISO1, but not used
AD9833 fgb( FGB_CS_PIN, &SPI1 );

// In setup()
SPI1.setMOSI( FGB_MOSI_PIN );
SPI1.setSCK( FGB_SCK_PIN );
SPI1.setCS( FGB_CS_PIN  );
SPI1.setDataMode( SPI_MODE2 );
SPI1.begin();
fgb.begin();
 
Back
Top