Questions about twi library

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

Hello,

 

I'm studying twi library to improve my I2C library.

 

I have 3 questions about this library:

 

1. Why there are two routines for sending start and repeated start in the main function and the ISR, as follows:

 

a) In the main function "readFrom":

if (true == twi_inRepStart) {
    // if we're in the repeated start state, then we've already sent the start,
    // (@@@ we hope), and the TWI state machine is just waiting for the address byte.
    // We need to remove ourselves from the repeated start state before we enable interrupts,
    // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
    // up. Also, don't enable the START interrupt. There may be one pending from the
    // repeated start that we sent ourselves, and that would really confuse things.
    twi_inRepStart = false;   // remember, we're dealing with an ASYNC ISR
    do {
      TWDR = twi_slarw;
    } while(TWCR & _BV(TWWC));
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

 

b) In the ISR:

    case TW_START:     // sent start condition
    case TW_REP_START: // sent repeated start condition
      // copy device address and r/w bit to output register and ack
      TWDR = twi_slarw;
      twi_reply(1);
      break;

 

2. Do I have to check for TWINT with the while loop?

 

a) In the datasheet, it's required to check for this bit after each operation set this bit but not required in twi stop condition. So I do it in my I2C functions; for example:

void I2C_reply(uint8_t ack)
{
    if (ack)
    {
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA)|(1<<TWIE);  // sneding ACKs
        while (!(TWCR & (1<<TWINT)));
    }
    else
    {
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWIE);            // sending NACK
        while (!(TWCR & (1<<TWINT)));
    }
}

 

b) In twi library, it's in certain parts of the code and not after each twi operation, so what the key here? For example:

void twi_reply(uint8_t ack)
{
  // transmit master read ready signal, with or without ack
  if(ack){
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
  }else{
   TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
  }
}

 

3. Checking other bits in certain parts and not in every twi operation; for example:

 

a) TWWC bit in the main start function and not in the ISR start function

    twi_inRepStart = false;   // remember, we're dealing with an ASYNC ISR
    do {
      TWDR = twi_slarw;
    } while(TWCR & _BV(TWWC));
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
  }

 

b) Stop bit

 

  while(TWCR & _BV(TWSTO)){
    continue;
  }

 

This topic has a solution.

Last Edited: Wed. Dec 5, 2018 - 05:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
I'm studying twi library
You might want to say what library you are talking about? Is that Fleury or are you talking about something from Arduino? Or something else?

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

OMG LOL I thought it's the AVR GCC twi library, while it's related to Arduino twi development libraries! Just realized that :)

 

Well, it's the twi library in:

G:\Program Files\Arduino\hardware\arduino\avr\libraries\Wire\src\utility

 

So, where is the original I2C library for AVR GCC ?

Last Edited: Fri. Dec 15, 2017 - 06:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, avr-libc  http://www.nongnu.org/avr-libc/u... has no specific TWI library (I noticed that its time library was better than the PC/RPi one, as one can compute sunshine duration and  moon phase -though out of topic, it is nice enough to be remembered) )

but an example using the TWI interface (at the very end of the link, where one is likely to find it, like in detective novels).

Last Edited: Fri. Dec 15, 2017 - 04:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, you're right. I searched in winAVR for twi source code other than header files and found only one source file which is twitest.c.

 

 

I looked into that source code, it's really different than the one which took me months to start understand. But, studying that one helped me to read twitest.c more easily!

 

The main difference I noticed which is really interesting that an official twi library like twitest.c doesn't apply ISR data handling where twi.c has! I don't know why?

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

There’s nothing that compells you to use interrupts. For instance, with twi, I rarely use interrupts and the same with the ADC. I try to minimise the use of interrupts - i wrote a tutorial on “the traps when using interrupts” that explains why.

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

I thought it's the most efficient way to handle data as it provides a space for the CPU to do other things. That what I understood about SFR modules; like, timers, ADC, UART .. etc. And I thought ISR is the best way to handle the data.

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

‘Most efficient’ is a bit of a sweeping statement. Sometimes it can be less efficient - there is a degree of overhead associated with interrupts, so they are not a magic bullet for all situations.

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

OK, I didn't quite understand this aspect in microcontrollers. I thought interrupts are so fast and can handle most of the time sensitive things.

 

 

Anyway, I modified the I2C code again, and now it's not working again. I have my first simple I2C draft working, but I want to develop another one which receive pointer to arrays for send and receive operations. But, I think I have a problem passing these arrays.

 

For example, I have one application code in Arduino IDE and 2 libraries which are I2C and HMC5883L.

 

In the application code I have this array:

uint8_t data[6];

