Dissecting Peter Fleury's TWI libraries

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

Hello again... I hope this time I can keep focus in my goal and get some good help!

My main goal is to understand Peter Fleury's code for the TWI.

 

I've been reading a little bit about the registers used in Peter Fleury's code! I'll call Peter Fleury's code "PF code" from now on to make things easier!

 

First of all, I'll state what I'm using, regarding hardware and software, my semi-goals and after that I'll ask my questions!

I'm using AtMega328P-PU, 28 pins as my uC.

I'm using Microchip 24LC08B and/or 24LC10B external memory ICs to save and read data into and from it!

I have 4k7 ohm pull-up resistors at SDA and SDL pins!

I'm using 16MHz crystal and 22pF capacitors at AtMega328P-PU pins 9 and 10.

I'm using a pressure switch to make the reset to the AtMega328P-PU uC when uploading firmware to it!

I'm using 1uF and 10uF decoupling capacitors between + and - lines (don't hane any other caps atm)!

All the remaining AtMega328P-PU pins are floating!

I'm using an USB-to-Serial converter to make the connection between the AtMega328P-PU and the laptop.

Hope I have not forgot anything!

 

About the software:

I'm a Debian Jessie 64bit user!

I'm using Atmel AVR 8-bit Toolchain 3.5.4 - Linux 64-bit which includes avr-gcc-4.8.1.

I'm using avrdude-6.3.

I'm using this AtMega328P-PU datasheet

I'm using this Microchip 24LC08B datasheet

 

I have downloaded PF libraries and example codes.

My focus will be each function in his code to ask some questions about some of the lines of code!

 

Hope I haven't forgot anything!

 

I'll start from the beginning of PF code:

 

From the AtMega328P-PU datasheet I can read the sequence of commands to initiate the TWI interface, page 268.

The first step is to issue a Start Condition. This is accomplished by:

 

TWCR0 = (1<<TWINT) | (1<<TWSTA)|(1<<TWEN);

 

From PF code:

File: twimaster.c

Function: unsigned char i2c_start(unsigned char address)

 

he uses:

TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

 

Why the datasheet uses TWCR0 and PF code uses TWCR... One is addressing a single bit of the register the other uses the whole byte of the register! Is there any specific reason?

The same question applies to the other lines of code where TWCR is used rather than TWCR0.

 

 

Now, I wanted to understand the following step... It is described in the datasheet of AtMega328P-PU as:

3. The application software should now examine the value of TWSRn, to make sure that the START condition was successfully transmitted. If TWSRn indicates otherwise, the application software might take some special action, like calling an error routine. Assuming that the status code is as expected, the application must load SLA+W into TWDR. Remember that TWDRn is used both for address and data. After TWDRn has been loaded with the desired SLA+W, a specific value must be written to TWCRn, instructing the TWI n hardware to transmit the SLA+W present in TWDRn. Which value to write is described later on. However, it is important that the TWINT bit is set in the value written. Writing a one to TWINT clears the flag. The TWI will not start any operation as long as the TWINT bit in TWCRn is set. Immediately after the application has cleared TWINT, the TWI will initiate transmission of the address packet.

The suggested code to do this is described in the datasheet as:

if ((TWSR0 & 0xF8) != START)
    ERROR();
 
TWDR0 = SLA_W;
TWCR0 = (1<<TWINT) | (1<<TWEN);

 

PF's code is the following:

// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START))
    return 1;

// send device address
TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN);

 Why and how is the datasheet "START" compared to the PF's code "TW_REP_START"??? Isn't this "TW_REP_START" a repeated start? And how is the TWINT flag being checked?

This topic has a solution.
Last Edited: Thu. Nov 10, 2016 - 11:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n wrote:
Why the datasheet uses TWCR0 and PF code uses TWCR...

Many AVRs have just one TWI interface, the control register is called TWCR. (e.g. ATmega328P)

In AVRs with multiple TWI interfaces, they are TWCR0 and TWCR1. (e.g. ATmega328PB)

 

Edit: and looking at a recent datasheet (Atmel-42735A-ATmega328/P_Datasheet_Complete-06/2016), I see that they are mixing usages of TWCR and TWCR0 in several examples.

See page 270, Table 26-2, Example 5 the assembly code example uses TWCR, the C code TWCR0.

David (aka frog_jr)

Last Edited: Mon. Nov 7, 2016 - 09:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n wrote:
Why the datasheet uses TWCR0 and PF code uses TWCR... One is addressing a single bit of the register the other uses the whole byte of the register! I

What are you going on about?  Why do you say TWCR0 addresses a single bit of a register?

 

Different models of AVR may well have more than one substantiation of a peripheral subsystem.  So otherwise similar/identical subsystems might need a qualifier.  Does TCNT0 address just one bit?  Or is it because that AVR might have TCNT0, TCNT1, TCNT2, ...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

TWCR vs TWCR0

 

PF's code is written to work on many AVR's, some of them have more then one TWI, if so, then its called TWCR0, the second one would be TWCR1, and not a bit position as you thought.

Somewhere in the headers it equates TWCR to TWCR0 for your AVR model. I don't use GCC so you will have to find it or someone else will point out where.

 

Start vs Repeat Start

 

Again, PF's code is very flexible, so this function will work for both start and repeat-start.

 

