En realidad, hacer IIC en el firmware es muy fácil.
Para garantizar que no exceda la velocidad máxima en baudios del dispositivo esclavo, inserte un retraso mínimo entre cada borde. La velocidad máxima es de 400 kBit / s, lo que significa que el tiempo mínimo por bit es de 2.5 µs. Cada bit tiene al menos dos bordes, por lo que significa que está seguro si espera al menos 1,25 µs entre dos cosas que las rutinas de IIC hacen al bus. Dependiendo de la velocidad y la arquitectura de su procesador, eso podría ser simplemente insertar algunos NOP en los lugares correctos. Si el procesador está funcionando a 10 MIPS, por ejemplo, solo necesita 13 ciclos de instrucción entre dos cambios de estado de bus. Es posible que tenga que hacer otras cosas suficientes para que solo se necesite una pequeña cantidad de NOP.
Para retrasos breves como este, uso una macro que toma argumentos del tiempo total que quiero esperar, y el número de ciclos de instrucción ya incluidos en esa espera. Esta macro obtiene el tiempo de ciclo de instrucción de las constantes de tiempo de compilación y calcula el número de NOP en tiempo de compilación. Si el código se transfiere a un procesador diferente o se cambia el reloj, todo sigue funcionando.
Aquí está esta macro para las partes de 16 bits de Microchip:
////////////////////
//
// Macro BUSYWAIT time [, cycles]
//
// Causes a busy-wait for time TIME minus CYCLES instruction cycles. TIME is
// in units of seconds. Explicit code will be written that wastes the
// indicated time, so this macro should only be used for very short waits.
//
// The total wait time is rounded to the nearest whole instruction cycles.
//
/macro busywait
/if [exist -1 arg] then
/show "Dumb place for a label, moron. The " [qstr [ucase [arg -1] " macro does"
/show "not support a label."
.error "Label"
/stop
/endif
/var local time real = [arg 1] ;time to wait in seconds
/var local mincy integer = 0
/if [exist 2 arg] then
/set mincy [arg 2]
/endif
/var local cy integer ;final number of instructions to wait
/set cy [rnd [* time freq_inst]] ;instructions to wait due to TIME
/set cy [- cy mincy] ;minus CYCLES
waitcy [v cy] ;write the instructions to do the wait
/endmac
Esto utiliza en gran medida mi preprocesador de ensamblador PIC (comandos que comienzan con "/" y funciones en línea [...]), pero debería poder deducir lo que está pasando. Las instrucciones de pérdida de tiempo son realmente emitidas por la macro WAITCY (segunda línea desde el final). Esto conoce algunas instrucciones que ocupan el mismo espacio que un NOP pero desperdician dos ciclos.