I have run into a challenge with the app_twi API, and I'd like to run it by you for your advice. To be clear, I am not blocked. But I am not pleased with the source code maintainability aesthetics of my current solution and I'd either like your advice or an API enhancement; not sure which.
In general, I like the static-array oriented programming style of setting up arrays of TWI transfers. For my current project I have written about a dozen drivers of varying complexity, with tables such as you'll see at the bottom of this post.
But I just ran into a challenge. As I'm sure you're aware, many if not most chips are designed with at least two hard-wired I2C addresses, using one or two pins to program which of those addresses to use.
In this project I have found the inescapable need to have TWO instances of identical chips within the design, each using a unique I2C address. At first, I thought "ok, no problem, I'll just parameterize the driver". I figured that at driver init I'd just pass an argument as to which instance I am initializing, etc.
However, it isn't that simple. At all. As it turns out, all the macros for API_TWI_READ/WRITE are designed around static const-based tables. Even if it were possible to write code to dynamically rewrite the I2C_ADDRESS (operation) field prior to the app_twi_schedule, it is certainly not stylistically the intent of those macros or that function to allow dynamic reassignment. The developer's intent was clearly that the driver would be statically-compiled for a single I2C address.
Right now, the way I am solving this (and I find it ugly) by placing my entire complex driver, in essence, into a header file that is #included by two other files that generate two runtime instances of the driver with different I2C addresses. This works just fine functionally, but I just don't think it's a good practice stylistically.
Is there a different coding style for multi-instance that was intended by the app_twi developer?
I've thought about what I might like to see in terms of an API change to make this easier for the developer, and so please do take this constructively as an enhancement suggestion:
I'd be very surprised if a high percentage of app_twi_transfer tables intermix multiple I2C addresses in the same transfer. As such, I might suggest introducing a new 3-argument form of APP_TWI_READ and APP_TWI_WRITE that don't have any I2C addresses specified. When using this form, you might then at runtime place the I2C address directly into an argument in a NON-const non-static app_twi_transaction, or into a new variation of the call to app_twi_schedule itself.
I'd be surprised if this didn't then become the most commonly-used API mechanism, given that most drivers issue all commands to the same address.
Looking forward to your thoughts.
static app_twi_transfer_t const transfers[] = {
APP_TWI_WRITE(LIS_I2C_ADDRESS, &status_aux[0], sizeof(status_aux[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &status_aux[1], sizeof(status_aux[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc1_l[0], sizeof(out_adc1_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc1_l[1], sizeof(out_adc1_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc1_h[0], sizeof(out_adc1_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc1_h[1], sizeof(out_adc1_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc2_l[0], sizeof(out_adc2_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc2_l[1], sizeof(out_adc2_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc2_h[0], sizeof(out_adc2_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc2_h[1], sizeof(out_adc2_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc3_l[0], sizeof(out_adc3_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc3_l[1], sizeof(out_adc3_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_adc3_h[0], sizeof(out_adc3_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_adc3_h[1], sizeof(out_adc3_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_x_l[0], sizeof(out_x_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_x_l[1], sizeof(out_x_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_x_h[0], sizeof(out_x_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_x_h[1], sizeof(out_x_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_y_l[0], sizeof(out_y_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_y_l[1], sizeof(out_y_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_y_h[0], sizeof(out_y_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_y_h[1], sizeof(out_y_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_z_l[0], sizeof(out_z_l[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_z_l[1], sizeof(out_z_l[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &out_z_h[0], sizeof(out_z_h[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &out_z_h[1], sizeof(out_z_h[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &int1_cfg[0], sizeof(int1_cfg[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &int1_cfg[1], sizeof(int1_cfg[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &int1_ths[0], sizeof(int1_ths[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &int1_ths[1], sizeof(int1_ths[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &int1_src[0], sizeof(int1_src[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &int1_src[1], sizeof(int1_src[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, &who[0], sizeof(who[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, &who[1], sizeof(who[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®1[0], sizeof(reg1[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®1[1], sizeof(reg1[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®2[0], sizeof(reg2[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®2[1], sizeof(reg2[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®3[0], sizeof(reg3[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®3[1], sizeof(reg3[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®4[0], sizeof(reg4[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®4[1], sizeof(reg4[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®5[0], sizeof(reg5[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®5[1], sizeof(reg5[1]), 0),
APP_TWI_WRITE(LIS_I2C_ADDRESS, ®6[0], sizeof(reg6[0]), APP_TWI_NO_STOP),
APP_TWI_READ(LIS_I2C_ADDRESS, ®6[1], sizeof(reg6[1]), 0),
};