diff --git a/megaavr/libraries/Wire/README.md b/megaavr/libraries/Wire/README.md index e1acf4f2..32e3856f 100644 --- a/megaavr/libraries/Wire/README.md +++ b/megaavr/libraries/Wire/README.md @@ -208,6 +208,18 @@ uint8_t checkPinLevels(); ``` This function returns the level of the master TWI pins, depending on the used TWI module and port multiplexer settings. Bit 0 represents SDA line and bit 1 represents SCL line. This is useful on initialisation, where you want to make sure that all devices have their pins ready in open-drain mode. A value of 0x03 indicates that both lines have a HIGH level and the bus is ready. +#### `uint8_t masterTransmit()` +```c++ +uint8_t masterTransmit(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop); +``` +This functions allows to transmit a buffer of data without having to copy the data to the internal buffer of the library. This allows transmission lengths as long as the RAM size without having to define the TWI_BUFFER_SIZE in the platform.txt. The return value is the same as endTransmission(). length will be overwritten with the actual amount of written bytes. + +#### `uint8_t masterReceive()` +```c++ +uint8_t masterReceive(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop); +``` +This functions allows to receive a buffer of data without having to copy the data from the internal buffer of the library. This allows transmission lengths as long as the RAM size without having to define the TWI_BUFFER_SIZE in the platform.txt. The return value is the same as endTransmission(). length will be overwritten with the actual amount of received bytes. + ### Additional New Methods not available on all parts These new methods are available exclusively for parts with certain specialized hardware; Most full-size parts support enableDualMode (but tinyAVR does not), while only the DA and DB-series parts have the second TWI interface that swapModule requires. #### `void swapModule()` @@ -323,6 +335,8 @@ When the second or third argument was used, `Wire.getIncomingAddress()` should b If (and only if) the Master and Slave option is selected in the Tools -> Wire mode, the Wire interface can be enabled for both master and slave. Even when Dual Mode is used, the correct option must still be selected to enable acting as both master and slave. +The Arduino library defines the array lengths as `BUFFER_LENGTH`, however, this define name might create conflicts, as the name is quite arbitrary. The newest library version uses the name `TWI_BUFFER_LENGTH` instead. However, it is still backwards compatible, as the `BUFFER_LENGTH` is still applied, but a compiler warning is generated to notify about the change. + #### `void setClock()` ```c++ void setClock(uint32_t); @@ -419,7 +433,7 @@ The implementation isn't identical, but the behaviour is unchanged, or is differ #### Write Sequence 1. Master generates start condition. -2. Master clocks out the slave address with out read-bit set.. +2. Master clocks out the slave address without read-bit set.. 3. Slave detects and ACKs. 4. Master clocks out 1 or more data bytes as slave ACKs them. 5. Master generates a Stop Condition. diff --git a/megaavr/libraries/Wire/examples/master_and_slave/master_and_slave.ino b/megaavr/libraries/Wire/examples/master_and_slave/master_and_slave.ino deleted file mode 100644 index 72946fad..00000000 --- a/megaavr/libraries/Wire/examples/master_and_slave/master_and_slave.ino +++ /dev/null @@ -1,84 +0,0 @@ -/* Wire Master and Slave example - * by MX682X - * - * Demonstrates use of the New Wire library - * This example requires the setting Master And Slave - * If anything is sent through the Serial Monitor and terminated with NL and/or CR - * The current millis value is sent to the slave. If enableDualMode is NOT called, - * the slave pins are the same as the master pins. This creates a loopback mode, - * in which the sent millis value is received in the same device and is printed back to - * the Serial Monitor. - * - */ - -#include - -int8_t rxLen = 0; -int8_t len = 0; - -#define MySerial Serial // The serial port connected to the to the computer. - - -void setup() { - Wire.onReceive(receiveDataWire); // Set the slave receive ISR - //Wire.enableDualMode(false) // used to use separate pins for master and slave. If enabled, - // it would disable the loopback feature this example relies upon - //Wire.swap() // used to select the pins of the master and the slave - - Wire.begin(); // Enables the master functionality - Wire.begin(0x54); // Enables the slave functionality - - MySerial.begin(115200); // Use 115200 baud - this is the 2020's, and these are modern AVRs. -} - -void loop() { - if (MySerial.available() > 0) { // as soon as the first byte is received on Serial - readFromSerial(); // read the data from the Serial interface - if (len > 0) { // after the while-loop, if there was useful data, - sendDataWire(); // send the data over I2C - } - len = 0; // since the data was sent, the position is 0 again - } -} - -void readFromSerial() { - while (true) { // in an endless while-loop - while (MySerial.available() == 0);// means we've taken all the bytes in, and are still waiting for a cr/lf. - char c = MySerial.read(); // read the next char, now that there's one available. - if (c == '\n' || c == '\r') { // until a new line or carriage return is found - break; // if so, break the endless while-loop - } // otherwise - len++; // increment the position. data is not saved as it is not needed in this case - } -} -void sendDataWire() { - uint32_t ms = 0x22334455; - #ifndef MILLIS_USE_TIMERNONE - ms = millis(); // overwrite the default value - #endif - - Wire.beginTransmission(0x54); // prepare transmission to slave with address 0x54 - Wire.write((uint8_t) ms); - Wire.write((uint8_t)(ms >> 8)); - Wire.write((uint8_t)(ms >> 16)); - Wire.write((uint8_t)(ms >> 24)); - - Wire.endTransmission(); // finish transmission -} - - -void receiveDataWire(int16_t numBytes) { - // Warning: This is terrible practice! Never print from within an ISR! - // It only works in very simple cases. - if (numBytes != 4) { - MySerial.print("Slave received only "); - MySerial.println(numBytes); - } - MySerial.print("Slave Received: "); - uint32_t temp = 0; - temp |= (uint32_t)Wire.read(); - temp |= (uint32_t)Wire.read() << 8; - temp |= (uint32_t)Wire.read() << 16; - temp |= (uint32_t)Wire.read() << 24; - MySerial.println(temp); -} diff --git a/megaavr/libraries/Wire/examples/master_read/master_read.ino b/megaavr/libraries/Wire/examples/master_read/master_read.ino new file mode 100644 index 00000000..2e9a58a5 --- /dev/null +++ b/megaavr/libraries/Wire/examples/master_read/master_read.ino @@ -0,0 +1,55 @@ +/* Wire Master Read + * by MX682X + * + * Demonstrates use of the New Wire library + * Reads data from an I2C/TWI slave device + * Refer to the "Wire Slave Write" example for use with this + * + * This example takes the input from Serial. If the serial input is 'm' or 'M', + * this code requests 4 bytes from the slave with the address 0x54. + * When using together with the complementary example, the slave sends it's millis() value. + * This value is then sent to the serial monitor + * + * To use this, you need to connect the SCL and SDA pins of this device to the + * SCL and SDA pins of a second device running the Wire Slave Write example. + * + * Pullup resistors must be connected between both data lines and Vcc. + * See the Wire library README.md for more information. + */ + +#define MySerial Serial + +#include + +int8_t rxLen = 0; +int8_t len = 0; + +void setup() { + Wire.begin(); // initialize master + MySerial.begin(115200); +} + +void loop() { + if (MySerial.available() > 0) { // as soon as the first byte is received on Serial + char c = MySerial.read(); // read the data from serial. + if (c == 'm' || c == 'M') { + sendDataWire(); // send the data over I2C + } + len = 0; // since the data was sent, the position is 0 again + } +} + +void sendDataWire() { + uint32_t ms; + if (4 == Wire.requestFrom(0x54, 4, 0x01)) { // request from slave + while (Wire.available()) { + ms = (uint32_t)Wire.read(); // read out 32-bit wide data + ms |= (uint32_t)Wire.read() << 8; + ms |= (uint32_t)Wire.read() << 16; + ms |= (uint32_t)Wire.read() << 24; + MySerial.println(ms); // print the milliseconds from Slave + } + } else { + MySerial.println("Wire.requestFrom() timed out!"); + } +} diff --git a/megaavr/libraries/Wire/examples/master_write/master_write.ino b/megaavr/libraries/Wire/examples/master_write/master_write.ino new file mode 100644 index 00000000..cfaac9fe --- /dev/null +++ b/megaavr/libraries/Wire/examples/master_write/master_write.ino @@ -0,0 +1,64 @@ +/* Wire Master Write + * by MX682X + * + * Demonstrates use of the New Wire library + * Writes data to an I2C/TWI slave device + * Refer to the "Wire Slave Read" example for use with this + * + * Enter any data using serial monitor or other console followed by either or + * both of the line ending characters, and it will be sent to the slave, which + * should print it out on it's serial port. + * + * To use this, you need to connect the SCL and SDA pins of this device to the + * SCL and SDA pins of a second device running the Wire Slave Read example. + * + * Pullup resistors must be connected between both data lines and Vcc. + * See the Wire library README.md for more information. + */ + +#include + +char input[32]; +int8_t len = 0; + +#define MySerial Serial // The serial port connected to the to the computer. + +void setup() { + Wire.begin(); // initialize master + // MySerial.swap(1); // Remember to swap serial pins if you need to do that with your connections. + MySerial.begin(115200); // Use 115200 baud - this is the 2020's, and these are modern AVRs. +} + +void loop() { + if (MySerial.available() > 0) { // as soon as the first byte is received on Serial + readFromSerial(); // read the data from the Serial interface + if (len > 0) { // after the while-loop, if there was useful data, + sendDataWire(); // send the data over I2C + } + len = 0; // since the data was sent, the position is 0 again + } +} + +void readFromSerial() { + while (true) { // in an endless while-loop + while (MySerial.available() == 0);// means we've taken all the bytes in, and are still waiting for a cr/lf. + char c = MySerial.read(); // read the next char, now that there's one available. + if (c == '\n' || c == '\r') { // until a new line or carriage return is found + break; // if so, break the endless while-loop + } // otherwise + input[len] = c; // save the char + len++; // increment the position + if (len > 30) { // if there was too much data + break; // break the while-loop to avoid buffer overflow + } + } +} + +void sendDataWire() { + Wire.beginTransmission(0x54); // prepare transmission to slave with address 0x54 + for (uint8_t i = 0; i < len; i++) { + Wire.write(input[i]); // Write the received data to the bus buffer + } + Wire.write("\r\n"); // add new line and carriage return for the Serial monitor + Wire.endTransmission(); // finish transmission +} diff --git a/megaavr/libraries/Wire/examples/master_write_read/master_write_read.ino b/megaavr/libraries/Wire/examples/master_write_read/master_write_read.ino deleted file mode 100644 index f650c6de..00000000 --- a/megaavr/libraries/Wire/examples/master_write_read/master_write_read.ino +++ /dev/null @@ -1,113 +0,0 @@ -/* Wire Master Write/Read - * by MX682X - * - * Demonstrates use of the New Wire library - * Writes or reads data to/from an I2C/TWI slave device - * Refer to the "Wire slave_write_read" example for use with this - * - * Enter any data using serial monitor or other console followed by either or - * both of the line ending characters. Depending on the first char, one of the - * following happens: - * - if it's a 'm' or 'M', a master write is performed (if the #define is set) - * - otherwise a master read of 4 bytes is performed (if the #define is set) - * - * To use this, you need to connect the SCL and SDA pins of this device to the - * SCL and SDA pins of a second device running the Wire Slave Read example. - * - * Pullup resistors must be connected between both data lines and Vcc. - * See the Wire library README.md for more information. - */ - -#include - -#define MySerial Serial // The serial port connected to the to the computer. - -// The complete example might not fit on some parts. In that case, you can easily -// comment out some functionality. DECODE_ERROR bloats the code the most. -#define ENABLE_WRITE_TO // Enables the master write functionality -#define ENABLE_READ_FROM // Enables the master read functionality -#define DECODE_ERROR // Prints status messages. - -char input[32]; -int8_t len = 0; - - -void setup() { - Wire.begin(); // initialize master - MySerial.begin(115200); // Use 115200 baud - this is the 2020's, and these are modern AVRs. -} - -void loop() { - if (MySerial.available() > 0) { // as soon as the first byte is received on Serial - readFromSerial(); // read the data from the Serial interface - if (len > 0) { // after the while-loop, if there was useful data, - char c = input[0]; - if (c == 'm' || c == 'M') { // If the first char is that - sendDataWire(); // send the data over I2C to the slave - } else { // otherwise - requestDataWire(); // request data from I2C slave - } - } - len = 0; // since the data was sent, the position is 0 again - } -} - -void readFromSerial() { - while (true) { // in an endless while-loop - while (MySerial.available() == 0);// means we've taken all the bytes in, and are still waiting for a cr/lf. - char c = MySerial.read(); // read the next char, now that there's one available. - if (c == '\n' || c == '\r') { // until a new line or carriage return is found - break; // if so, break the endless while-loop - } // otherwise - input[len] = c; // save the char - len++; // increment the position - if (len > 30) { // if there was too much data - break; // break the while-loop to avoid buffer overflow - } - } -} - -void sendDataWire() { - #ifdef ENABLE_WRITE_TO - - uint8_t err = 0; - Wire.beginTransmission(0x54); // prepare transmission to slave with address 0x54 - Wire.write(input, len); - Wire.write("\r\n"); // add new line and carriage return for the Serial monitor - err = Wire.endTransmission(); // finish transmission - - #ifdef DECODE_ERROR - - switch (err) { - case 0x00: MySerial.println("Wire transmit was successful"); break; - case 0x02: MySerial.println("Address was NACK'd"); break; - case 0x03: MySerial.println("Data was NACK'd"); break; - case 0x04: MySerial.println("Unknown error occurred"); break; - case 0x05: MySerial.println("Transmission time-outed"); break; - // The library also supports some extended errors that might give a hint on what is failing. - case 0x10: MySerial.println("Wire is uninitialized"); break; - case 0x11: MySerial.println("Pullups are missing"); break; - case 0x12: MySerial.println("Arbitration lost"); break; - } - - #endif /* DECODE_ERROR */ - #endif /* ENABLE_WRITE_TO */ -} - -void requestDataWire() { - #ifdef ENABLE_READ_FROM - uint8_t len = 4; - uint32_t ms = 0; - if (len == Wire.requestFrom(0x54, len, 0x01)) { // request from slave - ms = (uint32_t)Wire.read(); // read out 32-bit wide data - ms |= (uint32_t)Wire.read() << 8; - ms |= (uint32_t)Wire.read() << 16; - ms |= (uint32_t)Wire.read() << 24; - MySerial.println(ms); // print the milliseconds from Slave - } else { - #ifdef DECODE_ERROR - MySerial.println("Wire.requestFrom() failed"); - #endif - } - #endif -} diff --git a/megaavr/libraries/Wire/examples/slave_read/slave_read.ino b/megaavr/libraries/Wire/examples/slave_read/slave_read.ino new file mode 100644 index 00000000..2e0a8d74 --- /dev/null +++ b/megaavr/libraries/Wire/examples/slave_read/slave_read.ino @@ -0,0 +1,40 @@ +/* Wire Slave Read + * by MX682X + * + * Demonstrates use of the Wire library + * Receives data as an I2C/TWI slave device + * Refer to the "Wire Master Write" example for use with this + * + * This example prints everything that is received on the I2C bus to the + * serial monitor. + * + * To use this, you need to connect the SCL and SDA pins of this device to the + * SCL and SDA pins of a second device running the Master Write example. + * + * Pullup resistors must be connected between both data lines and Vcc. + * See the Wire library README.md for more information. + */ + +#include + +#define MySerial Serial + +void setup() { + Wire.begin(0x54); // join i2c bus with address 0x54 + Wire.onReceive(receiveDataWire); // give the Wire library the name of the function + // that will be called on a master write event + MySerial.begin(115200); +} + +void loop() { + delay(100); +} + +// function that executes whenever data is received from master +// this function is registered as an event, see setup() +void receiveDataWire(int16_t numBytes) { // the Wire library tells us how many bytes + for (uint8_t i = 0; i < numBytes; i++) { // were received so we can for loop for that + char c = Wire.read(); // amount and read the received data + MySerial.write(c); // to print it to the Serial Monitor + } +} diff --git a/megaavr/libraries/Wire/examples/slave_write/slave_write.ino b/megaavr/libraries/Wire/examples/slave_write/slave_write.ino new file mode 100644 index 00000000..e8085e18 --- /dev/null +++ b/megaavr/libraries/Wire/examples/slave_write/slave_write.ino @@ -0,0 +1,37 @@ +/* Wire Slave Write + * + * This sketch responds to a request for data directed at address 0x54 + * with up to 4 bytes of data - namely, the current value of millis. + * + * If millis is disabled, it will instead always return the sequence + * 1234567890 (1,234,567,890, or 0x499602D2 represented in hexadecimal) + * + * To use this, you need to connect the SCL and SDA pins of this device to the + * SCL and SDA pins of a second device running the Master Read example. + * + * Pullup resistors must be connected between both data lines and Vcc. + * See the Wire library README.md for more information. + */ + +#include + +void setup() { + Wire.begin(0x54); // initialize slave + Wire.onRequest(transmitDataWire); // register transmitDataWire as the handler +} + +void loop() { + +} + +void transmitDataWire() { + #if !defined(MILLIS_USE_TIMERNONE) + uint32_t ms = millis(); + #else + uint32_t ms = 123456789UL; // placeholder - there's no millis to send because it's disabled. + #endif + Wire.write((uint8_t) ms); + Wire.write((uint8_t)(ms >> 8)); + Wire.write((uint8_t)(ms >> 16)); + Wire.write((uint8_t)(ms >> 24)); +} diff --git a/megaavr/libraries/Wire/examples/slave_write_read/slave_write_read.ino b/megaavr/libraries/Wire/examples/slave_write_read/slave_write_read.ino deleted file mode 100644 index 5b82e173..00000000 --- a/megaavr/libraries/Wire/examples/slave_write_read/slave_write_read.ino +++ /dev/null @@ -1,67 +0,0 @@ -/* Wire Slave Write/Read - * by MX682X - * - * Demonstrates use of the Wire library - * Writes/Reads data as an I2C/TWI slave device - * Refer to the "Wire master_write_read" example for use with this - * - * If the #define ENABLE_RECEIVE is set, every byte in a Master Write is - * printed to the Serial Monitor. - * - * If the #define ENABLE_REQUEST is set, the slave sends its millis() - * (or placeholder) value to the master. - * - * To use this, you need to connect the SCL and SDA pins of this device to the - * SCL and SDA pins of a second device running the Master Write example. - * - * Pullup resistors must be connected between both data lines and Vcc. - * See the Wire library README.md for more information. - */ - -#include - -#define MySerial Serial - -#define ENABLE_RECEIVE -#define ENABLE_REQUEST - - -void setup() { - #ifdef ENABLE_RECEIVE - Wire.onReceive(receiveDataWire); // give the Wire library the name of the function - // that will be called on a master write event - #endif - #ifdef ENABLE_REQUEST - Wire.onRequest(transmitDataWire); // same as above, but master read event - #endif - - Wire.begin(0x54); // join i2c bus with address 0x54 - MySerial.begin(115200); -} - -void loop() { - delay(100); -} - -#ifdef ENABLE_RECEIVE -void receiveDataWire(int16_t numBytes) { // the Wire API tells us how many bytes - for (uint8_t i = 0; i < numBytes; i++) { // were received so we can for loop for that - char c = Wire.read(); // amount and read the received data - MySerial.write(c); // to print it to the Serial Monitor - } -} -#endif - -#ifdef ENABLE_REQUEST -void transmitDataWire() { - #if !defined(MILLIS_USE_TIMERNONE) - uint32_t ms = millis(); - #else - uint32_t ms = 123456789UL; // placeholder - there's no millis to send because it's disabled. - #endif - Wire.write((uint8_t) ms); - Wire.write((uint8_t)(ms >> 8)); - Wire.write((uint8_t)(ms >> 16)); - Wire.write((uint8_t)(ms >> 24)); -} -#endif diff --git a/megaavr/libraries/Wire/keywords.txt b/megaavr/libraries/Wire/keywords.txt index ac633cbb..c233906d 100644 --- a/megaavr/libraries/Wire/keywords.txt +++ b/megaavr/libraries/Wire/keywords.txt @@ -5,6 +5,8 @@ ####################################### # Datatypes (KEYWORD1) ####################################### +twi_buf_index_t KEYWORD1 +TWI_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -18,6 +20,8 @@ setClock KEYWORD2 beginTransmission KEYWORD2 endTransmission KEYWORD2 requestFrom KEYWORD2 +masterTransmit KEYWORD2 +masterReceive KEYWORD2 onReceive KEYWORD2 onRequest KEYWORD2 getIncomingAddress KEYWORD2 @@ -25,6 +29,8 @@ getBytesRead KEYWORD2 slaveTransactionOpen KEYWORD2 checkPinLevels KEYWORD2 specialConfig KEYWORD2 +enableDualMode KEYWORD2 +returnError KEYWORD2 WIRE_SDA_HOLD_OFF KEYWORD2 WIRE_SDA_HOLD_50 KEYWORD2 WIRE_SDA_HOLD_300 KEYWORD2 diff --git a/megaavr/libraries/Wire/library.properties b/megaavr/libraries/Wire/library.properties index 0b78069e..f1150b79 100644 --- a/megaavr/libraries/Wire/library.properties +++ b/megaavr/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=2.0.10 +version=2.0.11 author=@MX682X, with contributions by @SpenceKonde. Original library, mostly reimplemented, was by Arduino. maintainer=Spence Konde sentence=This library allows you to communicate with I2C devices, acting as either a master, slave, or both master and slave. diff --git a/megaavr/libraries/Wire/src/Wire.cpp b/megaavr/libraries/Wire/src/Wire.cpp index 198418d1..295bced2 100644 --- a/megaavr/libraries/Wire/src/Wire.cpp +++ b/megaavr/libraries/Wire/src/Wire.cpp @@ -21,7 +21,7 @@ Modified 2019-2021 by Spence Konde for megaTinyCore and DxCore. This version is part of megaTinyCore and DxCore; it is not expected to work with other hardware or cores without modifications. - Modified extensively 2021-22 by MX682X for megaTinyCore and DxCore. + Modified extensively 2021-23 by MX682X for megaTinyCore and DxCore. Added Support for Simultaneous host/client, dual mode and Wire1. */ // *INDENT-OFF* astyle wants this file to be completely unreadable with no indentation for the many preprocessor conditionals! @@ -333,8 +333,8 @@ void TwoWire::begin(uint8_t address, bool receive_broadcast, uint8_t second_addr return; } } - - + + #if defined(TWI_MANDS) // Check if the user wants to use Master AND Slave if (_bools._clientEnabled == 1) { // Master is allowed to be enabled, don't re-enable the client though return; @@ -620,24 +620,25 @@ uint8_t TwoWire::specialConfig( __attribute__ ((unused)) bool smbuslvl, __attrib *@brief masterReceive sends a host READ with the specified client address * * This function will read an arbitrary number of bytes from the TWI module - * and store them into the specified buffer. This allows the user to use + * and store them into the specified buffer. This allows the user to use * custom sized buffers and avoids extra copy operations through read. * - *@param auto length - amount of bytes to be read (first arg so it is placed in r24:r25) - *@param uint8_t addr - the address of the client (7-bit) + *@param auto* length - pointer to a 8/16 bit variable containing the length. Will be overwritten with the amount of received bytes *@param uint8_t* buffer - pointer to the memory area to be written upon. + *@param uint8_t addr - the address of the client (7-bit) *@param uint8_t/bool sendStop - if the transaction should be terminated with a STOP condition * - *@return auto (uint8_t/uint16_t) - depends on the usage - *@retval amount of bytes that were actually read. If 0, no read took place due to a bus error. + *@return uint8_t + *@retval error code, see masterTrasnmit */ -auto TwoWire::masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t sendStop) { +uint8_t TwoWire::masterReceive(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop) { TWI_t *module = _module; + __asm__ __volatile__("\n\t" : "+z"(module)); TWIR_INIT_ERROR; // local variable for errors - auto dataRead = 0; + auto dataToRead = *length; uint8_t currentSM; uint8_t currentStatus; @@ -645,14 +646,14 @@ auto TwoWire::masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t #if defined (TWI_TIMEOUT_ENABLE) uint16_t timeout = (F_CPU/1000); #endif - + while (true) { currentStatus = module->MSTATUS; currentSM = currentStatus & TWI_BUSSTATE_gm; // get the current mode of the state machine - + if (currentSM == TWI_BUSSTATE_UNKNOWN_gc) { - TWIR_SET_ERROR(TWI_ERR_UNINITIALIZED); - return dataRead; + TWIR_SET_ERROR(TWI_ERR_UNINIT); + break; } #if defined(TWI_TIMEOUT_ENABLE) @@ -667,13 +668,12 @@ auto TwoWire::masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t break; } #endif - + if (currentStatus & TWI_ARBLOST_bm) { // Check for Bus error TWIR_SET_ERROR(TWI_ERR_BUS_ARB); // set error flag break; // leave RX loop } - if (currentSM != TWI_BUSSTATE_BUSY_gc) { if (state == 0x00) { module->MADDR = ADD_READ_BIT(addr); // Send Address with read bit @@ -683,17 +683,16 @@ auto TwoWire::masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t #endif } else { if (currentStatus & TWI_WIF_bm) { - TWIR_SET_ERROR(TWI_ERR_RXACK); // set error flag + TWIR_SET_ERROR(TWI_ERR_ACK_ADR); // set error flag module->MCTRLB = TWI_MCMD_STOP_gc; // free the bus break; } else if (currentStatus & TWI_RIF_bm) { *buffer = module->MDATA; buffer++; - dataRead++; #if defined (TWI_TIMEOUT_ENABLE) timeout = (F_CPU/1000); // reset timeout #endif - if (dataRead < length) { + if (--dataToRead != 0) { module->MCTRLB = TWI_MCMD_RECVTRANS_gc; // send an ACK so the Slave so it can send the next byte } else { if (sendStop != 0) { @@ -707,10 +706,8 @@ auto TwoWire::masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t } } } - #if defined(TWI_READ_ERROR_ENABLED) && defined(TWI_ERROR_ENABLED) - _errors = TWIR_GET_ERROR; // save error flags - #endif - return dataRead; + *length -= dataToRead; + return TWIR_GET_ERROR; } @@ -741,11 +738,12 @@ twi_buf_index_t TwoWire::requestFrom(uint8_t address, twi_buf_index_t quantity } _clientAddress = address << 1; - - twi_buf_index_t count = masterReceive(quantity, _clientAddress, _hostBuffer, sendStop); - _bytesToReadWrite = count; // for available/read/peek + _bytesToReadWrite = quantity; // for available/read/peek _bytesReadWritten = 0; - return count; + + masterReceive(&_bytesToReadWrite, _hostBuffer, _clientAddress, sendStop); // We ignore the error that gets returned + + return _bytesToReadWrite; } @@ -805,8 +803,7 @@ void TwoWire::beginTransmission(uint8_t address) { */ uint8_t TwoWire::endTransmission(bool sendStop) { // transmit (blocking) - - return masterTransmit(_bytesToReadWrite, _clientAddress, _hostBuffer, sendStop); + return masterTransmit(&_bytesToReadWrite, _hostBuffer, _clientAddress, sendStop); } @@ -815,29 +812,30 @@ uint8_t TwoWire::endTransmission(bool sendStop) { *@brief masterTransmit sends a host WRITE with the specified client address * * This function will write an arbitrary number of bytes to the TWI module - * and read the data from the specified buffer. This allows the user to use + * and read the data from the specified buffer. This allows the user to use * custom sized buffers and avoids extra copy operations through write. * - *@param auto length - amount of bytes to be read (first arg so it is placed in r24:r25) - *@param uint8_t addr - the address of the client (7-bit) + *@param auto* length - pointer to a 8/16 bit variable containing the length. Will be overwritten with the amount of written bytes *@param uint8_t* buffer - pointer to the memory area to be read from. + *@param uint8_t addr - the address of the client (7-bit) *@param uint8_t/bool sendStop - if the transaction should be terminated with a STOP condition * *@return uint8_t *@retval errors (see endTransmission) */ -uint8_t TwoWire::masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint8_t sendStop) { +uint8_t TwoWire::masterTransmit(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop) { TWI_t* module = _module; __asm__ __volatile__("\n\t" : "+z"(module)); - + TWI_INIT_ERROR; uint8_t currentSM; uint8_t currentStatus; uint8_t stat = 0; + auto dataToWrite = *length; #if defined (TWI_TIMEOUT_ENABLE) uint16_t timeout = (F_CPU/1000); #endif - + if ((module->MCTRLA & TWI_ENABLE_bm) == 0x00) { // If the module is disabled, abort return TWI_ERR_UNINIT; } @@ -845,7 +843,7 @@ uint8_t TwoWire::masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint while (true) { currentStatus = module->MSTATUS; currentSM = currentStatus & TWI_BUSSTATE_gm; // get the current mode of the state machine - + if (currentSM == TWI_BUSSTATE_UNKNOWN_gc) { // If the bus was not initialized return TWI_ERR_UNINIT; // abort } @@ -867,7 +865,7 @@ uint8_t TwoWire::masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint TWI_SET_ERROR(TWI_ERR_BUS_ARB); // set error flag break; // leave TX loop } - + if (currentSM != TWI_BUSSTATE_BUSY_gc) { // Undefined was excluded, so make sure it's IDLE or OWNER if (stat == 0x00) { // At the start, we send the ADDR module->MADDR = ADD_WRITE_BIT(addr); // clear bit 0 @@ -879,17 +877,17 @@ uint8_t TwoWire::masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint if (currentStatus & TWI_WIF_bm) { // ADDR was sent, check for completed write if (currentStatus & TWI_RXACK_bm) { // got a NACK, see how much was written if (stat & 0x02) { // bit 1 set, data was already sent - if (length != 0) // the client may send an ACK at the end. If we + if (dataToWrite != 0) // the client may send an ACK at the end. If we TWI_SET_ERROR(TWI_ERR_ACK_DAT); // transferred everything, we can ignore the NACK } else { // otherwise, no data sent, ADDR NACK TWI_SET_ERROR(TWI_ERR_ACK_ADR); } break; } else { // No NACK on write - if (length != 0) { // check if there is data to be written + if (dataToWrite != 0) { // check if there is data to be written module->MDATA = *buffer; // Writing to the register to send data buffer++; - length--; + dataToWrite--; stat |= 0x02; // remember that we've sent data #if defined (TWI_TIMEOUT_ENABLE) timeout = (F_CPU/1000); // reset timeout @@ -903,7 +901,7 @@ uint8_t TwoWire::masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint } /* currentSM != TWI_BUSSTATE_BUSY_gc */ } /* while */ - + *length -= dataToWrite; if ((sendStop != 0) || (TWI_ERR_SUCCESS != TWI_GET_ERROR)) { module->MCTRLB = TWI_MCMD_STOP_gc; // Send STOP } @@ -1285,7 +1283,7 @@ void TwoWire::deselectSlaveBuffer(void) { -void TwoWire::HandleSlaveIRQ(TwoWire* wire_s) { +void TwoWire::HandleSlaveIRQ(TwoWire *wire_s) { if (wire_s == NULL) { return; } @@ -1314,7 +1312,7 @@ void TwoWire::HandleSlaveIRQ(TwoWire* wire_s) { if (clientStatus & TWI_APIF_bm) { // Address/Stop Bit set - + if ((*head) > 0) { // At this point, we have either a START, REPSTART or a STOP if (wire_s->user_onReceive != NULL) { // at START, head should be 0, as it was set so on the last STOP wire_s->user_onReceive((*head)); // otherwise, we notify the use sketch, as we have an REPSTART/STOP @@ -1345,7 +1343,7 @@ void TwoWire::HandleSlaveIRQ(TwoWire* wire_s) { } } else { // Stop bit set popSleep(); - + (*head) = 0; // clear whatever might be left due errors (*tail) = 0; action = TWI_SCMD_COMPTRANS_gc; // "Wait for any Start (S/Sr) condition" @@ -1416,13 +1414,6 @@ void TwoWire::onRequest(void (*function)(void)) { } -#if defined(TWI_READ_ERROR_ENABLED) && defined(TWI_ERROR_ENABLED) -uint8_t TwoWire::returnError() { - return vars._errors; -} -#endif - - /** *@brief TWI0 Slave Interrupt vector */ diff --git a/megaavr/libraries/Wire/src/Wire.h b/megaavr/libraries/Wire/src/Wire.h index d0485315..1e9e199d 100644 --- a/megaavr/libraries/Wire/src/Wire.h +++ b/megaavr/libraries/Wire/src/Wire.h @@ -18,7 +18,7 @@ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts - Modified 2021-2022 by MX682X for megaTinyCore and DxCore. + Modified 2021-2023 by MX682X for megaTinyCore and DxCore. Added Support for Simultaneous master/slave, dual mode and Wire1. */ @@ -43,9 +43,9 @@ * and while the enhanced wire library *will* fit on 2k parts, you have very little flash left for anything else. * and the practicality of using it there is limited. */ - - - #ifndef ADD_READ_BIT + + +#ifndef ADD_READ_BIT #define ADD_READ_BIT(address) (address | 0x01) #endif #ifndef ADD_WRITE_BIT @@ -62,10 +62,11 @@ #error "This part only provides a single Wire interface." #endif +/* Instead of requiring changes to the library to switch between DxCore and megaTinyCore, we can check + * if the part supports dual mode. Goal is that the identical library can be used on both, so updates + * in one can be propagated to the other by just copying files. + */ #if ((defined(TWI0_DUALCTRL) && !defined(TWI_USING_WIRE1)) || (defined(TWI1_DUALCTRL) && defined(TWI_USING_WIRE1))) - /* Instead of requiring changes to the library to switch between DxCore and megaTinyCore, we can check - * if the part supports dual mode. Goal is that the identical library can be used on both, so updates - * in one can be propagated to the other by just copying files. */ #define TWI_DUALCTRL // This identifies if the device supports dual mode, where slave pins are different from the master pins #endif @@ -132,7 +133,7 @@ #define TWI_TIMEOUT_ENABLE // Enabled by default, might be disabled for debugging or other reasons #define TWI_ERROR_ENABLED // Enabled by default, TWI Master Write error functionality -//#define TWI_READ_ERROR_ENABLED // Enabled on Master Read too +#define TWI_READ_ERROR_ENABLED // Enabled on Master Read too //#define DISABLE_NEW_ERRORS // Disables the new error codes and returns TWI_ERR_UNDEFINED instead. // Errors from Arduino documentation: @@ -207,30 +208,30 @@ class TwoWire: public Stream { private: TWI_t *_module; uint8_t MasterCalcBaud(uint32_t frequency); - + uint8_t client_irq_mask; struct twiDataBools _bools; // the structure to hold the bools for the class #if defined(TWI_READ_ERROR_ENABLED) uint8_t _errors; #endif - + void (*user_onRequest)(void); void (*user_onReceive)(int); - + uint8_t _clientAddress; twi_buf_index_t _bytesToReadWrite; twi_buf_index_t _bytesReadWritten; twi_buf_index_t _bytesTransmittedS; #if defined(TWI_MANDS) - uint8_t _incomingAddress; - twi_buf_index_t _bytesToReadWriteS; - twi_buf_index_t _bytesReadWrittenS; + uint8_t _incomingAddress; + twi_buf_index_t _bytesToReadWriteS; + twi_buf_index_t _bytesReadWrittenS; #endif - + uint8_t _hostBuffer[TWI_BUFFER_LENGTH]; #if defined(TWI_MANDS) - uint8_t _clientBuffer[TWI_BUFFER_LENGTH]; + uint8_t _clientBuffer[TWI_BUFFER_LENGTH]; #endif public: @@ -258,9 +259,9 @@ class TwoWire: public Stream { } twi_buf_index_t requestFrom(uint8_t address, twi_buf_index_t quantity, uint8_t sendStop = 1); - - uint8_t masterTransmit(auto length, uint8_t addr, uint8_t* buffer, uint8_t sendStop); - auto masterReceive(auto length, uint8_t addr, uint8_t* buffer, uint8_t sendStop); + + uint8_t masterTransmit(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop); + uint8_t masterReceive(auto *length, uint8_t *buffer, uint8_t addr, uint8_t sendStop); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *, size_t); @@ -301,11 +302,7 @@ class TwoWire: public Stream { } size_t readBytes(char *data, size_t quantity); - #if defined(TWI_READ_ERROR_ENABLED) && defined(TWI_ERROR_ENABLED) - uint8_t returnError(); - #endif - - static void HandleSlaveIRQ(TwoWire* wire_s); + static void HandleSlaveIRQ(TwoWire *wire_s); }; diff --git a/megaavr/libraries/Wire/src/twi_pins.c b/megaavr/libraries/Wire/src/twi_pins.c index 7a828b9b..6f65970f 100644 --- a/megaavr/libraries/Wire/src/twi_pins.c +++ b/megaavr/libraries/Wire/src/twi_pins.c @@ -216,9 +216,9 @@ bool TWI0_Pins(uint8_t sda_pin, uint8_t scl_pin) { #endif #if defined(PIN_WIRE_SDA_PINSWAP_2) #if !defined(ERRATA_TWI0_MUX2) - if (sda_pin == PIN_WIRE_SDA_PINSWAP_2 && scl_pin == PIN_WIRE_SCL_PINSWAP_2) { - PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI0_ALT2_gc; - return true; + if (sda_pin == PIN_WIRE_SDA_PINSWAP_2 && scl_pin == PIN_WIRE_SCL_PINSWAP_2) { + PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI0_ALT2_gc; + return true; #else return false; #endif @@ -269,7 +269,7 @@ bool TWI0_swap(uint8_t state) { #if defined(PORTMUX_TWI0_bm) if (state == 1) { // Use pin swap - PORTMUX.CTRLB |= PORTMUX_TWI0_bm; + PORTMUX.CTRLB |= PORTMUX_TWI0_bm; return true; } else if (state == 0) { // Use default configuration @@ -305,8 +305,8 @@ bool TWI0_swap(uint8_t state) { if (state == 2) { // Use pin swap #if !defined(ERRATA_TWI0_MUX2) - PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI0_ALT2_gc; - return true; + PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI0_ALT2_gc; + return true; #else return false; #endif @@ -551,7 +551,7 @@ uint8_t TWI0_checkPinLevel(void) { #if defined(TWI1) void TWI1_ClearPins() { #if defined(PIN_WIRE1_SDA_PINSWAP_2) || defined(TWI1_DUALCTRL) - uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm; + uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm; #endif #if defined(PIN_WIRE1_SDA_PINSWAP_2) if (portmux == PORTMUX_TWI1_ALT2_gc) { // make sure we don't get errata'ed @@ -582,7 +582,7 @@ bool TWI1_Pins(uint8_t sda_pin, uint8_t scl_pin) { #if (defined(PIN_WIRE1_SDA_PINSWAP_1) || defined(PIN_WIRE1_SDA_PINSWAP_2)) // Danger: 'portmux' in this context means all the other settings in portmux, since we're replacing the PORTMUX setting for TWI1, and will bitwise-or with the _gc constants. // Elsewhere, 'portmux' refers to the setting for this peripheral only, and we compare it to PORTMUX_TWI1_xxx_gc - uint8_t portmux = PORTMUX.TWIROUTEA & ~PORTMUX_TWI1_gm; + uint8_t portmux = PORTMUX.TWIROUTEA & ~PORTMUX_TWI1_gm; #if defined(PIN_WIRE1_SDA_PINSWAP_2) if (sda_pin == PIN_WIRE1_SDA_PINSWAP_2 && scl_pin == PIN_WIRE1_SCL_PINSWAP_2) { // Use pin swap @@ -628,7 +628,7 @@ bool TWI1_Pins(uint8_t sda_pin, uint8_t scl_pin) { bool TWI1_swap(uint8_t state) { // Danger: 'portmux' in this context means all the other settings in portmux, since we're replacing the PORTMUX setting for TWI1, and will bitwise-or with the _gc constants. // Elsewhere, 'portmux' refers to the setting for this peripheral only, and we compare it to PORTMUX_TWI1_xxx_gc - uint8_t portmux = PORTMUX.TWIROUTEA & (~PORTMUX_TWI1_gm); + uint8_t portmux = PORTMUX.TWIROUTEA & (~PORTMUX_TWI1_gm); #if defined(PIN_WIRE1_SDA_PINSWAP_2) if (state == 2) { // Use pin swap @@ -666,7 +666,7 @@ bool TWI1_swap(uint8_t state) { void TWI1_usePullups() { #if defined(PIN_WIRE1_SDA_PINSWAP_2) || defined(TWI1_DUALCTRL) - uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm; + uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm; #endif PORT_t *port; #if defined(PORTB) //All parts with a TWI1 have a PORTF @@ -678,6 +678,7 @@ void TWI1_usePullups() { #else port = &PORTF; #endif + port->OUTCLR = 0x0C; // bits 2 and 3 port->PIN2CTRL |= PORT_PULLUPEN_bm; port->PIN3CTRL |= PORT_PULLUPEN_bm; @@ -735,8 +736,8 @@ uint8_t TWI1_setConfig(bool smbuslvl, bool longsetup, uint8_t sda_hold, bool smb } else if (sda_hold_dual || smbuslvl_dual) { return 0x04; } - #endif - return 0; + #endif + return 0; } diff --git a/megaavr/libraries/Wire/src/twi_pins.h b/megaavr/libraries/Wire/src/twi_pins.h index 6bb8493b..d18de535 100644 --- a/megaavr/libraries/Wire/src/twi_pins.h +++ b/megaavr/libraries/Wire/src/twi_pins.h @@ -34,9 +34,9 @@ //If a core provides badArg() and badCall(), it should define CORE_HAS_ERRORFUNS to = 1. //otherwise we need to add them in here - this is for ease of using with MegaCoreX #if !defined(CORE_HAS_ERRORFUNS) - void badArg(const char*) __attribute__((error(""))); //used when an argument is known at compile time to be an invalid value + void badArg(const char *) __attribute__((error(""))); //used when an argument is known at compile time to be an invalid value // but the function being called is valid to call at this time. - void badCall(const char*) __attribute__((error(""))); //used when it is known at compile time that a whole function cannot be called, + void badCall(const char *) __attribute__((error(""))); //used when it is known at compile time that a whole function cannot be called, // regardless of what arguments it is passed! Typically this is things like timekeeping when millis is disabled, and functions that // only work on some parts - things like TWI1_swap() which has only 1 option on some parts. #endif