Beginner choices for working with i2c

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

Hey, total newbie question.  I'm shifting from using Arduino to a more bare-metal approach, and trying to figure out the best/easiest way to do i2c/TWI on an ATMega (328P right now, in an Uno).

 

I've seen Peter Fleury's library, and there's also the ASF library.  I've also seen some straight C code that just pokes the registers.  Anything else?

 

Any advice on which of the options is best?  On the particular project I'm working on right now, "best" is probably defined as "easiest learning curve, lowest power consumption, fastest running", in roughly that order.

 

Thanks.

This topic has a solution.
Last Edited: Sun. Jan 14, 2018 - 04:52 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greetings and welcome to AVR Freaks!

 

The Fleury library is highly recommended for I2C. One important note: DO NOT IGNORE ERROR VALUES RETURNED BY MANY FUNCTIONS!

 

As I recall, there is a version that uses the internal I2C (called TWI in AVR-wold) interface and a version with the SAME function calls to do it totally in software. In a M328P, use the internal hardware.

 

Forget ASF. It is minimally useable on something like a M328. It does not really make sense until you get to something like an XMega.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:
The Fleury library is highly recommended for I2C. One important note: DO NOT IGNORE ERROR VALUES RETURNED BY MANY FUNCTIONS!

Or hear the wrath of David Prentice!!  You have been warned wink

 

ka7ehk wrote:
Forget ASF.

Yes! Please!

 

What are you looking to control using I2C anyway?  Some of us might already have some code to share...

 

Jim on the East Side

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

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

I do wonder how you would get more ‘bare metal’ than the arduino implementation? Is it ‘blue steel’, ‘latigra’ or ‘magnum’?

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

I'm controlling an Si5351B clock chip. Yes, there are libraries out there for managing that chip, but this project is as much about learning things as about completing the thing, so I'm trying to do most of that code myself. I want to be sure I understand the chip completely.

I'm talking to it just fine with the Arduino library, but want to avoid using those libraries.

Kartman wrote:
I do wonder how you would get more ‘bare metal’ than the arduino implementation? Is it ‘blue steel’, ‘latigra’ or ‘magnum’?

By "bare metal" I mean not using the Arduino libraries and their overhead. Mostly because other parts of the project require fairly precise timing on the uC, for which I am mucking about with the timers. Easier to do that in plain C than through the Arduino interface. That part of the code is done and working.

Tha KS for the advice, folks, I'll try the Fleury libraries.

Last Edited: Thu. Jan 11, 2018 - 03:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fleury is simple to use and understand.   However it polls the TWI instead of using interrupts.    So you will not be using the AVR resources efficiently.

 

On the other hand,  the Wire library uses interrupts but blocks the AVR in just the same way as polling the TWI (e.g. Fleury).

 

Here is a sketch that illustrates the behaviour.

I have used a DS1307 because the RAM is simple to access.

You could use the same code for a 24C01 .. 24C16 EEPROM but allow time for the Write-page to complete.

You could use 24C32 .. 24C512 EEPROM.  Just add an extra Wire.write(byte(0)) to set the hi-byte of a 16-bit eeprom cell  address.

 

#include <Wire.h>
const byte DS1307 = 0x68;

void setup()
{
    // put your setup code here, to run once:
    uint32_t t, elapsed;
    Serial.begin(9600);
    Wire.begin();
    t = micros();
    Serial.println("Serial uses interrupts");
    elapsed = micros() - t;
    Serial.print("24 chars in message took ");
    Serial.print(elapsed);
    Serial.println("us");
    Serial.println("actual transmission @ 9600 baud is 24960 us");
    delay(100);  //make sure Serial has drained
    t = micros();
    Wire.beginTransmission(DS1307);
    Wire.write(byte(32));           //set eeprom cell address
    Wire.write("David Prentice");   //and write 14 bytes of data
    Wire.endTransmission();
    elapsed = micros() - t;
    Serial.print("Writing 14 bytes to DS1307 RAM took ");
    Serial.print(elapsed);
    Serial.println("us");
    Serial.println("actual transmission on 100kHz bus is 1440 us");
    Wire.beginTransmission(DS1307);
    Wire.write(byte(32));   //set eeprom cell address
    Wire.endTransmission();
    delay(100);  //make sure Serial has drained
    t = micros();
    Wire.requestFrom(DS1307, 14);
    elapsed = micros() - t;
    Serial.print("Requesting 14 bytes from DS1307 RAM took ");
    Serial.print(elapsed);
    Serial.println("us");
    Serial.println("actual request on 100kHz bus is 1350 us for completion");
    Serial.print("We receive : \"");
    for (int i = 0; i < 14; i++) Serial.print(char(Wire.read()));
    Serial.println("\"");
    Serial.println("In an ideal world,  we request 14 bytes and return immediately");
    Serial.println("and just check Wire.available() for each byte");
    Serial.println("i.e. using interrupts properly like Serial does");
}

