Maybe updates for: SPI, Wire, ili9341_t3 libraries...

Status
Not open for further replies.

KurtE

Senior Member+
I have been playing around with some of the core libraries and either have outstanding Pull requests are potentially soon will generate additional Pull requests. Wondering about what people thought and or if anyone would like to test out some of these, such that maybe we can get some of them into a beta release when hopefully Paul is not under the gun to get a new release out as to match a new Arduino IDE...

Some of the changes include:

SPI: SPI.transfer(buf, cnt) - goes a lot faster as it keeps the queue full using 16 bit writes... Currently this is PR: #23

Wondering if it would make sense to maybe create some new versions of transfer methods that maybe allow you to make use of the hardware CS pins without having to know about PUSHR?

Also wondering if it also would make sense to merge (or migrate) some of the functionality in ILI9341_t3 library into may SPI? I did so with my SPIN C++ classes, so had versions of the wait for fifo not full, wait for fill empty... Also in SPIN is functions like: is this pin a MISO pin, or MOSI or SCLK, like the CS pins. That way in theory when new boards are introduced you just need to update in the main library. Also if your code can then be adapted to something like SPIN where you pass in which buss to use, the underlying library takes care of knowing which pins are valid.

ILI9341_t3: - Merge in some of the things from my ILI9341_t3n library - Have not issued PR yet, code changes up at: https://github.com/KurtE/ILI9341_t3/tree/Merge-Bounds-offsets-_T3N

Changes include:
Some of the changes done be blackketter in PR13 - Clip Rectangle, and setting drawing offsets
Draw Opaque Font characters - I re-implemented this to work like Opaque system font draw
Can still function with only one hardware CS pin (must be DC).
Also can function if MISO is not valid - but read functions will fail...

What I did not merge in from my ILI9341_t3n library includes: my ability to turn on a logical frame buffer on T3.5/6 (Note: I am not using the DMA stuff here). Also did not merge in my ability to work on different SPI busses, as this uses my SPIN library, which is not part of the normal system.

Wire: I have a version of Wire library, which again I have not done a PR on. I actually have 2 versions more in a second. But the later version is up at:
https://github.com/KurtE/Wire/tree/SUpport-Wire123-Malloc

These versions created a base class for Wire and I created subclasses for Wire0 (Wire), Wire1, Wire2, Wire3. I mentioned above 2 versions. The first version, simply created the objects with all of the data. But as each of these have default 2*32 byte queues, each was eating up something like 80+ bytes. So when T3.2 and TLC have two of these, was eating up 160+ bytes, 240 on T3.5 and 320 on T3.6. So second version, put most of the data into a structure that only gets allocated if you call certain apes, like begin. Obviously if you are using a TLC and you do a begin on both Wire and Wire1, it will use that amount of memory, but that is as expected... But if you only use one of them you are only paying the price for one of them...

The Wire library still needs some testing before I issue a PR. Will explain more about what I have done in the next posting.

Thoughts?
 
What I have done so far for testing these changes to Wire:

