Variable declaration question

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

Greetings Freaks -

 

I am mucking around in the Arduino twi module for M4809 and friends written by Microchip. I've made a few changes to help it run well as a c module but that mostly involved interrupt handling, as I recall. twi.c contains this:

 

/* Master variables */
static register8_t  master_slaveAddress;                       /*!< Slave address */
static register8_t* master_writeData;                          /*!< Data to write */
static register8_t* master_readData;                           /*!< Read data */
static register8_t  master_bytesToWrite;                       /*!< Number of bytes to write */
static register8_t  master_bytesToRead;                        /*!< Number of bytes to read */
static register8_t  master_bytesWritten;                       /*!< Number of bytes written */
static register8_t  master_bytesRead;                          /*!< Number of bytes read */
static register8_t  master_sendStop;                           /*!< To send a stop at the end of the transaction or not */
static register8_t  master_trans_status;                       /*!< Status of transaction */
static register8_t  master_result;                             /*!< Result of transaction */

And in twi.h, there is

 

/*! Master Transaction result enumeration. */
typedef enum TWIM_RESULT_enum {
	TWIM_RESULT_UNKNOWN          = (0x00<<0),
	TWIM_RESULT_OK               = (0x01<<0),
	TWIM_RESULT_BUFFER_OVERFLOW  = (0x02<<0),
	TWIM_RESULT_ARBITRATION_LOST = (0x03<<0),
	TWIM_RESULT_BUS_ERROR        = (0x04<<0),
	TWIM_RESULT_NACK_RECEIVED    = (0x05<<0),
	TWIM_RESULT_FAIL             = (0x06<<0),
} TWIM_RESULT_t;

 

 

Now, I am compiling this with our AS7/gcc and get no  complaints about typedef register8_t. I am puzzled because they seem to be able to assign values from that typedef'd enum (shown above) to master_result without any casting. Can someone explain what is going on, here? In the code module, I can find no other reference to either TWIM_RESULT_enum or TWIM_RESULT_t.

 

And, why all the "static" declarations? These are declared globally to the module.

 

And, in the enum, why all the, for example, (0x06<<0) statements? If the value  is left-shifted zero times, what is wrong with, for example, 0x06? Why make it more complicated than it needs to be? Oh, sorry, I forgot. This is Arduino code!

 

Many thanks

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Fri. Oct 16, 2020 - 04:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You declare variables static when you don't want them "known" outside of this compilation unit.

 

The result enums are just values.     You can assign a value like 1 or 2 to any variable.

 

If you are using an Arduino,  I strongly advise you to use the "given" Wire.h library.    It is written specifically for the target CPU.

If using Microchip "Application Note" code,   stick with the Microchip style.

 

Regarding (0x06 << 0) this hints that it is bits stored in the bottom bits of a SFR.

(0x05 << 4) implies that it is a bitfield starting at 4

 

Using the official BITFIELD name in your code will document what you are trying to do.   And the actual bit placement is not your concern.

Think about SPI or USART.   The individual bits might be in different positions in an SFR.   And the hardware pins are often in different positions on a GPIO PORT.

 

David.

Last Edited: Fri. Oct 16, 2020 - 04:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, that clears up two of the questions.

 

But, that sort of begs the question. One of the reasons, in my mind, for using typedefs is to avoid errors that result from assigning the wrong type from one variable to another. If I can do type_anything8_t = TWIM_RESULT_OK, why bother with the unique typedef for the enum? Why not just uint8_t?

 

This is a non-Arduino application of the twi module that has been extracted from Arduino-world because it works with M4909 et al.  Have no intention of using wire.h. This is the first time I have ever seen register8_t and have no idea where it came from or what it implies. Why isn't gcc complaining about it? As far as I can tell, there are no SFRs involved here; these appear to all be "ordinary" variables.

 

Thanks

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Fri. Oct 16, 2020 - 04:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

register8_t is defined by the compiler. On my Mac it's in /Users/xxx/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/avr/include/avr/ioxxx.h, where xxx depends on the specific hardware architecture (there are dozens).

 

The definition is: typedef volatile uint8_t register8_t

 

Arduino hardware 'cores' (aka 'board support packages') use a base architecture e.g. avr, sam, etc. which is then overlaid with files specific to the sub-architecture.

 

The Wire library is a fairly thin abstraction over the twi.[ch] files supplied by Microchip, mainly to suit the Wire 'model' expected by Arduino users. e.g. data is received by an ISR into a fixed buffer, which is then accessed with methods such as .available() and .read(). Most of the code in Wire.[ch] is buffer index management.

 

Yes, it works, but it's a pretty simplistic model as expected by Arduino users.

 

Last Edited: Fri. Oct 16, 2020 - 05:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
And, in the enum, why all the, for example, (0x06<<0) statements?