And, passes it with HMC5883L function which receives a pointer and length of the data:

data_read(data,6);

 

Then, the HMC5883L function passes it again to an I2C function which receives another pointer:

void data_read (uint8_t *dat, uint8_t len)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register,1);
  I2C_start(HMC5883L_read);
  I2C_rx(dat,6);
  I2C_stop();
}

And this is the I2C function:

void I2C_rx(uint8_t *data, uint8_t len)
{
    uint8_t i;
    len = len - 1;
    for (i=0;i<len;i++)
    {
        data[i] = TWDR;
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
        while (!(TWCR & (1<<TWINT)));
    }
    data[len] = TWDR;
 TWCR = (1<<TWINT)|(1<<TWEN);
 while (!(TWCR & (1<<TWINT)));
}

 

 

Is my way correct?

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

What happens if len=1? Think about re-organising your code so that it is coped with cleanly.
Why does data_read have a len parameter that is not used? Data_read is a useless name for the function by itself.if it were a member function of a class that hen that would be ok but otherwise how do we know what data it reads?

Apart from that, your pointer handling is ok, but you can check that yourself.

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

Kartman wrote:
‘Most efficient’ is a bit of a sweeping statement.

 

Indeed - and pretty meaningless without saying what "efficiency" we're talking about; eg,

  • Code space
  • Execution time
  • Developer's time
  • Maintainer's time

 

In particular, code space & execution time are often contradictory - one can often be traded for the other.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're absolutely right! I thought about that point. I worked more on the code and surprisingly I concluded to an amazing result :)

 

The code is working very well, but the Arduino serial monitor stops working after moving the module arbitrary, but it works very similar to sparkfun code which is a big accomplishment to me.

 

I just have to see the cause of this crash because it happens too with the sparkfun with more arbitrary moving. Maybe the wiring isn't so robust.

 

void data_read (uint8_t *dat, uint8_t len_hmc_rx)
{
  uint8_t i,loop = len_hmc_rx - 1;
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);
  I2C_start(HMC5883L_read);

  dat[0]=I2C_rx(1);
  dat[1]=I2C_rx(1);
  dat[2]=I2C_rx(1);
  dat[3]=I2C_rx(1);
  dat[4]=I2C_rx(1);
  dat[5]=I2C_rx(0);
  I2C_stop();
}

This is my function.

 

I'm looking for a way to read the bytes with more functional method instead of getting the value for each array byte, with sending the nack with the last received byte.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void I2C_rx(uint8_t *data, uint8_t len)
{
    uint8_t i = 0;

    while (i < len)
    {
        if (i == (len-1))
            {
            TWCR = (1<<TWINT)|(1<<TWEN);
            }
        else
            {
            TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
            }
        while (!(TWCR & (1<<TWINT)));
        data[i] = TWDR;
       // alternately you could write
        *data++ = TWDR;
        i++;
    }
}

I've not used the TWI peripheral before, but don't you need to tickle it before reading data?? I have no idea on why you have embarked on this long and torturous path on recreating some i2c code that has been done a zillion times. Why don't you pursue something infinitely more useful to you like learning how to program? Get the book 'Code Complete' for starters.

 

Last Edited: Sun. Dec 17, 2017 - 10:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What you mean by "tickle"? Do you mean I have to tweak the code or do something before reading the data from the function or you mean reading data out of coding perspective?

 

I have no idea on why you have embarked on this long and torturous path on recreating some i2c code that has been done a zillion times

 Because, coding is not easy to me as a beginner, I don't have an impressive experience with programming especially in C and any kind of programming, I coded little in PIC assembly, and then decided to change my focus to C and not C++ with Arduino because I understood that C is the most important programming language with embedded systems and Linux.

 

Why don't you pursue something infinitely more useful to you like learning how to program?

 Do you mean PC software programs development? Well, it's very nice to program computer applications with HTML, python, C++, Java .. etc. But, I really would like to work with embedded systems and do interesting projects, because after all I'm electronics trainer and I have to advise diploma projects especially the ones with microcontrollers, and then I may have to present special internal courses for our department employees, that's why I have to be very good with C and have the competencies to talk professionally about programming in C.

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

The twi peripheral needs to be told (ie tickle) to do something. Point being how can you read a value when you haven’t told it to read - with or without an ack?

