ESP32 SPI default config

Posts: 4
Joined: Mon Jan 13, 2020 7:23 am

ESP32 SPI default config

Postby dotbot » Mon Jan 13, 2020 7:42 am

Hello All
I’m using the SPI bus (VSPI) to communicate with an AD9833 chip and a TS7735 LCD display (via the arduino SPI lib), I have been able to communicate with each individually and I have also been able to communicate with the TS7735 and then the AD9833. However, I can’t communicate with the TS7735 after the AD9833, I believe that the AD9833 library I’m using is changing SPI configuration such that the TS7735 can no longer work.

Looking at the AD9833 library it uses the Arduino SPI library and the only configuration I can see is it sets the mode (SPI.setDataMode(SPI_MODE2);) I have tried setting it to all the other modes after the AD9833 communication has finished but I still can’t talk to the TS7735 after the AD9833. I have tried looking at the TS7735 library (Adafruit_ST7735.cpp and .h files) I’m using to see how it configures the SPI bus but can’t seem to find where it does it (I’m fairly new to Cpp and the ESP32).

I have also tried instantiating the TS7735 object after talking to the AD9833 which doesn’t help, for this reason, my thinking is that the TS7735 lib doesn’t set its SPI config but just works with the default (which gets changed when the AD9833 lib runs).

I would greatly appreciate it if someone could share some thoughts on what the issue might be or how I can find the SPI config that the ST7735 is using.

Below is the Code i'm using as well as the AD9833.cpp and AD9833.h file, the ST7735 lib can be found here

Code: Select all

#include <Arduino.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include "AD9833.h"
#include <SPI.h>

#define TFT_CS  5
#define TFT_DC  12
#define TFT_MOSI  23
#define TFT_SCLK  18
#define TFT_RST 14

#define squareWave 1
#define sineWave 0
#define triangleWave 2

const int FYSNC_Pin = 27;        //AD9833 CS pin  
const int ledPin1 = 13;          //debugging LED

long masterClock_freq = 24000000;
unsigned char currentMode = 0;
unsigned long frequency1 = 100; //starting freq
unsigned long phase = 0;

char buffer[32];

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);   //config pins for screen, screen CS is pin GPIO5
AD9833 sigGen(FYSNC_Pin, 24000000);                                                   //config CS and freq for AD9833 

