Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dattasaurabh82 committed Jul 21, 2021
0 parents commit c47179b
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 0 deletions.
9 changes: 9 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright (c) 2021 Saurabh Datta

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
169 changes: 169 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# TinyMegaI2C Library

Inspiration: https://github.com/technoblogy/tiny-i2c

## Why?
To have it in Arduino's `Library Manager`

## Description
**TinyMegaI2C** is a set of minimal I2C routines for the new 0-series and 1-series ATtiny and ATmega microcontrollers. They allow any of these processors to act as an I2C Master and connect to I2C peripherals. For more information see

The main difference between these routines and the standard Arduino Tiny Wire library is that these don't use buffers, so have minimal memory requirements, and don't impose a limit on transmissions.

_**Note**_: These routines are designed for the latest ATtiny 0-series and 1-series processors, and the 0-series ATmega chips.

## Introduction

Naming of these routines as `TinyMegaI2C` was done to distinguish them from the existing Arduino Wire libraries, such as the one included in Spence Konde's megaTinyCore. These routines don't follow the Arduino Wire library naming conventions.
In addition, these routines differ from the Tiny Wire library routines in the following ways:

### Low memory requirements

These routines don't use buffers, reducing their RAM requirements to a couple of bytes. The standard 0-series ATmega Wire library uses 128-byte send and receive buffers, and the 0-series and 1-series ATtiny Wire libraries use 32-byte or 16-byte buffers, which on the smaller chips is a significant part of the available RAM. As far as I can see there's no need for buffering as the I2C protocol incorporates handshaking, using the ACK/NACK pulses.

### Unlimited transmission length

These routines don't impose any limit on the length of transmissions. The standard Wire libraries limit the length of any transmission to the size of the buffer. This isn't a problem with many I2C applications, such as reading the temperature from a sensor, but it is a problem with applications such as driving an I2C OLED display, which requires you to send 1024 bytes to update the whole display.

### Flexible read

These routines allow you to specify in advance how many bytes you want to read from an I2C peripheral, or you can leave this open-ended and mark the last byte read. This is an advantage when you don't know in advance how many bytes you are going to want to read.

## Pollong

For simplicity these routines use polling rather than interrupts, so they won't interfere with other processes using interrupts.

## Compatibility

The beauty of the latest ATtiny 0-series, ATtiny 1-series, and ATmega 0-series ranges from Microchip is that they use the same peripherals, so these routines should work on any microcontroller in the range.

## Description

Here's a description of the TinyMegaI2C routines:

### TinyMegaI2C.start(address, type)

Starts a transaction with the slave device with the specified address, and specifies if the transaction is going to be a read or a write. It returns a true/false value to say whether the start was successful.

The **type** parameter can have the following values:

* 0: Write to the device.
* 1 to 32767: Read from the device. The number specifies how many reads you are going to do.
* -1: Read an unspecified number of bytes from the device.

If **type** is specified as -1 you must identify the last read by calling **TinyMegaI2C.readLast()** rather than **TinyMegaI2C.read()**.

### TinyMegaI2C.write(data)

Writes a byte of data to a slave device. It returns true if the write was successful or false if there was an error.

### TinyMegaI2C.read()

Returns the result of reading from a slave device.

### TinyMegaI2C.readLast()

Returns the result of reading from a slave device and tells the slave to stop sending. You only need to use **TinyMegaI2C.readlast()** if you called **TinyMegaI2C.start()** or **TinyMegaI2C.restart()** with **type** set to -1.

### TinyMegaI2C.restart(address, type);

Does a restart. The **type** parameter is the same as for **TinyMegaI2C.start()**.

### TinyMegaI2C.stop()

Ends the transaction.

## Examples

To use the routines install the TinyMegaI2C library and include this at the top of your program:

````
#include <TinyMegaI2C.h>
````

