SPI Transactions Implementation

Status
Not open for further replies.

arturolaz

Member
Hi, I've been trying to implement SPI transactions on an ADE7753 library so I can share the SPI Bus with the nrf8001 breakout board from adafruit. I started from this code wich Paul posted on the adafruit forum
Code:
// http://forum.pjrc.com/threads/27069-Adafruit_nrf8001-and-SDFat-problem-on-teensy-3-1

#include <SPI.h>
#include <Adafruit_BLE_UART.h>
#include <SD.h>

#define SDCARD_CS         4
#define ADAFRUITBLE_REQ  10
#define ADAFRUITBLE_RST   9
#if defined(__AVR__) && defined(TEENSYDUINO)
  #define ADAFRUITBLE_RDY 5  // pin 5 on Teensy 2.0
#else
  #define ADAFRUITBLE_RDY 2  // pin 2 on Arduino Uno & Teensy 3.1
#endif
  
Adafruit_BLE_UART uart = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);


/**************************************************************************/
/*!
    This function is called whenever select ACI events happen
*/
/**************************************************************************/
void aciCallback(aci_evt_opcode_t event)
{
  switch(event)
  {
    case ACI_EVT_DEVICE_STARTED:
      Serial.println(F("Advertising started"));
      break;
    case ACI_EVT_CONNECTED:
      Serial.println(F("Connected!"));
      break;
    case ACI_EVT_DISCONNECTED:
      Serial.println(F("Disconnected or advertising timed out"));
      break;
    default:
      break;
  }
}

/**************************************************************************/
/*!
    This function is called whenever data arrives on the RX channel
*/
/**************************************************************************/
void rxCallback(uint8_t *buffer, uint8_t len)
{
  Serial.print(F("Received "));
  Serial.print(len);
  Serial.print(F(" bytes: "));
  for(int i=0; i<len; i++)
   Serial.print((char)buffer[i]); 

  Serial.print(F(" ["));

  for(int i=0; i<len; i++)
  {
    Serial.print(" 0x"); Serial.print((char)buffer[i], HEX); 
  }
  Serial.println(F(" ]"));

  /* Echo the same data back! */
  uart.write(buffer, len);
  
  
}

/**************************************************************************/
/*!
    Configure the Arduino and start advertising with the radio
*/
/**************************************************************************/
void setup(void)
{
  // first, bring both chip selects up to logic high
  // each library will make the pin an output, but
  // this prevents either device from "hearing" the
  // communication to the other's initialization.
  pinMode(ADAFRUITBLE_RDY, INPUT_PULLUP);
  pinMode(SDCARD_CS, INPUT_PULLUP);
  pinMode(14, OUTPUT);
  digitalWrite(14, LOW);  // test LED on pin 14
  delay(50);
  

  
  Serial.begin(9600);
  while(!Serial); // Leonardo/Micro should wait for serial init
  Serial.println(F("Adafruit Bluefruit Low Energy nRF8001 Callback Echo demo"));
  
  //SD.begin(SDCARD_CS);
  
  uart.setRXcallback(rxCallback);
  uart.setACIcallback(aciCallback);
   uart.setDeviceName("NEWNAME"); /* 7 characters max! */
  uart.begin();
}

/**************************************************************************/
/*!
    Constantly checks for new events on the nRF8001
*/
/**************************************************************************/
void loop()
{
  uart.pollACI();
}


void get_sd_card_files()
{
  Serial.println(F("Opening SD card to generate list"));
  Serial.print("EIMSK = ");
  Serial.println(EIMSK);
  if (SD.begin(SDCARD_CS)) {
    Serial.println(F("SD card is ok :-)"));
    //printDirectory(SD.open("/"), 0);
  } else {
    Serial.println(F("no SD card"));
  }
  Serial.print("EIMSK = ");
  Serial.println(EIMSK);
  //Serial.print("SPI.interruptMode = ");
  //Serial.println(SPI.interruptMode);
}


void printDirectory(File dir, int numTabs) {
   while(true) {
     
     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       //Serial.println("**nomorefiles**");
       break;
     }
     for (uint8_t i=0; i<numTabs; i++) {
       Serial.print('\t');
     }
     Serial.print(entry.name());
     if (entry.isDirectory()) {
       Serial.println("/");
       printDirectory(entry, numTabs+1);
     } else {
       // files have sizes, directories do not
       Serial.print("\t\t");
       Serial.println(entry.size(), DEC);
     }
     entry.close();
   }
}

I deleted the SD part and it runs without problem. So I started adding the ADE7753 code with the transactions implementation. This is my try.