void setup(void) {
  Serial.print("Hello! ST77xx TFT Test");

  // Use this initializer if using a 1.8" TFT screen:
  tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab

  uint16_t time = millis();
  time = millis() - time;

  Serial.println(time, DEC);

  // large block of text
  //drawtext_line1("Test Line 1", ST77XX_WHITE);
  drawtext_line2("Test Line 2", ST77XX_GREEN);
  pinMode(ledPin1, OUTPUT);
  pinMode(FYSNC_Pin, OUTPUT);
  digitalWrite(FYSNC_Pin, HIGH);

void loop() {
  digitalWrite(ledPin1, HIGH);
  digitalWrite(ledPin1, LOW);

  setOutput(frequency1, sineWave);              //Tell the AD9833 to change to a new frequency

  frequency1 += 500;
  if(frequency1 > 2000){
    frequency1 = 100;

  //Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);  //Used to test if reinstantiating ST7735 object fixed the issue
  drawtext_line3(buffer, ST77XX_RED);

void drawtext_line1(char *text, uint16_t color) {
  //tft.setCursor(10, 0);

void drawtext_line2(char *text, uint16_t color) {
  tft.setCursor(10, 25);

void drawtext_line3(char *text, uint16_t color) {
  tft.setCursor(10, 50);
  tft.setCursor(10, 50);

void setOutput(long frequency, int waveForm){
  //digitalWrite(FYSNC_Pin, LOW);          //not needed handled in AD9833 lib
  //digitalWrite(FYSNC_Pin, HIGH);

Code: Select all

#include <Arduino.h>
#include <SPI.h>
#include "AD9833.h"

AD9833::AD9833(int _FSYNC, unsigned long _mclk) {
  pinMode(FSYNC, OUTPUT);     // Set FSYNC pin as input
  mclk = _mclk;               // Set the oscillator used, for example 24MHz
  controlRegister = 0x2000;   // Default control register : FREQ0, PHASE0, Sine
  fqRegister = 0x4000;        // Default frequency register is 0
  pRegister = 0xC000;         // Default phase register is 0
  //SPI.begin();                // Initialise the SPI BUS
  //SPI.setDataMode(SPI_MODE2); // Set SPI in mode2, this should be moved in
  // methods when SPI.transfer is called in case you
  // have multiple devices using the SPI bus

void AD9833::writeData(int data) {
  // FSYNC pin must be pulled low when new data is received by AD9833
  digitalWrite(FSYNC, LOW);
  // Send the first 8 MSBs of data
  // Send the last 8 LSBs of data
  // Set the FSYNC pin to high then end SPI transaction
  digitalWrite(FSYNC, HIGH);
  // debugging
  if (debugging) {
    lcdDebug(String(data, HEX));

void AD9833::setFreq(unsigned long _freq) {
  // First check that the data received is fine
  unsigned long freqReg;
  // Frequency cannot be negative
  if (_freq < 0) {
    freqReg = 0;
    freq = 0;
  // If the frequency is more than maximum frequency, just set it to maximum
  else if (_freq > mclk) {
    freqReg = pow(2, 28) - 1;
    freq = mclk / 2;
  // If all is good, compute the freqReg knowhing that the analog output is
  // (mclk/2^28) * freqReg
  else {
    freq = _freq;
    freqReg = (freq * pow(2, 28)) / mclk;
  // Initialise two variables that are 16bit long which we use to divide the
  // freqReg in two words
  // set D15 to 0 and D14 to 1 to put data in FREQ0/1 register
  int MSW = ((int)(freqReg >> 14)) | fqRegister; // Take out the first 14bits
  // and set D15 to 0 and D14 to
  // 1 or viceversa depending on
  // FREQ reg
  int LSW = ((int)(freqReg & 0x3FFF)) |
            fqRegister; // Take only the last 14bits using a mask and set D15 to
  // 0 and D14 to 1 or viceversa depending on FREQ reg
  // Send the data, most significant word first
unsigned long AD9833::getFreq() {
  return freq;
void AD9833::setPhase(int _phase) {
  // Phase cannot be negative
  if (_phase < 0) {
    phase = 0;
  // Phase maximum is 2^12
  else if (_phase >= 4096) {
    phase = 4096 - 1;
  // If all is good, set the new phase value
  else {
    phase = _phase;
  // Extract the 12 bits from the freqReg and set D15-1, D14-1, D13-0, D12-X to
  // put data in PHASE0/1 register
  int phaseData = phase | pRegister;
  int LSW = (phase & 0x3FFF) | pRegister;
int AD9833::getPhase() {
  return phase;

void AD9833::setCtrlReg(unsigned long _controlRegister) {
  // Just make sure that the first two bits are set to 0
  controlRegister = _controlRegister & 0x3FFF;

int AD9833::getCtrlReg() {
  return controlRegister;

void AD9833::writeCtrlReg() {

void AD9833::sleep(int s) {
  switch (s) {

    case 0: {
        controlRegister &= 0xFF3F; // No power-down: D7-0 and D6-0
      } break;

    case 1: {
        controlRegister &= 0xFF7F; // DAC powered down: D7-0 and D6-1
        controlRegister |= 0x0040;
      } break;

    case 2: {
        controlRegister &= 0xFFBF; // Internal clock disabled: D7-1 and D6-0
        controlRegister |= 0x0080;
      } break;

    case 3: {
        controlRegister |=
          0x00C0; // Both DAC powered down and internal clock disabled
      } break;
  // Update the control register

void AD9833::reset(int r) {
  if (r == 0) {
    controlRegister &= 0xFEFF; // Set D8 to 0

  else if (r == 1) {
    controlRegister |= 0x0100; // Set D8 to 1
    SPI.begin();                // Initialise the SPI BUS
    SPI.setDataMode(SPI_MODE2); // Set SPI in mode2, this should be moved in

void AD9833::mode(int m) {
  switch (m) {
    case 0: {
        controlRegister &= 0xFFDD; // Output sine: D5-0 and D1-0
      } break;
    case 1: {
        controlRegister &= 0xFFDF; // Output triangle: D5-0 and D1-1
        controlRegister |= 0x0002;
      } break;
    case 2: {
        controlRegister &= 0xFFFD; // Output clock (rectangle): D5-1 and D1-0
        controlRegister |= 0x0020;
      } break;

void AD9833::setFPRegister(int r) {
  if (r == 0) {
    controlRegister &= 0xF3FF; // Set D11 and D10 in control register to 0
    fqRegister = 0x4000; // Set D15 to 0 and D14 to 1 in a variable that will
    // later choose the FREQ0 register
    pRegister =
      0xC000; // Set D15 to 1 and D14 to 1 and D13 to 0 for the PHASE register
  } else if (r == 1) {
    controlRegister |= 0x0C00; // Set D11 and D10 in control register to 1
    fqRegister = 0x8000; // Set D15 to 1 and D14 to 0 in a variable that will
    // later choose the FREQ1 register
    pRegister =
      0xD000; // Set D15 to 1 and D14 to 1 and D13 to 1 for the PHASE register

void AD9833::lcdDebugInit(LiquidCrystal_I2C *_debugger) {
  debugger = _debugger;
  debugging = true;

void AD9833::lcdDebug(String message) {
  if (debugging) {
    debugger->setCursor(0, 1);

Code: Select all

#ifndef AD9833_H
#define AD9833_H

#include <SPI.h>
#include "LiquidCrystal_I2C.h"

class AD9833
    //Initialise the AD9833
    //_FYNC is the pin on the uC where FYNC is connected
    //_mclk is the frequency of the crystal generator 
    AD9833(int _FSYNC, unsigned long _mclk);
    //Set and get for frequency
    void setFreq(unsigned long _freq);
    unsigned long getFreq();
    //Set and get for phase
    void setPhase(int _phase);
    int getPhase();
    //Set and get for control register
    void setCtrlReg(unsigned long _controlRegister);
    int getCtrlReg();
    //Send data to AD9833
    void writeData(int data);
    //Send the control register
    void writeCtrlReg();
    //Update the control register with appopiate values for sleep/reset/mode
    //s = 0 No power down
    //s = 1 DAC powered down
    //s = 2 Internal clock powered down
    //s = 3 Both DAC and internal clock powered down
    void sleep(int s);
    //r = 0 Disable Reset
    //r = 1 Enable Reset
    void reset(int r);
    //m = 0 Sine
    //m = 1 Triangle
    //m = 2 Clock (rectangle)
    void mode(int m);
    //r = 0 FREQ0 and PHASE0 are used
    //r = 1 FREQ1 and PHASE1 are used
    void setFPRegister(int r);
    void lcdDebugInit(LiquidCrystal_I2C *);
    void lcdDebug(String);

    int FSYNC;          //FSYNC Pin of AD9833 has to be connected to a GPIO pin
    unsigned long freq; //Output frequency
    int phase;          //Signal phase shift
    unsigned long mclk; //External oscillator frequency
    int fqRegister;    //Switch between Frequency and Phase register 1/0
    int pRegister;
    int controlRegister;
    LiquidCrystal_I2C *debugger; //Debugging stuff through I2C
    bool debugging;


Posts: 4
Joined: Mon Jan 13, 2020 7:23 am

Re: ESP32 SPI default config

Postby dotbot » Tue Jan 14, 2020 8:10 am

I have been able to solve the problem myself.

If i instatiated the TS7735 object using the default hardware SPI bus

Code: Select all

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
rather then the bit banged SPI

Code: Select all

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
I can get the desired behaviour.
Another case of RTFM

I'm still curious about what the default SPI config is and how the adafruit lib is setting up the SPI bus, if i can work it out i will post here in case anyone is interested.

Posts: 3
Joined: Wed Oct 31, 2018 10:48 pm

Re: ESP32 SPI default config

Postby YordanY » Tue Jan 21, 2020 12:02 am

I was just about to do something alike, and probably enter in the same situation. Anyway I am going to use HSPI for the AD9833 and VSPI for one of these 1.8" color displays.

Who is online

Users browsing this forum: bittailor and 18 guests