Coding is the last phase of programming. Knowing the ins an outs of a language doesn’t mean you are a good programmer. The concepts of programming apply to any language - whether i write machine code, php, javascript, c,c++ etc. When i design for an embedded system, i am more conscious of performance and ram usage. This is well before writing the code. The basis of programming is to give you the tools to manage the complexity.
You’ve been at this for over a year, so you should be beyond the beginner stage. You should have a grasp of basic programming concepts and the use of the C language. After i sent my last post, i thought i’d have a quick look at the datasheet in reference to the twi peripheral. Have you read this and understood it? If you had no internet, could you write your code from the beginning?
Next time I suggest some reading to you, i don’t want you to argue with your preconceived notions. I’m giving you a recommendation based on my experience and i want to give you the tools to move forward.

Last Edited: Sun. Dec 17, 2017 - 10:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are so right and I feel so honored with your reply, the aspects of programming you explained are very important to me and I really feel that these aspects are also key concepts in understanding programming.

 

I know about the layers of programming development, the steps, layers, and a lot of theoretical information. This comes out of professional electronics, software engineers who develop professional software and source codes, doctors and many professional who work in programming and software development, and also professional hobbits, they normally develop books about programming, and I find a lot of PDF files about Arduino projects, raspberry pie, and also desktop programming.

 

I downloaded a lot of these files but now I'm not interested in reading them, because these books won't help me in solving my I2C problem :)

 

I just realized I have to accomplish I2C because it's very important in dealing with a lot of sensors and modules, so I have to understand I2C very well so I know how to deal with the sensors and other modules.

 

UART is my next step after, but before that I have to do the modifies and tweaks about my I2C code so it fits other sensors and modules.

 

Coding is the last phase of programming

 I know for companies and professional engineers, programming is the next step when they design the hardware.

 

ram usage

 I really want to go into memory management, I reached the level to know that in any embedded project, the variables declared on the ram are used all the time, but maybe because it's an embedded program it runs only one program, so there's enough space for variables, unless the program requires a lot of memory and all the memory registers are used, I have to free the unused registers. Here I have to know what space I have and how to mange the memory and if I have to free the unused space. And if malloc is very useful or declaring variables and arrays is enough.

 

Have you read this and understood it?

Yes, I read some documents and websites about I2C, but the answer was easy, I just wrote I2C in google images and saw the procedure in reading and writing data, but I just didn't realized the story about NACK in reading data.

 

If you had no internet, could you write your code from the beginning?

 No, I have to ask people so they tell me about the key tips and solutions in programming. But I can do something :)

 

Next time I suggest some reading to you, i don’t want you to argue with your preconceived notions. I’m giving you a recommendation based on my experience and i want to give you the tools to move forward.

I know, but right now I want to finish I2C, UART .. etc. Then programing wifi module, run TFT screen and touch functions, the camera is an important one.

 

Then I may read books about raspberry pie or Arduino projects for break times :)

 

Last Edited: Mon. Dec 18, 2017 - 09:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You make a lot of assumptions and unfortunately most of them don’t reflect reality. Throw away those assumptions and deal with facts. You’ve been at this I2c gig for over a year and you’re still really none the wiser based on the fundamental questions you ask.

Does my function in#13 work? Have you tried it?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One way to learn about a device is to study what other code has been written, for the TWI, Atmel has a number of app notes available with code on how to use it, also most of us use code written by another AVRFreak, Peter Fleury, here is a link to his code page:  http://homepage.hispeed.ch/peter... He has code examples for TWI, USART and LCD. 

We all use it.  So study recommended examples, App Notes, and search the projects page here for more examples, as well as code posted in the forums by the moderators and regulars to the forum.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Yes, I just tested it, it works like a charm :)

 

Thank you for the support.

 

I know that I'm working around an easy code which not so much programmers care about, but this is a challenge to me. But, I learn things I won't say a lot, but I have to do it well.

 

I really wish I finished this one within weeks, but it took me so much time to understand it along with the other things which kept me busy. But, I love programming microcontrollers, beside my work as an electronics trainer and also I have a course in microcontrollers, but it's easy with Flowcode and simple tasks. 

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

I tried to do the same technique with I2C_tx function but didn't work!

 

void I2C_tx(uint8_t *data_tx, uint8_t length_tx)
{
    uint8_t i = 0;
    while (i < length_tx)
    {
        TWDR = *data_tx++;
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
        while (!(TWCR & (1<<TWINT)));
        i++;
    }
}

 

This is why I need to work on my C skills, then with the result experiences and knowing the logics of programming, then going to other projects would be easier.

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

The C code looks ok but are you talking to the TWI peripheral correctly? What status does it return?

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

I would like to point out: I don't think you have to understand I2C well to use it. You can use it just fine with an existing library, and probably never need to look further. It's great to be curious about things, but I would suggest starting with higher level topics and concepts, and avoiding the low-level internals whenever you don't specifically need them.