NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Master/Slave I2C

May 04, 2011
by Noter
Noter's Avatar

Here is an example of master/slave communication between the nerdkit and another ATmega using the I2C interface. Similar to the master/slave SPI, in this example the master also sends a number to the slave, the slave increments the number and sends the result back to the master. Using TWI.c and TWI.h the I2C interface is implemented using interrupts so they must be enabled in both the master and slave for things to work. Slave processing actually occurs in an interrupt callback function when a data packet is received from the master. The slave modifies the buffer and then the master reads back the buffer. Version V1.2 of TWI.c and TWI.h can be found in the Access Serial EEPROM using I2C thread near the bottom.

// master_I2C.c -   load on nerdkit with LCD
//                  note:   I2C and ATmel TWI (Two Wire Interface) are 
//                          the same thing.
#include <avr/io.h> 
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>

#include "../libnerdkits/lcd.h"
#include "TWI.h"

// preprocessor macros for port/pin manulipulation
#define INPUT2(port,pin) DDR ## port &= ~_BV(pin) 
#define OUTPUT2(port,pin) DDR ## port |= _BV(pin) 
#define CLEAR2(port,pin) PORT ## port &= ~_BV(pin) 
#define SET2(port,pin) PORT ## port |= _BV(pin) 
#define TOGGLE2(port,pin) PORT ## port ^= _BV(pin) 
#define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0)
#define INPUT(x) INPUT2(x) 
#define OUTPUT(x) OUTPUT2(x)
#define CLEAR(x) CLEAR2(x)
#define SET(x) SET2(x)
#define TOGGLE(x) TOGGLE2(x)
#define READ(x) READ2(x)
#define PULLUP_ON(x) INPUT2(x); SET2(x)
#define PULLUP_OFF(x) INPUT2(x); CLEAR2(x)

// define ports, pins
// wire master SCL (PC5) to slave SCL (PC5)
// wire master SDA (PC4) to slave SDA (PC4)
// put a 4.7k external pull-up resistor on SCL and another on SDA
// wire master PC1 to slave reset (PC6)
#define SLAVE_RESET     C,1

// LCD stream file - enable printf in functions outside of main()
FILE lcd_stream;

// settings for I2C
uint8_t I2C_buffer[sizeof(int)];
#define I2C_SLAVE_ADDRESS 0x10
void handle_I2C_error(volatile uint8_t TWI_match_addr, uint8_t status);

