/* | |||||
TwoWire.cpp - TWI/I2C library for Wiring & Arduino | |||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library 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 | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts | |||||
*/ | |||||
extern "C" { | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <inttypes.h> | |||||
#include "twi.h" | |||||
} | |||||
#include "Wire.h" | |||||
// Initialize Class Variables ////////////////////////////////////////////////// | |||||
uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; | |||||
uint8_t TwoWire::rxBufferIndex = 0; | |||||
uint8_t TwoWire::rxBufferLength = 0; | |||||
uint8_t TwoWire::txAddress = 0; | |||||
uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; | |||||
uint8_t TwoWire::txBufferIndex = 0; | |||||
uint8_t TwoWire::txBufferLength = 0; | |||||
uint8_t TwoWire::transmitting = 0; | |||||
void (*TwoWire::user_onRequest)(void); | |||||
void (*TwoWire::user_onReceive)(int); | |||||
// Constructors //////////////////////////////////////////////////////////////// | |||||
TwoWire::TwoWire() | |||||
{ | |||||
} | |||||
// Public Methods ////////////////////////////////////////////////////////////// | |||||
void TwoWire::begin(void) | |||||
{ | |||||
rxBufferIndex = 0; | |||||
rxBufferLength = 0; | |||||
txBufferIndex = 0; | |||||
txBufferLength = 0; | |||||
twi_init(); | |||||
} | |||||
void TwoWire::begin(uint8_t address) | |||||
{ | |||||
twi_setAddress(address); | |||||
twi_attachSlaveTxEvent(onRequestService); | |||||
twi_attachSlaveRxEvent(onReceiveService); | |||||
begin(); | |||||
} | |||||
void TwoWire::begin(int address) | |||||
{ | |||||
begin((uint8_t)address); | |||||
} | |||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) | |||||
{ | |||||
// clamp to buffer length | |||||
if(quantity > BUFFER_LENGTH){ | |||||
quantity = BUFFER_LENGTH; | |||||
} | |||||
// perform blocking read into buffer | |||||
uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); | |||||
// set rx buffer iterator vars | |||||
rxBufferIndex = 0; | |||||
rxBufferLength = read; | |||||
return read; | |||||
} | |||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) | |||||
{ | |||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); | |||||
} | |||||
uint8_t TwoWire::requestFrom(int address, int quantity) | |||||
{ | |||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); | |||||
} | |||||
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) | |||||
{ | |||||
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); | |||||
} | |||||
void TwoWire::beginTransmission(uint8_t address) | |||||
{ | |||||
// indicate that we are transmitting | |||||
transmitting = 1; | |||||
// set address of targeted slave | |||||
txAddress = address; | |||||
// reset tx buffer iterator vars | |||||
txBufferIndex = 0; | |||||
txBufferLength = 0; | |||||
} | |||||
void TwoWire::beginTransmission(int address) | |||||
{ | |||||
beginTransmission((uint8_t)address); | |||||
} | |||||
// | |||||
// Originally, 'endTransmission' was an f(void) function. | |||||
// It has been modified to take one parameter indicating | |||||
// whether or not a STOP should be performed on the bus. | |||||
// Calling endTransmission(false) allows a sketch to | |||||
// perform a repeated start. | |||||
// | |||||
// WARNING: Nothing in the library keeps track of whether | |||||
// the bus tenure has been properly ended with a STOP. It | |||||
// is very possible to leave the bus in a hung state if | |||||
// no call to endTransmission(true) is made. Some I2C | |||||
// devices will behave oddly if they do not see a STOP. | |||||
// | |||||
uint8_t TwoWire::endTransmission(uint8_t sendStop) | |||||
{ | |||||
// transmit buffer (blocking) | |||||
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); | |||||
// reset tx buffer iterator vars | |||||
txBufferIndex = 0; | |||||
txBufferLength = 0; | |||||
// indicate that we are done transmitting | |||||
transmitting = 0; | |||||
return ret; | |||||
} | |||||
// This provides backwards compatibility with the original | |||||
// definition, and expected behaviour, of endTransmission | |||||
// | |||||
uint8_t TwoWire::endTransmission(void) | |||||
{ | |||||
return endTransmission(true); | |||||
} | |||||
// must be called in: | |||||
// slave tx event callback | |||||
// or after beginTransmission(address) | |||||
size_t TwoWire::write(uint8_t data) | |||||
{ | |||||
if(transmitting){ | |||||
// in master transmitter mode | |||||
// don't bother if buffer is full | |||||
if(txBufferLength >= BUFFER_LENGTH){ | |||||
setWriteError(); | |||||
return 0; | |||||
} | |||||
// put byte in tx buffer | |||||
txBuffer[txBufferIndex] = data; | |||||
++txBufferIndex; | |||||
// update amount in buffer | |||||
txBufferLength = txBufferIndex; | |||||
}else{ | |||||
// in slave send mode | |||||
// reply to master | |||||
twi_transmit(&data, 1); | |||||
} | |||||
return 1; | |||||
} | |||||
// must be called in: | |||||
// slave tx event callback | |||||
// or after beginTransmission(address) | |||||
size_t TwoWire::write(const uint8_t *data, size_t quantity) | |||||
{ | |||||
if(transmitting){ | |||||
// in master transmitter mode | |||||
for(size_t i = 0; i < quantity; ++i){ | |||||
write(data[i]); | |||||
} | |||||
}else{ | |||||
// in slave send mode | |||||
// reply to master | |||||
twi_transmit(data, quantity); | |||||
} | |||||
return quantity; | |||||
} | |||||
// must be called in: | |||||
// slave rx event callback | |||||
// or after requestFrom(address, numBytes) | |||||
int TwoWire::available(void) | |||||
{ | |||||
return rxBufferLength - rxBufferIndex; | |||||
} | |||||
// must be called in: | |||||
// slave rx event callback | |||||
// or after requestFrom(address, numBytes) | |||||
int TwoWire::read(void) | |||||
{ | |||||
int value = -1; | |||||
// get each successive byte on each call | |||||
if(rxBufferIndex < rxBufferLength){ | |||||
value = rxBuffer[rxBufferIndex]; | |||||
++rxBufferIndex; | |||||
} | |||||
return value; | |||||
} | |||||
// must be called in: | |||||
// slave rx event callback | |||||
// or after requestFrom(address, numBytes) | |||||
int TwoWire::peek(void) | |||||
{ | |||||
int value = -1; | |||||
if(rxBufferIndex < rxBufferLength){ | |||||
value = rxBuffer[rxBufferIndex]; | |||||
} | |||||
return value; | |||||
} | |||||
void TwoWire::flush(void) | |||||
{ | |||||
// XXX: to be implemented. | |||||
} | |||||
// behind the scenes function that is called when data is received | |||||
void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) | |||||
{ | |||||
// don't bother if user hasn't registered a callback | |||||
if(!user_onReceive){ | |||||
return; | |||||
} | |||||
// don't bother if rx buffer is in use by a master requestFrom() op | |||||
// i know this drops data, but it allows for slight stupidity | |||||
// meaning, they may not have read all the master requestFrom() data yet | |||||
if(rxBufferIndex < rxBufferLength){ | |||||
return; | |||||
} | |||||
// copy twi rx buffer into local read buffer | |||||
// this enables new reads to happen in parallel | |||||
for(uint8_t i = 0; i < numBytes; ++i){ | |||||
rxBuffer[i] = inBytes[i]; | |||||
} | |||||
// set rx iterator vars | |||||
rxBufferIndex = 0; | |||||
rxBufferLength = numBytes; | |||||
// alert user program | |||||
user_onReceive(numBytes); | |||||
} | |||||
// behind the scenes function that is called when data is requested | |||||
void TwoWire::onRequestService(void) | |||||
{ | |||||
// don't bother if user hasn't registered a callback | |||||
if(!user_onRequest){ | |||||
return; | |||||
} | |||||
// reset tx buffer iterator vars | |||||
// !!! this will kill any pending pre-master sendTo() activity | |||||
txBufferIndex = 0; | |||||
txBufferLength = 0; | |||||
// alert user program | |||||
user_onRequest(); | |||||
} | |||||
// sets function called on slave write | |||||
void TwoWire::onReceive( void (*function)(int) ) | |||||
{ | |||||
user_onReceive = function; | |||||
} | |||||
// sets function called on slave read | |||||
void TwoWire::onRequest( void (*function)(void) ) | |||||
{ | |||||
user_onRequest = function; | |||||
} | |||||
// Preinstantiate Objects ////////////////////////////////////////////////////// | |||||
TwoWire Wire = TwoWire(); | |||||
/* | |||||
TwoWire.h - TWI/I2C library for Arduino & Wiring | |||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library 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 | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts | |||||
*/ | |||||
#ifndef TwoWire_h | |||||
#define TwoWire_h | |||||
#include <inttypes.h> | |||||
#include "Stream.h" | |||||
#define BUFFER_LENGTH 32 | |||||
class TwoWire : public Stream | |||||
{ | |||||
private: | |||||
static uint8_t rxBuffer[]; | |||||
static uint8_t rxBufferIndex; | |||||
static uint8_t rxBufferLength; | |||||
static uint8_t txAddress; | |||||
static uint8_t txBuffer[]; | |||||
static uint8_t txBufferIndex; | |||||
static uint8_t txBufferLength; | |||||
static uint8_t transmitting; | |||||
static void (*user_onRequest)(void); | |||||
static void (*user_onReceive)(int); | |||||
static void onRequestService(void); | |||||
static void onReceiveService(uint8_t*, int); | |||||
public: | |||||
TwoWire(); | |||||
void begin(); | |||||
void begin(uint8_t); | |||||
void begin(int); | |||||
void beginTransmission(uint8_t); | |||||
void beginTransmission(int); | |||||
uint8_t endTransmission(void); | |||||
uint8_t endTransmission(uint8_t); | |||||
uint8_t requestFrom(uint8_t, uint8_t); | |||||
uint8_t requestFrom(uint8_t, uint8_t, uint8_t); | |||||
uint8_t requestFrom(int, int); | |||||
uint8_t requestFrom(int, int, int); | |||||
virtual size_t write(uint8_t); | |||||
virtual size_t write(const uint8_t *, size_t); | |||||
virtual int available(void); | |||||
virtual int read(void); | |||||
virtual int peek(void); | |||||
virtual void flush(void); | |||||
void onReceive( void (*)(int) ); | |||||
void onRequest( void (*)(void) ); | |||||
inline size_t write(unsigned long n) { return write((uint8_t)n); } | |||||
inline size_t write(long n) { return write((uint8_t)n); } | |||||
inline size_t write(unsigned int n) { return write((uint8_t)n); } | |||||
inline size_t write(int n) { return write((uint8_t)n); } | |||||
using Print::write; | |||||
}; | |||||
extern TwoWire Wire; | |||||
#endif | |||||
// I2C SRF10 or SRF08 Devantech Ultrasonic Ranger Finder | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// and James Tichenor <http://www.jamestichenor.net> | |||||
// Demonstrates use of the Wire library reading data from the | |||||
// Devantech Utrasonic Rangers SFR08 and SFR10 | |||||
// Created 29 April 2006 | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(); // join i2c bus (address optional for master) | |||||
Serial.begin(9600); // start serial communication at 9600bps | |||||
} | |||||
int reading = 0; | |||||
void loop() | |||||
{ | |||||
// step 1: instruct sensor to read echoes | |||||
Wire.beginTransmission(112); // transmit to device #112 (0x70) | |||||
// the address specified in the datasheet is 224 (0xE0) | |||||
// but i2c adressing uses the high 7 bits so it's 112 | |||||
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00) | |||||
Wire.write(byte(0x50)); // command sensor to measure in "inches" (0x50) | |||||
// use 0x51 for centimeters | |||||
// use 0x52 for ping microseconds | |||||
Wire.endTransmission(); // stop transmitting | |||||
// step 2: wait for readings to happen | |||||
delay(70); // datasheet suggests at least 65 milliseconds | |||||
// step 3: instruct sensor to return a particular echo reading | |||||
Wire.beginTransmission(112); // transmit to device #112 | |||||
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02) | |||||
Wire.endTransmission(); // stop transmitting | |||||
// step 4: request reading from sensor | |||||
Wire.requestFrom(112, 2); // request 2 bytes from slave device #112 | |||||
// step 5: receive reading from sensor | |||||
if(2 <= Wire.available()) // if two bytes were received | |||||
{ | |||||
reading = Wire.read(); // receive high byte (overwrites previous reading) | |||||
reading = reading << 8; // shift high byte to be high 8 bits | |||||
reading |= Wire.read(); // receive low byte as lower 8 bits | |||||
Serial.println(reading); // print the reading | |||||
} | |||||
delay(250); // wait a bit since people have to read the output :) | |||||
} | |||||
/* | |||||
// The following code changes the address of a Devantech Ultrasonic Range Finder (SRF10 or SRF08) | |||||
// usage: changeAddress(0x70, 0xE6); | |||||
void changeAddress(byte oldAddress, byte newAddress) | |||||
{ | |||||
Wire.beginTransmission(oldAddress); | |||||
Wire.write(byte(0x00)); | |||||
Wire.write(byte(0xA0)); | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(oldAddress); | |||||
Wire.write(byte(0x00)); | |||||
Wire.write(byte(0xAA)); | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(oldAddress); | |||||
Wire.write(byte(0x00)); | |||||
Wire.write(byte(0xA5)); | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(oldAddress); | |||||
Wire.write(byte(0x00)); | |||||
Wire.write(newAddress); | |||||
Wire.endTransmission(); | |||||
} | |||||
*/ |
// I2C Digital Potentiometer | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// and Shawn Bonkowski <http://people.interaction-ivrea.it/s.bonkowski/> | |||||
// Demonstrates use of the Wire library | |||||
// Controls AD5171 digital potentiometer via I2C/TWI | |||||
// Created 31 March 2006 | |||||
// This example code is in the public domain. | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(); // join i2c bus (address optional for master) | |||||
} | |||||
byte val = 0; | |||||
void loop() | |||||
{ | |||||
Wire.beginTransmission(44); // transmit to device #44 (0x2c) | |||||
// device address is specified in datasheet | |||||
Wire.write(byte(0x00)); // sends instruction byte | |||||
Wire.write(val); // sends potentiometer value byte | |||||
Wire.endTransmission(); // stop transmitting | |||||
val++; // increment value | |||||
if(val == 64) // if reached 64th position (max) | |||||
{ | |||||
val = 0; // start over from lowest value | |||||
} | |||||
delay(500); | |||||
} | |||||
// Wire Master Reader | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// Demonstrates use of the Wire library | |||||
// Reads data from an I2C/TWI slave device | |||||
// Refer to the "Wire Slave Sender" example for use with this | |||||
// Created 29 March 2006 | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(); // join i2c bus (address optional for master) | |||||
Serial.begin(9600); // start serial for output | |||||
} | |||||
void loop() | |||||
{ | |||||
Wire.requestFrom(2, 6); // request 6 bytes from slave device #2 | |||||
while(Wire.available()) // slave may send less than requested | |||||
{ | |||||
char c = Wire.read(); // receive a byte as character | |||||
Serial.print(c); // print the character | |||||
} | |||||
delay(500); | |||||
} |
// Wire Master Writer | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// Demonstrates use of the Wire library | |||||
// Writes data to an I2C/TWI slave device | |||||
// Refer to the "Wire Slave Receiver" example for use with this | |||||
// Created 29 March 2006 | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(); // join i2c bus (address optional for master) | |||||
} | |||||
byte x = 0; | |||||
void loop() | |||||
{ | |||||
Wire.beginTransmission(4); // transmit to device #4 | |||||
Wire.write("x is "); // sends five bytes | |||||
Wire.write(x); // sends one byte | |||||
Wire.endTransmission(); // stop transmitting | |||||
x++; | |||||
delay(500); | |||||
} |
// Wire Slave Receiver | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// Demonstrates use of the Wire library | |||||
// Receives data as an I2C/TWI slave device | |||||
// Refer to the "Wire Master Writer" example for use with this | |||||
// Created 29 March 2006 | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(4); // join i2c bus with address #4 | |||||
Wire.onReceive(receiveEvent); // register event | |||||
Serial.begin(9600); // start serial for output | |||||
} | |||||
void loop() | |||||
{ | |||||
delay(100); | |||||
} | |||||
// function that executes whenever data is received from master | |||||
// this function is registered as an event, see setup() | |||||
void receiveEvent(int howMany) | |||||
{ | |||||
while(1 < Wire.available()) // loop through all but the last | |||||
{ | |||||
char c = Wire.read(); // receive byte as a character | |||||
Serial.print(c); // print the character | |||||
} | |||||
int x = Wire.read(); // receive byte as an integer | |||||
Serial.println(x); // print the integer | |||||
} |
// Wire Slave Sender | |||||
// by Nicholas Zambetti <http://www.zambetti.com> | |||||
// Demonstrates use of the Wire library | |||||
// Sends data as an I2C/TWI slave device | |||||
// Refer to the "Wire Master Reader" example for use with this | |||||
// Created 29 March 2006 | |||||
// This example code is in the public domain. | |||||
#include <Wire.h> | |||||
void setup() | |||||
{ | |||||
Wire.begin(2); // join i2c bus with address #2 | |||||
Wire.onRequest(requestEvent); // register event | |||||
} | |||||
void loop() | |||||
{ | |||||
delay(100); | |||||
} | |||||
// function that executes whenever data is requested by master | |||||
// this function is registered as an event, see setup() | |||||
void requestEvent() | |||||
{ | |||||
Wire.write("hello "); // respond with message of 6 bytes | |||||
// as expected by master | |||||
} |
####################################### | |||||
# Syntax Coloring Map For Wire | |||||
####################################### | |||||
####################################### | |||||
# Datatypes (KEYWORD1) | |||||
####################################### | |||||
####################################### | |||||
# Methods and Functions (KEYWORD2) | |||||
####################################### | |||||
begin KEYWORD2 | |||||
beginTransmission KEYWORD2 | |||||
endTransmission KEYWORD2 | |||||
requestFrom KEYWORD2 | |||||
send KEYWORD2 | |||||
receive KEYWORD2 | |||||
onReceive KEYWORD2 | |||||
onRequest KEYWORD2 | |||||
####################################### | |||||
# Instances (KEYWORD2) | |||||
####################################### | |||||
Wire KEYWORD2 | |||||
####################################### | |||||
# Constants (LITERAL1) | |||||
####################################### | |||||
/* | |||||
twi.c - TWI/I2C library for Wiring & Arduino | |||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library 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 | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts | |||||
*/ | |||||
#include <math.h> | |||||
#include <stdlib.h> | |||||
#include <inttypes.h> | |||||
#include <avr/io.h> | |||||
#include <avr/interrupt.h> | |||||
#include <compat/twi.h> | |||||
#include "Arduino.h" // for digitalWrite | |||||
#ifndef cbi | |||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) | |||||
#endif | |||||
#ifndef sbi | |||||
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) | |||||
#endif | |||||
#include "pins_arduino.h" | |||||
#include "twi.h" | |||||
static volatile uint8_t twi_state; | |||||
static volatile uint8_t twi_slarw; | |||||
static volatile uint8_t twi_sendStop; // should the transaction end with a stop | |||||
static volatile uint8_t twi_inRepStart; // in the middle of a repeated start | |||||
static void (*twi_onSlaveTransmit)(void); | |||||
static void (*twi_onSlaveReceive)(uint8_t*, int); | |||||
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; | |||||
static volatile uint8_t twi_masterBufferIndex; | |||||
static volatile uint8_t twi_masterBufferLength; | |||||
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; | |||||
static volatile uint8_t twi_txBufferIndex; | |||||
static volatile uint8_t twi_txBufferLength; | |||||
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; | |||||
static volatile uint8_t twi_rxBufferIndex; | |||||
static volatile uint8_t twi_error; | |||||
/* | |||||
* Function twi_init | |||||
* Desc readys twi pins and sets twi bitrate | |||||
* Input none | |||||
* Output none | |||||
*/ | |||||
void twi_init(void) | |||||
{ | |||||
// initialize state | |||||
twi_state = TWI_READY; | |||||
twi_sendStop = true; // default value | |||||
twi_inRepStart = false; | |||||
// activate internal pullups for twi. | |||||
digitalWrite(SDA, 1); | |||||
digitalWrite(SCL, 1); | |||||
// initialize twi prescaler and bit rate | |||||
cbi(TWSR, TWPS0); | |||||
cbi(TWSR, TWPS1); | |||||
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; | |||||
/* twi bit rate formula from atmega128 manual pg 204 | |||||
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) | |||||
note: TWBR should be 10 or higher for master mode | |||||
It is 72 for a 16mhz Wiring board with 100kHz TWI */ | |||||
// enable twi module, acks, and twi interrupt | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); | |||||
} | |||||
/* | |||||
* Function twi_slaveInit | |||||
* Desc sets slave address and enables interrupt | |||||
* Input none | |||||
* Output none | |||||
*/ | |||||
void twi_setAddress(uint8_t address) | |||||
{ | |||||
// set twi slave address (skip over TWGCE bit) | |||||
TWAR = address << 1; | |||||
} | |||||
/* | |||||
* Function twi_readFrom | |||||
* Desc attempts to become twi bus master and read a | |||||
* series of bytes from a device on the bus | |||||
* Input address: 7bit i2c device address | |||||
* data: pointer to byte array | |||||
* length: number of bytes to read into array | |||||
* sendStop: Boolean indicating whether to send a stop at the end | |||||
* Output number of bytes read | |||||
*/ | |||||
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) | |||||
{ | |||||
uint8_t i; | |||||
// ensure data will fit into buffer | |||||
if(TWI_BUFFER_LENGTH < length){ | |||||
return 0; | |||||
} | |||||
// wait until twi is ready, become master receiver | |||||
while(TWI_READY != twi_state){ | |||||
continue; | |||||
} | |||||
twi_state = TWI_MRX; | |||||
twi_sendStop = sendStop; | |||||
// reset error state (0xFF.. no error occured) | |||||
twi_error = 0xFF; | |||||
// initialize buffer iteration vars | |||||
twi_masterBufferIndex = 0; | |||||
twi_masterBufferLength = length-1; // This is not intuitive, read on... | |||||
// On receive, the previously configured ACK/NACK setting is transmitted in | |||||
// response to the received byte before the interrupt is signalled. | |||||
// Therefor we must actually set NACK when the _next_ to last byte is | |||||
// received, causing that NACK to be sent in response to receiving the last | |||||
// expected byte of data. | |||||
// build sla+w, slave device address + w bit | |||||
twi_slarw = TW_READ; | |||||
twi_slarw |= address << 1; | |||||
if (true == twi_inRepStart) { | |||||
// if we're in the repeated start state, then we've already sent the start, | |||||
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte. | |||||
// We need to remove ourselves from the repeated start state before we enable interrupts, | |||||
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning | |||||
// up. Also, don't enable the START interrupt. There may be one pending from the | |||||
// repeated start that we sent outselves, and that would really confuse things. | |||||
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR | |||||
TWDR = twi_slarw; | |||||
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START | |||||
} | |||||
else | |||||
// send start condition | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); | |||||
// wait for read operation to complete | |||||
while(TWI_MRX == twi_state){ | |||||
continue; | |||||
} | |||||
if (twi_masterBufferIndex < length) | |||||
length = twi_masterBufferIndex; | |||||
// copy twi buffer to data | |||||
for(i = 0; i < length; ++i){ | |||||
data[i] = twi_masterBuffer[i]; | |||||
} | |||||
return length; | |||||
} | |||||
/* | |||||
* Function twi_writeTo | |||||
* Desc attempts to become twi bus master and write a | |||||
* series of bytes to a device on the bus | |||||
* Input address: 7bit i2c device address | |||||
* data: pointer to byte array | |||||
* length: number of bytes in array | |||||
* wait: boolean indicating to wait for write or not | |||||
* sendStop: boolean indicating whether or not to send a stop at the end | |||||
* Output 0 .. success | |||||
* 1 .. length to long for buffer | |||||
* 2 .. address send, NACK received | |||||
* 3 .. data send, NACK received | |||||
* 4 .. other twi error (lost bus arbitration, bus error, ..) | |||||
*/ | |||||
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) | |||||
{ | |||||
uint8_t i; | |||||
// ensure data will fit into buffer | |||||
if(TWI_BUFFER_LENGTH < length){ | |||||
return 1; | |||||
} | |||||
// wait until twi is ready, become master transmitter | |||||
while(TWI_READY != twi_state){ | |||||
continue; | |||||
} | |||||
twi_state = TWI_MTX; | |||||
twi_sendStop = sendStop; | |||||
// reset error state (0xFF.. no error occured) | |||||
twi_error = 0xFF; | |||||
// initialize buffer iteration vars | |||||
twi_masterBufferIndex = 0; | |||||
twi_masterBufferLength = length; | |||||
// copy data to twi buffer | |||||
for(i = 0; i < length; ++i){ | |||||
twi_masterBuffer[i] = data[i]; | |||||
} | |||||
// build sla+w, slave device address + w bit | |||||
twi_slarw = TW_WRITE; | |||||
twi_slarw |= address << 1; | |||||
// if we're in a repeated start, then we've already sent the START | |||||
// in the ISR. Don't do it again. | |||||
// | |||||
if (true == twi_inRepStart) { | |||||
// if we're in the repeated start state, then we've already sent the start, | |||||
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte. | |||||
// We need to remove ourselves from the repeated start state before we enable interrupts, | |||||
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning | |||||
// up. Also, don't enable the START interrupt. There may be one pending from the | |||||
// repeated start that we sent outselves, and that would really confuse things. | |||||
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR | |||||
TWDR = twi_slarw; | |||||
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START | |||||
} | |||||
else | |||||
// send start condition | |||||
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs | |||||
// wait for write operation to complete | |||||
while(wait && (TWI_MTX == twi_state)){ | |||||
continue; | |||||
} | |||||
if (twi_error == 0xFF) | |||||
return 0; // success | |||||
else if (twi_error == TW_MT_SLA_NACK) | |||||
return 2; // error: address send, nack received | |||||
else if (twi_error == TW_MT_DATA_NACK) | |||||
return 3; // error: data send, nack received | |||||
else | |||||
return 4; // other twi error | |||||
} | |||||
/* | |||||
* Function twi_transmit | |||||
* Desc fills slave tx buffer with data | |||||
* must be called in slave tx event callback | |||||
* Input data: pointer to byte array | |||||
* length: number of bytes in array | |||||
* Output 1 length too long for buffer | |||||
* 2 not slave transmitter | |||||
* 0 ok | |||||
*/ | |||||
uint8_t twi_transmit(const uint8_t* data, uint8_t length) | |||||
{ | |||||
uint8_t i; | |||||
// ensure data will fit into buffer | |||||
if(TWI_BUFFER_LENGTH < length){ | |||||
return 1; | |||||
} | |||||
// ensure we are currently a slave transmitter | |||||
if(TWI_STX != twi_state){ | |||||
return 2; | |||||
} | |||||
// set length and copy data into tx buffer | |||||
twi_txBufferLength = length; | |||||
for(i = 0; i < length; ++i){ | |||||
twi_txBuffer[i] = data[i]; | |||||
} | |||||
return 0; | |||||
} | |||||
/* | |||||
* Function twi_attachSlaveRxEvent | |||||
* Desc sets function called before a slave read operation | |||||
* Input function: callback function to use | |||||
* Output none | |||||
*/ | |||||
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) ) | |||||
{ | |||||
twi_onSlaveReceive = function; | |||||
} | |||||
/* | |||||
* Function twi_attachSlaveTxEvent | |||||
* Desc sets function called before a slave write operation | |||||
* Input function: callback function to use | |||||
* Output none | |||||
*/ | |||||
void twi_attachSlaveTxEvent( void (*function)(void) ) | |||||
{ | |||||
twi_onSlaveTransmit = function; | |||||
} | |||||
/* | |||||
* Function twi_reply | |||||
* Desc sends byte or readys receive line | |||||
* Input ack: byte indicating to ack or to nack | |||||
* Output none | |||||
*/ | |||||
void twi_reply(uint8_t ack) | |||||
{ | |||||
// transmit master read ready signal, with or without ack | |||||
if(ack){ | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); | |||||
}else{ | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); | |||||
} | |||||
} | |||||
/* | |||||
* Function twi_stop | |||||
* Desc relinquishes bus master status | |||||
* Input none | |||||
* Output none | |||||
*/ | |||||
void twi_stop(void) | |||||
{ | |||||
// send stop condition | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); | |||||
// wait for stop condition to be exectued on bus | |||||
// TWINT is not set after a stop condition! | |||||
while(TWCR & _BV(TWSTO)){ | |||||
continue; | |||||
} | |||||
// update twi state | |||||
twi_state = TWI_READY; | |||||
} | |||||
/* | |||||
* Function twi_releaseBus | |||||
* Desc releases bus control | |||||
* Input none | |||||
* Output none | |||||
*/ | |||||
void twi_releaseBus(void) | |||||
{ | |||||
// release bus | |||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); | |||||
// update twi state | |||||
twi_state = TWI_READY; | |||||
} | |||||
ISR(TWI_vect) | |||||
{ | |||||
switch(TW_STATUS){ | |||||
// All Master | |||||
case TW_START: // sent start condition | |||||
case TW_REP_START: // sent repeated start condition | |||||
// copy device address and r/w bit to output register and ack | |||||
TWDR = twi_slarw; | |||||
twi_reply(1); | |||||
break; | |||||
// Master Transmitter | |||||
case TW_MT_SLA_ACK: // slave receiver acked address | |||||
case TW_MT_DATA_ACK: // slave receiver acked data | |||||
// if there is data to send, send it, otherwise stop | |||||
if(twi_masterBufferIndex < twi_masterBufferLength){ | |||||
// copy data to output register and ack | |||||
TWDR = twi_masterBuffer[twi_masterBufferIndex++]; | |||||
twi_reply(1); | |||||
}else{ | |||||
if (twi_sendStop) | |||||
twi_stop(); | |||||
else { | |||||
twi_inRepStart = true; // we're gonna send the START | |||||
// don't enable the interrupt. We'll generate the start, but we | |||||
// avoid handling the interrupt until we're in the next transaction, | |||||
// at the point where we would normally issue the start. | |||||
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; | |||||
twi_state = TWI_READY; | |||||
} | |||||
} | |||||
break; | |||||
case TW_MT_SLA_NACK: // address sent, nack received | |||||
twi_error = TW_MT_SLA_NACK; | |||||
twi_stop(); | |||||
break; | |||||
case TW_MT_DATA_NACK: // data sent, nack received | |||||
twi_error = TW_MT_DATA_NACK; | |||||
twi_stop(); | |||||
break; | |||||
case TW_MT_ARB_LOST: // lost bus arbitration | |||||
twi_error = TW_MT_ARB_LOST; | |||||
twi_releaseBus(); | |||||
break; | |||||
// Master Receiver | |||||
case TW_MR_DATA_ACK: // data received, ack sent | |||||
// put byte into buffer | |||||
twi_masterBuffer[twi_masterBufferIndex++] = TWDR; | |||||
case TW_MR_SLA_ACK: // address sent, ack received | |||||
// ack if more bytes are expected, otherwise nack | |||||
if(twi_masterBufferIndex < twi_masterBufferLength){ | |||||
twi_reply(1); | |||||
}else{ | |||||
twi_reply(0); | |||||
} | |||||
break; | |||||
case TW_MR_DATA_NACK: // data received, nack sent | |||||
// put final byte into buffer | |||||
twi_masterBuffer[twi_masterBufferIndex++] = TWDR; | |||||
if (twi_sendStop) | |||||
twi_stop(); | |||||
else { | |||||
twi_inRepStart = true; // we're gonna send the START | |||||
// don't enable the interrupt. We'll generate the start, but we | |||||
// avoid handling the interrupt until we're in the next transaction, | |||||
// at the point where we would normally issue the start. | |||||
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; | |||||
twi_state = TWI_READY; | |||||
} | |||||
break; | |||||
case TW_MR_SLA_NACK: // address sent, nack received | |||||
twi_stop(); | |||||
break; | |||||
// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case | |||||
// Slave Receiver | |||||
case TW_SR_SLA_ACK: // addressed, returned ack | |||||
case TW_SR_GCALL_ACK: // addressed generally, returned ack | |||||
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack | |||||
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack | |||||
// enter slave receiver mode | |||||
twi_state = TWI_SRX; | |||||
// indicate that rx buffer can be overwritten and ack | |||||
twi_rxBufferIndex = 0; | |||||
twi_reply(1); | |||||
break; | |||||
case TW_SR_DATA_ACK: // data received, returned ack | |||||
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack | |||||
// if there is still room in the rx buffer | |||||
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ | |||||
// put byte in buffer and ack | |||||
twi_rxBuffer[twi_rxBufferIndex++] = TWDR; | |||||
twi_reply(1); | |||||
}else{ | |||||
// otherwise nack | |||||
twi_reply(0); | |||||
} | |||||
break; | |||||
case TW_SR_STOP: // stop or repeated start condition received | |||||
// put a null char after data if there's room | |||||
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ | |||||
twi_rxBuffer[twi_rxBufferIndex] = '\0'; | |||||
} | |||||
// sends ack and stops interface for clock stretching | |||||
twi_stop(); | |||||
// callback to user defined callback | |||||
twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); | |||||
// since we submit rx buffer to "wire" library, we can reset it | |||||
twi_rxBufferIndex = 0; | |||||
// ack future responses and leave slave receiver state | |||||
twi_releaseBus(); | |||||
break; | |||||
case TW_SR_DATA_NACK: // data received, returned nack | |||||
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack | |||||
// nack back at master | |||||
twi_reply(0); | |||||
break; | |||||
// Slave Transmitter | |||||
case TW_ST_SLA_ACK: // addressed, returned ack | |||||
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack | |||||
// enter slave transmitter mode | |||||
twi_state = TWI_STX; | |||||
// ready the tx buffer index for iteration | |||||
twi_txBufferIndex = 0; | |||||
// set tx buffer length to be zero, to verify if user changes it | |||||
twi_txBufferLength = 0; | |||||
// request for txBuffer to be filled and length to be set | |||||
// note: user must call twi_transmit(bytes, length) to do this | |||||
twi_onSlaveTransmit(); | |||||
// if they didn't change buffer & length, initialize it | |||||
if(0 == twi_txBufferLength){ | |||||
twi_txBufferLength = 1; | |||||
twi_txBuffer[0] = 0x00; | |||||
} | |||||
// transmit first byte from buffer, fall | |||||
case TW_ST_DATA_ACK: // byte sent, ack returned | |||||
// copy data to output register | |||||
TWDR = twi_txBuffer[twi_txBufferIndex++]; | |||||
// if there is more to send, ack, otherwise nack | |||||
if(twi_txBufferIndex < twi_txBufferLength){ | |||||
twi_reply(1); | |||||
}else{ | |||||
twi_reply(0); | |||||
} | |||||
break; | |||||
case TW_ST_DATA_NACK: // received nack, we are done | |||||
case TW_ST_LAST_DATA: // received ack, but we are done already! | |||||
// ack future responses | |||||
twi_reply(1); | |||||
// leave slave receiver state | |||||
twi_state = TWI_READY; | |||||
break; | |||||
// All | |||||
case TW_NO_INFO: // no state information | |||||
break; | |||||
case TW_BUS_ERROR: // bus error, illegal stop/start | |||||
twi_error = TW_BUS_ERROR; | |||||
twi_stop(); | |||||
break; | |||||
} | |||||
} | |||||
/* | |||||
twi.h - TWI/I2C library for Wiring & Arduino | |||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library 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 | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
*/ | |||||
#ifndef twi_h | |||||
#define twi_h | |||||
#include <inttypes.h> | |||||
//#define ATMEGA8 | |||||
#ifndef TWI_FREQ | |||||
#define TWI_FREQ 100000L | |||||
#endif | |||||
#ifndef TWI_BUFFER_LENGTH | |||||
#define TWI_BUFFER_LENGTH 32 | |||||
#endif | |||||
#define TWI_READY 0 | |||||
#define TWI_MRX 1 | |||||
#define TWI_MTX 2 | |||||
#define TWI_SRX 3 | |||||
#define TWI_STX 4 | |||||
void twi_init(void); | |||||
void twi_setAddress(uint8_t); | |||||
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t); | |||||
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t); | |||||
uint8_t twi_transmit(const uint8_t*, uint8_t); | |||||
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); | |||||
void twi_attachSlaveTxEvent( void (*)(void) ); | |||||
void twi_reply(uint8_t); | |||||
void twi_stop(void); | |||||
void twi_releaseBus(void); | |||||
#endif | |||||