### Port scanner
These routines let you write a simple port scanner to print out the addresses of any I2C devices found on the bus:
````
void setup() {
TinyMegaI2C.init();
Serial.begin(115200);
while (!Serial) {
}
Serial.println(F("\nI2C Scanner"));
delay(1000);
}
void loop() {
byte address;
// byte error, address; //variable for error and I2C address
int nDevices;
Serial.println(F("Scanning..."));
nDevices = 0;
for (address = 1; address < 127; address++ ) {
if (TinyMegaI2C.start(address, 0)) {
nDevices++;
Serial.print("I2C device found at address 0x");
Serial.print(address, HEX);
Serial.println(" !");
}
}
if (nDevices == 0)
Serial.println(F("No I2C devices found\n"));
else
Serial.println(F("done\n"));
delay(5000);
}
````
It uses the fact that **TinyMegaI2C.start()** returns false if no device was found with the corresponding address. For example, with the I2C clock example it prints out:

````
Scanning...
I2C device found at address 0x32 !
done
````
### Writing to a slave

Writing to a slave is straightforward: for example, to write one byte:

````
TinyMegaI2C.start(Address, 0);
TinyMegaI2C.write(byte);
TinyMegaI2C.stop();
````
### Reading from a slave

The TinyMegaI2C routines allow you to identify the last byte read from a slave in either of two ways:

You can specify the total number of bytes you are going to read, as the second parameter of **TinyMegaI2C.start()**. With this approach **TinyMegaI2C.read()** will automatically terminate the last call with a NAK:

````
TinyMegaI2C.start(Address, 2);
int mins = TinyMegaI2C.read();
int hrs = TinyMegaI2C.read();
TinyMegaI2C.stop();
````
Alternatively you can just specify the second parameter of **TinyMegaI2C.start()** as -1, and explicitly identify the last **TinyMegaI2C.read** command by calling **TinyMegaI2C.readLast()**:

````
TinyMegaI2C.start(Address, -1);
int mins = TinyMegaI2C.read();
int hrs = TinyMegaI2C.readLast();
TinyMegaI2C.stop();
````
### Writing and reading

Many I2C devices require you to write one or more bytes before reading, to specify the register you want to read from; the read should be introduced with an **TinyMegaI2C.restart()** call; for example:

````
TinyMegaI2C.start(Address, 0);
TinyMegaI2C.write(1);
TinyMegaI2C.restart(Address, 2);
int mins = TinyMegaI2C.read();
int hrs = TinyMegaI2C.read();
TinyMegaI2C.stop();
````
36 changes: 36 additions & 0 deletions examples/tinyI2CScanner/tinyI2CScanner.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <TinyMegaI2C.h>

void setup() {
TinyMegaI2C.init();

Serial.begin(115200);
while (!Serial) {
}

Serial.println(F("\nI2C Scanner"));
delay(1000);
}

void loop() {

byte address;
// byte error, address; //variable for error and I2C address
int nDevices;

Serial.println(F("Scanning..."));
nDevices = 0;

for (address = 1; address < 127; address++ ) {
if (TinyMegaI2C.start(address, 0)) {
nDevices++;
Serial.print("I2C device found at address 0x");
Serial.print(address, HEX);
Serial.println(" !");
}
}
if (nDevices == 0)
Serial.println(F("No I2C devices found\n"));
else
Serial.println(F("done\n"));
delay(5000);
}
8 changes: 8 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
TinyMegaI2C KEYWORD1
init KEYWORD2
read KEYWORD2
readLast KEYWORD2
write KEYWORD2
start KEYWORD2
restart KEYWORD2
stop KEYWORD2
9 changes: 9 additions & 0 deletions library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name=TinyMegaI2C
version=0.0.1
author=Saurabh Datta
maintainer=Saurabh_Datta
sentence=An efficient optimised i2c library for new ATTINY series uCs
paragraph=An efficient and optimised i2c library for new ATTINY series uCs as a replacement for wire library with less footprint
category=Communication
url=https://github.com/dattasaurabh82/
architectures=*
67 changes: 67 additions & 0 deletions src/TinyMegaI2C.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/******************************************************************************
TinyMegaI2C.cpp
i2c Library for TinyMegaCore based ATTINY Atmel chips.
Saurabh Datta July 2021
Development environment specifics:
Arduino IDE: 1.8.13
uC: ATTINY-1607
******************************************************************************/