Probably just for consistency with cases where the shift is non-zero?

 

ka7ehk wrote:
One of the reasons, in my mind, for using typedefs is to avoid errors that result from assigning the wrong type from one variable to another.

No, not in 'C'; the 'C' language does not provide any such protection.

 

But arduino is C++ - and C++ does give you protection here, AIUI.

 

It's still good style in 'C' - but it doesn't actually give you any protection.

 

EDIT

 

In 'C', an enum doesn't have to be a type; eg, you could just have:

enum {
	TWIM_RESULT_UNKNOWN          = (0x00<<0),
	TWIM_RESULT_OK               = (0x01<<0),
	TWIM_RESULT_BUFFER_OVERFLOW  = (0x02<<0),
	TWIM_RESULT_ARBITRATION_LOST = (0x03<<0),
	TWIM_RESULT_BUS_ERROR        = (0x04<<0),
	TWIM_RESULT_NACK_RECEIVED    = (0x05<<0),
	TWIM_RESULT_FAIL             = (0x06<<0),
};

This can still have the advantage over #defines that the compiler knows the names of the constants - so these can be included in debug information

Top Tips:

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

obdevel wrote:
register8_t is defined by the compiler.
No it isn't. It's defined in C library headers:

C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain>grep register8_t * -r | grep typedef
avr/include/avr/iox128a1.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128a1u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128a3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128a3u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128a4u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128b1.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128b3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox128d4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox16a4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox16a4u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox16c4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox16d4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox16e5.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox192a3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox192a3u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox192c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox192d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256a3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256a3b.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256a3bu.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256a3u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox256d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32a4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32a4u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32c4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32d4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox32e5.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox384c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox384d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64a1.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64a1u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64a3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64a3u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64a4u.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64b1.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64b3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64c3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64d3.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox64d4.h:typedef volatile uint8_t register8_t;
avr/include/avr/iox8e5.h:typedef volatile uint8_t register8_t;

So it's merely a synonym for "volatile uint8_t".

ka7ehk wrote:
I am puzzled because they seem to be able to assign values from that typedef'd enum (shown above) to master_result without any casting.
That's because an enum is really a form of "int" and you can assign an int to uint8_t with implicit truncation. Of course this (in C++ anyway) only works one way:

#include <avr/io.h>

typedef volatile uint8_t register8_t;

typedef enum TWIM_RESULT_enum {
	TWIM_RESULT_UNKNOWN          = (0x00<<0),
	TWIM_RESULT_OK               = (0x01<<0),
	TWIM_RESULT_BUFFER_OVERFLOW  = (0x02<<0),
	TWIM_RESULT_ARBITRATION_LOST = (0x03<<0),
	TWIM_RESULT_BUS_ERROR        = (0x04<<0),
	TWIM_RESULT_NACK_RECEIVED    = (0x05<<0),
	TWIM_RESULT_FAIL             = (0x06<<0),
} TWIM_RESULT_t;

register8_t foo;
TWIM_RESULT_t bar;

int main(void)
{
	foo = TWIM_RESULT_BUS_ERROR;
	bar = 12345;
	
    while (1) 
    {
    }
}
		.././main.cpp: In function 'int main()':
D:\test\cpp_app\cpp_app\main.cpp(27,6): error: invalid conversion from 'int' to 'TWIM_RESULT_t {aka TWIM_RESULT_enum}' [-fpermissive]
		  bar = 12345;
		      ^

Faced with the same C is a little more permissive...

		.././main.c: In function 'main':
D:\test\test\main.c(21,8): warning: large integer implicitly truncated to unsigned type [-Woverflow]
		  bar = 12345;
		        ^

(one of the many reasons to prefer C++ over C even if you only stick to writing the C subset of it!)

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

Thanks for the clarification on typedef in C. That helps, unfortunately (I was expecting more from it).

 

And the rest, I guess, just  "is what it  is".

 

Appreciate the help. All of your postings have been "the answer" and you postings have been most enlightening.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Fri. Oct 16, 2020 - 05:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
One of the reasons, in my mind, for using typedefs is to avoid errors that result from assigning the wrong type from one variable to another

awneil wrote:

No, not in 'C'; the 'C' language does not provide any such protection.

C does offer protection but only for structs & unions. So if you really want type checking, you can wrap your variable into a struct.

 

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

In C, typedefs are just aliases.  They don't provide protection as othersl point out.

 

In C, enums are treated as integers.   You are assigning a small integer constant (w/ 16 bit integer type) to an 8 bit integer.

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

