My Adventures in Writing a TWI Bitbang Driver

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want to share my experiences as a newbie with GNU-GCC for AVR. I looked hard for simple, generic, complete and documented I2C bitbang code for any AVR, but couldn't find this anywhere. My application is intended to be a hardware+software building block which can be integrated into any larger project/product. It's not terribly technologically elegant, but is very straight-forward and doesn't use the AVR TWI advanced function hardware. Any two pins on any one port can be used for SCLK and SDA (ACK*). No hardware timers are used - only software ones.

Any hardcopy of this code "is suitable for framing or wrapping fish".

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm amazed that in your searches you did not come across the old atmel app note, or peter fleury's code. Either way, I'm sure someone will find your contributions helpful... thanks for sharing.

[edit]
looking at your code, you have made a very dangerous implementation because you are driving the lines high and low. You also do not do clock monitoring at the bit level to check for clock stretching on the bus.

You should never be setting the pins as output high. Instead you should only ever be toggling between input, and output low. The high is generated by the external pull-ups on the bus. [the internal pull-ups are not strong enough for reliable I2C communications] If you really cannot afford proper external pull-up resistors, and the internal ones are strong enough, then there is an extra cycle penalty in your transitions, but in this case port should only ever be set as a 1 while the DDR bit is a 0. That means set it to 1 after clearing DDR, and clearing it before setting DDR to 1.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR300 and AVR302 come to mind.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Glitch is right.

If you *really* know what you are doing and what devices there are on the bus, you may be able to pull it off as push-pull outputs (some implementations have SCL always as output, but SDA as open-drain), but hell no for a generic drop-in implementation.

For starters your init is incomplete, if you reset your AVR when some device is transmitting (data or ack) to AVR, it is not possible to generate a stop condition (AVR can control clock, but other device for example keeps SDA=0).

Then your byte transmit code keeps SDA as output half a clock when the other device already puts ACK on the bus. Chaos will ensue when other device pulls SDA low for ACK while you still drive SDA high for half a clock.

Also there is no difference between sending address or data bytes, so address sending function could just call send data with correct address byte. No reason to have same code twice.

Also your stop makes SCL and SDA high immediately. SCL should already be high for long enough before SDA high.
Same thing with start. SCL should be reliably low before setting SDA low. You should read the I2C bus specs.

100kHz may not be good for everyone. Besides most chips use the 400kHz Fast Mode specs, which require certain timings. 100kHz Standard Mode devices can work with Fast Mode specs bus when clock is just kept under 100kHz, but Fast Mode specs devices on Standard Mode specs bus violate at least max rise/fall timings. So you want to support Fast Mode and 400kHz.

It would not be a big change to have SDA and SCL on different ports if needed. And the code would be perfect if it would support multiple buses too.