/* * i2c.c - functions to work with HW I2C * * Copyright 2015 Edward V. Emelianoff * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /* * I2C_FREQR - peripherial input clock (>=1MHz - standard, >=4MHz - fast) * + clock control & rise time * I2C_CR1 - enable * I2C_CR2 - start bit (generate + set SB bit + interrupt if ITEVTEN==1) * writing can be in 7 & 10 bit modes * * ** 7bit transmit** * 1) start; 2) read SR1 & write address to DR; 3) check ADDR, if ACK received * it would be 1 - clear it by read SR1 & SR3, wait for TXE==1 & BTF==1 (ready for transmit) * and send data writing to DR; to send EOT set STOP=1 * * ** 10bit transmit** * 1) start; 2) read SR1 & write 11110xx0 (xx - MS bits of address) to DR; * 3) ADD10=1, clear it by reading SR1 & write last 8 bits of address to DR; * 4) similar to 3) for 7bits * * ** receive ** * 1) clear ADDR after sending address * 2) set ACK if need to generate ACK bit * 3) check RXNE or get data by interrupt (if ITEVTEN==1 & ITBUFEN==1) * in case of byte transfer finished, BTF=1 (to clear it, read SR1 & DR) * in 10bit receive mode send start & 11110xx1 after sending address * in 7bit receive mode send address with LSB set * * TRA bit indicates I2C mode * * ** close ** * Method 1 (higher interrupts): * 1) reset ACK=0 to generate NACK; 2) to generate Stop/Restart set STOP/START * all this should be done at second last byte reading (to get a single byte * do this just after ADDR sent); 3) read last byte * * Method 2 (low interrupt priority or polling, valid only for N>2): * 1) not read DR & wait for RXNE==1&&BTF==1 * 2) clear ACK * 3) read DR (DATA_N-2) & set STOP/START * 4) read DR (DATA_N-1), read DR (DATA_N) * * to read 1 byte use Method 1; * to read 2 bytes with Method2, set POS&ACK, wait ADDR, clear ADDR&ACK, * wait BTF, set STOP, read DR twice * * * *** REGISTERS *** * I2C_CR1: | NOSTRETCH | ENGC | reserved[5:1] | PE | * NOSTRETCH - ==1 to slave mode * ENGC - generall call EN/DIS * PE - ==1 to enable I2C * * I2C_CR2: | SWRST | res[6:4] | POS | ACK | STOP | START | * SWRST - ==1 to software reset * POS - ACK position (==0 for current byte, ==1 for next byte) * ACK - send ACK after reading byte * STOP - send STOP after reading byte * START - send repeated START * DO NOT make any changes with I2C_CR2 when sending START/STOP * before they'll be cleared by hardware! * * I2C_FREQR: | res[7:6] | FREQ[5:0] | * FREQ - I2C clock frequency (in MHz, from 1 to 24) * * I2C_OARL: | ADD[7:1] | ADD0 | - LSB of address * ADD0 - 0th bit of address in 10bit mode, don't care in 7bit mode * * I2C_OARH: | ADDMODE | ADDCONF | res[5:3] | ADD[9:8] | res | * ADDMODE - 0 for 7bit, 1 for 10bit address * ADDCONF - must always be written as 1 ("address is set") * * I2C_DR - data I/O register * * I2C_SR1: | TXE | RXNE | res | STOPF | ADD10 | BTF | ADDR | SB | * TXE - set when DR is empty for transmission * RXNE - set when DR not empty in receiver mode * STOPF - stop detected (slave mode) * ADD10 - 10 bit header sent (first byte) * BTF - byte transfer finished (set when NOSTRETCH==0) * ADDR - address sent (not set if NACK) * SB - start condition * * I2C_SR2: | res[7:6] | WUFH | res | OVR | AF | ARLO | BERR | * WUFH - wakeup from halt * OVR - overrun/underrun * AF - acknowledge failure (must be cleared by software) * ARLO - arbitration lost (another master on line, clear it by SW) * BERR - bus error (clear by SW) * * I2C_SR3: | res[7:5] | GENCALL | res | TRA | BUSY | MSL | * GENCALL - general call in slave mode * TRA - transmitter(1)/receiver(0) flag * BUSY - bus busy (another communication detected) * MSL - slave(0)/master(1) mode, set/cleared by HW * * I2C_ITR: | res[7:3] | ITBUFEN | ITEVTEN | ITERREN | * ITBUFEN - enable buffer interrupt * ITEVTEN - enable event interrupt * ITERREN - enable error interrupt * * I2C_CCRL: | CCR[7:0] | * SCLH clock in master mode * standard: T = 2*CCR*tmaster, Tlow = Thigh = CCR*tmaster * fast: (DUTY==0) T = 3*CCR*tmaster, Thigh = CCR*tmaster, Tlow = 2*Thigh * (DUTY==1) T = 25*CCR*tmaster, Thigh=9*CCR*tmaster, Tlow=16*CCR*tmaster * fmaster - clock by I2C_FREQR * minimum allowed value is 4 (exept FAST DUTY, when it is 1) * * I2C_CCRH: | F/S | DUTY | res[5:4] | CCR[11:8] | * F/S - == 1 in fast mode * DUTY - (see upper) * IN standard mode 100kHz is: FREQR=8, CCR=0x28 * * I2C_TRISER: | res[7:6] | TRISE[5:0] | * TRISE - maximum rise time (0x09 for standard @100kHz) * */ #include "i2c.h" #include "ports_definition.h" static U8 addr7r = 0, addr7w = 0; extern volatile unsigned long Global_time; static U16 _c; static unsigned long wtm; #define I2C_WAIT(evt, tmo) do{wtm = Global_time; \ while(Global_time-wtm