typedefs are invaluable for code designed for multiple platforms/parts, but it relies on people using the facility. I've just been working on the Arduino EEPROM library for AVR-DA, which 'inherited' code from smaller parts with 256 bytes of EEPROM, for which an 8 bit index variable sufficed. Of course, the DA has 512 bytes and requires a 16 bit value. A typedef would have involved a single edit rather than many, and you always miss one or two. All the more frustrating as the code probably originated from the m328p which has 1024 bytes (and uses a plain int) ! Code generated by MPLABX MCC defines eeprom_addr_t. 

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

For whatever reason, enum constants in C are ints.

To make that work, assigning an int to an enum has to be valid.

For some reason, even assignments between different enum types are allowed.

Doing a switch on an enum type can sometimes get one a useful warning.

Iluvatar is the better part of Valar.

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

Good question Jim! 

And great answers too, thanks all!

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

A bit puzzled, again, I have programs with enums and often a variable typedef'd to the enum. Does that mean that the variable is really a uint16_t? Is there a simple way to have 8-bit enums and variables? It seems wasteful to scatter 16-bit things around when they are not needed. When they are used as return values, that also increases the execution time (yes, I know, it is "pretty small") but when you have a relatively large program with significant timing constraints, you have to look under every pebble to make sure that its all clean as possible.

 

Thanks

Jim

 

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

gcc has a -fshort-enums option (https://gcc.gnu.org/onlinedocs/g...). There are potential ABI compatibility issues you should look into before using it. I'm not sure if they are relevant for AVR.

 

With C++ (starting with C++11) you can explicitly set the underlying type of an enum.

enum Foo : std::uint8_t {
    WIBBLE,
    WOBBLE,
    WUBBLE,
};

C++ (starting with C++11) also has strongly typed enums whose enumerators are scoped.

enum class Foo : std::uint8_t {
    WIBBLE,
    WOBBLE,
    WUBBLE,
};

 

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

Last Edited: Fri. Oct 16, 2020 - 07:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
     // compiled from my TWI (which has lots of bloat as I am trying to do muli-master on AVR128DA)
     avr-size BlinkLED.elf
   text    data     bss     dec     hex filename
   6274      22     214    6510    196e BlinkLED.elf
     // with
    static volatile TWIM_RESULT_t master_result;
    
            master_result = TWIM_RESULT_UNKNOWN;
     828:	10 92 5e 40 	sts	0x405E, r1	; 0x80405e <master_result>
     82c:	10 92 5f 40 	sts	0x405F, r1	; 0x80405f <master_result+0x1>
     830:	36 2f       	mov	r19, r22
     832:	aa e6       	ldi	r26, 0x6A	; 106
     834:	b0 e4       	ldi	r27, 0x40	; 64
     836:	fb 01       	movw	r30, r22
     
     // then compiled again 
     avr-size BlinkLED.elf
   text    data     bss     dec     hex filename
   6222      22     213    6457    1939 BlinkLED.elf
     // with
     static volatile uint8_t master_result;
     
             master_result = TWIM_RESULT_UNKNOWN;
     7fc:	10 92 5e 40 	sts	0x405E, r1	; 0x80405e <master_result>
     800:	36 2f       	mov	r19, r22
     802:	a9 e6       	ldi	r26, 0x69	; 105
     804:	b0 e4       	ldi	r27, 0x40	; 64
     806:	fb 01       	movw	r30, r22

Interesting.

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

ron_sutherland wrote:

static volatile TWIM_RESULT_t master_result;

            master_result = TWIM_RESULT_UNKNOWN;
     828:	10 92 5e 40 	sts	0x405E, r1	; 0x80405e <master_result>
     82c:	10 92 5f 40 	sts	0x405F, r1	; 0x80405f <master_result+0x1>

I assume this was not compiled using -fshort-enums ?

In which case (from some experimenting with gcc) it will use int or unsigned int as the type for an enum (unsigned int if none of the values are negative).

With -fshort-enums it would use 8 bit type if the values all fit in 8 bit.

Seems like for AVR using -fshort-enums would be preferable.

 

enums in C are a bit of a mess I think once you get away from the 'everything is an int' type programming.

According to standard C, the enum values (TWIM_RESULT_UNKNOWN, TWIM_RESULT_OK etc.) are of type int.

The enum type (TWIM_RESULT_t) however is allowed to be basically anything the compiler wants as long as it can hold the range of values.

 

EDIT

Possible advantages to use enum type despite the unsatisfactory nature of not necessarily knowing what the frickin hell you will end up with!

1) although C language doesn't care if you assign an enum value to whateve type you like (it's just a value of type int), tools like lint can report a warning which can be quite useful

2) a debugging tool might recognise an enum type and be able to display its value as eg. TWIM_RESULT_OK rather than 1.

3) gcc can give a warning if you use a switch on an enum type

 -Wswitch
           Warn whenever a "switch" statement has an index of enumerated type and lacks a "case" for one or more of the named codes of that enumeration.  (The presence of a "default"
           label prevents this warning.)  "case" labels outside the enumeration range also provoke warnings when this option is used (even if there is a "default" label).  This warning is
           enabled by -Wall.

 