// --------------------------------------------------------------------------------------------------------
int main() {
    // initialize LCD display
    fdev_setup_stream(&lcd_stream, lcd_putchar, 0, _FDEV_SETUP_WRITE); 
    fprintf_P(&lcd_stream, PSTR("master_I2C"));

    // Specify startup parameters for the TWI/I2C driver
    TWI_init(   F_CPU,                      // clock frequency
                300000L,                    // desired TWI/IC2 bitrate
                I2C_buffer,                 // pointer to comm buffer
                sizeof(I2C_buffer),         // size of comm buffer
                0                           // optional pointer to callback function

    // Enable interrupts

    // reset the slave for a clean start

    // send 100 test bytes
    int i;
        // set value into buffer

        // transmit
        TWI_master_start_write_then_read(   I2C_SLAVE_ADDRESS,  // slave device address
                                            sizeof(I2C_buffer), // number of bytes to write
                                            sizeof(I2C_buffer)  // number of bytes to read

        // wait for completion

        // if error, notify and quit
            fprintf_P(&lcd_stream, PSTR("TWI error at %d"), i); 

        // check result
            fprintf_P(&lcd_stream, PSTR("%d OK"),i); 
        else {
            fprintf_P(&lcd_stream, PSTR("Error at byte %d"), i); 
            fprintf_P(&lcd_stream, PSTR("expected %d, got %d"), i, *(int*)I2C_buffer); 

    // done
// --------------------------------------------------------------------------------------------------------


// slave_I2C.c
#include <avr/io.h> 
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>

#include "TWI.h"

// settings for I2C
uint8_t I2C_buffer[25];
#define I2C_SLAVE_ADDRESS 0x10
void handle_I2C_interrupt(volatile uint8_t TWI_match_addr, uint8_t status);

// --------------------------------------------------------------------------------------------------------
int main() {
    // Initialize I2C
    TWI_init(   F_CPU,                      // clock frequency
                100000L,                    // desired TWI/IC2 bitrate
                I2C_buffer,                 // pointer to comm buffer
                sizeof(I2C_buffer),         // size of comm buffer
                &handle_I2C_interrupt       // pointer to callback function

    // Enable interrupts

    // give our slave address and enable I2C
    TWI_enable_slave_mode(  I2C_SLAVE_ADDRESS,      // device address of slave
                            0,                      // slave address mask
                            0                       // enable general call

    // received data is processed in the callback
    // nothing else to do here
// --------------------------------------------------------------------------------------------------------
void handle_I2C_interrupt(volatile uint8_t TWI_match_addr, uint8_t status){
        // increment the integer in the buffer
        // and it will be returned during the read cycle
June 06, 2013
by Ralphxyz
Ralphxyz's Avatar

So hey Paul, have you updated this code or made any changes?


June 06, 2013
by Noter
Noter's Avatar

I don't remember making any updates. Haven't looked at these demo programs for a couple of years but I still use the TWI code. Lately I've been working on a project that uses a DS3232 RTC to time stamp measurement data as it is logged to eeprom. Both the DS3232 and eeprom use I2C and both are working fine with the TWI library code.

Why do you ask? Have you been using the TWI code?

June 06, 2013
by Ralphxyz
Ralphxyz's Avatar

I have a project coming up, that I am picturing using the TWI Master/Slave.

I will be running a Temperature process (filament extruding) with a stepper motor. I am thinking of running the stepper off the slave freeing the master to monitor temperature and control.

I sure appreciate the work you put into the I2C projects it really helped me.


June 06, 2013
by Noter
Noter's Avatar

I'm glad you found it helpful. I like master/slave projects but it is extra work and so far I've been doing ok with interrupt driven programs that are able to use all available cycles. I don't use delay.h functions (don't even include delay.h) but use the built-in timers instead. I also like to run 18.432Mhz because it's the fastest crystal for for the mega168/328 that is still baud rate friendly.

June 07, 2013
by Ralphxyz
Ralphxyz's Avatar

ha, I actually understand I2C better than interrupts.

It would be interesting to do a stepper driver using interrupts.


June 07, 2013
by Noter
Noter's Avatar

Sounds like you have an excellent opportunity to work more with interrupts. It will get easier with practice just like I2C did.

May 06, 2014
by lsoltmann
lsoltmann's Avatar

How easy or difficult is it to implement this if the master is a RaspberryPi using Python?

I'm working on developing a flight computer for research and need to be able to read PWM signals from an RC receiver, manipulate them, and then send them to the servos. I've posted on the Adafruit forum about the RaspberryPi and PWM and it appears that it is not able to read or really produce a PWM signal (at least not at the speeds I need). A user suggested using one micro controller to read the PWM and another micro controller to send PWM. I immediately thought of the nerd kit. The code posted here appears to be exactly what I need, but since my programming is rather limited, I was wondering how to implement this if the master is using Python (or should I stick with C?). Would it be possible to just add the PWM code into the above slave code and then read accordingly from the RaspberryPi? Thanks!

May 06, 2014
by Noter
Noter's Avatar

Yes, it should be about that easy. The slave doesn't care where or what the master is as long as the signals on the wire are correct.

January 06, 2015
by lsoltmann
lsoltmann's Avatar

I've working with the slave code, starting at a basic level, and am having some problems. I used the code as is, but added a


inside the while loop. In Python I have


and it gives back a value of 2. If I change the 3 to a 5, it gives back zero. What is the structure of the I2C_buffer/how should data be stored to it? Is the simple read_byte enough or should read_byte_data be used along with the position in the array?

January 07, 2015
by BobaMosfet
BobaMosfet's Avatar


In Noter's code, he declared the buffer thus:

uint8_t I2C_buffer[25];

That means it's a byte (unsigned char) buffer, 25 characters in size (consecutive).

Therefore, the following simply puts 0x03 in the first byte of that array space:



January 08, 2015
by lsoltmann
lsoltmann's Avatar

I guess my question would be how do I read from the I2C_buffer? I set I2C_buffer[0]=3 so I know the value and to see if I could read that value in python on a different device. I tried setting a few more of the array locations to known values and tried reading them in python using




where reg is the location in the I2C_buffer array. Both don't give the expected value back.

January 08, 2015
by JimFrederickson
JimFrederickson's Avatar

My first questions would be:

1 - Where did you get the Python Program to Run I2C?
2 - Have you used this Python Program to successfully communicate with 'other' I2C

For me that would be the first part.

Try to validate that the Python Program is working with other Devices.

We already know that "Noter's Program" is good.

It does seem like you are more comfortable with a "Computer" though, so that is
why I would start there.

Get your "Master I2C Implementation" to work and then proceed to communicating with
the AVR Microcontroller.

Without that you basically have a "2 Term Equation with 2 unkowns".

ALWAYS, much harder to solve.

January 09, 2015
by lsoltmann
lsoltmann's Avatar

The python script I am using to read the MCU is one that I wrote for a calibration of some I2C sensors so I know it works. I'm running the script on a Raspberry Pi and I've gutted it to just the necessities

import smbus
import time
bus = smbus.SMBus(1)

address = 0x27

while True:
    print number1
    print number2

I went back into the I2C_slave program and inside the while(true) loop, added


to see if I could read back any of the values. read_byte appears to just read back what ever was read previously and read_byte_data gives back a value equal to reg+1 (I tried multiple different values for reg). Are the array locations in I2C_buffer equivalent to registers?

January 09, 2015
by BobaMosfet
BobaMosfet's Avatar


Generally speaking, when we talk about buffers in any programming language, we are talking about a block of memory. Usually a consecutive block of elements of a given size. The Address is normally the physical memory location of any one of the elements. A single dimension array is nothing more than a consecutive block of ram of the specified type.

For example-- each byte/char/uint8_t is exactly 8-bits in length. String those together consecutively and you have a buffer that has an address at the start and for each byte location in RAM.

I2C_buffer looks like this in RAM. I'm just using location 0x00 as a starting point, but it would be wherever the linker locates it based on the make-file setup and architecture.

0x0000  00 00 00 00 00 00 00 00
0x0008  00 00 00 00 00 00 00 00
0x0010  00 00 00 00 00 00 00 00
0x0018  00

When you say: I2C_buffer[0] = 12; what you're saying is in reality:

0x0000  0C 00 00 00 00 00 00 00
0x0008  00 00 00 00 00 00 00 00
0x0010  00 00 00 00 00 00 00 00
0x0018  00

See? You stuffed a 12 (0x0C) into location 0, at address 0x0000.

The variable named I2C_buffer contains a single value: An Address: 0x0000, in my example above. that makes it a pointer. So when you access an array, you are indirectly accessing an offset index added to that base address.

In my example, if you asked for the address of the 20th location in the I2C buffer:

addr = &I2C_buffer[20];     // 0x0000 + 0x14

'addr' would be a variable with a value of: 0x0014

Which would be the location marked with asterisks, below:

0x0000  0C 00 00 00 00 00 00 00
0x0008  00 00 00 00 00 00 00 00
0x0010  00 00 00 00 ** 00 00 00
0x0018  00

Hope that clarifies it. Sorry for typos, just banged this out on my way out the door.


January 09, 2015
by JimFrederickson
JimFrederickson's Avatar

BM -

I think there is a problem with your last post.

c arrays/buffers are always referenced relative to "0".

So the 1st location of an array is an index of "0", which is currently set to

The "20th location" would be "0x13", which is currently set to "0x00".

The "21st location" would be "0x14", which is the location marked with asterisks.

January 09, 2015
by BobaMosfet
BobaMosfet's Avatar


You are correct-- when I counted it, I started with zero... It didn't look quite right, but as I said I was rushing out the door.... :: hand to face ::

:) Good catch.


January 12, 2015
by lsoltmann
lsoltmann's Avatar

BobaMosfet, thanks for the enlightenment on the buffers. What you said makes a lot of sense and I’m trying to implement it into the code. The ultimate goal of what I’m trying to do is measure a pulse length (which is proportional to RPM) and send it to a Raspberry Pi over I2C. The pulse length code I have working (thanks to Icarus, Now I’m trying to send it over I2C to the Pi. The pulse length values range from 5000 usec to 200 usec.

Lets say the pulse length is 1226 usec (defined as a uint32_t), since Noters code has the buffer as a uint8_t, the 1226 is divided up into two bytes, 202 and 4, and stored in the buffer. For simplicity, I hard coded


which based on what you said, would look like this in memory.

0x0000  CA 04 00 00 00 00 00 00
0x0008  00 00 00 00 00 00 00 00
0x0010  00 00 00 00 00 00 00 00
0x0018  00

On the Pi I can see the MCU at an address of 0x27, which is what I set it at. Using the wiringPi I2C library (, shouldn’t the commands

int setup=wiringPiI2CSetup(0x27);

return the values located at I2C_buffer[0] and I2C_buffer[1] respectively? Based on some I2C tutorials ( the “register” value is the internal address, which I thought was 0 and 1 (at least for the places I stored data).

Also, where should the “data manipulation” take place, inside the while(true) loop in the main function or inside the handle_I2C_interrupt function? Thanks for all the help so far!

January 13, 2015
by BobaMosfet
BobaMosfet's Avatar


If your buffer is a uint32_t, that is 4 bytes in length (32 bits), not 2. Would look more like this:

1226 = 0x000004CA

If MSB (most significant bit) is on the left in RAM it would appear thus:

0x0000  00 00 04 CA 00 00 00 00
0x0008  00 00 00 00 00 00 00 00
0x0010  00 00 00 00 00 00 00 00
0x0018  00

Because we are dealing with a memory block, you can tell the compiler to address it in any format you wish. Noter's code specifies it as an array of bytes-- that nomenclature is FOR THE COMPILER ONLY. Not for the CPU/MCU. All that does is tell the linker what size the offsets are for the base pointer of the memory block.

You could address his memory block as an array of unsigned longs (which is what a uint32 is), simply by adjusting how you address it by using type coercion (now called 'casting'). Casting is an incorrect word developed by a newer generation who didn't understand what was actually being done. Coercion is accurate because you are coercing or forcing a type to behave differently than what it was defined-- because you know what you are doing.

uint8_t I2C_buffer[25];
uint32_t myLongPtr;
uint32_t aULong;

myLongPtr = (uint32_t*)I2C_buffer;

aULong = myLongPtr[0];               // will return 0x000004CA

Now regarding the I2C communications, the Raspberry Pi link you sent makes me believe it wants to be the master, and your MCU would be the slave device. The 0x27 is supposed to be returned by the system as a unix file handle where it 'sees' an I2C device, and then you hand that value to the 'WiringPiI2C' library.

In which case, you need to initialize the MCU as a slave device for your I2C protocol, give it an address (like '1') and go from there.

Any location in RAM can be called a 'register' if you regularly use that location to handle a specific value. A register can be a byte, 2 bytes, 4, etc.

When you call 'wiringPiI2CReadReg8(setup,0);' Raspberry PI expects the underlying I2C code to be valid on both ends, and the wiring correct. It will try to issue a command to the specified slave device, and will expect that slave device to return a value to it from that specified 'register' within the slave device. Raspberry Pi neither knows nor cares about anything on the MCU. All it expects is that the device understands I2C and responds accordingly.

I would study the datasheet for the ATMEGA chip you are using, they have an I2C section which (usually) provides an example and a flow example also, so you can get a better understanding of the required interaction between the two devices. Raspberry Pi obfuscates all this stuff under the hood that you now have to learn in order to make it work on the embedded platform.


January 13, 2015
by Noter
Noter's Avatar

Casting is conversion of type only, underlying data not converted.

Coercion is conversion of type as well as data by the compiler.

Look it up, they are not synonymous.

January 13, 2015
by BobaMosfet
BobaMosfet's Avatar


Welcome back :)

Actually, if you look at the underlying disassembly (which is what we're really talking about), they are the same. Casting/Coercion is nothing more than telling the linker how to address the object in question in a way other than that which it may have been typedef'd.

In binary, there is no concept of 'type'-- just an address and an offset.


January 14, 2015
by Noter
Noter's Avatar

By definition they are not the same. Most people are confused by the two because their syntax is the same and both are commonly referenced as casting. If you are looking at a cast of integers and structures then only simple type conversion is required and can lead one to believe it is all the same. However if you look at the assembly when casting (converting) to/from a floating point variable in a math operation you will see the compiler actually performs coercion for you. It's about the compiler and how it produces the 'binary' rather than the 'binary' itself.

January 14, 2015
by BobaMosfet
BobaMosfet's Avatar

Noter brings up a good point, but although he and I like to reduce things to black and white in our own worlds, many people find a gray area here, and I think some of that is because there are other issues involved. It may help a newcomer to consider the context of implicit .v. explicit. For example, an implicit conversion is normally where the compiler performs extra work because the programmer didn't explicitly define the conversion desired. As in the case of using a char interchangeably with an int, as many people do.

I can reference publications where it's described that a programmer can "...use a cast to coerce the type of a given expression into another type." So, perhaps it's more accurate to say coercion is performed by casting? It isn't different so much as it is the act of performing an explicit coercion. Perhaps this school of thought is the source of why people use the term interchangeably. For me personally, I was taught it was 'coercion' when I began coding in C in the 70's. Before that I was doing a lot of assembly-language, so I was already thinking about how things worked below the compiler level.

As Noter mentioned the float, I wanted to be sure we addressed it. A 'float' is a special case, because it has a representation factor that relates to scale and magnitude, unlike any other type. Floats require extra steps by the compiler to alter that representation, showing in the disassembly (below).

Here is an example with me explicitly casting/coercing, and then without-- compiler made the right choice and the end assembly is identical either way even though 'aULong' is declared as an 'unsigned long' and 'floatNum' is declared as a 'float'. You can see the extra 2 calls into the float library code for the representation conversion and then equality testing:

Note that 'floatNum' is declared as a float and then initialized in main() to this value:

floatNum = 3.1415926536;

Now for the disassembly:

if(aULong == floatNum)
d2: 0e 94 b7 00     call    0x16e   ; 0x16e <__floatunsisf>
d6: 2b ed           ldi r18, 0xDB   ; 219
d8: 3f e0           ldi r19, 0x0F   ; 15
da: 49 e4           ldi r20, 0x49   ; 73
dc: 50 e4           ldi r21, 0x40   ; 64
de: 0e 94 87 00     call    0x10e   ; 0x10e <__eqsf2>
e2: 88 23           and r24, r24
e4: 29 f4           brne    .+10        ; 0xf0 <main+0x4a>

if((float)aULong == floatNum)
d2: 0e 94 b7 00     call    0x16e   ; 0x16e <__floatunsisf>
d6: 2b ed           ldi r18, 0xDB   ; 219
d8: 3f e0           ldi r19, 0x0F   ; 15
da: 49 e4           ldi r20, 0x49   ; 73
dc: 50 e4           ldi r21, 0x40   ; 64
de: 0e 94 87 00     call    0x10e   ; 0x10e <__eqsf2>
e2: 88 23           and r24, r24
e4: 29 f4           brne    .+10        ; 0xf0 <main+0x4a>

I use AVR Studio for an ATMEGA168, as that was convenient, and familiar to most everyone here.

I also did a bad cast, trying to misuse the float, and you can see that the compiler takes my word for it and simply does a subtract, compare and appropriate branch:

if(aULong == (unsigned long)floatNum)
bc: 03 97           sbiw    r24, 0x03   ; 3
be: a1 05           cpc r26, r1
c0: b1 05           cpc r27, r1
c2: 29 f4           brne    .+10

And here, people can see what happens with an implicit conversion between an unsigned long taking the value of a float (according to the C-Standard it truncates0:

aULong = floatNum;
a8: 83 e0           ldi r24, 0x03   ; 3
aa: 90 e0           ldi r25, 0x00   ; 0
ac: a0 e0           ldi r26, 0x00   ; 0
ae: b0 e0           ldi r27, 0x00   ; 0

Hope the helps everyone.


January 19, 2015
by BobaMosfet
BobaMosfet's Avatar

At the end of the day, controlling the binary is what you're after, and the compiler is nothing more than a tool, or layer between you and the binary. It's no different than talking to someone else-- you master your larynx and the language to allow you to communicate efficiently and effectively.

The float is a good example of that above. You can successfully coerce a float to an unsigned long, without allowing the compiler to do anything with the representation.

But why would this be useful?

Well, just as a real-world example, you might find it worthwhile to be able to store a float in 4-bytes (it's raw representation) in an EEPROM, for retrieval later. Such as an in-the-field sensor device recording relative humidity. If you store it as an ASCII representation, it might take 12 bytes, but the raw data only requires 4 to store the same precision.

Knowing how things really work, and what you can really do with the tools and objects you work with, is what allows you to become not just a great developer, but one of the few who know it as a black art and can craft things in ways the majority of developers will never achieve.

Let your imagination and knowledge guide you.


January 19, 2015
by Noter
Noter's Avatar

In your example above the AVR cross compiler is failing to convert the float to to long which is a bug in the compiler. Not that it matters much in the world of AVR but try the same test with the gcc compiler and you will see coercion performed every time as expected.

float f=3.14;
unsigned long l=3;
void main(){
    if(l==f) l=0;
    if((float)l==f) l=0;
    if(l==(long)f) l=0;
    if(f==l) l=0;
    if((long)f==l) l=0;
    if(f==(float)l) l=0;
    l=(unsigned long)f;

Compiled with "gcc -c -g -O0 -Wa,-a,-ad x.c > x.lst".

GAS LISTING /tmp/ccXJV0e3.s             page 1

   1                    .file   "x.c"
   2                    .text
   3                .Ltext0:
   4                    .globl  f
   5                    .data
   6                    .align 4
   9                f:
  10 0000 C3F54840      .long   1078523331
  11                    .globl  l
  12 0004 00000000      .align 8
  15                l:
  16 0008 03000000      .quad   3
  16      00000000 
  17                    .text
  18                    .globl  main
  20                main:
  21                .LFB0:
  22                    .file 1 "x.c"
   1:x.c           **** float f=3.14;
   2:x.c           **** unsigned long l=3;
   3:x.c           **** void main(){
  23                    .loc 1 3 0
  24                    .cfi_startproc
  25 0000 55            pushq   %rbp
  26                    .cfi_def_cfa_offset 16
  27                    .cfi_offset 6, -16
  28 0001 4889E5        movq    %rsp, %rbp
  29                    .cfi_def_cfa_register 6
   4:x.c           ****     if(l==f) l=0;
  30                    .loc 1 4 0
  31 0004 488B0500      movq    l(%rip), %rax
  31      000000
  32 000b 4885C0        testq   %rax, %rax
  33 000e 7807          js  .L2
  34 0010 F3480F2A      cvtsi2ssq   %rax, %xmm0
  34      C0
  35 0015 EB15          jmp .L3
  36                .L2:
  37 0017 4889C2        movq    %rax, %rdx
  38 001a 48D1EA        shrq    %rdx
  39 001d 83E001        andl    $1, %eax
  40 0020 4809C2        orq %rax, %rdx
  41 0023 F3480F2A      cvtsi2ssq   %rdx, %xmm0
  41      C2
  42 0028 F30F58C0      addss   %xmm0, %xmm0
  43                .L3:
  44 002c F30F100D      movss   f(%rip), %xmm1
  44      00000000 
  45 0034 0F2EC1        ucomiss %xmm1, %xmm0
  46 0037 7A10          jp  .L4
  47 0039 0F2EC1        ucomiss %xmm1, %xmm0
  48 003c 750B          jne .L4
  49                    .loc 1 4 0 is_stmt 0 discriminator 1
  50 003e 48C70500      movq    $0, l(%rip)
  50      00000000 
  50      000000
  51                .L4:
GAS LISTING /tmp/ccXJV0e3.s            page 2

   5:x.c           ****     if((float)l==f) l=0;
  52                    .loc 1 5 0 is_stmt 1
  53 0049 488B0500      movq    l(%rip), %rax
  53      000000
  54 0050 4885C0        testq   %rax, %rax
  55 0053 7807          js  .L6
  56 0055 F3480F2A      cvtsi2ssq   %rax, %xmm0
  56      C0
  57 005a EB15          jmp .L7
  58                .L6:
  59 005c 4889C2        movq    %rax, %rdx
  60 005f 48D1EA        shrq    %rdx
  61 0062 83E001        andl    $1, %eax
  62 0065 4809C2        orq %rax, %rdx
  63 0068 F3480F2A      cvtsi2ssq   %rdx, %xmm0
  63      C2
  64 006d F30F58C0      addss   %xmm0, %xmm0
  65                .L7:
  66 0071 F30F100D      movss   f(%rip), %xmm1
  66      00000000 
  67 0079 0F2EC1        ucomiss %xmm1, %xmm0
  68 007c 7A10          jp  .L8
  69 007e 0F2EC1        ucomiss %xmm1, %xmm0
  70 0081 750B          jne .L8
  71                    .loc 1 5 0 is_stmt 0 discriminator 1
  72 0083 48C70500      movq    $0, l(%rip)
  72      00000000 
  72      000000
  73                .L8:
   6:x.c           ****     if(l==(long)f) l=0;
  74                    .loc 1 6 0 is_stmt 1
  75 008e F30F1005      movss   f(%rip), %xmm0
  75      00000000 
  76 0096 F3480F2C      cvttss2siq  %xmm0, %rax
  76      C0
  77 009b 4889C2        movq    %rax, %rdx
  78 009e 488B0500      movq    l(%rip), %rax
  78      000000
  79 00a5 4839C2        cmpq    %rax, %rdx
  80 00a8 750B          jne .L10
  81                    .loc 1 6 0 is_stmt 0 discriminator 1
  82 00aa 48C70500      movq    $0, l(%rip)
  82      00000000 
  82      000000
  83                .L10:
   7:x.c           ****     if(f==l) l=0;
  84                    .loc 1 7 0 is_stmt 1
  85 00b5 488B0500      movq    l(%rip), %rax
  85      000000
  86 00bc 4885C0        testq   %rax, %rax
  87 00bf 7807          js  .L11
  88 00c1 F3480F2A      cvtsi2ssq   %rax, %xmm0
  88      C0
  89 00c6 EB15          jmp .L12
  90                .L11:
  91 00c8 4889C2        movq    %rax, %rdx
  92 00cb 48D1EA        shrq    %rdx
GAS LISTING /tmp/ccXJV0e3.s            page 3

  93 00ce 83E001        andl    $1, %eax
  94 00d1 4809C2        orq %rax, %rdx
  95 00d4 F3480F2A      cvtsi2ssq   %rdx, %xmm0
  95      C2
  96 00d9 F30F58C0      addss   %xmm0, %xmm0
  97                .L12:
  98 00dd F30F100D      movss   f(%rip), %xmm1
  98      00000000 
  99 00e5 0F2EC1        ucomiss %xmm1, %xmm0
 100 00e8 7A10          jp  .L13
 101 00ea 0F2EC1        ucomiss %xmm1, %xmm0
 102 00ed 750B          jne .L13
 103                    .loc 1 7 0 is_stmt 0 discriminator 1
 104 00ef 48C70500      movq    $0, l(%rip)
 104      00000000 
 104      000000
 105                .L13:
   8:x.c           ****     if((long)f==l) l=0;
 106                    .loc 1 8 0 is_stmt 1
 107 00fa F30F1005      movss   f(%rip), %xmm0
 107      00000000 
 108 0102 F3480F2C      cvttss2siq  %xmm0, %rax
 108      C0
 109 0107 4889C2        movq    %rax, %rdx
 110 010a 488B0500      movq    l(%rip), %rax
 110      000000
 111 0111 4839C2        cmpq    %rax, %rdx
 112 0114 750B          jne .L15
 113                    .loc 1 8 0 is_stmt 0 discriminator 1
 114 0116 48C70500      movq    $0, l(%rip)
 114      00000000 
 114      000000
 115                .L15:
   9:x.c           ****     if(f==(float)l) l=0;
 116                    .loc 1 9 0 is_stmt 1
 117 0121 488B0500      movq    l(%rip), %rax
 117      000000
 118 0128 4885C0        testq   %rax, %rax
 119 012b 7807          js  .L16
 120 012d F3480F2A      cvtsi2ssq   %rax, %xmm0
 120      C0
 121 0132 EB15          jmp .L17
 122                .L16:
 123 0134 4889C2        movq    %rax, %rdx
 124 0137 48D1EA        shrq    %rdx
 125 013a 83E001        andl    $1, %eax
 126 013d 4809C2        orq %rax, %rdx
 127 0140 F3480F2A      cvtsi2ssq   %rdx, %xmm0
 127      C2
 128 0145 F30F58C0      addss   %xmm0, %xmm0
 129                .L17:
 130 0149 F30F100D      movss   f(%rip), %xmm1
 130      00000000 
 131 0151 0F2EC1        ucomiss %xmm1, %xmm0
 132 0154 7A10          jp  .L18
 133 0156 0F2EC1        ucomiss %xmm1, %xmm0
 134 0159 750B          jne .L18
GAS LISTING /tmp/ccXJV0e3.s            page 4

 135                    .loc 1 9 0 is_stmt 0 discriminator 1
 136 015b 48C70500      movq    $0, l(%rip)
 136      00000000 
 136      000000
 137                .L18:
  10:x.c           ****     f=l;
 138                    .loc 1 10 0 is_stmt 1
 139 0166 488B0500      movq    l(%rip), %rax
 139      000000
 140 016d 4885C0        testq   %rax, %rax
 141 0170 7807          js  .L20
 142 0172 F3480F2A      cvtsi2ssq   %rax, %xmm0
 142      C0
 143 0177 EB15          jmp .L21
 144                .L20:
 145 0179 4889C2        movq    %rax, %rdx
 146 017c 48D1EA        shrq    %rdx
 147 017f 83E001        andl    $1, %eax
 148 0182 4809C2        orq %rax, %rdx
 149 0185 F3480F2A      cvtsi2ssq   %rdx, %xmm0
 149      C2
 150 018a F30F58C0      addss   %xmm0, %xmm0
 151                .L21:
 152 018e F30F1105      movss   %xmm0, f(%rip)
 152      00000000 
  11:x.c           ****     f=(float)l;
 153                    .loc 1 11 0
 154 0196 488B0500      movq    l(%rip), %rax
 154      000000
 155 019d 4885C0        testq   %rax, %rax
 156 01a0 7807          js  .L22
 157 01a2 F3480F2A      cvtsi2ssq   %rax, %xmm0
 157      C0
 158 01a7 EB15          jmp .L23
 159                .L22:
 160 01a9 4889C2        movq    %rax, %rdx
 161 01ac 48D1EA        shrq    %rdx
 162 01af 83E001        andl    $1, %eax
 163 01b2 4809C2        orq %rax, %rdx
 164 01b5 F3480F2A      cvtsi2ssq   %rdx, %xmm0
 164      C2
 165 01ba F30F58C0      addss   %xmm0, %xmm0
 166                .L23:
 167 01be F30F1105      movss   %xmm0, f(%rip)
 167      00000000 
  12:x.c           ****     l=f;
 168                    .loc 1 12 0
 169 01c6 F30F1005      movss   f(%rip), %xmm0
 169      00000000 
 170 01ce 0F2E0500      ucomiss .LC0(%rip), %xmm0
 170      000000
 171 01d5 7307          jae .L24
 172 01d7 F3480F2C      cvttss2siq  %xmm0, %rax
 172      C0
 173 01dc EB1E          jmp .L25
 174                .L24:
 175 01de F30F100D      movss   .LC0(%rip), %xmm1
GAS LISTING /tmp/ccXJV0e3.s            page 5

 175      00000000 
 176 01e6 F30F5CC1      subss   %xmm1, %xmm0
 177 01ea F3480F2C      cvttss2siq  %xmm0, %rax
 177      C0
 178 01ef 48BA0000      movabsq $-9223372036854775808, %rdx
 178      00000000 
 178      0080
 179 01f9 4831D0        xorq    %rdx, %rax
 180                .L25:
 181 01fc 48890500      movq    %rax, l(%rip)
 181      000000
  13:x.c           ****     l=(unsigned long)f;
 182                    .loc 1 13 0
 183 0203 F30F1005      movss   f(%rip), %xmm0
 183      00000000 
 184 020b 0F2E0500      ucomiss .LC0(%rip), %xmm0
 184      000000
 185 0212 7307          jae .L26
 186 0214 F3480F2C      cvttss2siq  %xmm0, %rax
 186      C0
 187 0219 EB1E          jmp .L27
 188                .L26:
 189 021b F30F100D      movss   .LC0(%rip), %xmm1
 189      00000000 
 190 0223 F30F5CC1      subss   %xmm1, %xmm0
 191 0227 F3480F2C      cvttss2siq  %xmm0, %rax
 191      C0
 192 022c 48BA0000      movabsq $-9223372036854775808, %rdx
 192      00000000 
 192      0080
 193 0236 4831D0        xorq    %rdx, %rax
 194                .L27:
 195 0239 48890500      movq    %rax, l(%rip)
 195      000000
  14:x.c           **** }
 196                    .loc 1 14 0
 197 0240 5D            popq    %rbp
 198                    .cfi_def_cfa 7, 8
 199 0241 C3            ret
 200                    .cfi_endproc
 201                .LFE0:
 203                    .section    .rodata
 204                    .align 4
 205                .LC0:
 206 0000 0000005F      .long   1593835520
 207                    .text
 208                .Letext0:
GAS LISTING /tmp/ccXJV0e3.s            page 6

                            *ABS*:0000000000000000 x.c
     /tmp/ccXJV0e3.s:9      .data:0000000000000000 f
     /tmp/ccXJV0e3.s:15     .data:0000000000000008 l
     /tmp/ccXJV0e3.s:20     .text:0000000000000000 main

January 20, 2015
by Noter
Noter's Avatar

It occured to me this morning that maybe you encountered a bug because like most nerdkit users, you have an old version of avrgcc. I ran the same test again with avrgcc v3.4.3 and coercion was performed every time, the float was converted to unsigned long. It's a good idea to stay current with all your compilers.

January 20, 2015
by BobaMosfet
BobaMosfet's Avatar


In the example above, I'm using GNU GCC with the WinAVR stub. The stub doesn't interfere with how the GCC compiler deals with coercion. It's not a bug, it's compiler 'implementation dependent' as this is not specified in the standard. This is one of the reasons it's important to understand your tools and what they generate.

All you're showing is that your standard float library, that came with that compiler for the processor you're showing code for, is enforcing a type coercion in the examples you've given. And even so, I could still force that compiler to not do it with careful crafting of C. I don't allow a compiler to limit my capabilities-- it does what I want it to do because _I_ am the architect of my logic, not it. It cannot, nor will it ever understand what I am trying to achieve.

As the ATMEGA code is what is pertinent to most folks here, I'm sticking with that in this forum.


January 22, 2015
by Ralphxyz
Ralphxyz's Avatar

Thanks Paul and BM, I sure love your "discussions".

It is interesting most of what BM says is how I learnt C.

I spent a lot of time in the old C developer newsgroups and even had some answers to my questions answered by Ritchie himself.

Of course I never became a programmer but have had some rich exposure.

January 22, 2015
by BobaMosfet
BobaMosfet's Avatar


Thanks. I think it's important for people to bear in mind that they are discussions. Noter and I view things differently, have different experiences and knowledge, but his knowledge is no less valuable or competent than mine. We both have done many, many things over the years and it works for each of us.

The original thread gets derailed a bit, but by the same time, by expounding upon our differing views and exploring them, people in the forums that know less, get exposed to more useful information. The truth is, most of the really valuable information about programming is no longer available to most people wanting to learn. Computer sections in bookstores are so small now-- they used to be shelves and shelves. Most of the books in them have little useful information. Manufacturers used to give away reference manuals for their processors just to get people to consider their chip. There were dozens of magazines with all sorts of things (Byte, Dr. Dobbs Journal, etc...).


January 22, 2015
by Noter
Noter's Avatar


Interesting that AVR studio compiler truncates rather than converting from float to long like the other gcc compilers. I won't be testing that one since I haven't a single windows box left after moving all of them over to linux.

Anyway, I hope we can agree that casting and coercion are not synonymous. Casting is conversion of type only while coercion is conversion of type and underlying data. May not match what you learned back in the stone age but now's always a good time to catch up. lol - I should know, I'm probably way older than you anyway.


If you want to be a programmer then all you have to do is program, program a lot. It takes lots of practice. Stick with a language until you get it by writing at least a handful of reasonably complex programs. I've been writing a gui program in python this week that controls an arduino over serial usb. First time I've really gotten into python and I like it. I've always liked developing with interpreters compared to compiled languages but no matter the language, it's always rewarding and fun to get a program working.

December 13, 2015
by escartiz
escartiz's Avatar

Reviving old thread to see if I can get some guidance. I am playing with 2 nerdkits to understand twi/i2c. I uploaded Noter's files intact, the only difference in the master is that since I don't have an lcd at the moment, I commented out all lcd lines. instead I am using uart to display the messages to my labtop.

I am getting "Error at byte 1 expected 1, got 1".

This makes no sense to me. If it is expecting 1 and the slave sends 1 isn't it suppose to request the next number instead of breaking?

December 16, 2015
by BobaMosfet
BobaMosfet's Avatar


TWI/I2C (hereafter just I2C) is relatively straightforward to use. All you have to do is the initial calculations to get the speed right (set TWBR and so on, based on appropriate calculations for your MCU speed). Once that's done, you need to know the address of the slave, usually there is an initial default value (like 0xA0, for example. Yours may differ). You need a buffer, like 60 bytes (depends on size of largest xmit/rcv data you might handle).

Beyond that, you have 13 TWSR responses to deal with, 9 TWI commands, and then monitoring/setting a few flags and/or registers and the Hi/Lo relationship between SDA and SCL. Pay particular attention to the state each of these is in, because usually if the slave doesn't understand something, or didn't get an ACK from the MASTER it will hold the SCL low (to let the Master know it's still processing). Even without a scope, you can check this with a logic probe.

I recommend, initially, you simply work with I2C in the simple form, just a Master and a Slave until you have that mastered, then add the rest of the logic to handle multiple, simultaneous I2C conversations. The datasheet has a bare-metal working example you can base your code off of.

I wouldn't waste time jumping into someone else's code until after you've read the datasheet and have your mind around I2C to some extent-- then if you look at other code, you'll have a better idea what you're looking at when registers, lines, and flags are referenced, and the logic in that code will be more evident-- but ultimately learn the way that works best for you. Hopefully, you have a fast enough oscilloscope-- I know that I usually have my scope set to the 5us to 10us range so I can see the pulse train for I2C.


Post a Reply

Please log in to post a reply.

Did you know that talking to the microcontroller over the USB/Serial link is easy under Windows, Linux, and OS X? Learn more...