#include "TinyMegaI2C.h"

TinyMegaI2CMaster::TinyMegaI2CMaster(void) {
}


void TinyMegaI2CMaster::init () {
pinMode(PIN_WIRE_SDA, INPUT_PULLUP);
pinMode(PIN_WIRE_SCL, INPUT_PULLUP);
uint32_t baud = ((F_CPU / FREQUENCY) - (((F_CPU * T_RISE) / 1000) / 1000) / 1000 - 10) / 2;
TWI0.MBAUD = (uint8_t)baud;
TWI0.MCTRLA = TWI_ENABLE_bm; // Enable as master, no interrupts
TWI0.MSTATUS = TWI_BUSSTATE_IDLE_gc;
}

uint8_t TinyMegaI2CMaster::read (void) {
if (I2Ccount != 0) I2Ccount--;
while (!(TWI0.MSTATUS & TWI_RIF_bm)); // Wait for read interrupt flag
uint8_t data = TWI0.MDATA;
// Check slave sent ACK?
if (I2Ccount != 0) TWI0.MCTRLB = TWI_MCMD_RECVTRANS_gc; // if ACK = more bytes to read
else TWI0.MCTRLB = TWI_ACKACT_bm | TWI_MCMD_RECVTRANS_gc; // else Send NAK
return data;
}

uint8_t TinyMegaI2CMaster::readLast (void) {
I2Ccount = 0;
return TinyMegaI2CMaster::read();
}

bool TinyMegaI2CMaster::write (uint8_t data) {
while (!(TWI0.MSTATUS & TWI_WIF_bm)); // Wait for write interrupt flag
TWI0.MDATA = data;
TWI0.MCTRLB = TWI_MCMD_RECVTRANS_gc; // Do nothing
return !(TWI0.MSTATUS & TWI_RXACK_bm); // Returns true if slave gave an ACK
}

// Start transmission by sending address
bool TinyMegaI2CMaster::start (uint8_t address, int readcount) {
bool read;
if (readcount == 0) read = 0; // Write
else { I2Ccount = readcount; read = 1; } // Read
TWI0.MADDR = address<<1 | read; // Send START condition
while (!(TWI0.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))); // Wait for write or read interrupt flag
if ((TWI0.MSTATUS & TWI_ARBLOST_bm)) return false; // Return false if arbitration lost or bus error
return !(TWI0.MSTATUS & TWI_RXACK_bm); // Return true if slave gave an ACK
}

bool TinyMegaI2CMaster::restart(uint8_t address, int readcount) {
return TinyMegaI2CMaster::start(address, readcount);
}

void TinyMegaI2CMaster::stop (void) {
TWI0.MCTRLB = TWI_ACKACT_bm | TWI_MCMD_STOP_gc; // Send STOP
}

TinyMegaI2CMaster TinyMegaI2C = TinyMegaI2CMaster();
37 changes: 37 additions & 0 deletions src/TinyMegaI2C.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/******************************************************************************
TinyMegaI2C.h
i2c Library for TinyMegaCore based ATTINY Atmel chips.
Saurabh Datta July 2021
Development environment specifics:
Arduino IDE: 1.8.13
uC: ATTINY-1607
******************************************************************************/

#pragma once

#include <stdint.h>
#include <Arduino.h>
#include <avr/io.h>

// 400kHz clock
uint32_t const FREQUENCY = 400000L; // Hardware I2C clock in Hz
uint32_t const T_RISE = 300L; // Rise time

class TinyMegaI2CMaster {
public:
TinyMegaI2CMaster( void );

void init(void);

uint8_t read(void);
uint8_t readLast(void);
bool write(uint8_t data);
bool start(uint8_t address, int readcount);
bool restart(uint8_t address, int readcount);
void stop(void);
private:
int I2Ccount;
};

extern TinyMegaI2CMaster TinyMegaI2C;

0 comments on commit c47179b

Please sign in to comment.