2019-07-15 10:32:13 -07:00
/* Copyright (C) 2019 Elia Ritterbusch
+
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
2018-05-14 15:17:24 +01:00
/* Library made by: g4lvanix
2020-05-26 12:43:14 +10:00
* GitHub repository: https://github.com/g4lvanix/I2C-master-lib
2018-05-14 15:17:24 +01:00
*/
#include <avr/io.h>
#include <util/twi.h>
#include "i2c_master.h"
2018-06-12 23:37:06 -04:00
#include "timer.h"
2019-03-13 03:23:28 +10:00
#include "wait.h"
2018-05-14 15:17:24 +01:00
2018-10-08 23:27:04 +01:00
#ifndef F_SCL
2019-08-30 11:19:03 -07:00
# define F_SCL 400000UL // SCL frequency
2018-10-08 23:27:04 +01:00
#endif
2019-08-31 02:47:11 +02:00
2021-06-18 09:10:06 +10:00
#ifndef I2C_START_RETRY_COUNT
# define I2C_START_RETRY_COUNT 20
#endif // I2C_START_RETRY_COUNT
2019-08-31 02:47:11 +02:00
#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
2018-05-14 15:17:24 +01:00
2021-06-18 09:10:06 +10:00
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
2019-03-13 03:23:28 +10:00
void i2c_init ( void ) {
2019-08-30 11:19:03 -07:00
TWSR = 0 ; /* no prescaler */
TWBR = ( uint8_t ) TWBR_val ;
2019-04-06 17:30:48 -07:00
2019-08-30 11:19:03 -07:00
#ifdef __AVR_ATmega32A__
// set pull-up resistors on I2C bus pins
PORTC |= 0 b11 ;
2019-04-06 17:30:48 -07:00
2019-08-30 11:19:03 -07:00
// enable TWI (two-wire interface)
TWCR |= ( 1 << TWEN );
2019-04-06 17:30:48 -07:00
2019-08-30 11:19:03 -07:00
// enable TWI interrupt and slave address ACK
TWCR |= ( 1 << TWIE );
TWCR |= ( 1 << TWEA );
#endif
2018-05-14 15:17:24 +01:00
}
2021-06-18 09:10:06 +10:00
static i2c_status_t i2c_start_impl ( uint8_t address , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
// reset TWI control register
TWCR = 0 ;
// transmit START condition
TWCR = ( 1 << TWINT ) | ( 1 << TWSTA ) | ( 1 << TWEN );
2018-06-12 23:37:06 -04:00
2019-08-30 11:19:03 -07:00
uint16_t timeout_timer = timer_read ();
while ( ! ( TWCR & ( 1 << TWINT ))) {
if (( timeout != I2C_TIMEOUT_INFINITE ) && (( timer_read () - timeout_timer ) >= timeout )) {
return I2C_STATUS_TIMEOUT ;
}
2018-06-12 23:37:06 -04:00
}
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
// check if the start condition was successfully transmitted
if ((( TW_STATUS & 0xF8 ) != TW_START ) && (( TW_STATUS & 0xF8 ) != TW_REP_START )) {
return I2C_STATUS_ERROR ;
2018-06-12 23:37:06 -04:00
}
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
// load slave address into data register
TWDR = address ;
// start transmission of address
TWCR = ( 1 << TWINT ) | ( 1 << TWEN );
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
timeout_timer = timer_read ();
while ( ! ( TWCR & ( 1 << TWINT ))) {
if (( timeout != I2C_TIMEOUT_INFINITE ) && (( timer_read () - timeout_timer ) >= timeout )) {
return I2C_STATUS_TIMEOUT ;
}
}
// check if the device has acknowledged the READ / WRITE mode
uint8_t twst = TW_STATUS & 0xF8 ;
if (( twst != TW_MT_SLA_ACK ) && ( twst != TW_MR_SLA_ACK )) {
return I2C_STATUS_ERROR ;
}
return I2C_STATUS_SUCCESS ;
2018-05-14 15:17:24 +01:00
}
2021-06-18 09:10:06 +10:00
i2c_status_t i2c_start ( uint8_t address , uint16_t timeout ) {
// Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.
uint16_t timeout_timer = timer_read ();
uint16_t time_slice = MAX ( 1 , ( timeout == ( I2C_TIMEOUT_INFINITE )) ? 5 : ( timeout / ( I2C_START_RETRY_COUNT ))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries
i2c_status_t status ;
do {
status = i2c_start_impl ( address , time_slice );
} while (( status < 0 ) && (( timeout == I2C_TIMEOUT_INFINITE ) || ( timer_elapsed ( timeout_timer ) < timeout )));
return status ;
}
2019-03-13 03:23:28 +10:00
i2c_status_t i2c_write ( uint8_t data , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
// load data into data register
TWDR = data ;
// start transmission of data
TWCR = ( 1 << TWINT ) | ( 1 << TWEN );
2018-06-12 23:37:06 -04:00
2019-08-30 11:19:03 -07:00
uint16_t timeout_timer = timer_read ();
while ( ! ( TWCR & ( 1 << TWINT ))) {
if (( timeout != I2C_TIMEOUT_INFINITE ) && (( timer_read () - timeout_timer ) >= timeout )) {
return I2C_STATUS_TIMEOUT ;
}
2018-06-12 23:37:06 -04:00
}
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
if (( TW_STATUS & 0xF8 ) != TW_MT_DATA_ACK ) {
return I2C_STATUS_ERROR ;
}
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
return I2C_STATUS_SUCCESS ;
2018-05-14 15:17:24 +01:00
}
2019-03-13 03:23:28 +10:00
int16_t i2c_read_ack ( uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
// start TWI module and acknowledge data after reception
TWCR = ( 1 << TWINT ) | ( 1 << TWEN ) | ( 1 << TWEA );
2018-06-12 23:37:06 -04:00
2019-08-30 11:19:03 -07:00
uint16_t timeout_timer = timer_read ();
while ( ! ( TWCR & ( 1 << TWINT ))) {
if (( timeout != I2C_TIMEOUT_INFINITE ) && (( timer_read () - timeout_timer ) >= timeout )) {
return I2C_STATUS_TIMEOUT ;
}
2018-06-12 23:37:06 -04:00
}
2019-08-30 11:19:03 -07:00
// return received data from TWDR
return TWDR ;
2018-05-14 15:17:24 +01:00
}
2019-03-13 03:23:28 +10:00
int16_t i2c_read_nack ( uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
// start receiving without acknowledging reception
TWCR = ( 1 << TWINT ) | ( 1 << TWEN );
2018-06-12 23:37:06 -04:00
2019-08-30 11:19:03 -07:00
uint16_t timeout_timer = timer_read ();
while ( ! ( TWCR & ( 1 << TWINT ))) {
if (( timeout != I2C_TIMEOUT_INFINITE ) && (( timer_read () - timeout_timer ) >= timeout )) {
return I2C_STATUS_TIMEOUT ;
}
2018-06-12 23:37:06 -04:00
}
2019-08-30 11:19:03 -07:00
// return received data from TWDR
return TWDR ;
2018-05-14 15:17:24 +01:00
}
2019-04-15 22:32:57 -05:00
i2c_status_t i2c_transmit ( uint8_t address , const uint8_t * data , uint16_t length , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
i2c_status_t status = i2c_start ( address | I2C_WRITE , timeout );
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
for ( uint16_t i = 0 ; i < length && status >= 0 ; i ++ ) {
status = i2c_write ( data [ i ], timeout );
}
2018-07-18 19:55:57 +03:00
2019-08-30 11:19:03 -07:00
i2c_stop ();
2018-07-18 19:55:57 +03:00
2019-08-30 11:19:03 -07:00
return status ;
2018-05-14 15:17:24 +01:00
}
2019-03-13 03:23:28 +10:00
i2c_status_t i2c_receive ( uint8_t address , uint8_t * data , uint16_t length , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
i2c_status_t status = i2c_start ( address | I2C_READ , timeout );
2018-05-15 22:30:58 -04:00
2019-08-30 11:19:03 -07:00
for ( uint16_t i = 0 ; i < ( length - 1 ) && status >= 0 ; i ++ ) {
status = i2c_read_ack ( timeout );
if ( status >= 0 ) {
data [ i ] = status ;
}
2018-06-22 21:26:30 -04:00
}
2018-05-14 15:17:24 +01:00
2019-03-13 03:23:28 +10:00
if ( status >= 0 ) {
2019-08-30 11:19:03 -07:00
status = i2c_read_nack ( timeout );
if ( status >= 0 ) {
data [( length - 1 )] = status ;
}
2019-03-13 03:23:28 +10:00
}
2018-05-14 15:17:24 +01:00
2019-08-30 11:19:03 -07:00
i2c_stop ();
2018-06-22 21:26:30 -04:00
2019-08-30 11:19:03 -07:00
return ( status < 0 ) ? status : I2C_STATUS_SUCCESS ;
2018-05-14 15:17:24 +01:00
}
2019-04-15 22:32:57 -05:00
i2c_status_t i2c_writeReg ( uint8_t devaddr , uint8_t regaddr , const uint8_t * data , uint16_t length , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
i2c_status_t status = i2c_start ( devaddr | 0x00 , timeout );
if ( status >= 0 ) {
status = i2c_write ( regaddr , timeout );
2018-05-14 15:17:24 +01:00
2019-08-30 11:19:03 -07:00
for ( uint16_t i = 0 ; i < length && status >= 0 ; i ++ ) {
status = i2c_write ( data [ i ], timeout );
}
2019-03-13 03:23:28 +10:00
}
2018-05-14 15:17:24 +01:00
2019-08-30 11:19:03 -07:00
i2c_stop ();
2018-05-14 15:17:24 +01:00
2019-08-30 11:19:03 -07:00
return status ;
2018-05-14 15:17:24 +01:00
}
2019-03-13 03:23:28 +10:00
i2c_status_t i2c_readReg ( uint8_t devaddr , uint8_t regaddr , uint8_t * data , uint16_t length , uint16_t timeout ) {
2019-08-30 11:19:03 -07:00
i2c_status_t status = i2c_start ( devaddr , timeout );
if ( status < 0 ) {
goto error ;
2018-06-22 21:26:30 -04:00
}
2019-08-30 11:19:03 -07:00
status = i2c_write ( regaddr , timeout );
if ( status < 0 ) {
goto error ;
}
status = i2c_start ( devaddr | 0x01 , timeout );
for ( uint16_t i = 0 ; i < ( length - 1 ) && status >= 0 ; i ++ ) {
status = i2c_read_ack ( timeout );
if ( status >= 0 ) {
data [ i ] = status ;
}
}
2019-03-13 03:23:28 +10:00
if ( status >= 0 ) {
2019-08-30 11:19:03 -07:00
status = i2c_read_nack ( timeout );
if ( status >= 0 ) {
data [( length - 1 )] = status ;
}
2019-03-13 03:23:28 +10:00
}
2018-06-22 21:26:30 -04:00
2019-03-13 03:23:28 +10:00
error :
2019-08-30 11:19:03 -07:00
i2c_stop ();
2018-06-22 21:26:30 -04:00
2019-08-30 11:19:03 -07:00
return ( status < 0 ) ? status : I2C_STATUS_SUCCESS ;
2018-06-22 21:26:30 -04:00
}
2019-03-13 03:23:28 +10:00
void i2c_stop ( void ) {
2019-08-30 11:19:03 -07:00
// transmit STOP condition
TWCR = ( 1 << TWINT ) | ( 1 << TWEN ) | ( 1 << TWSTO );
2019-01-10 11:26:40 -05:00
}