Possible advantage to use enum values (not the type) rather than#defines

1) you have a long list and you don't care what the values are, saves having to give each one a value explicitly.

2) can't think of any other reason. (I think you could define enum inside a function and its scope would be limited to within that function. So what. Doesn't solve any real world problem).

Last Edited: Fri. Oct 16, 2020 - 10:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrKendo wrote:
I assume this was not compiled using -fshort-enums

 

Correct, I will be adding that to my Makefile.  Sadly the main reason I am using enum is that when IntelliSense shows me the function parameters, I can cut and paste the leading part of the enum'd (or whatever I should call it) parameter and then see the enum options that fit.

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

ron_sutherland wrote:

Sadly the main reason I am using enum is that when IntelliSense shows me the function parameters, I can cut and paste the leading part of the enum'd (or whatever I should call it) parameter and then see the enum options that fit.

 

Nothing sad about that.

It should have been in my lsit of possible advantages to using enum type ie. it makes the intent of the code clearer in that you know this thing is intended to hold only one of the named values

 

EDIT

And to add to possible advantage of using enum values (but not the type), the syntax of having related values grouped together surrounded by curly braces makes it clearer that it is a set of related values.

So if you have lots of 'groups', this is a clearer than having lots of 'groups' of #defines where there is nothing to delimit each 'group' other than stick a few blank lines between each 'group'. Maybe that's the approach taken by the Microchip code. Not unreasonable.

Last Edited: Fri. Oct 16, 2020 - 11:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of  my reasons for using enum is that the watch window shows a variable typed to the enum with names instead of the numeric value, and that is immensely helpful.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Can anyone suggest what the register8_t declaration accomplishes? Why not just volatile uint8_t?

 

Thanks

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Maybe forces the use of a register for storage?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

ka7ehk wrote:
the register8_t declaration

 

I am not willing to look at "register8_t" and think "volatile uint8_t", my tiny mind will not do that, I only want to see register8_t for things that are registers.

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

It is shorter? It tries to convene a meaning that this behaves differently than uin8_t?
.
Like the enum above, why choose enum over uint? Because it conveys a very different meaning...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

Last Edited: Sat. Oct 17, 2020 - 02:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Things are so much easier in assembler.... devil well we haven't had a war for a while....wink

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

ron_sutherland wrote:
ka7ehk wrote:
the register8_t declaration

 

I am not willing to look at "register8_t" and think "volatile uint8_t", my tiny mind will not do that, I only want to see register8_t for things that are registers.

I suspect that that was the intended use, but it got used for other things.

Iluvatar is the better part of Valar.

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

>Why not just volatile uint8_t?

 

Why not just use volatile unsigned char? Same reason you have uint8_t, which is also a typedef- to give more meaning for a type without having to write and visually decode the 'long version'. I like to use my own typedefs to replace some of the uint8_t kind of typedefs- like u8, i8, u16, i16, etc., simply because its less typing and still easily gives the meaning without requiring the use of the shift key (_). If they stay in the source file (not in the headers) then the names do not leak into global space, but I usually just make them globally available and their names then cannot be used for other things, which is ok with me.

 

Now, register8_t may make sense for a register type but doesn't really work well when starting to use it just to get the volatile (the var is not a register). Then its best (IMHO) just to create another type, or find a better name that is more universal- like vu8 :)

 

 

 

>I am mucking around in the Arduino twi module for M4809 and friends written by Microchip

 

If you can get yourself to a point where you understand most of what is going on with the twi module, then the actual code is not that hard to do yourself, the way you want it. Future enhancements/updates/fixes can then also be done easily since you understand the details of the code, even when time passes it doesn't take long to get up to speed again. The style you use, including typedefs/enums/whatever will be your own.

 

This is a simple C example 'extracted/converted' from my C++ twi master driver-

https://godbolt.org/z/q3Tzoo

 

This is not a complete version (is enough to work for most cases), but it shows ultimately there is not that much going on and can be done without outside help. Its a twi master 'module', and a very limited tmp117 'module' as an example, and it works (I tested this example C version on a 3217 which has a tmp117 connected, but will work the same for all avr0/1). I'm sure its different than other drivers, but it does what I want- uses irq, can poll for completion or set/use a callback, succeeds or fails (and don't need to know why it fails), and since irq is used the user of the twi can figure out if a transaction is taking too long if they want.

 

reading the ID register of a tmp117-

 

 I have been using this for a number of i2c devices including an oled display. Seems to work ok- I poll for simple things and for things like the oled just give it the buffer and let it do its thing while other code can do some work, and in both cases the irq is being used.