void loop()
{
    // put your main code here, to run repeatedly:
}

Note that Fleury uses 8-bit Slave Address.   e.g. 0xD0 for DS1307.  0xA0 for 24C16

 

David.

Last Edited: Thu. Jan 11, 2018 - 09:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgalak wrote:
By "bare metal" I mean not using the Arduino libraries and their overhead

What 'overhead' has the wire library got that anything else presumably also has?

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

jgalak wrote:
not using the Arduino libraries and their overhead.

So, instead, using the ASF libraries and their overhead?!

 

The whole point of a library is to be generally useful in a wide range of applications - so, clearly, that means that a library will never be optimised to one specific application.

 

other parts of the project require fairly precise timing on the uC

So just use "bare" C for those parts - that doesn't preclude using Arduino (or other libraries/frameworks) for the rest of the Project.

 

I2C is pretty slow, and the timing is defined by the hardware.

 

 

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

awneil wrote:

jgalak wrote:
not using the Arduino libraries and their overhead.

So, instead, using the ASF libraries and their overhead?!

 

That's why I'm asking here.  I have no idea what the overhead of ASF is - I'm new to this.

 

Quote:

 

The whole point of a library is to be generally useful in a wide range of applications - so, clearly, that means that a library will never be optimised to one specific application.

 

other parts of the project require fairly precise timing on the uC

So just use "bare" C for those parts - that doesn't preclude using Arduino (or other libraries/frameworks) for the rest of the Project.

 

I2C is pretty slow, and the timing is defined by the hardware.

 

The problem is my code completely changes the timers on the ATMega, which the Arduino libraries rely on for a number of things.  Not sure which things....

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

Hi,

what was the the targeted use of the I2C com you had in mind ?
Ok, reread, yr application is for a clock chip, right. So its just a p-p com then

 

OK again; that seems to be a serious piece of silica.

Last Edited: Fri. Jan 12, 2018 - 02:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgalak wrote:
The problem is my code completely changes the timers on the ATMega, which the Arduino libraries rely on for a number of things.

Well, that's always going to be the case with using libraries - they are always going to rely upon some resources on the target.

 

 

Not sure which things....

It should be clearly stated in the Library documentation.

(no idea how good Arduino is on this)

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

Arduino uses T0 for the millis(), micros() functions, other than that unless you do PWM the other timers are free to use as you please.

As far as overhead, the worst seems to be accessing the port pins via (D0...Dxx) notation, which is easy to avoid by using normal PORT/PIN/DDR macros to specific port pins,

you just have to look up what port pin Dxx really is talking to.....  Not hard, just takes a little digging.

You can go as "bare metal" using the Arduino IDE as you want to go.  It is, after all, the same compiler as AS7

 

Jim

 

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

Thanks everyone.  I agree, I can do much of this under the aegis of Arduino.  However, I like the Atmel Studio IDE much better than the Arduino one, I've gotten avrdude installation working from Studio for installing over USB, and I picked up an Atmel ICE so I can even try debugging down the road.  Additionally, by doing this without the Arduino library's help, I'm learning the AVR chip details that the Arduino allows one to gloss over.  So, all in all, I'm going to continue to focus on that side, especially as the project grows larger and more complex.

 

But for quickly trying some bit of functionality, Arduino is very convenient to have as a prototyping tool.  Which is what I did here - I wanted to make sure I understood the clock chip details, so I threw together a quick Arduino sketch.  Now that I mostly understand it, I'm going to write the "real" code in C in Atmel Studio.

 

Now, next question is, how to import the Fleury library into AS7....

Last Edited: Fri. Jan 12, 2018 - 01:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgalak wrote:
Now, next question is, how to import the Fleury library into AS7....