I created a version of the Adafruit_SSD1306 library (https://github.com/KurtE/Adafruit_SSD1306/tree/Wire!/2/3) that allows me to pass in a reference to which Wire object to use for a display object.

So I have a version of their test program: ssd1306_128x64_i2c, which makes use of this. I only have one of these displays so I have not tried using multiple ones in the same program.
But currently in my test program I have:
Code:
Adafruit_SSD1306 display(OLED_RESET, Wire2);
And the display works on the Wire2 pins (3/4). Note: I have a different branch of this library that has similar changes to work with i2c_t3 library.

Other Test:
I have a master/Client test program, that tests both client and master code in the same program. The master code tries to logically query registers from the client and also updates them...
Currently this program looks like:
Code:
// Quick and dirty I2c Master/Slave combination test.

#define SLAVE_ADDR 8
#include <Wire.h>
// Lets try alternate pins for Wire
#define SDA0 34
#define SCL0 33
#define SLAVE_I2C  Wire3
uint8_t *buffer;
#define MASTER_I2C  Wire
#define REG_COUNT 8
extern void requestEvent();
void setup()
{
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  while (!Serial && (millis() < 3000)) ;
  Serial.begin(115200);
  delay(250);
  buffer = (uint8_t*)malloc(32);
  // Quick check to see if we can change pins
  Wire.setSCL(SCL0);
  Wire.setSDA(SDA0);

  // Start up both Master and save SPIs
  Serial.println("Before Master begin");
  MASTER_I2C.begin(); // join i2c bus (address optional for master)
  Serial.println("Before Slave Begin");
  SLAVE_I2C.onReceive(receiveEvent);  // register a slave even handler
  SLAVE_I2C.onRequest(requestEvent);
  SLAVE_I2C.begin(SLAVE_ADDR);
  pinMode(13, OUTPUT); 
  digitalWrite(13, LOW);
}

uint8_t vals_to_write = REG_COUNT;

void loop()
{
  digitalWrite(13, HIGH);
  for (uint8_t i = 0; i < REG_COUNT; i++) {
    digitalWrite(0, HIGH);
    uint8_t reg_val = ReadReg(i);
    digitalWrite(0, LOW);
    Serial.printf("Reg %d = %d\n", i, reg_val);
  }

  // lets try updating all of them...
  Serial.println("Update values");
  for (uint8_t i = 0; i < REG_COUNT; i++) {
    digitalWrite(0, HIGH);
    WriteReg(i, vals_to_write++);
    digitalWrite(0, LOW);
  }
  digitalWrite(13, LOW);

  
  Serial.println("\nHit any key to repeat");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ; // get rid of other data.
}

//==========================================================================================
// Some master request or set register functions
uint8_t ReadReg(uint8_t reg) {
  MASTER_I2C.beginTransmission(SLAVE_ADDR); // transmit to device #8
  MASTER_I2C.write(0);              // Do a read
  MASTER_I2C.write(reg);
  MASTER_I2C.write(1);              // one byte
  MASTER_I2C.endTransmission();    // stop transmitting

  // Then request data from other side
  if (MASTER_I2C.requestFrom(SLAVE_ADDR, 1)) { // Try to get the data back from the other side
    return MASTER_I2C.read(); // read in the result and return it.
  }
  return 0xff;
}

void WriteReg(uint8_t reg, uint8_t val) {
  //Serial.printf("Write Reg %d = %d\n", reg, val);
  MASTER_I2C.beginTransmission(SLAVE_ADDR); // transmit to device #8
  MASTER_I2C.write(1);              // Do a read
  MASTER_I2C.write(reg);
  MASTER_I2C.write(val);              // one byte
  MASTER_I2C.endTransmission();    // stop transmitting
}

//===========================================================================================
// Some slave registers and state values
uint8_t registers[REG_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7}; // some dummy registers to play with.
uint8_t  read_write = 0;
uint8_t  start_reg = 0;
uint8_t  cnt_reg = 1;

void receiveEvent(int count_bytes)
{
  digitalWrite(1, HIGH);
//  Serial.println("Receive");
  read_write = SLAVE_I2C.read();  // first byte is command
  if (count_bytes > 1) start_reg = SLAVE_I2C.read();
  if (read_write) {
    while (SLAVE_I2C.available()) {
      registers[start_reg] = SLAVE_I2C.read();
      //Serial.printf("RcE: %d %d\n", start_reg, registers[start_reg]);
      start_reg++;
    }

  } else {
    // read mode
    if (SLAVE_I2C.available()) {
      cnt_reg = SLAVE_I2C.read();
    }
  }
  digitalWrite(1, LOW);
}

void requestEvent()
{
  digitalWrite(1, HIGH);
//  Serial.println("Request");
  // Host is asking for data.
  for (uint8_t i = 0; i < cnt_reg; i++) {
    //Serial.printf("RQE: %d %d\n", start_reg, registers[start_reg]);
    SLAVE_I2C.write(registers[start_reg++]);
  }
  digitalWrite(1, LOW);
}
I still need to try this one out more still with different combinations of which buss is master and which is client...

Note: I mentioned in another thread that I was debugging a timing issue. What I was running into was in this loop:
Code:
 for (uint8_t i = 0; i < REG_COUNT; i++) {
    digitalWrite(0, HIGH);
    uint8_t reg_val = ReadReg(i);
    digitalWrite(0, LOW);
    Serial.printf("Reg %d = %d\n", i, reg_val);
  }
That I was seeing longer delays and some times timeouts, especially after I finished this loop and then started trying to set registers.

What I found was when the ReadReg is called it does a transmission to the client of 3 data bytes so 4 bytes including the address and then it does a requestFrom from the client...

When was not working at added a Serial.print to the start of requestFrom and then the transmissions worked fine. Commented out the print, got the delays/timeouts... So I cached the status of the S register before the Serial.print and after and found that the BUSY flag was on before but not after the call to Serial.print. Looking at the Logic Analyzer it looked like maybe was called before the Stop bit was processed...

So I removed the Serial.print and added some code to wait until not busy:
Code:
	// BUGBUG: see if I can loop a little 
	uint8_t delay_count = 0xff;
	while (delay_count-- && (i2c_status(kinetisk_pi2c) & I2C_S_BUSY)) ;

       // Needs to be done before we clobber the status line
	kinetisk_pi2c->S = I2C_S_IICIF | I2C_S_ARBL;

Now it appears like it is working fine... Not sure if there is a better way.

Thoughts?
 
Quick Wire/I2C update:

Earlier today, I verified I could use the SSD1306 using my library changes (Wire and Adafruit_SSD...) and tried a display on T3.6 using the 4 different I2C busses. Also tried on T3.2 on Wire1, likewise on T-LC on Wire1 and Wire. Also fixed compile issue that it would not build on T3.0 and likewise for T2.0.

I then rebased --interactive and squashed all of the changes into 1. Then issued a Pull request: https://github.com/PaulStoffregen/Wire/pull/3
So Paul can take a look and decide if it is appropriate to pull in...

Since the final push for me to try this again came from the wanting multiple IMU's thread, I decided to quickly make versions of the two Polo libraries(lsm303 and l3g) that were being used, such that I could pass in which Wire buss to use to the constructor.

The changes were pretty simple. I simply added a new parameter to the constructor to be a reference to my Wire base class and default to Wire object. (Needed to add an include of Wire.h in the header files ) The constructor than saved this reference into a member variable _WireD.
I then did a simple Search and replace Wire. -> _Wired. and then made sure I did not change the #include <Wire.h> to _WireD.h

I have not tried running these updated libraries, but did verify I could build without changing the Example Program and also could still compile when I updated an object to pass in a Wire object, example:
Code:
LSM303 compass(Wire2);

This is probably the last I will do on this change for awhile unless something comes up...
 
do the changes also include the bus recover? it works way better now but i still, although rarely, get a lockup
 
do the changes also include the bus recover? it works way better now but i still, although rarely, get a lockup
Probably not? My updates to the Wire library are simply duplicated the basic functionality of the Wire object when appropriate to have Wire1 and likewise Wire2 and Wire3.

It does not add in any of the additional functionality that i2c_t3 library provides. It is simply a stepping stone to allow you to quickly change existing code to allow the different busses. For more advanced things I recommend i2c_t3.
 
Quick update: Maybe done for now as I have outstanding Pull requests for all of these now.

Now back to my Well Monitor code. For now will probably continue using my own version of ili9341_t3 library (ili9341_t3n) as all functionality in main branch...

One thing I wonder about is when using fonts, if it would make sense to maybe extract the font definition out of the ili9341_t3.h file and make a separate file like, ili9341_font.h, which does not depend on any non standard thing. Maybe then put extra copy in projects like: ili9341_fonts project and similar places. Then I would believe that they would work unedited in the main ili9341_t3 library as well as mine and probably ili9341_dma...

Just a thought.

Kurt
 
Sounds good.

FYI - today I also put in a Pull request for Radiohead library. I did most of the changes maybe 5 months ago. I pulled in at that time a more recent copy of Raidohead (maybe too much as it included makefile manifest...

I then added SPI transaction support to the library.

I then made a copy of the HardwareSPI code to make it possible to use SPI1. More recently I made another copy to support SPI2... I am using the SPI1 support on my own Well monitor (sudo flex board).

An example program I used to test this shows this:
Code:
// rf95_server.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple messageing server
// with the RH_RF95 class. RH_RF95 class does not provide for addressing or
// reliability, so you should only use RH_RF95  if you do not need the higher
// level messaging abilities.
// It is designed to work with the other example rf95_client
// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with
// the RFM95W, Adafruit Feather M0 with RFM95

#include <SPI.h>
#include <RH_RF95.h>
#define TRY_SPI1
#ifdef TRY_SPI1
#include <RHHardwareSPI1.h>
// MISO 1, MOSI 0, SCK 20
#define RFM95_CS 31
#define RFM95_RST 37
#define RFM95_INT 2

// SPI1 Miso=D5, Mosi=21, sck=20, CS=31
RH_RF95 rf95(RFM95_CS, RFM95_INT, hardware_spi1);

// Singleton instance of the radio driver
#else
//  RH_RF95(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi);
#define RFM95_CS 10
#define RFM95_RST 9
#define RFM95_INT 2

RH_RF95 rf95(RFM95_CS, RFM95_INT);
#endif

// Change to 434.0 or other frequency, must match RX's freq!
#define RF95_FREQ 915.0


void setup() 
{
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  Serial.begin(9600);
  while (!Serial) ; // Wait for serial port to be available

#ifdef TRY_SPI1
  SPI1.setMISO(5);
  SPI1.setMOSI(21);
  SPI1.setSCK(20); 
#endif

  if (!rf95.init())
    Serial.println("init failed");  
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  if (!rf95.setFrequency(RF95_FREQ)) {
    Serial.println("setFrequency failed");
    while (1);
  }
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
  rf95.setTxPower(23, false);

}

void loop()
{
  if (rf95.available())
  {
    // Should be a message for us now   
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    if (rf95.recv(buf, &len))
    {
//      digitalWrite(led, HIGH);
//      RH_RF95::printBuffer("request: ", buf, len);
      Serial.print("got request: ");
      Serial.println((char*)buf);
      Serial.print("RSSI: ");
      Serial.println(rf95.lastRssi(), DEC);
      
      // Send a reply
      uint8_t data[] = "And hello back to you";
      delay(5);
      bool sent = rf95.send(data, sizeof(data));
      Serial.print("Send reply ");
      Serial.println((int)sent, DEC);
      rf95.waitPacketSent();
      Serial.println("Sent a reply");
//       digitalWrite(led, LOW);
    }
    else
    {
      Serial.println("recv failed");
    }
  }
}
Again not sure if anyone was interested in these changes, but thought I would post in case...
 
I too would be interested in seeing KurtE's modifications of Radiohead merged into TD. Gonna start playing around with RFM95W nodes very soon, and SPI transactions and the ability to use different SPI busses looks promising.
 
Status
Not open for further replies.
Back
Top