Jim

 

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
https://www.onegold.com/join/7134f67c2b814c5ca8144a458eccfd61

 

 

 

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

frog_jr wrote:

PsySc0rpi0n wrote:
Why the datasheet uses TWCR0 and PF code uses TWCR...

Many AVRs have just one TWI interface, the control register is called TWCR. (e.g. ATmega328P)

In AVRs with multiple TWI interfaces, they are TWCR0 and TWCR1. (e.g. ATmega328PB)

 

Edit: and looking at a recent datasheet (Atmel-42735A-ATmega328/P_Datasheet_Complete-06/2016), I see that they are mixing usages of TWCR and TWCR0 in several examples.

See page 270, Table 26-2, Example 5 the assembly code example uses TWCR, the C code TWCR0.

 

Yeah, just noticed!

 

theusch wrote:

PsySc0rpi0n wrote:
Why the datasheet uses TWCR0 and PF code uses TWCR... One is addressing a single bit of the register the other uses the whole byte of the register! I

What are you going on about?  Why do you say TWCR0 addresses a single bit of a register?

 

Different models of AVR may well have more than one substantiation of a peripheral subsystem.  So otherwise similar/identical subsystems might need a qualifier.  Does TCNT0 address just one bit?  Or is it because that AVR might have TCNT0, TCNT1, TCNT2, ...

 

Well, after reading the other replies I see that I was wrong about TWCR0 addressing only one bit of a certain register! I'm cleared now about that now!!

 

ki0bk wrote:

TWCR vs TWCR0

 

PF's code is written to work on many AVR's, some of them have more then one TWI, if so, then its called TWCR0, the second one would be TWCR1, and not a bit position as you thought.

Somewhere in the headers it equates TWCR to TWCR0 for your AVR model. I don't use GCC so you will have to find it or someone else will point out where.

 

Start vs Repeat Start

 

Again, PF's code is very flexible, so this function will work for both start and repeat-start.

 

Jim

 

Yes, I can understand that about flexibility and also that a repeated start is just the calling of the start function! But that's not my question! My question is rather how is the following datasheet statement:

 

 The application software should now examine the value of TWSRn, to make sure that the START condition was successfully transmitted. If TWSRn indicates otherwise, the application software might take some special action, like calling an error routine.

 

executed in PF's code:

 

twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START))
    return 1;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Note the function description comments (e.g. i2cstart()):