Code:
#include <SPI.h>
#include <Adafruit_BLE_UART.h>
#include "ADE7753.h"


#define ADAFRUITBLE_REQ 8
#define ADAFRUITBLE_RDY 2     // This should be an interrupt pin, on Uno thats #2 or #3
#define ADAFRUITBLE_RST 7
#define ratioTraf (127/12.3)          //18.3
  
Adafruit_BLE_UART uart = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);


/**************************************************************************/
/*!
    This function is called whenever select ACI events happen
*/
/**************************************************************************/
void aciCallback(aci_evt_opcode_t event)
{
  switch(event)
  {
    case ACI_EVT_DEVICE_STARTED:
      Serial.println(F("Advertising started"));
      break;
    case ACI_EVT_CONNECTED:
      Serial.println(F("Connected!"));
      break;
    case ACI_EVT_DISCONNECTED:
      Serial.println(F("Disconnected or advertising timed out"));
      break;
    default:
      break;
  }
}

/**************************************************************************/
/*!
    This function is called whenever data arrives on the RX channel
*/
/**************************************************************************/
void rxCallback(uint8_t *buffer, uint8_t len)
{
  Serial.print(F("Received "));
  Serial.print(len);
  Serial.print(F(" bytes: "));
  for(int i=0; i<len; i++)
   Serial.print((char)buffer[i]); 

  Serial.print(F(" ["));

  for(int i=0; i<len; i++)
  {
    Serial.print(" 0x"); Serial.print((char)buffer[i], HEX); 
  }
  Serial.println(F(" ]"));

  /* Echo the same data back! */
  uart.write(buffer, len);
  
  // Triger card read if 'K" is in the buffer.
//  if (*buffer == 'K' || *buffer == 'k') {
//    get_sd_card_files();
//  }
}

/**************************************************************************/
/*!
    Configure the Arduino and start advertising with the radio
*/
/**************************************************************************/



void setup(void)
{
  
  
  // first, bring both chip selects up to logic high
  // each library will make the pin an output, but
  // this prevents either device from "hearing" the
  // communication to the other's initialization.
  pinMode(ADAFRUITBLE_RDY, INPUT_PULLUP);
  
  
  
  delay(50);

  
  
  Serial.begin(9600);
  while(!Serial); // Leonardo/Micro should wait for serial init
  Serial.println(F("Adafruit Bluefruit Low Energy nRF8001 Callback Echo demo"));
  

  //SD.begin(SDCARD_CS);
  
  uart.setRXcallback(rxCallback);
  uart.setACIcallback(aciCallback);
  uart.setDeviceName("NEWNAME"); // 7 characters max! 
  uart.begin();
}

/**************************************************************************/
/*!
    Constantly checks for new events on the nRF8001
*/
/**************************************************************************/
void loop()
{
  ADE7753 meter = ADE7753();
  
  uart.pollACI();
   
  
  meter.analogSetup(GAIN_1,GAIN_1, 0,0,0,0);  
  meter.setMode(0x0080);
  
  long v1,i1,e1,e2,e3,ae1,ae2,ae3,r1,r2,r3;
  float totEnergy = 0;

  float kv,ki,ke,ka,kt,f1,basetime;
  float voltage, current, energy, aparent, reactive, PF, litros;
  String typeLoad = "";
  int t1;
  int loopCounter = 1;
  char buffer[10];


  //Constantes de proporcionalidad.
  kv       = (ratioTraf)*VOLTDIV*(0.5/0x2851EC);    //(0.5/0x2851EC) From Datasheet                    
  ki       = CURRDIV*(0.5/0x17D338);  //(0.5/0x17D338) From Datasheet
  ke       = (10/16.0)*VOLTDIV*CURRDIV/4096.0; // 1/(2^12)
  kt       = CLKIN/8;  //period register, resolution x.y[us]/LSB -per bit-
  basetime = (1.0*NUMCYC)/100.0; // tiempo por el cual se acumula energia
 
  //--Medición de voltaje--
    //v1=meter.vrms();
    
    
    
    
  
  
 
 

  
}

The thing is, those meter.analogSetup(GAIN_1,GAIN_1, 0,0,0,0); meter.setMode(0x0080); functions only involve writing to some registers, but when I uncomment the meter.vrms() function (wich is a reading function), the bluetooth part just doesn't work anymore.

Here is the rest of the code involved