Easy, just add the .c file to the project and add an #include "xxx.h" file near the top of your main file.  Place both files in your project directory.

There are other ways to do this that those with more experience with AS7 can help you with.

 

Jim

 

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

Yes,   Arduino is excellent for prototyping or experimenting with ideas.

 

Paste / attach your working sketch.   And explain your design goal(s).

Explain where you are having a speed problem.

 

Quite honestly,   you use a proven library as  a "reliable black box".    Follow the documentation.   Pass the data in one end.    Receive the expected result on the output.

 

If something is wrong,  compare the "received" with "expected" and post with your question.

 

Did you try my example?

Did you see how Serial works efficiently?

Did you see how Wire has no advantage over Fleury and vice-versa.

 

Incidentally,   it is sometimes useful to have a critical task run with interrupts and let the rest of the world remain polled.   Or vice-versa.

When you explain the purpose of a project,   it is easy to see what strategy is appropriate.

 

David.

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

jgalak wrote:
Now, next question is, how to import the Fleury library into AS7....

See your other thread: http://www.avrfreaks.net/forum/adding-library-as7

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

awneil wrote:

jgalak wrote:
Now, next question is, how to import the Fleury library into AS7....

See your other thread: http://www.avrfreaks.net/forum/adding-library-as7

 

Uhmm... Yes.  I figured the AS forum was the place to ask it. 

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

david.prentice wrote:

Yes,   Arduino is excellent for prototyping or experimenting with ideas.

 

Paste / attach your working sketch.   And explain your design goal(s).

Explain where you are having a speed problem.

 

Quite honestly,   you use a proven library as  a "reliable black box".    Follow the documentation.   Pass the data in one end.    Receive the expected result on the output.

 

If something is wrong,  compare the "received" with "expected" and post with your question.

 

Did you try my example?

Did you see how Serial works efficiently?

Did you see how Wire has no advantage over Fleury and vice-versa.

 

Incidentally,   it is sometimes useful to have a critical task run with interrupts and let the rest of the world remain polled.   Or vice-versa.

When you explain the purpose of a project,   it is easy to see what strategy is appropriate.

 

David.

 

My speed problems have nothing to do with i2c.  The i2c is just there to configure Si5351b at startup and place it into the correct mode.  After that, it pretty much shuts down.  (well, I'll likely use it for other parts of the project, but it's still not being used when critical timing issues are happening).

 

The timing issues had to do with generating APRS AFSK signals - I needed to generate 1200 and 2200 Hz tones and switch between them (maintaining phase) at a 1200 Hz rate.  I was unable to find a way to do this with Arduino libraries - the timings are too exact, and Arduino has various interrupts on by default that can throw things out of kilter.

 