/*************************************************************************
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
**********************************************************************
unsigned char i2c_start(unsigned char address) ...

 

If the function returns a 1 there is an error. If it returns (in the case of i2cstart) a 0 it means the device is available.

 

 

Edit: The key here is the word "might".

Notice how the i2c_start is used in the test_i2cmaster.c file:

    ret = i2c_start(Dev24C02+I2C_WRITE);       // set device address and write mode
    if ( ret ) {
        /* failed to issue start condition, possibly no device found */
        i2c_stop();
        PORTB=0x00;                            // activate all 8 LED to show error */
    }else {

So, if the device is not available the routine issues the i2c_stop then turns all LEDs on, indicating an error.

 

 

 

David (aka frog_jr)

Last Edited: Mon. Nov 7, 2016 - 11:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:

Note the function description comments (e.g. i2cstart()):

/*************************************************************************
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
**********************************************************************
unsigned char i2c_start(unsigned char address) ...

 

If the function returns a 1 there is an error. If it returns (in the case of i2cstart) a 0 it means the device is available.

 

 

Edit: The key here is the word "might".

Notice how the i2c_start is used in the test_i2cmaster.c file:

    ret = i2c_start(Dev24C02+I2C_WRITE);       // set device address and write mode
    if ( ret ) {
        /* failed to issue start condition, possibly no device found */
        i2c_stop();
        PORTB=0x00;                            // activate all 8 LED to show error */
    }else {

So, if the device is not available the routine issues the i2c_stop then turns all LEDs on, indicating an error.

 

I also understand that!

What I want to understand is how PF code checks the TWSR register with prescaler bits masked to see if the START was successfully issued. But I think I'm getting there!

 

I'm not sure what means "Mask prescaler bits" but looks like the value returned by the value present in TWSR masked with the prescaler value must be different from the value present in the TW_START and TW_REP_START macros. If this is true, then something wrong happened. The START condition was successful otherwise!

 

Am I correct? 

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

PsySc0rpi0n wrote:
Am I correct?

I believe so, but perhaps this will clarify:

Masking the prescaler bits (in this case) just means removing them from consideration when looking at TWSR.

  If TWSR = 0bXXXXXXXX, then (TWSR & 0xF8) = 0bXXXXX000.

 

Bottom line: If the value of (TWSR & 0xF8) is not equal to either 0x08 or 0x10 [TW_START (0b00001000) or  TW_REP_START (0b00010000)] then there is a problem.

 

Edit: If you were using an IDE (e.g. Eclipse, Code::Blocks, etc.) you could just (right) click (or hover over) the macro and be taken directly to its implementation.

David (aka frog_jr)

Last Edited: Tue. Nov 8, 2016 - 02:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I'm not going to bother much with prescaler bits. I prefer to focus now on the general code of each function.

 

PF's code has 2 similar functions:

unsigned char i2c_start(unsigned char address)
void i2c_start_wait(unsigned char address)

 

Why is there the need for the 1st function?

The 1st function has the ability to handle errors with the "if" conditions, and it returns 1 in case of error.

The 2nd function doesn't return any value, it's a "void" function so it cannot handle errors but it has the ability to wait for the transmissions to be successful. In fact there is a comment line of code right before the "break;" statement that would return a "1". Why weren't these two functions combined into a one that could handle errors and wait for transmissions to be successful????

 

After understanding this point, I think I'm done with these two functions!

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

The i2c_start is typically used on initial access to a device to verify its existence. Once a device has been verified to exist and be functional, i2c_start_wait can be used to access the device, waiting on it to complete any previous operation before returning.
 

David (aka frog_jr)

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

The explanation is simple.   This is for intelligent punters that use the return value:

unsigned char i2c_start(unsigned char address)

And this is for the punters that are allergic to return values.    And prefer to crash and die:

void i2c_start_wait(unsigned char address)

I suggest that you always use the first version.    It will tell you if you have used the wrong Slave address, the wiring is wrong or a Slave has been removed from the bus.

 

If you are confident that the Slave chip is present and correctly addressed,   the second version can be used.    For example,   a 24Cxxx EEPROM will not ACK while it is busy doing a Page-Write.     This will keep trying until the EEPROM is ready.

 

Of course,   many punters just like to whinge that I2C is "difficult".    E.g. they wear a blindfold and then complain that they can not see where they are going.

 

David.

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

PsySc0rpi0n wrote:
Why is there the need for the 1st function?
If only Peter had written a user manual. Oh wait a minute...

 

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

 

Note the difference on that page between start() and start_wait(). Specifically the return value or lack of it. Also the fact that the _wait one does what it says on the tin - it waits.

 

Clearly the intended use is either i2c_start() to "sniff" whether a device is present and be able to take action if it isn't while i2c_start_wait() knows the device is there and will doggedly try to make the connection.

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

Ok...

 

Another question!

 

AtMega328P-PU datasheet keeps saying:

it is important that the TWINT bit is set in the value written. Writing a one to TWINT clears the flag. The TWI will not start any operation as long as the TWINT bit in TWCRn is set.

This is confusing. They say that it is important that TWINT bit is set, therefore "1". Then they say that writing a "1" to TWINT clears the flag. Then they say that the TWI won't start any operation until the bit TWINT in TWCR is set which is the same as TWINT = 1 which is the same as the "bit cleared"... But isn't "clearing a flag" unset it, or make it to be "0"???

 

I'm struggling to understand if the transmission starts with TWINT = 1 or with TWINT = 0.

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

Have you ever thought of running the Peter Fleury code in the simulator, or better still, a real AVR with a debugger attached? That way you can follow it in to every last write to TWCR, TWAR, TWDR or whatever else it may be accessing. There's quite a difference between "dry reading" C code and trying to determine what it is going to do and the alternative of being able to actually watch it in action and see exactly what it is doing!

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

Looking at the (very confused) example in the datasheet (page 216) and the diagram of a typical transmission (Figure 22-10, page 214):

  1. Write TWCR setting TWINT (clears any the interrupt flag (if present), initiates START condition), TWSTA (requesting START), and TWEN (enable TWI interface).
  2. After the TWI transmits the START condition, TWINT will be set (by hardware), so wait for TWINT to go high
  3. Check the Status (in TWSR) and if not equal to start, process as an error, else, load the slave address + write (SLA+W) into the TWDR and then write TWCR setting bits TWINT (to clear the existing interrupt flag) and TWEN (to enable the transmission)
  4. Wait until the hardware sets the TWINT

and so on...

 

The hardware sets the TWINT flag. (making the bit 1)

Software writing a 1 to the TWINT bit clears the flag. (making the bit 0)

 

Edit: typos

Edit: This is referencing the ATmega48A/PA/88A/PA/168A/PA/328/P datasheet Atmel-8271J-AVR- ATmega-Datasheet_11/2015

David (aka frog_jr)

Last Edited: Tue. Nov 8, 2016 - 11:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quite honestly,   you read the HTML documentation,   use the library according to the docs.

 

Treat any library as a Black Box.   i.e. obey the rules.   supply the input.   receive the output.

 

Yes,   when you have gained experience from using a library correctly,  you might want to study how the library works.

I would start by riding a bicycle before you start worrying about carbon-fibre,  bearings,  tyres, ...

 

You will see that every TWI command probably has the TWINT bit set.

This simply clears the TWINT flag (if it is set by some previous operation)

 

If the TWINT was not set,  clearing it makes no difference.

 

Seriously,   you don't need to worry about TWINT or any other low level TWI registers.    Just use the library.

 

David.

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

clawson wrote:

Have you ever thought of running the Peter Fleury code in the simulator, or better still, a real AVR with a debugger attached? That way you can follow it in to every last write to TWCR, TWAR, TWDR or whatever else it may be accessing. There's quite a difference between "dry reading" C code and trying to determine what it is going to do and the alternative of being able to actually watch it in action and see exactly what it is doing!

 

I have already run the code in my AtMega328P-PU. The code was rearranged by jmgdesign and it worked. But I have never run the code using a debugger! I'm not very familiar with debuggers! If you can explain me how to do it, I can try!

 

frog_jr wrote:

Looking at the (very confused) example in the datasheet (page 216) and the diagram of a typical transmission (Figure 22-10, page 214):

  1. Write TWCR setting TWINT (clears any the interrupt flag (if present), initiates START condition), TWSTA (requesting START), and TWEN (enable TWI interface).
  2. After the TWI transmits the START condition, TWINT will be set (by hardware), so wait for TWINT to go high
  3. Check the Status (in TWSR) and if not equal to start, process as an error, else, load the slave address + write (SLA+W) into the TWDR and then write TWCR setting bits TWINT (to clear the existing interrupt flag) and TWEN (to enable the transmission)
  4. Wait until the hardware sets the TWINT

and so on...

 

The hardware sets the TWINT flag. (making the bit 1)

Software writing a 1 to the TWINT bit clears the flag. (making the bit 0)

 

Edit: typos

Edit: This is referencing the ATmega48A/PA/88A/PA/168A/PA/328/P datasheet Atmel-8271J-AVR- ATmega-Datasheet_11/2015

 

Hum, ok... I got it!

 

david.prentice wrote:

Quite honestly,   you read the HTML documentation,   use the library according to the docs.

 

Treat any library as a Black Box.   i.e. obey the rules.   supply the input.   receive the output.

 

Yes,   when you have gained experience from using a library correctly,  you might want to study how the library works.

I would start by riding a bicycle before you start worrying about carbon-fibre,  bearings,  tyres, ...

 

You will see that every TWI command probably has the TWINT bit set.

This simply clears the TWINT flag (if it is set by some previous operation)

 

If the TWINT was not set,  clearing it makes no difference.

 

Seriously,   you don't need to worry about TWINT or any other low level TWI registers.    Just use the library.

 

David.

 

Yes, you might be right, but atm this is my goal... To understand PF's code! So I want to stick with my goal. Then, when I try PF's code, probably I'll be able to use it flawlessly or almost flawlessly because I already have a good insight of what is going on in the background!

Last Edited: Tue. Nov 8, 2016 - 12:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n wrote:
But I have never run the code using a debugger! I'm not very familiar with debuggers! If you can explain me how to do it, I can try!
I was going to say you just spend $50, get an Atmel ICE and go play but of course Microchip came along and doubled that $50 to $100.

 

Anyway if you have an ICE then it can be used as a simple ISP programmer like you probably already have but it has another trick up it's sleeve. It is a USB connected device just like your current programmer probably is but alongside programming it can also take control of an AVR as it runs. It can either make the AVR execute a single opcode at a time so you can run each in turn and see what is going on inside the AVR at every step or, more usually you can tag a line in C or Asm as a place you want it to stop (you can have several of these breakpoints) and you can just say "let the AVR run until it hits one of these". Whether stepping or breakpointing you arrive at a particular line in your program and it can then instantly show you what every variable is set to and what all the bits in all the AVR control registers are set to. So you can put a break on the point in the code where Fleury is making writes to TWXX registers and see the exact bit pattern (TWINT or whatever else) is about to be written so you can watch exactly what the code is doing.

 

While you can do the same in simulation without the real AVR the fact is that what the simulator will not simulate is the behaviour of the I2C device at the other end of the SDA/SCl wires. If you use a real AVR with a debugger it will really be talking to the 24LC08 or whatever it and you can see the way in which it responds (or the view the AVR has of that anyway).

 

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

clawson wrote:

PsySc0rpi0n wrote:
Why is there the need for the 1st function?
If only Peter had written a user manual. Oh wait a minute...

 

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

 

Note the difference on that page between start() and start_wait(). Specifically the return value or lack of it. Also the fact that the _wait one does what it says on the tin - it waits.

 

Clearly the intended use is either i2c_start() to "sniff" whether a device is present and be able to take action if it isn't while i2c_start_wait() knows the device is there and will doggedly try to make the connection.

 

Ok, are you sure that I have not read that before? I can't understand the sarcasm again! Anyway, that user manual doesn't answer my questions!

 

I already knowledge about the need of the 1st function... No need further explanations! Thanks anyway!

 

david.prentice wrote:

Quite honestly,   you read the HTML documentation,   use the library according to the docs.

 

Treat any library as a Black Box.   i.e. obey the rules.   supply the input.   receive the output.

 

Yes,   when you have gained experience from using a library correctly,  you might want to study how the library works.

I would start by riding a bicycle before you start worrying about carbon-fibre,  bearings,  tyres, ...

 

You will see that every TWI command probably has the TWINT bit set.

This simply clears the TWINT flag (if it is set by some previous operation)

 

If the TWINT was not set,  clearing it makes no difference.

 

Seriously,   you don't need to worry about TWINT or any other low level TWI registers.    Just use the library.

 

David.

 

I have already done that! I want to dissect PF's code! Can we please focus on my goal??? I just want to understand his code!

 

clawson wrote:

PsySc0rpi0n wrote:
But I have never run the code using a debugger! I'm not very familiar with debuggers! If you can explain me how to do it, I can try!
I was going to say you just spend $50, get an Atmel ICE and go play but of course Microchip came along and doubled that $50 to $100.

 

I'm not sure what you mean with "Microchip has just doubled it from $50 to $100. I paid like 2€ for 24LC08B and 24LC16B...

 

clawson wrote:

Anyway if you have an ICE then it can be used as a simple ISP programmer like you probably already have but it has another trick up it's sleeve. It is a USB connected device just like your current programmer probably is but alongside programming it can also take control of an AVR as it runs. It can either make the AVR execute a single opcode at a time so you can run each in turn and see what is going on inside the AVR at every step or, more usually you can tag a line in C or Asm as a place you want it to stop (you can have several of these breakpoints) and you can just say "let the AVR run until it hits one of these". Whether stepping or breakpointing you arrive at a particular line in your program and it can then instantly show you what every variable is set to and what all the bits in all the AVR control registers are set to. So you can put a break on the point in the code where Fleury is making writes to TWXX registers and see the exact bit pattern (TWINT or whatever else) is about to be written so you can watch exactly what the code is doing.

 

While you can do the same in simulation without the real AVR the fact is that what the simulator will not simulate is the behaviour of the I2C device at the other end of the SDA/SCl wires. If you use a real AVR with a debugger it will really be talking to the 24LC08 or whatever it and you can see the way in which it responds (or the view the AVR has of that anyway).

 

 

Hum, ok... But that looks like a budget too high for just a couple of tests! I'll try to stick to what I have at the moment!

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

My observation is that you tend to read too fast and miss critical things. This frustrates us as your responses show that you missed something critical. It also means these threads drag along and don't converge.

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

Kartman wrote:
My observation is that you tend to read too fast and miss critical things. This frustrates us as your responses show that you missed something critical. It also means these threads drag along and don't converge.

 

It is not converging because some of the replies are diverging. I tried to do everything that was asked to me to make a good thread and to get the help I want, but some of the people replying to the thread are suggesting me to do other things that I don't want to do. My goal is one... Dissect PF's code! So, if people keeps telling me to do other things, then yes, the thread will never converge to anywhere because I'm going to get lost answering over and over that I want to dissect PF's code and nothing else, for the moment!

 

Yes, you're probably right when you say that I might be missing some critical details! But that is why I'm here asking for help. If I missed nothing, probably I wouldn't need any help, right? But, let's keep focus in my goal, if possible!

 

I think that I have generally understood most of PF's twimaster.c file code. I'm now going to try to make a code to write and to read something and see if I have any further questions! 

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

The twimaster.c code is very straightforward.

 

You asked about TWINT.   I think that this was explained to you.

The functions clear any TWINT flag,  issue a TWI command.    Wait for it to complete i.e. TWINT is set.

 

If you do not understand the difference between i2c_start() and i2c_start_wait(),   you have to sit down with a nice cup of tea and a biscuit.

 

I suggest that you quote a specific function and ask a specific question.

You will get a specific answer.

 

I still reckon that you should gain experience by usng a "Black Box" before dissecting any library code.

Of course your learning schedule might suit you better.

 

I had deliberately avoided this thread.   It did not seem to be going very well.

 

David.

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

david.prentice wrote:

The twimaster.c code is very straightforward.

 

You asked about TWINT.   I think that this was explained to you.

The functions clear any TWINT flag,  issue a TWI command.    Wait for it to complete i.e. TWINT is set.

 

If you do not understand the difference between i2c_start() and i2c_start_wait(),   you have to sit down with a nice cup of tea and a biscuit.

 

I suggest that you quote a specific function and ask a specific question.

You will get a specific answer.

 

I still reckon that you should gain experience by usng a "Black Box" before dissecting any library code.

Of course your learning schedule might suit you better.

 

I had deliberately avoided this thread.   It did not seem to be going very well.

 

David.

 

But I already said that I have understood most of PF's code! I also said that I already acknowledge about TWINT and about i2c_start and i2c_start_wait! I didn't need any further explanations about PF's code... I said it in my last post! I'm already trying to write some data to my eeprom to test my code! So, everything's going good so far! 

Last Edited: Tue. Nov 8, 2016 - 11:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I have made a smaller version of a code that was sent to me a couple of days ago by jgmdesign that was working perfectly. My code is kinda working but there is a slight difference, and should there is no difference! When I upload my code and it is executed in the uC, the LEDs intensity is very very tiny! I barely can see the LED's ON.Why is that? With jgmdesign code, the LED's turn ON just perfect and I change nothing on the hardware!

 

Going to place here jgmdesign code and my code. They are pretty much the same but I have commented my code, my own way!

 

jgmdesign working code:

https://ideone.com/kaeo5l

 

My own code:

https://ideone.com/PfsAFX

 

Is there anything wrong with my code comparing to jgmdesign's one?

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

With what you're doing there is no reason for you to ask questions on a forum. You are littering cyberspace. You can find all you need to know on the interwebs - you just need to look, read and experiment. If i can write an iphone app that records live video with live data via bluetooth simply by persistance and Googling the web and following tutorials, then getting an eeprom working with an AVR that has been done a zillion times by a zillion people that have posted code and tutorials should be a no brainer.
Personally, i would start with an Arduino example and work backwards down to the register level. You'll be surprised at what you learn.

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

Please do not put words in my mouth.  I intended no sarcasm at all.  If you think that I cannot help thinking that you are over sensitive and probably have personality problems.  I won't trouble any of your tiresome threads in future where I was simply trying to help you understand. Frankly you aren't worth the effort. 

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

Oh well, I had such high hopes for this one.

 

Before the OP started this thread a PM was exchanged between the two of us.  In that exchange I gave some very explicit instructions and suggestions on how to start this thread AND how to not get caught up in the extra advice.  Whether it is useful or not.  Things started off pretty good, but here we are with a whole lot of pissy people which could have been avoided.

 

The OP listed in the OP to this thread everything he has to work with and the software tools he is using - At my advice so as everyone is on the same page.  Give him credit for making that effort.  If some of your suggestions were made without reading that OP,  his ( I admit) obnoxious reply is somewhat expected as I did tell him to include EVERYTHING.  He made the effort per my suggestions to prevent this from happening and it may look to him like it backfired.   It does not make his reaction out of frustration proper, but at least see it from that perspective.  If you still feel that attempting to help is not worth the effort, then I ask you to no longer contribute...then there is no more frustrations

 

I seem to remember when I started here getting flamed by several of you pretty bad as well.  Especially when I really started to dive into C.  How Cliff stuck with it after my ranting, and a nasty PM I sent him regarding a thread I started that the lot went and derailed before it magically was deleted remains a mystery to this date to me.  Many stopped contributing to my threads for a long time until I 'grew up' a little bit.  I still ask sucky questions, but I at least don't bark back at the hands that feed me too.

 

Point I am trying to make is that the OP is clearly very new, very inexperience, and also very eager to learn.  Albeit not in the typical convention we would prefer.  The OP is not asking anyone to do his homework, and although his responses are at times tangents I do see some methodology to where his thinking is.  He's at least trying.  Which is more than I can say for some of the Trolls and such we get.

 

Your mileage and thoughts may vary.

 

To PsySc0rpi0n,

THis is the one, and only time I am going to stick up for you.  I gave you some good, sound advice, and I also warned you about becoming hostile at those that are trying to help you.  No one is paid to be here, and as such owes you nothing.  Looks like you gave up on that advice.  I can understand your getting frustrated.  It's tough to type out what you are thinking at the moment.  BUT that's no excuse for jumping down someones throat either.  One of the best pieces of advice I can give you is that you THINK before you type.  THis is your third thread, and like the other two, it's turned into a schoolyard fight.

 

David gave a great piece of advice:

david.prentice wrote:
you have to sit down with a nice cup of tea and a biscuit.

That means when you are getting frustrated, stop what you are doing, and go do something else for a few minutes, DO NOT respond to a post.  Go outside and take a breath. 

 

 

PsySc0rpi0n wrote:
Ok, I have made a smaller version of a code that was sent to me a couple of days ago by jgmdesign that was working perfectly. My code is kinda working but there is a slight difference, and should there is no difference!

Prime example of THINK before you type.  " there is a slight difference, and should there is no difference"

 

There is a BIG difference.  You pulled out a lot of the admittedly unneeded code that wrote 0x75 to the 5th address in the memory chip.  I ran your code and it does work.....almost....Keep reading

 

 

PsySc0rpi0n wrote:
When I upload my code and it is executed in the uC, the LEDs intensity is very very tiny! I barely can see the LED's ON.Why is that? With jgmdesign code, the LED's turn ON just perfect and I change nothing on the hardware!

 

Hmmm, you changed nothing in the hardware.  OK, but you changed the code(firmware) didn't you?  My code worked, yours did not.  Did you compare your code to mine line by line?  I will put ten bucks USD you did not.

 

Unlike in your last thread I am not going to give you the answer right away.  I just made a very OBVIOUS hint at what you should do.  As soon as I saw it, and made the correction your code will work.

 

So, I suggest you stop performing any more "dissections" of Peters code, and perform one on yours.

 

 

david.prentice wrote:
sit down with a nice cup of tea and a biscuit.

Splendid idea!  I am going to take that advice right now.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

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

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

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

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

PsySc0rpi0n wrote:

But I have never run the code using a debugger! I'm not very familiar with debuggers! If you can explain me how to do it, I can try!

 

My absolute favourite debugging tools...

 

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

clawson wrote:

Please do not put words in my mouth.  I intended no sarcasm at all.  If you think that I cannot help thinking that you are over sensitive and probably have personality problems.  I won't trouble any of your tiresome threads in future where I was simply trying to help you understand. Frankly you aren't worth the effort. 

 

Well, I'm sorry if I saw something where there was nothing! If you're not going to participate anymore, I'll understand your decision! But I still want to thank you for the help given so far!

Psy

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

jgmdesign wrote:

...

...

...

 

PsySc0rpi0n wrote:
Ok, I have made a smaller version of a code that was sent to me a couple of days ago by jgmdesign that was working perfectly. My code is kinda working but there is a slight difference, and should there is no difference!

Prime example of THINK before you type.  " there is a slight difference, and should there is no difference"

 

There is a BIG difference.  You pulled out a lot of the admittedly unneeded code that wrote 0x75 to the 5th address in the memory chip.  I ran your code and it does work.....almost....Keep reading

 

 

PsySc0rpi0n wrote:
When I upload my code and it is executed in the uC, the LEDs intensity is very very tiny! I barely can see the LED's ON.Why is that? With jgmdesign code, the LED's turn ON just perfect and I change nothing on the hardware!

 

Hmmm, you changed nothing in the hardware.  OK, but you changed the code(firmware) didn't you?  My code worked, yours did not.  Did you compare your code to mine line by line?  I will put ten bucks USD you did not.

 

Unlike in your last thread I am not going to give you the answer right away.  I just made a very OBVIOUS hint at what you should do.  As soon as I saw it, and made the correction your code will work.

 

So, I suggest you stop performing any more "dissections" of Peters code, and perform one on yours.

 

 

david.prentice wrote:
sit down with a nice cup of tea and a biscuit.

Splendid idea!  I am going to take that advice right now.

 

JIm

 

Yes, I have removed some lines because I thought, obviously wrong, they were just doing the same as the code that is writing 1, 2, 4 and 8 to the eeprom. Issuing a start+write (before the if), setting an address, writing a value, issuing a stop. Then issuing another start+write, write an address (again), issuing a rep_start+read, read the value and issuing a stop.

So, as someone said, I just tried my own stuff and actually experimented! It didn't work, so I get a conclusion: those removed lines are indeed needed! Why?? That is my question. :) Hope I can get or the answer or tips to get to the answer!

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

Put yourself in my position. How could i possibly answer your question? Where are the missing lines?
Do i need to dig through the history to find them?
You want to learn - so start learning. Do your research and answer your own questions for once. You've got plenty of evidence to work from.

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

I am at a loss.
.
I looked at jgm's code in #24. I can see a big problem. He does not wait for the Page-write to complete.
I looked at your code. It looks fine to me. EDIT. apart from DDRB should make PORTB output.
.
Of course, we have no idea what is current code. Or what edits you might have made.
.
If you take the time and effort to compose your questions, you could get accurate answers.
.
And I would be prepared to actually run and debug your code. Possibly offer constructive advice.
.
David.

Last Edited: Wed. Nov 9, 2016 - 08:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

Put yourself in my position. How could i possibly answer your question? Where are the missing lines?
Do i need to dig through the history to find them?
You want to learn - so start learning. Do your research and answer your own questions for once. You've got plenty of evidence to work from.

 

Sure, I'm sorry!

 

In jgmdesign code, there were these lines:

 

ret = i2c_start(Dev_24LC08B+I2C_WRITE);   // set device address and write mode
if ( ret ){
   /* failed to issue start condition, possibly no device found */
   i2c_stop();
   PORTB=0x00;                            // activate all 8 LED to show error */
}else{
   /* issuing start condition ok, device accessible */
   i2c_write(0x05);                       // write address = 5
   i2c_write(0x75);                       // ret=0 -> Ok, ret=1 -> no ACK
   i2c_stop();                            // set stop conditon = release bus

   /* write ok, read value back from eeprom address 0x05, wait until
   the device is no longer busy from the previous write operation */
   i2c_start_wait(Dev_24LC08B+I2C_WRITE); // set device address and write mode
   i2c_write(0x05);                       // write address = 5
   i2c_rep_start(Dev_24LC08B+I2C_READ);   // set device address and read mode
   ret = i2c_readNak();                   // read one byte
   i2c_stop();

   PORTB = ~ret;                          // output byte on the LED's

So, there is the i2c_start to probe for the device's presence! Then there is the code inside the if in case of error!

Then in the else there is the routine to set address 0x05 and write there the value 0x75. Then a i2c_stop.

Then he set 0x05 address again using the i2c_start_wait+write and after that he reads back from 0x05 using i2c_rep_start + read the value of 0x75 using i2c_readNak and issuing the i2c_stop!

 

I have removed these lines because I thought they were just doing the same as the code below that one only using different address memory (0x05) and different value (0x75). In the code below that, there is the same procedure but using memory address 0x00 and values 0x01, 0x02, 0x04 and 0x08!

 

So, I have removed these lines that sets address 0x05 and writes the value 0x75 and the lines that reads back the value from 0x05 and tried just to write 0x01, 0x02, 0x04 and 0x08 to address 0x00 and next.

 

But looks like it is not working this way. I want to understand why that procedure is needed. I mean the procedure of writing 0x75 to address 0x05 and read it back!

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

Fix the DDRB in #24. Then your code looks fine to me. i.e. the LEDs can be driven properly.
.
David.

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

david.prentice wrote:

Fix the DDRB in #24. Then your code looks fine to me. i.e. the LEDs can be driven properly.
.
David.

 

So as far as I understood, that piece of code that writes 0x75 to the address 0x05 is not needed in fact, is that it?

And the only problem in my code is that I set DDRB pins as input instead of output, right?

 

I cannot test the corrected code because I'm at work and I don't have the hardware with me! Tomorrow I'll test it again!

 

Thanks

Last Edited: Wed. Nov 9, 2016 - 09:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here you are.   I built your code for a AT24C32 EEPROM running on a regular UNO clone.

You can see my edits by looking for ".kbv"

 

I would recommend buying a Uno clone.   Especially the ones with extra rows of holes for male header pins.

You don't need to run Arduino code.   I simply build with AS7 and use the Bootloader via External Tools.

 

The best thing about Arduino is that you always have Serial comms.

And you can add ready-made shields, libraries, sketches, ...

 

/*
* psyscorpion_fleury.c
*
* Created: 09/11/2016 08:40:28
* Author : David
*/

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <inttypes.h>

#include "i2cmaster.h"     //.kbv header is in project directory

#define Dev24LC08B  0xA0

int main(void){
    uint8_t readbyte;
    
    DDRB = 0x0F;
    PORTB = 0x0F;
    
    i2c_init();
    
    if( i2c_start(Dev24LC08B + I2C_WRITE) ){//check if device is present
        i2c_stop();
        return -1;
        }else{
        //write data to eeprom
        i2c_write(0x00);//.kbv HI location for my AT24C32 EEPROM
        i2c_write(0x00);//send address where to save data
        i2c_write(0x01);//send data to write in the previous address
        i2c_write(0x02);//send data to write in the next address
        i2c_write(0x04);//send data to write in the next address
        i2c_write(0x08);//send data to write in the next address
        i2c_stop();
        
        //read data from eeprom
        
        //send write command to set the address where to start reading
        i2c_start_wait(Dev24LC08B + I2C_WRITE);
        i2c_write(0x00);//.kbv HI location for my AT24C32 EEPROM
        i2c_write(0x00);
        
        //send a read command and read the bytes that were previously written to eeprom
        i2c_rep_start(Dev24LC08B + I2C_READ);
        readbyte = i2c_readAck();
        PORTB = readbyte;
        _delay_ms(500);
        readbyte = i2c_readAck();
        PORTB = readbyte;
        _delay_ms(500);
        readbyte = i2c_readAck();
        PORTB = readbyte;
        _delay_ms(500);
        readbyte = i2c_readNak();
        PORTB = readbyte;
        _delay_ms(500);
        i2c_stop();
    }
    
    while(0x01);
    return 0;
}

David.

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

Well I was hoping everyone would have stopped posting so as to wait and see if the OP would have found his mistake, but David apparently did it for him instead.

 

david.prentice wrote:
I looked at jgm's code in #24. I can see a big problem. He does not wait for the Page-write to complete.

David, I have attached Peter Fleury's code to this post.  If you compare them side by side you will see that the only changes I made were:

 

Added PORTB to show outputs.

Changed the value of the variable stored in the EEPROM to only use the lower nibble

Added outputting the value of 'ret' to PORTB after reading it from the EEprom

 

The reason I only used the lower nibble is because the OP's setup uses an external crystal which uses PORTB's pins 6 and 7.

So if my code improperly does not wait for a page write, then the fault is in Peters code as thats all I used.  Granted in the OP's case since he is using a larger part, maybe then some additional code is needed?

 

PsySc0rpi0n wrote:
So as far as I understood, that piece of code that writes 0x75 to the address 0x05 is not needed in fact, is that it?

Yes, correct.

 

PsySc0rpi0n wrote:
And the only problem in my code is that I set DDRB pins as input instead of output, right?

Pretty much.  David has some other points you might want to look at, and he can respond to if he desires.

 

 

I too think I will take Cliffs path and walk away from this as well, and go put my soapbox back in the closet.

 

Time for some tea and a biscuit.

 

JIm

Attachment(s): 

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

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

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

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

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

@Jim,

 

I owe you an apology.    This is the "suspect" sequence from the link in #24 that I misread:

 

   /* issuing start condition ok, device accessible */
      i2c_write(0x05);                       // write address = 5
      i2c_write(0x75);                       // ret=0 -> Ok, ret=1 -> no ACK
      i2c_stop();                            // set stop conditon = release bus
 
      /* write ok, read value back from eeprom address 0x05, wait until
       the device is no longer busy from the previous write operation */
      i2c_start_wait(Dev_24LC08B+I2C_WRITE); // set device address and write mode
      i2c_write(0x05);                       // write address = 5
      i2c_rep_start(Dev_24LC08B+I2C_READ);   // set device address and read mode
      ret = i2c_readNak();                   // read one byte
      i2c_stop();

I had misread the i2c_start_wait() that follows the i2c_stop() !!

 

This is one of the penalties of reading code on a Tablet instead of a proper PC.

 

So your code would write 0x75 at location #5 in the page buffer.  

The i2c_stop() would initiate the Page-Write.

The i2c_start_wait() would poll the EEPROM until it is ready.

The i2c_write(0x05) sets the location register in the EEPROM

And i2c_rep_start() puts the EEPROM into read mode.

 

David.

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

david.prentice wrote:
I owe you an apology. This is the "suspect" sequence from the link in #24 that I misread:

No worries David.  I went back and re-checked what I wrote to make sure I did not miss something as your observations are usually spot on.  Thanks for keeping me on my toes.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

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

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

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

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

Ok, yesterday when I got home (late at night) I yet changed the DDRB value but I couldn't load the firmware to the AtMega328P-PU for some unknown reason. I kept saying that the programmer was not responding! I will try again today because I'll get home earlier than yesterday!

 

I might start another thread because the goal of this one is already achieved, which it was to dissect and understand PF's code!

Now, I want to do something else, namely use Peter's libraries for LCD and for I2C to show in the LCD the contents of the 24LC08B IC!

 

Cheers

Psy