Code:
void ADE7753::analogSetup(char gain_ch1, char gain_ch2,char os_ch1,char os_ch2,char scale_ch1,char integrator_ch1){
  char pga = (gain_ch2<<5) | (scale_ch1<<3) | (gain_ch1);
  char sign = 0;
  char ch1os = 0, ch2os = 0;


  write8(GAIN,pga);//write GAIN register, format is |3 bits PGA2 gain|2 bits full scale|3 bits PGA1 gain

  //ch1 offset, sign magnitude and integrator    
  if(os_ch1<0){
    sign=1;
    os_ch1=-os_ch1;
  }
  ch1os = (integrator_ch1<<7) | (sign<<5) | os_ch1;
  write8(CH1OS,ch1os);

  //ch2 offset, sign magnitude (ch2 doesn't have integrator) and the offset applied is inverted (ie offset of -1 sums 1)
  if(os_ch2<0){
    sign=0;
    os_ch1=-os_ch1;
  }
  else{
    sign=1;
  }
  ch2os = (sign<<5) | os_ch2;
  write8(CH2OS,ch2os);
}


Code:
void ADE7753::write8(char reg, char data){
  SPI.beginTransaction(SPI_BLE_SETTINGS);
  enableChip();
  //we have to send a 1 on the 8th bit in order to perform a write
  reg |= WRITE;
  delayMicroseconds(10);
  SPI.transfer((unsigned char)reg);          //register selection
  delayMicroseconds(5);
  SPI.transfer((unsigned char)data);
  delayMicroseconds(5);
  disableChip();
  SPI.endTransaction();
}

Code:
void ADE7753::setMode(int m){
  write16(MODE, m);
}

Code:
void ADE7753::write16(char reg, int data){
  SPI.beginTransaction(SPI_BLE_SETTINGS);
  enableChip();
  char aux=reg;
  unsigned char data0=0,data1=0;
  reg |= WRITE;
  //split data
  data0 = (unsigned char)data;
  data1 = (unsigned char)(data>>8);

  //register selection, we have to send a 1 on the 8th bit to perform a write
  delayMicroseconds(10);
  SPI.transfer((unsigned char)reg);    
  delayMicroseconds(5);    
  //data send, MSB first
  SPI.transfer((unsigned char)data1);
  delayMicroseconds(5);
  SPI.transfer((unsigned char)data0);  
  delayMicroseconds(5);
  disableChip();
  SPI.endTransaction();
}
Code:
long ADE7753::vrms(){
  char i=0;
  long v=0;
  getVRMS();//Ignore first reading to avoid garbage
  for(i=0;i<20;++i){
    v+=getVRMS();
  }
  return v/20;
}

Code:
long ADE7753::getVRMS(void){
  long lastupdate = 0;
  ADE7753::getresetInterruptStatus(); // Clear all interrupts
  lastupdate = millis();
  while( !  ( ADE7753::getInterruptStatus() & ZX )  )   // wait Zero-Crossing
  {
  } 
  //        while( !  ( ADE7753::getInterruptStatus()  )  )   // wait Zero-Crossing
  //	{} 
  return read24(VRMS);
}

Code:
ADE7753::ADE7753() {

  pinMode(CS, INPUT_PULLUP);
  pinMode(CS,OUTPUT);
  
  digitalWrite(CS, HIGH);//disabled by default
  // SPI Init
  SPI.begin();


  // Serial.begin(9600);//enable serial port

  delay(10);
}

Code:
void ADE7753::enableChip(void){
  digitalWrite(CS,LOW);
}

void ADE7753::disableChip(void){
  digitalWrite(CS,HIGH);  
}

Maybe is just the way I add the transactions part to the ade7753 library but so far I have no clue, I hope anyone can help me, thanks.
 
Does the ADE7753 use SPI in other functions, besides write8 and write16? Does it ever read from the chip? Is there any other SPI usage which is not protected by transactions?
 
Does the ADE7753 use SPI in other functions, besides write8 and write16? Does it ever read from the chip?
Yes, sorry I forgot to include that part of the code. Read24 is used by vrms.

Code:
unsigned long ADE7753::read24(char reg){
  SPI.beginTransaction(SPI_BLE_SETTINGS);
  enableChip();
  unsigned char b2,b1,b0;
  delayMicroseconds(10);
  SPI.transfer(reg);
  delayMicroseconds(25);
  b2=SPI.transfer(0x00);
  delayMicroseconds(5);
  b1=SPI.transfer(0x00);
  delayMicroseconds(5);
  b0=SPI.transfer(0x00);
  return (unsigned long)b2<<16 | (unsigned long)b1<<8 | (unsigned long)b0;
  disableChip();
  SPI.endTransaction();
}

Is there any other SPI usage which is not protected by transactions?

I dont think so, here are the complete files.

View attachment ADE7753.cpp View attachment ADE7753.h
 