That part of the code is working great in straight C/avr-libc. I have one timer generating overflow interrupts at either 1200*16 or 2200*16 Hz and writing sin values from a lookup table to a 4-bit R2R ladder DAC.  I have a second timer firing at generating overflow interrupts at 1200Hz and deciding whether to change the first timer from one frequency to the other.  While generating tones, all other interrupts get shut off, to avoid conflicts.  (this way of using two timers, with one modifying the other, isn't mine, a friend did this on a non-AVR uC). All that proved quite easy to do. 

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

You need to sit down with a nice cup of tea.

 

Write down your design goals.

 

If you are generating 1200Hz and 2200Hz sine waves,  it won't really matter if an interrupt fires.   At worst you get one of your steps delayed by a few us.

 

Regarding I2C.   Yes,   it is commonly used to "set up" a chip.   Never used again.   The execution / efficiency is unimportant.   You just want something that works.

 

This is why it is important to write your questions carefully.    It seems that your sine generation is actually what is critical.

It is no more difficult to use Fleury I2C or Wire I2C.

 

Post your existing Arduino sketch.

 

David.

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

For generating fsk, i’ve used a dds technique that uses 1timer set to a constant frequency. You can derive all your timing from that.

Basically, have a variable named phaseAcc. Make him uint32_t first up.
Setup a timer in ctc mode with interrupt. Make it,say, 8kHz.
In this interrupt, add a value to phaseAcc, then use the top x bits to look up your sine table and output to dac. Change the value you add to phaseAcc to change the frequency. This gives you sub Hz resolution.
In the same timer isr, you can generate the timing for serialising the data and ptt timing.

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

Kartman wrote:

For generating fsk, i’ve used a dds technique that uses 1timer set to a constant frequency. You can derive all your timing from that.

Basically, have a variable named phaseAcc. Make him uint32_t first up.
Setup a timer in ctc mode with interrupt. Make it,say, 8kHz.
In this interrupt, add a value to phaseAcc, then use the top x bits to look up your sine table and output to dac. Change the value you add to phaseAcc to change the frequency. This gives you sub Hz resolution.
In the same timer isr, you can generate the timing for serialising the data and ptt timing.

 

That's not too dissimilair from what I'm doing.  I looked at both using a DDS approach and this one, and the one I chose seemed simpler.  That's probably just "simpler to me" - DDS math seemed more challenging to figure out.  Regardless, it's working well.

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

Dds itself is just adds, the calc is multiplies and divides. You might want to look at using a walsh function for your dac rather than r2r - you’ll get a nicer sine wave.

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

Folks were asking for my Arduino sketch.  Here it is.  Works fine, I get the expected output from the si5351b chip.  Note this is purely configuration - the sine wave generations is, for now, in a totally separate (AS7) project.  I wanted to get various things working independently before merging them. 

 

#include <Wire.h>

#include "C:\Users\jg\Dropbox\cubesat\APRSTracker\Si5351Test\Si5351B-RevB-Registers.h"
//#include "C:\Users\jg\Dropbox\cubesat\APRSTracker\Si5351B-RevB-Regmap.h"

#define I2C_BUS_ADDR 0x60

void setup() {

  Wire.begin();
  
  delay(3000);  //so it doesn't go right away.
  initSi();


}

void loop() {
  // put your main code here, to run repeatedly:

}

void initSi() {
  uint8_t addr = 0;
  uint8_t val = 0;



  //Disable outputs (CLK_OEB is Reg 3, set to 0xFF
  addr = 0x0003;
  val = 0xFF;
  si_write(addr, val);
  Serial.print("Result: ");
  Serial.println(debug_result);


  //Power down drivers
 
  val = 0x80;

  //Clk 0
  addr = 0x0010;

  si_write(addr, val);

  //Clk 1
  addr = 0x0011;
  si_write(addr, val);

  //Clk 2
  addr = 0x0012;
  si_write(addr, val);


  //Clk 3
  addr = 0x0013;
  si_write(addr, val);


  //Clk 4
  addr = 0x0014;
  si_write(addr, val);


  //Clk 5
  addr = 0x0015;
  si_write(addr, val);

  //Clk 6
  addr = 0x0016;
  si_write(addr, val);

  //Clk 7
  addr = 0x0017;
  si_write(addr, val);


  //Configure interrupts

      //Don't care in an Si5351B - C model only

  //install register settings
  for (int i = 0; i < SI5351B_REVB_REG_CONFIG_NUM_REGS; i++) {

    addr = si5351b_revb_registers[i].address;
    val = si5351b_revb_registers[i].value;

    si_write(addr, val);
  }

  //PLLA and PLLB Soft reset
  addr = 0x00B1;
  val = 0b10100000; //bits 7 and 5 need a one for a reset, leave rest alone.
  si_write(addr, val);


  //Enable desired outputs
  addr = 0x00B1;
  val = 0b10100000; //bits 7 and 5 need a one for a reset, leave rest alone.
  si_write(addr, val);


}

uint8_t si_write(uint8_t addr, uint8_t val) {
  Wire.beginTransmission(I2C_BUS_ADDR );
  Wire.write(addr);
  Wire.write(val);
return Wire.endTransmission();
}

uint8_t si_read(uint8_t addr)
{

  uint8_t reg_val=0;
  
  Wire.beginTransmission(I2C_BUS_ADDR);
  Wire.write(addr);
  Wire.endTransmission();


  Wire.requestFrom((uint8_t)I2C_BUS_ADDR , (uint8_t)1, (uint8_t)false);
  

  while(Wire.available())
  {
    reg_val=Wire.read();
  }

  return reg_val;
    
}

 

Note:  I stripped out the debugging code, since it makes things hard to read.  Might have missed a few lines.

 

 

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

So now I'm trying to do the same thing in AS7 with the Fleury library.  Much less joy.

 

Here's the relevant code, stripped down to the very basics, just trying to send a single byte of data:

 

 int main(void)
 {

	 i2c_init();                             // initialize I2C library

	 _delay_ms(3000);  //just wait a bit before starting

	 si_write(0x10,0x80);

	 for(;;);
 }

uint8_t si_write(uint8_t addr, uint8_t val) {

	DDRB = 0xff; //For DEBUG

	uint8_t ret;//holds return value - 0 is successful, 1 is failure.

//	ret = i2c_start(Si5351_Addr+I2C_WRITE);	// set device address and write mode
	ret = i2c_start(0x60+I2C_WRITE);	// set device address and write mode

	if (ret)
	{
		i2c_stop();
		PORTB = 0xff;
	}
	else
	{

		ret = i2c_write(addr);					// write address
		ret +=	i2c_write(val);					// write value
		i2c_stop();                             // set stop conditon = release bus
	}
	return ret;
}

 

The port manipulation is debugging code.  LED lights up if the i2cstart fails.  And it _always_ fails.  When I hooked up a scope and turned on i2c, it showed an attempt to start a write to address 0x30, with NACK (obviously, since the chip in address 0x60).  Huh???  Earlier, I tried this with i2c_start_wait() rather than i2c_start() and it just send an endless stream of the same started writes.

 

Also, I'm not sure why the SCL line is much lower amplitude than the SDL line.  Both are pulled up with the same recommended resistors to the 3V3 Vcc line.  The processor is an Arduino Pro Mini running at 3V3/8MHz.

 

F_CPU is set elsewhere to 8000000UL

 

I'm sure it's something totally boneheaded and obvious, but I'm not finding it....

Attachment(s): 

Last Edited: Sat. Jan 13, 2018 - 03:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Dds itself is just adds, the calc is multiplies and divides. You might want to look at using a walsh function for your dac rather than r2r - you’ll get a nicer sine wave.

 

The 4 bit R2R, followed by a simple low pass RC filter, is working pretty well.  Sine wave looks nice and clean on the scope (I don't have a Spec An so can't tell how clean it really is).  We'll see if it's clean enough once I've got all the pieces together.

Last Edited: Sat. Jan 13, 2018 - 03:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You probably do have a spectrum analyser - your PC’s soundcard. There’s free software available to do a fft.

Some i2c libraries will shift the slave address other won’t. Ie if the 7bit i2c address is 0x30, some will accept this others may expect 0x60.

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

That's true, I can do that.  But ultimately, I'm interested in how the RF tone as modulated by the AF tone looks, and that I can see using an RTL-SDR as a spec an.  But not there yet.

 

The Fleury library seems to expect the address+R/W bit.  Not sure if a shift is required, there doesn't seem to be anything saying so. 

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

Hi, just curious. What "Fleury lib" are you talking about ?

I half a year ago some, took from a "Peter Fleury" page a "bit-bang" code
marked as by  "Pascal Stang" or something for origin. That one was basically flawed for keeping

the clock going versus data in 90degree. (at least what I found). Remembering though that there also were a TWI-support version

on those pages.

Last Edited: Sat. Jan 13, 2018 - 11:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gechxx wrote:
What "Fleury lib" are you talking about ?

http://homepage.hispeed.ch/peter...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Ok, seems Im confusing (again). The files at your indicated site is not the ones I picked, those were in C to begin with. [ Now major-redone and working fairly (what I see without problems) well. ]

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

gechxx wrote:
half a year ago some, took from a "Peter Fleury" page a "bit-bang" code marked as by  "Pascal Stang" or something for origin.

I'm not sure, but I suspect you are mixing names up. Both Pascal Stang and Peter Fleury are very well known names in the history of AVRs. Both have published code collections that have become "de-facto re-use code".

 

Pascal Stang has not been seen active for many years now - his "Procyon AVR Library" was last updated in 2005.

 

Peter Fleury is/was a member of the AVRfreaks community - his last post was made in 2008. 

 

Code from both persons have historically been judged by many to be stable and working well.

 

There are no references to Pascal Stang anywhere on Peter Fleurys web site, as far as I (and Google) can tell.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I think this thread has run its course.  The specific question I asked - what library is recomended - has been answered.  I'm going to start a new thread for help troubleshooting.

 

Thanks everyone.