|
- // -------------------------------------------------------------------------------------------
- // I2C Advanced Master
- // -------------------------------------------------------------------------------------------
- //
- // This creates an I2C master device with simple read/write commands and a small
- // addressable memory. Note that this communication adds a protocol layer on top of
- // normal I2C read/write procedures. As such, it is meant to pair with the advanced_slave
- // sketch. The read/write commands are described below.
- //
- // This code assumes the slave config has 256 byte memory and I2C addr is 0x44.
- //
- // Tests are as follows:
- // Pull pin12 input low to send/receive 256 bytes to/from slave in 32 byte blocks.
- // Pull pin11 input low to send/receive 256 bytes to/from slave in single block.
- // Pull pin10 input low to send/receive 256 bytes to/from slave in single block, using
- // non-blocking commands.
- // Pull pin9 input low to send/receive 256 bytes to/from slave in single block, using
- // non-blocking commands in DMA mode.
- // Pull pin8 input low to run I2C rate sweep test. This sweeps the I2C rates on both master
- // and slave and times the duration of a 256 byte transfer at each rate.
- //
- // For basic I2C communication only, refer to basic_master and basic_slave example sketches.
- //
- // This example code is in the public domain.
- //
- // -------------------------------------------------------------------------------------------
- // Slave protocol is as follows:
- // -------------------------------------------------------------------------------------------
- // WRITE - The I2C Master can write to the device by transmitting the WRITE command,
- // a memory address to store to, and a sequence of data to store.
- // The command sequence is:
- //
- // START|I2CADDR+W|WRITE|MEMADDR|DATA0|DATA1|DATA2|...|STOP
- //
- // where START = I2C START sequence
- // I2CADDR+W = I2C Slave address + I2C write flag
- // WRITE = WRITE command
- // MEMADDR = memory address to store data to
- // DATAx = data byte to store, multiple bytes are stored to increasing address
- // STOP = I2C STOP sequence
- // -------------------------------------------------------------------------------------------
- // READ - The I2C Master can read data from the device by transmitting the READ command,
- // a memory address to read from, and then issuing a STOP/START or Repeated-START,
- // followed by reading the data. The command sequence is:
- //
- // START|I2CADDR+W|READ|MEMADDR|REPSTART|I2CADDR+R|DATA0|DATA1|DATA2|...|STOP
- //
- // where START = I2C START sequence
- // I2CADDR+W = I2C Slave address + I2C write flag
- // READ = READ command
- // MEMADDR = memory address to read data from
- // REPSTART = I2C Repeated-START sequence (or STOP/START if single Master)
- // I2CADDR+R = I2C Slave address + I2C read flag
- // DATAx = data byte read by Master, multiple bytes are read from increasing address
- // STOP = I2C STOP sequence
- // -------------------------------------------------------------------------------------------
- // SETRATE - The I2C Master can adjust the Slave configured I2C rate with this command
- // The command sequence is:
- //
- // START|I2CADDR+W|SETRATE|RATE0|RATE1|RATE2|RATE3|STOP
- //
- // where START = I2C START sequence
- // I2CADDR+W = I2C Slave address + I2C write flag
- // SETRATE = SETRATE command
- // RATE0-3 = I2C frequency (uint32_t) LSB-to-MSB format
- // -------------------------------------------------------------------------------------------
-
- #include <i2c_t3.h>
-
- // Command definitions
- #define WRITE 0x10
- #define READ 0x20
- #define SETRATE 0x30
-
- // Function prototypes
- void print_i2c_setup(void);
- void print_i2c_status(void);
- void test_rate(uint8_t target, uint32_t rate);
-
- // Memory
- #define MEM_LEN 256
- uint8_t databuf[MEM_LEN];
-
- void setup()
- {
- pinMode(LED_BUILTIN,OUTPUT); // LED
- digitalWrite(LED_BUILTIN,LOW); // LED off
- pinMode(12,INPUT_PULLUP); // Control for Test1
- pinMode(11,INPUT_PULLUP); // Control for Test2
- pinMode(10,INPUT_PULLUP); // Control for Test3
- pinMode(9,INPUT_PULLUP); // Control for Test4
- pinMode(8,INPUT_PULLUP); // Control for Test5
-
- Serial.begin(115200);
-
- // Setup for Master mode, pins 18/19, external pullups, 400kHz
- Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
-
- Wire.setDefaultTimeout(250000); // 250ms default timeout
- }
-
- void loop()
- {
- size_t addr, len;
- uint8_t target = 0x44; // slave addr
- uint32_t count;
-
- //
- // A sequence of different read/write techniques.
- // Pull respective control pin low to initiate sequence.
- //
- // All tests will first write values to the slave, then read back the values.
- // The readback values should match.
- //
- // The LED is turned on during I2C operations. If it gets stuck on then the
- // ISR likely had a problem. This can happen with excessive baud rate.
- //
- // Various forms of the Wire calls (blocking/non-blocking/STOP/NOSTOP) are
- // used in the different tests.
- //
-
- if(digitalRead(12) == LOW)
- {
- digitalWrite(LED_BUILTIN,HIGH); // LED on
- Serial.print("---------------------------------------------------------\n");
- Serial.print("Test1 : Using blocking commands:\n");
- Serial.print(" 1) WRITE memory in 32 byte blocks\n");
- Serial.print(" 2) READ back memory in 32 byte blocks\n");
- Serial.print("---------------------------------------------------------\n");
-
- // Writing to Slave --------------------------------------------------------
- for(addr = 0; addr < 256; addr += 32) // sweep addr in 32byte blocks
- {
- for(len = 0; len < 32; len++) // prepare data to send
- databuf[len] = (addr+len)^0xFF; // set data (equal to bit inverse of memory address)
-
- Serial.printf("I2C WRITE 32 bytes to Slave 0x%0X at MemAddr %d\n", target, addr);
- Serial.print("Writing: ");
- for(len = 0; len < 32; len++) { Serial.printf("%d ",databuf[len]); }
- Serial.print("\n");
-
- Wire.beginTransmission(target); // slave addr
- Wire.write(WRITE); // WRITE command
- Wire.write(addr); // memory address
- Wire.write(databuf, 32); // write 32 byte block
- Wire.endTransmission(); // blocking write (when not specified I2C_STOP is implicit)
-
- print_i2c_status(); // print I2C final status
- }
-
- // Reading from Slave ------------------------------------------------------
- for(addr = 0; addr < 256; addr += 32) // sweep addr in 32byte blocks
- {
- Wire.beginTransmission(target); // slave addr
- Wire.write(READ); // READ command
- Wire.write(addr); // memory address
- Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP triggers RepSTART on next I2C command)
- Wire.requestFrom(target,32,I2C_STOP);// blocking read (request 32 bytes)
-
- Serial.printf("I2C READ 32 bytes from Slave 0x%0X at MemAddr %d\n", target, addr);
- Serial.print("Received: "); // print received bytes
- while(Wire.available()) { Serial.printf("%d ", Wire.readByte()); }
- Serial.print("\n");
- print_i2c_status(); // print I2C final status
- }
-
- digitalWrite(LED_BUILTIN,LOW); // LED off
- delay(500); // delay to space out tests
- }
-
-
- if(digitalRead(11) == LOW)
- {
- digitalWrite(LED_BUILTIN,HIGH); // LED on
- Serial.print("---------------------------------------------------------\n");
- Serial.print("Test2 : Using blocking commands:\n");
- Serial.print(" 1) WRITE entire memory in a single 256 byte block\n");
- Serial.print(" 2) READ back entire memory in a single 256 byte block\n");
- Serial.print("---------------------------------------------------------\n");
-
- // Writing to Slave --------------------------------------------------------
- addr = 0;
- for(len = 0; len < 256; len++) // prepare data to send
- databuf[len] = (addr+len)^0xFF; // set data (equal to bit inverse of memory address)
-
- Serial.printf("I2C WRITE 256 bytes to Slave 0x%0X at MemAddr %d\n", target, addr);
- Serial.print("Writing: ");
- for(len = 0; len < 256; len++) { Serial.printf("%d ",databuf[len]); }
- Serial.print("\n");
-
- Wire.beginTransmission(target); // slave addr
- Wire.write(WRITE); // WRITE command
- Wire.write(addr); // memory address
- Wire.write(databuf, 256); // write 256 byte block
- Wire.endTransmission(I2C_STOP); // blocking write (using explicit I2C_STOP)
-
- print_i2c_status(); // print I2C final status
-
- // Reading from Slave ------------------------------------------------------
- Wire.beginTransmission(target); // slave addr
- Wire.write(READ); // READ command
- Wire.write(addr); // memory address
- Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP triggers RepSTART on next I2C command)
- Wire.requestFrom(target,256,I2C_STOP); // blocking read (request 256 bytes)
-
- Serial.printf("I2C READ %d bytes from Slave 0x%0X at MemAddr %d\n", Wire.available(), target, addr);
- Serial.print("Received: "); // print received bytes
- while(Wire.available()) { Serial.printf("%d ", Wire.readByte()); }
- Serial.print("\n");
- print_i2c_status(); // print I2C final status
-
- Serial.printf("Rate (Hz): %d\n", Wire.i2c->currentRate);
-
- digitalWrite(LED_BUILTIN,LOW); // LED off
- delay(500); // delay to space out tests
- }
-
-
- if(digitalRead(10) == LOW)
- {
- digitalWrite(LED_BUILTIN,HIGH); // LED on
- Serial.print("---------------------------------------------------------\n");
- Serial.print("Test3 : Using ISR NON-blocking commands:\n");
- Serial.print(" 1) WRITE a 256 byte block to Slave. While block is\n");
- Serial.print(" transferring, perform other commands.\n");
- Serial.print(" 2) READ back the 256 byte block from Slave. While\n");
- Serial.print(" block is transferring, perform other commands.\n");
- Serial.print("---------------------------------------------------------\n");
-
- // Set operating mode to ISR
- Wire.setOpMode(I2C_OP_MODE_ISR);
-
- // Writing to Slave --------------------------------------------------------
- addr = 0;
- for(len = 0; len < 256; len++) // prepare data to send
- databuf[len] = (addr+len)^0xFF; // set data (equal to bit inverse of memory address)
-
- Serial.printf("I2C WRITE 256 bytes to Slave 0x%0X at MemAddr %d\n", target, addr);
- Serial.print("Writing: ");
- for(len = 0; len < 256; len++) { Serial.printf("%d ",databuf[len]); }
- Serial.print("\n");
-
- Wire.beginTransmission(target); // slave addr
- Wire.write(WRITE); // WRITE command
- Wire.write(addr); // memory address
- Wire.write(databuf, 256); // write 256 byte block
- Wire.sendTransmission(); // NON-blocking write (when not specified I2C_STOP is implicit)
-
- Serial.print("...write sent, counting while waiting for Wire.done()...\n");
- count = 1;
- while(!Wire.done()) count++; // Since write is non-blocking, do some counting while waiting
- Serial.printf("Counted to: %d\n", count++);
- print_i2c_status(); // print I2C final status
-
- // Reading from Slave ------------------------------------------------------
- Wire.beginTransmission(target); // slave addr
- Wire.write(READ); // READ command
- Wire.write(addr); // memory address
- Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP triggers RepSTART on next I2C command)
- Wire.sendRequest(target,256,I2C_STOP); // NON-blocking read (request 256 bytes)
-
- // Since request is non-blocking, do some other things.
- Serial.print("...request sent, doing one thing then waiting for Wire.finish()...\n");
-
- // After doing something, use finish() to wait until I2C done
- Wire.finish();
-
- Serial.printf("I2C READ %d bytes from Slave 0x%0X at MemAddr %d\n", Wire.available(), target, addr);
- Serial.print("Received: "); // print received bytes
- while(Wire.available()) { Serial.printf("%d ", Wire.readByte()); }
- Serial.print("\n");
- print_i2c_status(); // print I2C final status
-
- digitalWrite(LED_BUILTIN,LOW); // LED off
- delay(500); // delay to space out tests
- }
-
-
- if(digitalRead(9) == LOW)
- {
- digitalWrite(LED_BUILTIN,HIGH); // LED on
- Serial.print("---------------------------------------------------------\n");
- Serial.print("Test4 : Using DMA NON-blocking commands:\n");
- Serial.print(" 1) WRITE a 256 byte block to Slave. While block is\n");
- Serial.print(" transferring, perform other commands.\n");
- Serial.print(" 2) READ back the 256 byte block from Slave. While\n");
- Serial.print(" block is transferring, perform other commands.\n");
- Serial.print("---------------------------------------------------------\n");
-
- // Set operating mode to DMA
- Serial.print("Trying to set DMA mode : ");
- Wire.setOpMode(I2C_OP_MODE_DMA);
- if(Wire.i2c->opMode == I2C_OP_MODE_DMA)
- Serial.printf("OK (Channel %d)\n",Wire.i2c->DMA->channel);
- else
- Serial.print("Failed, using ISR\n");
-
- // Writing to Slave --------------------------------------------------------
- addr = 0;
- for(len = 0; len < 256; len++) // prepare data to send
- databuf[len] = (addr+len)^0xFF; // set data (equal to bit inverse of memory address)
-
- Serial.printf("I2C WRITE 256 bytes to Slave 0x%0X at MemAddr %d\n", target, addr);
- Serial.print("Writing: ");
- for(len = 0; len < 256; len++) { Serial.printf("%d ",databuf[len]); }
- Serial.print("\n");
-
- Wire.beginTransmission(target); // slave addr
- Wire.write(WRITE); // WRITE command
- Wire.write(addr); // memory address
- Wire.write(databuf, 256); // write 256 byte block
- Wire.sendTransmission(); // NON-blocking write (when not specified I2C_STOP is implicit)
-
- Serial.print("...write sent, counting while waiting for Wire.done()...\n");
- count = 1;
- while(!Wire.done()) count++; // Since write is non-blocking, do some counting while waiting
- Serial.printf("Counted to: %d\n", count++);
- print_i2c_status(); // print I2C final status
-
- // Reading from Slave ------------------------------------------------------
- Wire.beginTransmission(target); // slave addr
- Wire.write(READ); // READ command
- Wire.write(addr); // memory address
- Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP triggers RepSTART on next I2C command)
- Wire.sendRequest(target,256,I2C_STOP); // NON-blocking read (request 256 bytes)
-
- // Since request is non-blocking, do some other things.
- Serial.print("...request sent, doing one thing then waiting for Wire.finish()...\n");
-
- // After doing something, use finish() to wait until I2C done
- Wire.finish();
-
- Serial.printf("I2C READ %d bytes from Slave 0x%0X at MemAddr %d\n", Wire.available(), target, addr);
- Serial.print("Received: "); // print received bytes
- while(Wire.available()) { Serial.printf("%d ", Wire.readByte()); }
- Serial.print("\n");
- print_i2c_status(); // print I2C final status
-
- digitalWrite(LED_BUILTIN,LOW); // LED off
- delay(500); // delay to space out tests
- }
-
-
- if(digitalRead(8) == LOW)
- {
- uint8_t fail=0;
-
- digitalWrite(LED_BUILTIN,HIGH); // LED on
- Serial.print("---------------------------------------------------------\n");
- Serial.print("Test5 : Rate adjustment tests. This sweeps the I2C rates\n");
- Serial.print(" on both Master and Slave and times the duration \n");
- Serial.print(" of a 256 byte transfer at each rate.\n");
- Serial.print("---------------------------------------------------------\n");
-
- for(uint8_t opMode = I2C_OP_MODE_IMM; opMode <= I2C_OP_MODE_DMA; opMode++)
- {
- Wire.setOpMode((i2c_op_mode)opMode); // set op mode
-
- test_rate(target, 10000, fail);
- test_rate(target, 100000, fail);
- test_rate(target, 200000, fail);
- test_rate(target, 300000, fail);
- test_rate(target, 400000, fail);
- test_rate(target, 600000, fail);
- test_rate(target, 800000, fail);
- test_rate(target, 1000000, fail);
- test_rate(target, 1200000, fail);
- test_rate(target, 1500000, fail);
- test_rate(target, 1800000, fail);
- test_rate(target, 2000000, fail);
- test_rate(target, 2400000, fail);
- test_rate(target, 2800000, fail);
- test_rate(target, 3000000, fail);
- test_rate(target, 4000000, fail);
- test_rate(target, 5000000, fail);
- test_rate(target, 6000000, fail);
-
- // Restore normal settings
-
- // Change Slave rate
- Wire.beginTransmission(target); // slave addr
- Wire.write(SETRATE); // SETRATE command
- Wire.write((uint8_t)400000&0xFF); // rate LSB
- Wire.write((uint8_t)(400000>>8)&0xFF);
- Wire.write((uint8_t)(400000>>16)&0xFF);
- Wire.write((uint8_t)(400000>>24)&0xFF); // rate MSB
- Wire.endTransmission(); // blocking write
-
- // Change Master rate
- Wire.setClock(400000);
-
- fail = 0; // reset flag
- }
- Wire.setOpMode(I2C_OP_MODE_ISR); // restore default ISR mode
-
- print_i2c_status(); // print I2C final status
-
- // Restore normal settings (400kHz)
-
- // Change Slave rate
- Wire.beginTransmission(target); // slave addr
- Wire.write(SETRATE); // SETRATE command
- Wire.write((uint8_t)400000&0xFF); // rate LSB
- Wire.write((uint8_t)(400000>>8)&0xFF);
- Wire.write((uint8_t)(400000>>16)&0xFF);
- Wire.write((uint8_t)(400000>>24)&0xFF); // rate MSB
- Wire.endTransmission(); // blocking write
-
- // Change Master rate
- Wire.setClock(400000);
-
- digitalWrite(LED_BUILTIN,LOW); // LED off
- delay(500); // delay to space out tests
- }
- }
-
-
- //
- // print current setup
- //
- void print_i2c_setup()
- {
- Serial.print("Mode:");
- switch(Wire.i2c->opMode)
- {
- case I2C_OP_MODE_IMM: Serial.print("IMM "); break;
- case I2C_OP_MODE_ISR: Serial.print("ISR "); break;
- case I2C_OP_MODE_DMA: Serial.printf("DMA[%d] ",Wire.i2c->DMA->channel); break;
- }
- Serial.printf("Pins: %d/%d ", Wire.i2c->currentSCL, Wire.i2c->currentSDA);
- }
-
-
- //
- // print I2C status
- //
- void print_i2c_status(void)
- {
- switch(Wire.status())
- {
- case I2C_WAITING: Serial.print("I2C waiting, no errors\n"); break;
- case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
- case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
- case I2C_ARB_LOST: Serial.print("Bus Error: Arbitration Lost\n"); break;
- case I2C_TIMEOUT: Serial.print("I2C timeout\n"); break;
- case I2C_BUF_OVF: Serial.print("I2C buffer overflow\n"); break;
- default: Serial.print("I2C busy\n"); break;
- }
- }
-
-
- //
- // print I2C rate
- //
- void print_rate(uint32_t rate)
- {
- Serial.printf("%d Hz ", rate);
- }
-
-
- //
- // test rate
- //
- void test_rate(uint8_t target, uint32_t rate, uint8_t& fail)
- {
- uint32_t deltatime=0;
- size_t len;
-
- if(!fail)
- {
- for(len = 0; len < 256; len++) // prepare data to send
- databuf[len] = len; // set data (equal to addr)
-
- // Change Slave rate
- Wire.beginTransmission(target); // slave addr
- Wire.write(SETRATE); // SETRATE command
- Wire.write((uint8_t)rate&0xFF); // rate LSB
- Wire.write((uint8_t)(rate>>8)&0xFF);
- Wire.write((uint8_t)(rate>>16)&0xFF);
- Wire.write((uint8_t)(rate>>24)&0xFF); // rate MSB
- Wire.endTransmission(); // blocking write
- fail = Wire.getError();
-
- if(!fail)
- {
- // Change Master rate
- Wire.setClock(rate);
-
- // Setup write buffer
- Wire.beginTransmission(target); // slave addr
- Wire.write(WRITE); // WRITE command
- Wire.write(0); // memory address
- Wire.write(databuf, 256); // write 256 byte block
-
- // Write to Slave
- elapsedMicros deltaT;
- Wire.endTransmission(); // blocking write
- deltatime = deltaT;
- fail = Wire.getError();
-
- if(!fail)
- {
- Wire.beginTransmission(target); // slave addr
- Wire.write(READ); // READ command
- Wire.write(0); // memory address
- Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP triggers RepSTART on next I2C command)
- Wire.requestFrom(target,256,I2C_STOP);// blocking read
- fail = Wire.getError();
-
- if(!fail)
- {
- for(len = 0; len < 256; len++) // verify block
- if(databuf[len] != Wire.readByte()) { fail=1; break; }
- }
- }
- }
-
- print_i2c_setup();
- if(!fail)
- {
- // Print result
- Serial.print("256 byte transfer at ");
- print_rate(rate);
- Serial.printf(" (Actual Rate (Hz): %d) : %d us : ", Wire.getClock(), deltatime);
- print_i2c_status();
- }
- else
- {
- Serial.printf("Transfer fail : %d us : ",deltatime);
- print_i2c_status();
- }
- }
- }
|