I included SPI.begin in the .cpp inside de ADE7753 function, should it be anywhere else?
Code:
ADE7753::ADE7753() {

  
  pinMode(CS, INPUT_PULLUP);
  pinMode(CS,OUTPUT);

  digitalWrite(CS, HIGH);//disabled by default
  // SPI Init
  SPI.begin();


  // Serial.begin(9600);//enable serial port

  delay(10);
}

Also, I have added it to the setup in the sketch, but it still doesn't work, thanks for your help Paul.

Code:
void setup(void)
{
  
  SPI.begin();
  // first, bring both chip selects up to logic high
  // each library will make the pin an output, but
  // this prevents either device from "hearing" the
  // communication to the other's initialization.
  pinMode(ADAFRUITBLE_RDY, INPUT_PULLUP);
  
  
  
  
  
  delay(50);

  
  
  Serial.begin(9600);
  while(!Serial); // Leonardo/Micro should wait for serial init
  Serial.println(F("Adafruit Bluefruit Low Energy nRF8001 Callback Echo demo"));

  

  //SD.begin(SDCARD_CS);
  
  uart.setRXcallback(rxCallback);
  uart.setACIcallback(aciCallback);
  uart.setDeviceName("NEWNAME"); // 7 characters max! 
  uart.begin();
}
 
Here is another attemmpt. I modified the ADE7753 class to include the SPI.begin as well as the SPI settings. I also included SPI.begin in the setup of the sketch, but it is still not working

Here is the code and the files are below.

Code:
ADE7753::ADE7753() {

  SPI.begin();
  pinMode(CS,OUTPUT);
  digitalWrite(CS, HIGH);//disabled by default
  // SPI Init
  SPI.setDataMode(SPI_MODE2);
  SPI.setClockDivider(SPI_CLOCK_DIV32);
  SPI.setBitOrder(MSBFIRST);
  delay(10);
}

Code:
void setup(void)
{
  
  SPI.begin();
  // first, bring both chip selects up to logic high
  // each library will make the pin an output, but
  // this prevents either device from "hearing" the
  // communication to the other's initialization.
  pinMode(ADAFRUITBLE_RDY, INPUT_PULLUP);
  delay(50);
  
  Serial.begin(9600);
  while(!Serial); // Leonardo/Micro should wait for serial init
  Serial.println(F("Adafruit Bluefruit Low Energy nRF8001 Callback Echo demo"));

  uart.setRXcallback(rxCallback);
  uart.setACIcallback(aciCallback);
  uart.setDeviceName("NEWNAME"); // 7 characters max! 
  uart.begin();
}
View attachment ADE7753.cpp View attachment ADE7753.h View attachment DemoQuickStartSPI2.ino
 
Before I look at this again (for the 3rd time), can you try adding pullup resistors on both CS signals.

http://dorkbotpdx.org/blog/paul/better_spi_bus_design_in_3_steps

Also, try the 2-resistor test on MISO. With both CS signals high, MISO should float to approx 1.66 volts due to the resistors.

These hardware problems can really cause a lot of trouble. The CS issue is easy to fix with resistors, and the MISO tri-state can at least be verified easily with only resistors and a voltmeter.
 
Ok, I tried the pull-up resistors (2.2k) and it still doesnt work. When both CS are high I get a reading of 2.37v on the MISO line. I assume this is ok as Im powering it from 5v right? Thanks Paul for dedicating some time to solve my problem.
 
When both CS are high I get a reading of 2.37v on the MISO line. I assume this is ok as Im powering it from 5v right?

How did you connect the 2 resistors to MISO? If one is to ground and the other to +5V, measure the 5V line. Is it really about 4.7 volts, instead of 5V?

Do you understand the purpose of this MISO test? That's important for understanding the results.
 
How did you connect the 2 resistors to MISO?

Just like the image in this link http://dorkbotpdx.org/blog/paul/better_spi_bus_design_in_3_steps

If one is to ground and the other to +5V, measure the 5V line. Is it really about 4.7 volts, instead of 5V?

Yes, I tested it again 4.894v on the power line and 2.439 on the MISO line while both CS are high

Do you understand the purpose of this MISO test? That's important for understanding the results.

Yes, when both CS are logic high the MISO on both slaves is HI-Z so the voltage reading on the line should be half VCC, that indicates that the slaves release the MISO line when not active.
 
I would think a pull up or pull down would be betternon miso. Leaving a cos pin to float can lead to much badness.
 
What does the transaction function do?
if I use it at a app level and try a device lob function call will this be blocked?
I am trying to get up and an the ethernet lib to work. Does the Trans fun need to be incorporated into the device library?
 
Status
Not open for further replies.
Back
Top