I'm new to the forum and have only been working with GCB and the AVR128DA28 for a few weeks.
The AVR128DA28 has a hardware bug at TWI (I2C).
The Output Pin Override Does not Function as Expected:
It overrides the output pin driver but not the output value when TWI is enabled. The output on the
line will always be high when the value in the PORTx.OUT register is ‘1’ for the pins corresponding to
the SDA or SCL.
Work Around:
Ensure that the values in the PORTx.OUT register corresponding to the SCL and SDA pins are ‘0’
before enabling the TWI.
(Source: AVR128DA-28-32-48-64-SilConErrataClarif-DS80000882.pdf)
So hardware i2c does not work with AVR128DA28 in GCB, but I found a workaround for GCB. I only tested this with the AVR128DA28, but it should also work with the other chips affected by the error.
The attached file contains the necessary additions.
Very good catch. Looks like I got away with this when I was testing. :-)
Yes, the code that you add ( quite correctly ) sets HI2C_DATA = 0 and HI2C_CLOCK = 0 (and any preceding bit-banging recovery code in AVRDXTWI0MODE) is required for reliable hardware I²C/TWI operation on the mega4809.
Why it is required
Potential I²C bus lock-up on AVR devices
The AVR hardware TWI module (especially on the DX series like mega4809) can encounter a "stuck bus" condition at power-up, after a reset, or if a previous transaction was interrupted. A slave device may hold SDA low, preventing normal operation. The hardware TWI peripheral cannot recover from this on its own – it will simply hang or report bus errors.
Standard recovery technique requires bit-banging
The only reliable way to clear a stuck bus is to manually bit-bang clock pulses on SCL while monitoring/forcing SDA:
Configure SCL as output and toggle it (typically 9+ times) while keeping SDA as input or forcing it high.
This allows any slave holding SDA low to complete its byte and release the line.
After recovery, a STOP condition is generated (SCL high → SDA high).
GCBASIC's AVRDXTWI0MODE routine implements exactly this recovery sequence using bit-banging before enabling the hardware TWI peripheral.
Role of setting HI2C_DATA = 0 and HI2C_CLOCK = 0
These lines are part of the recovery/initialisation sequence:
They force both lines low initially (as outputs) to ensure a known state.
Then the bit-banging code toggles SCL while pulling SDA high (or leaving it as input with pull-up) to generate clock pulses.
After recovery, the pins are reconfigured for hardware TWI use (open-drain with pull-ups).
Without forcing the lines low first, the recovery sequence could start in an undefined state and fail.
Why not rely on hardware TWI alone?
The mega4809 TWI peripheral has no built-in bus recovery. Microchip's application notes (e.g., AN3122 on TWI bus recovery) and AVR forums consistently recommend manual clocking (bit-banging) before enabling the peripheral. GCBASIC follows this best practice.
Consequences of removing it
On a clean power-up with no prior issues → it might work anyway.
In real-world scenarios (brown-out, incomplete transaction, slave malfunction) → the TWI will lock up, HI2CStart/HI2CSend will timeout or fail, and the GLCD will not initialise correctly.
Conclusion
The bit-banging recovery (including the HI2C_DATA = 0 / HI2C_CLOCK = 0 lines) is required for robust operation. It is not redundant – it protects against common real-world bus lock-up conditions that the hardware TWI module cannot handle itself. Removing it would make the demo (and any hardware I²C code) fragile and prone to intermittent failures. This is why GCBASIC includes it automatically when HI2CMode Master is used on AVR DX devices.
I will change the standard library immediately and you will see this in the next release. See attached for the .h that I will submit to the next release.
No doubt, the bit-banging recovery is required for robust operation.
I tested your new file with my AVR128DA28 and – unfortunately, it doesn't work.
The AVR128DA28 has a bug as described above. I had hoped that my posted suggestion would both provide a workaround for the bug and prevent that two transistors working against each other on the I2C bus (the slave pulls to GND and the AVR pulls to +5V). At best, this results in half the operating voltage on the bus, and at worst, it can damage the output drivers on the chips.
Is there a plausible reason why the bit-bang doesn't use the direction registers as I demonstrated?
If that's the case, and you continue using the SDA + SDL outputs as before, you can also work around the hardware bug as follows:
After the bit bang, set both port pins to input (the bus remains unchanged) and then set both outputs to low before accessing the TWI0 registers. I haven't tested this myself, but it corresponds to the workaround mentioned by Microchip.
Ralf
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, there are two 4.7kΩ pull-up resistors (adapter: YwRobot LCD1602 IIC).
It works for me with the subroutine I posted in my first post.
I copied the template for this subroutine from your hwi2c.h library. Using a trick I found in your help file, it replaces the subroutine AVRDXTWI0MODE (which is otherwise found in the ASM file).
In my (replacement) subroutine, I made changes to the bus drivers for the bit bang so that the I2C ports on the AVR128DA28 function as open-collector outputs. A nice side effect is that the hardware bug in the AVR128DA28 no longer occurs because the OUT registers of the I2C ports always remain low.
And, sorry, after you posted the modified hwi2c.h file, I thought you had incorporated my changes exactly and I could now do without the trick from your help file.
Regards,
Ralf
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
// Bit bang a START/STOP sequenceDirHI2C_DATAIn// SDA and SCL idle highDirHI2C_CLOCKInWait1usDirHI2C_DATAOut// then, SDA low while SCL still highWait1us// for this amount of timeDirHI2C_CLOCKOut// end with SCL low, ready to clockDirHI2C_DATAOutWait1us// let ports settleDirHI2C_CLOCKIn// make SCL=1 firstWait1us// hold for normal clock width timeDirHI2C_DATAIn// then make SDA=1 afterwardswait1ms
I will setup other AVRs to test this has not broken anything. I completed extensive tests on the MEGA4809 family. It will be easy to isolate these different IC2 pieces of code if required.
Good work.
Evan
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Why would that change give stability? The reset sequence needs the ports to be output to drive the lines high and low. So, the change would now stop the reset from working.
The problem may be related have the I2C/TWI working/operational when the reset sequence operating. I have looked into that and this could cause issues.
Try this - this caches the setting. We need to cache/restore as the I2CInit a prior method.
Sub AVRDxTWI0Mode
Dim HI2CCurrentMode as Byte
Dim TWI0Timeout as Byte Alias HI2CWaitMSSPTimeout
Dim HI2C1StateMachine as byte
Dim HI2CACKPOLLSTATE as Byte
Dim TWI0ACKPOLLSTATE as Byte Alias HI2CACKPOLLSTATE
Dim HI2C1lastError as Byte
Dim TWI0LastError as Byte Alias HI2C1lastError
Dim TWIStateCache as Byte Alias HI2C1lastError
HI2CCurrentMode = 0
TWI0Timeout = 0
TWIStateCache = TWI0_MCTRLA
If TWI0_MCTRLA.TWI_ENABLE_bp = 1 Then
// Disable TWI explicitly, but, the state must
TWI0_MCTRLA.TWI_ENABLE_bp = 0
End If
Dir HI2C_DATA OUT
Dir HI2C_CLOCK OUT
Do
TWI0Timeout = 0
HI2C_DATA = 0
HI2C_CLOCK = 0
Do
If TWI0Timeout = 255 then Exit Sub // Users can check for TWI0Timeout = TRUE
TWI0Timeout++
// Reset the TWI!
TWI0_MCTRLB = TWI_FLUSH_bm
TWI0_MSTATUS= 0
TWI0_CTRLA = 0
TWI0_MCTRLA = 0
TWI0_MCTRLB = 0
// Bit bang a START/STOP sequence
Dir HI2C_DATA In // SDA and SCL idle high
Dir HI2C_CLOCK In
Wait 1 us
Dir HI2C_DATA Out // then, SDA low while SCL still high
Wait 1 us // for this amount of time
Dir HI2C_CLOCK Out // end with SCL low, ready to clock
Dir HI2C_DATA Out
Wait 1 us // let ports settle
Dir HI2C_CLOCK In // make SCL=1 first
Wait 1 us // hold for normal clock width time
Dir HI2C_DATA In // then make SDA=1 afterwards
wait 1 ms
Loop While ( TWI0_MSTATUS and 3 ) = 3
TWI0_CTRLA = SCRIPT_TWI_FAST_MODE // for slow mode = 0
TWI0_DUALCTRL = 0
//Debug Run
TWI0_DBGCTRL = 0x00
//Master Baud Rate Control
TWI0_MBAUD = SCRIPT_TWI_BAUD //(uint8_t)TWI0_BAUD(100000, 0)
TWI0_MCTRLA = 0x02
TWI0_MSTATUS = 0x61
//Master Address
TWI0_MADDR = 0x00
//FLUSH ACKACT ACK MCMD NOACT
TWI0_MCTRLB = 0x08
//Master Data
TWI0_MDATA = 0x00
TWI0_MCTRLA.0 = 1
wait 10 ms
Loop While ( TWI0_MSTATUS and 3 ) = 3
// Restore the cache
TWI0_MCTRLA = TWIStateCache
End sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Meanwhile, my LCD was also displaying strange characters with my last change. As you rightly suspected, this error has nothing to do with what I changed last. Unfortunately, errors that only occur sporadically are the hardest to find. I can only try different things and do a lot of testing. Perhaps it's also due to my display. I've now connected my YwRobot.
Why would that change give stability? The reset sequence needs the ports to be output to drive the lines high and low. So, the change would now stop the reset from working.
Yes, sorry, that was my mistake. It should actually be changed as shown below. I wanted to avoid the current spike that could occur if the slave pulls the bus low while the AVR's output pin is still high. Does it thus (hown below) the requirements of the reset sequence? It meets the requirements of the AVRs regarding the hardware error. And a short on the I2C bus can no longer occur.
Do
TWI0Timeout = 0
HI2C_DATA = 0
HI2C_CLOCK = 0
Dir HI2C_DATA OUT
Dir HI2C_CLOCK OUT
Do
We need to cache/restore as the I2CInit a prior method.
I don't understand why you're saving the state of TWI0_MCTRLA at the beginning of the subroutine and restoring it at the end. TWI0_MCTRLA is modified multiple times between these two commands. These changes were previously available to the rest of the program. Now, the state of TWI0_MCTRLA before the subroutine was executed is available to the rest of the program. Is this intentional?
Ralf
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
From my experience I would increase the intra byte delays. 1 ms can make a huge difference, or try a slower LCDSpeed
re Caching. The Caching now supports reusing this mode function to clear an I2C. Whatever the state was, it is reverted. Costs no RAM and only a few clock cycles.
So, to resolve. Look at the delays.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I recently bought a different LCD2004 display and thoroughly tested the program with it. The strange characters didn't reappear on the new display. However, when I reconnected my old LCD2004 display, the strange characters reappeared at irregular intervals. Therefore, I assume it's a hardware defect in my display.
I also included the subroutine AVRDxTWI0Mode with "caching/restoring TWI0_MCTRLA" in the file hwi2c.h and tested it. For the test, I used an AVR128DA28. The program didn't run with it. Therefore, I added a few more lines of code. The TWI0_MCTRLA register will now only be restored if it has been backed up beforehand. This prevents a random value from being written to TWI0_MCTRLA. Now the program runs. I have marked the changed lines with a comment.
This version runs flawlessly on my AVR128DA28 and meets the requirements of the I2C bus. I hope this change doesn't hinder the "I2CInit a prior method". Unfortunately, I cannot judge that because I am not familiar enough with the internal functions of GCB.
Ralf
Sub AVRDxTWI0Mode
Dim HI2CCurrentMode as Byte
Dim TWI0Timeout as Byte Alias HI2CWaitMSSPTimeout
Dim HI2C1StateMachine as byte
Dim HI2CACKPOLLSTATE as Byte
Dim TWI0ACKPOLLSTATE as Byte Alias HI2CACKPOLLSTATE
Dim HI2C1lastError as Byte
Dim TWI0LastError as Byte Alias HI2C1lastError
Dim TWIStateCache as Byte Alias HI2C1lastError
HI2CCurrentMode = 0
TWI0Timeout = 0
// Set the output driver to LOW first before configuring them
// as outputs, otherwise a short circuit may occur.
HI2C_DATA = 0 // added by Ralf
HI2C_CLOCK = 0 // added by Ralf
TWIStateCache = TWI0_MCTRLA
If TWI0_MCTRLA.TWI_ENABLE_bp = 1 Then
// Disable TWI explicitly, but, the state must
TWI0_MCTRLA.TWI_ENABLE_bp = 0
End If
Dir HI2C_DATA OUT
Dir HI2C_CLOCK OUT
Do
TWI0Timeout = 0
HI2C_DATA = 0
HI2C_CLOCK = 0
Do
If TWI0Timeout = 255 then Exit Sub // Users can check for TWI0Timeout = TRUE
TWI0Timeout++
// Reset the TWI!
TWI0_MCTRLB = TWI_FLUSH_bm
TWI0_MSTATUS= 0
TWI0_CTRLA = 0
TWI0_MCTRLA = 0
TWI0_MCTRLB = 0
// Bit bang a START/STOP sequence
Dir HI2C_DATA In // SDA and SCL idle high
Dir HI2C_CLOCK In
Wait 1 us
Dir HI2C_DATA Out // then, SDA low while SCL still high
Wait 1 us // for this amount of time
Dir HI2C_CLOCK Out // end with SCL low, ready to clock
Dir HI2C_DATA Out
Wait 1 us // let ports settle
Dir HI2C_CLOCK In // make SCL=1 first
Wait 1 us // hold for normal clock width time
Dir HI2C_DATA In // then make SDA=1 afterwards
wait 1 ms
Loop While ( TWI0_MSTATUS and 3 ) = 3
TWI0_CTRLA = SCRIPT_TWI_FAST_MODE // for slow mode = 0
TWI0_DUALCTRL = 0
//Debug Run
TWI0_DBGCTRL = 0x00
//Master Baud Rate Control
TWI0_MBAUD = SCRIPT_TWI_BAUD //(uint8_t)TWI0_BAUD(100000, 0)
TWI0_MCTRLA = 0x02
TWI0_MSTATUS = 0x61
//Master Address
TWI0_MADDR = 0x00
//FLUSH ACKACT ACK MCMD NOACT
TWI0_MCTRLB = 0x08
//Master Data
TWI0_MDATA = 0x00
TWI0_MCTRLA.0 = 1
wait 10 ms
Loop While ( TWI0_MSTATUS and 3 ) = 3
// Restore cache - if data is cached
If TWIStateCache.TWI_ENABLE_bp = 1 Then // added by Ralf
TWI0_MCTRLA = TWIStateCache
End If // added by Ralf
End sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
First, really well done on isolating the LCD issue — testing with a second module was exactly the right diagnostic step, and it’s great that you were able to confirm the behaviour so clearly. That kind of methodical approach always pays off.
On the TWI side, your routine does reset the peripheral, but it’s worth spelling out exactly what’s happening so the behaviour is fully clear.
What your code resets correctly
Inside the routine, you explicitly clear:
TWI0_MCTRLB (with TWI_FLUSH_bm)
TWI0_MSTATUS
TWI0_CTRLA
TWI0_MCTRLA
TWI0_MCTRLB again
This sequence forces the TWI master state machine back to its idle state, clears pending commands, and removes any lingering status flags. In other words, the internal TWI hardware is fully reset, and that part is absolutely correct.
What happens on the bus lines
The more subtle part is the bus‑line recovery. Your routine:
Drives SDA/SCL low briefly
Then switches both pins to input, allowing them to float high via pull‑ups
Then toggles direction to create transitions
This works because the pull‑ups restore the lines to a valid idle level. It’s a practical approach, and in many real‑world cases it’s enough to get the bus unstuck.
How this differs from the recommended bus‑clear sequence
Microchip’s recommended I²C bus‑clear procedure — described in application notes such as AVR315: Using the TWI Module as I²C Master — is more explicit. The official sequence is:
Drive SCL high (not float it).
Pulse SCL low→high up to 9 times to release a slave that may be holding SDA low.
Drive SDA high.
Generate a STOP condition (SDA rising while SCL is high).
Your routine doesn’t actively drive the lines high; instead, it releases them and relies on the pull‑ups. That’s why it works, but it’s not the full, guaranteed bus‑clear method recommended by Microchip.
Where this leaves things
Given that:
your hardware behaves correctly with this approach,
the TWI peripheral is fully reset, and
the floating‑line recovery is sufficient for your setup,
I’m completely happy to leave your implementation as‑is. It’s a practical solution, and it clearly works reliably for your environment.
If you ever want to revisit the bus‑clear logic in the future, the AVR315 sequence is the reference point — but there’s no need to change anything right now.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thank you very much for the detailed explanations.
On which page of the AVR315 application note did you read about the I²C bus-clear procedure? I can't find anything about it there. Is it possible that you read about it somewhere else?
In certain applications, series resistors (e.g., 220 ohms) are installed in the I²C bus to protect the driver transistors (and prevent reflections on the bus). In this case, safe push-high operation is of course possible with the I²C bus-clear procedure.
My hardware behaves correctly with this approach.
GCB should be used with this modification, and if users report errors, I will gladly revise the subroutine.
Cheers
Ralf
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The AVR315 is the reference document. The reset uses turns off TWI and then issues STOPs etc. This cannot be achieved with the either the SDA or SCL floating.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello,
I'm new to the forum and have only been working with GCB and the AVR128DA28 for a few weeks.
The AVR128DA28 has a hardware bug at TWI (I2C).
The Output Pin Override Does not Function as Expected:
It overrides the output pin driver but not the output value when TWI is enabled. The output on the
line will always be high when the value in the PORTx.OUT register is ‘1’ for the pins corresponding to
the SDA or SCL.
Work Around:
Ensure that the values in the PORTx.OUT register corresponding to the SCL and SDA pins are ‘0’
before enabling the TWI.
(Source: AVR128DA-28-32-48-64-SilConErrataClarif-DS80000882.pdf)
So hardware i2c does not work with AVR128DA28 in GCB, but I found a workaround for GCB. I only tested this with the AVR128DA28, but it should also work with the other chips affected by the error.
The attached file contains the necessary additions.
Ralf - a great post. I have reviewed the change.
Very good catch. Looks like I got away with this when I was testing. :-)
Yes, the code that you add ( quite correctly ) sets HI2C_DATA = 0 and HI2C_CLOCK = 0 (and any preceding bit-banging recovery code in
AVRDXTWI0MODE) is required for reliable hardware I²C/TWI operation on the mega4809.Why it is required
Potential I²C bus lock-up on AVR devices
The AVR hardware TWI module (especially on the DX series like mega4809) can encounter a "stuck bus" condition at power-up, after a reset, or if a previous transaction was interrupted. A slave device may hold SDA low, preventing normal operation. The hardware TWI peripheral cannot recover from this on its own – it will simply hang or report bus errors.
Standard recovery technique requires bit-banging
The only reliable way to clear a stuck bus is to manually bit-bang clock pulses on SCL while monitoring/forcing SDA:
GCBASIC's
AVRDXTWI0MODEroutine implements exactly this recovery sequence using bit-banging before enabling the hardware TWI peripheral.These lines are part of the recovery/initialisation sequence:
Without forcing the lines low first, the recovery sequence could start in an undefined state and fail.
Why not rely on hardware TWI alone?
The mega4809 TWI peripheral has no built-in bus recovery. Microchip's application notes (e.g., AN3122 on TWI bus recovery) and AVR forums consistently recommend manual clocking (bit-banging) before enabling the peripheral. GCBASIC follows this best practice.
Consequences of removing it
HI2CStart/HI2CSendwill timeout or fail, and the GLCD will not initialise correctly.Conclusion
The bit-banging recovery (including the
HI2C_DATA = 0/HI2C_CLOCK = 0lines) is required for robust operation. It is not redundant – it protects against common real-world bus lock-up conditions that the hardware TWI module cannot handle itself. Removing it would make the demo (and any hardware I²C code) fragile and prone to intermittent failures. This is why GCBASIC includes it automatically whenHI2CMode Masteris used on AVR DX devices.I will change the standard library immediately and you will see this in the next release. See attached for the .h that I will submit to the next release.
Evan/Anobium
Any build after 1547 will have this revised library.
:-)
Hi Evan,
Thank you for the quick response.
No doubt, the bit-banging recovery is required for robust operation.
I tested your new file with my AVR128DA28 and – unfortunately, it doesn't work.
The AVR128DA28 has a bug as described above. I had hoped that my posted suggestion would both provide a workaround for the bug and prevent that two transistors working against each other on the I2C bus (the slave pulls to GND and the AVR pulls to +5V). At best, this results in half the operating voltage on the bus, and at worst, it can damage the output drivers on the chips.
Is there a plausible reason why the bit-bang doesn't use the direction registers as I demonstrated?
If that's the case, and you continue using the SDA + SDL outputs as before, you can also work around the hardware bug as follows:
After the bit bang, set both port pins to input (the bus remains unchanged) and then set both outputs to low before accessing the TWI0 registers. I haven't tested this myself, but it corresponds to the workaround mentioned by Microchip.
Ralf
OK.
Do you have 4k (ish) pull-ups?
Make changes in the library to get this working? or, are you saying you new does work?
Yes, there are two 4.7kΩ pull-up resistors (adapter: YwRobot LCD1602 IIC).
It works for me with the subroutine I posted in my first post.
I copied the template for this subroutine from your hwi2c.h library. Using a trick I found in your help file, it replaces the subroutine AVRDXTWI0MODE (which is otherwise found in the ASM file).
In my (replacement) subroutine, I made changes to the bus drivers for the bit bang so that the I2C ports on the AVR128DA28 function as open-collector outputs. A nice side effect is that the hardware bug in the AVR128DA28 no longer occurs because the OUT registers of the I2C ports always remain low.
And, sorry, after you posted the modified hwi2c.h file, I thought you had incorporated my changes exactly and I could now do without the trick from your help file.
Regards,
Ralf
Let us get you change into the main library. Can you try and adapt the .h post be me previously?
I will then incorporate here.
Hi,
The modified library hwi2c.h is attached.
Thank you.
Thanks. I do see a few more changes.
I will setup other AVRs to test this has not broken anything. I completed extensive tests on the MEGA4809 family. It will be easy to isolate these different IC2 pieces of code if required.
Good work.
Evan
... it's not over now.
Unfortunately, I had to make another change to the hwi2c.h library.
The following error occurred:
During a continuous test, the LCD displayed strange characters after about half an hour.
I then changed the following:
With this, I have now tested the LCD for 12 hours without any errors.
The update is attached.
Ralf
Why would that change give stability? The reset sequence needs the ports to be output to drive the lines high and low. So, the change would now stop the reset from working.
The problem may be related have the I2C/TWI working/operational when the reset sequence operating. I have looked into that and this could cause issues.
Try this - this caches the setting. We need to cache/restore as the I2CInit a prior method.
Hi,
Meanwhile, my LCD was also displaying strange characters with my last change. As you rightly suspected, this error has nothing to do with what I changed last. Unfortunately, errors that only occur sporadically are the hardest to find. I can only try different things and do a lot of testing. Perhaps it's also due to my display. I've now connected my YwRobot.
Why would that change give stability? The reset sequence needs the ports to be output to drive the lines high and low. So, the change would now stop the reset from working.
Yes, sorry, that was my mistake. It should actually be changed as shown below. I wanted to avoid the current spike that could occur if the slave pulls the bus low while the AVR's output pin is still high. Does it thus (hown below) the requirements of the reset sequence? It meets the requirements of the AVRs regarding the hardware error. And a short on the I2C bus can no longer occur.
We need to cache/restore as the I2CInit a prior method.
I don't understand why you're saving the state of TWI0_MCTRLA at the beginning of the subroutine and restoring it at the end. TWI0_MCTRLA is modified multiple times between these two commands. These changes were previously available to the rest of the program. Now, the state of TWI0_MCTRLA before the subroutine was executed is available to the rest of the program. Is this intentional?
Ralf
From my experience I would increase the intra byte delays. 1 ms can make a huge difference, or try a slower LCDSpeed
re Caching. The Caching now supports reusing this mode function to clear an I2C. Whatever the state was, it is reverted. Costs no RAM and only a few clock cycles.
So, to resolve. Look at the delays.
Hi Evan,
I recently bought a different LCD2004 display and thoroughly tested the program with it. The strange characters didn't reappear on the new display. However, when I reconnected my old LCD2004 display, the strange characters reappeared at irregular intervals. Therefore, I assume it's a hardware defect in my display.
I also included the subroutine AVRDxTWI0Mode with "caching/restoring TWI0_MCTRLA" in the file hwi2c.h and tested it. For the test, I used an AVR128DA28. The program didn't run with it. Therefore, I added a few more lines of code. The TWI0_MCTRLA register will now only be restored if it has been backed up beforehand. This prevents a random value from being written to TWI0_MCTRLA. Now the program runs. I have marked the changed lines with a comment.
This version runs flawlessly on my AVR128DA28 and meets the requirements of the I2C bus. I hope this change doesn't hinder the "I2CInit a prior method". Unfortunately, I cannot judge that because I am not familiar enough with the internal functions of GCB.
Ralf
Hi Ralf,
First, really well done on isolating the LCD issue — testing with a second module was exactly the right diagnostic step, and it’s great that you were able to confirm the behaviour so clearly. That kind of methodical approach always pays off.
On the TWI side, your routine does reset the peripheral, but it’s worth spelling out exactly what’s happening so the behaviour is fully clear.
What your code resets correctly
Inside the routine, you explicitly clear:
TWI0_MCTRLB(withTWI_FLUSH_bm)TWI0_MSTATUSTWI0_CTRLATWI0_MCTRLATWI0_MCTRLBagainThis sequence forces the TWI master state machine back to its idle state, clears pending commands, and removes any lingering status flags. In other words, the internal TWI hardware is fully reset, and that part is absolutely correct.
What happens on the bus lines
The more subtle part is the bus‑line recovery. Your routine:
This works because the pull‑ups restore the lines to a valid idle level. It’s a practical approach, and in many real‑world cases it’s enough to get the bus unstuck.
How this differs from the recommended bus‑clear sequence
Microchip’s recommended I²C bus‑clear procedure — described in application notes such as AVR315: Using the TWI Module as I²C Master — is more explicit. The official sequence is:
Your routine doesn’t actively drive the lines high; instead, it releases them and relies on the pull‑ups. That’s why it works, but it’s not the full, guaranteed bus‑clear method recommended by Microchip.
Where this leaves things
Given that:
I’m completely happy to leave your implementation as‑is. It’s a practical solution, and it clearly works reliably for your environment.
If you ever want to revisit the bus‑clear logic in the future, the AVR315 sequence is the reference point — but there’s no need to change anything right now.
Hi Evan,
Thank you very much for the detailed explanations.
On which page of the AVR315 application note did you read about the I²C bus-clear procedure? I can't find anything about it there. Is it possible that you read about it somewhere else?
In certain applications, series resistors (e.g., 220 ohms) are installed in the I²C bus to protect the driver transistors (and prevent reflections on the bus). In this case, safe push-high operation is of course possible with the I²C bus-clear procedure.
My hardware behaves correctly with this approach.
GCB should be used with this modification, and if users report errors, I will gladly revise the subroutine.
Cheers
Ralf
The AVR315 is the reference document. The reset uses turns off TWI and then issues STOPs etc. This cannot be achieved with the either the SDA or SCL floating.