I2C Bitbanging
ATmega328P.pdf ( Register summary on p 428 )En mikroprocessor behöver i allmänhet inte ha flera än en I2C-enhet (TWI).
Den räcker ju till att ansluta upp till hundra stycken I2C-tillbehör!
Det finns emellertid enklare (billigare) mikroprocessorer som helt saknar I2C-enhet.
Då kan man behöva programmera I2C dataöverföringen bit för bit, så kallad BitBanging.

I2C-bussen bygger på att logiknivåerna "1" och "0" är olika starka, där "0" är den dominerande nivån.
Det räcker med att någon enhets utgång längs bussen är "0" för att det värdet ska gälla.
AVR-processorernas utgångar är däremot lika starka för "1" som för "0".
(Undantaget är TWI-pinnarna på de processorer som har TWI-enhet).
För att använda vanliga pinnar/utgångar som I2C-pinnar behöver man därför ta till ett konstgrepp.
Man börjar med att lagra "0" för pinnen i PORT registret.
Om man sedan väljer "1" (= utgång) för pinnen i DDR registret,
så blir pinnen en stark "0".
Om man istället väljer "0" (= ingång) i DDR registret så blir pinnen "bortkopplad" som utgång.
Då gäller PullUp-motståndet på bussledningen som en svag "1".
Man styr således I2C nivåerna med DDR registret och rör inte PORT registret.
Som exempel använder vi RTC kretsen DS1307,
och vi väljer att styra den med pinnarna PB0 (SCL) och PB1
(SDA).
PullUp-resistorerna sitter redan på RTC-modulens kretskort.
(Funktionerna följer tidsdiagrammet i figuren ovan.
Vi har inte gjort någon tidsoptimering utan nöjt oss med en måttlig överföringshastighet,
och behöver då inte heller implementera någon "clock stretching".)
/* I2C pin connections */ /* Redefine if other pins are used */ #define SCL_BBpin PB0 #define SCL_DDR DDRB #define SCL_PORT PORTB #define SCL_PIN PINB #define SDA_BBpin PB1 #define SDA_DDR DDRB #define SDA_PORT PORTB #define SDA_PIN PINB
SDA_PORT &= (~( 1 << SDA_BBpin )); // port = 0 SCL_PORT &= (~( 1 << SCL_BBpin )); // port = 0 SDA_DDR &=( ~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high
SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); // slow I2C clock SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low _delay_us (10); SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10);
SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high _delay_us (10);
I2C_Write_BB( ). Efter att ha skrivit 8 bitar behöver man lämna SDA "1" så att "slaven" med "0" (ACK) kan kvittera att byten är mottagen. Man kan läsa denna bit som debuginformation.
I2C_ReadACK_BB( ). Efter att ha läst 8 bitar, en byte, kan man ta emot ytterligare en byte i följd. Man måste då som en nionde bit skriva SDA "0" (ACK).
I2C_ReadNACK_BB( ). Vill man bara läsa en byte, eller när det gäller den sista byten i en följd, måste man som nionde bit skriva SDA "1" (NACK).
Jämför med samma programexempel för TWI-enheten.

RTC_setup_BB.txt
(for Arduino store code as: RTC_setup_BB\RTC_setup_BB.ino)
William Sandqvist willsandqvist@gmail.com