mirror of https://github.com/boxtec/tinyLCD_I2C
Added backlight and contrast features Added fixed TinyWireS as a needed library Extended example to use new featuresmaster
parent
bd29ac70ca
commit
4a4e1f7e65
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
TinyWireS.cpp - a wrapper class for Don Blake's usiTwiSlave routines.
|
||||||
|
Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment.
|
||||||
|
1/23/2011 BroHogan - brohoganx10 at gmail dot com
|
||||||
|
|
||||||
|
**** See TinyWireS.h for Credits and Usage information ****
|
||||||
|
|
||||||
|
This library 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.1 of the License, or 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "usiTwiSlave.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "TinyWireS.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
// Constructors ////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
USI_TWI_S::USI_TWI_S(){
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Public Methods //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void USI_TWI_S::begin(uint8_t slaveAddr){ // initialize I2C lib
|
||||||
|
usiTwiSlaveInit(slaveAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USI_TWI_S::send(uint8_t data){ // send it back to master
|
||||||
|
usiTwiTransmitByte(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t USI_TWI_S::available(){ // the bytes available that haven't been read yet
|
||||||
|
return usiTwiAmountDataInReceiveBuffer();
|
||||||
|
//return usiTwiDataInReceiveBuffer(); // This is wrong as far as the Wire API is concerned since it returns boolean and not amount
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t USI_TWI_S::receive(){ // returns the bytes received one at a time
|
||||||
|
return usiTwiReceiveByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets function called on slave write
|
||||||
|
void USI_TWI_S::onReceive( void (*function)(uint8_t) )
|
||||||
|
{
|
||||||
|
usi_onReceiverPtr = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets function called on slave read
|
||||||
|
void USI_TWI_S::onRequest( void (*function)(void) )
|
||||||
|
{
|
||||||
|
usi_onRequestPtr = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TinyWireS_stop_check()
|
||||||
|
{
|
||||||
|
if (!usi_onReceiverPtr)
|
||||||
|
{
|
||||||
|
// no onReceive callback, nothing to do...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(USISR & ( 1 << USIPF )))
|
||||||
|
{
|
||||||
|
// Stop not detected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t amount = usiTwiAmountDataInReceiveBuffer();
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
// no data in buffer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usi_onReceiverPtr(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c)
|
||||||
|
void tws_delay(unsigned long ms)
|
||||||
|
{
|
||||||
|
uint16_t start = (uint16_t)micros();
|
||||||
|
while (ms > 0)
|
||||||
|
{
|
||||||
|
TinyWireS_stop_check();
|
||||||
|
if (((uint16_t)micros() - start) >= 1000)
|
||||||
|
{
|
||||||
|
ms--;
|
||||||
|
start += 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preinstantiate Objects //////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
USI_TWI_S TinyWireS = USI_TWI_S();
|
||||||
|
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
TinyWireS.h - a wrapper class for Don Blake's usiTwiSlave routines.
|
||||||
|
Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment.
|
||||||
|
1/23/2011 BroHogan - brohoganx10 at gmail dot com
|
||||||
|
|
||||||
|
Major credit and thanks to Don Blake for his usiTwiSlave code which makes this possible
|
||||||
|
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=51467&start=all&postdays=0&postorder=asc
|
||||||
|
(Changed #define USI_START_COND_INT USISIF (was USICIF) in usiTwiSlave.h)
|
||||||
|
|
||||||
|
NOTE! - It's very important to use pullups on the SDA & SCL lines! More so than with the Wire lib.
|
||||||
|
Current Rx & Tx buffers set at 32 bytes - see usiTwiSlave.h
|
||||||
|
|
||||||
|
USAGE is modeled after the standard Wire library . . .
|
||||||
|
Put in setup():
|
||||||
|
TinyWireS.begin(I2C_SLAVE_ADDR); // initialize I2C lib & setup slave's address (7 bit - same as Wire)
|
||||||
|
|
||||||
|
To Receive:
|
||||||
|
someByte = TinyWireS.available(){ // returns the number of bytes in the received buffer
|
||||||
|
someByte = TinyWireS.receive(){ // returns the next byte in the received buffer
|
||||||
|
|
||||||
|
To Send:
|
||||||
|
TinyWireS.send(uint8_t data){ // sends a requested byte to master
|
||||||
|
|
||||||
|
TODO: (by others!)
|
||||||
|
- onReceive and onRequest handlers are not implimented.
|
||||||
|
- merge this class with TinyWireM for master & slave support in one library
|
||||||
|
|
||||||
|
This library 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.1 of the License, or 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TinyWireS_h
|
||||||
|
#define TinyWireS_h
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
|
||||||
|
class USI_TWI_S
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
//static uint8_t USI_BytesAvail;
|
||||||
|
|
||||||
|
public:
|
||||||
|
USI_TWI_S();
|
||||||
|
void begin(uint8_t I2C_SLAVE_ADDR);
|
||||||
|
void send(uint8_t data);
|
||||||
|
uint8_t available();
|
||||||
|
uint8_t receive();
|
||||||
|
void onReceive( void (*)(uint8_t) );
|
||||||
|
void onRequest( void (*)(void) );
|
||||||
|
};
|
||||||
|
|
||||||
|
void TinyWireS_stop_check();
|
||||||
|
// Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c)
|
||||||
|
void tws_delay(unsigned long);
|
||||||
|
|
||||||
|
extern USI_TWI_S TinyWireS;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* Example sketch for writing to and reading from a slave in transactional manner
|
||||||
|
*
|
||||||
|
* NOTE: You must not use delay() or I2C communications will fail, use tws_delay() instead (or preferably some smarter timing system)
|
||||||
|
*
|
||||||
|
* On write the first byte received is considered the register addres to modify/read
|
||||||
|
* On each byte sent or read the register address is incremented (and it will loop back to 0)
|
||||||
|
*
|
||||||
|
* You can try this with the Arduino I2C REPL sketch at https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino
|
||||||
|
* If you have bus-pirate remember that the older revisions do not like the slave streching the clock, this leads to all sorts of weird behaviour
|
||||||
|
*
|
||||||
|
* To read third value (register number 2 since counting starts at 0) send "[ 8 2 [ 9 r ]", value read should be 0xBE
|
||||||
|
* If you then send "[ 9 r r r ]" you should get 0xEF 0xDE 0xAD as response (demonstrating the register counter looping back to zero)
|
||||||
|
*
|
||||||
|
* You need to have at least 8MHz clock on the ATTiny for this to work (and in fact I have so far tested it only on ATTiny85 @8MHz using internal oscillator)
|
||||||
|
* Remember to "Burn bootloader" to make sure your chip is in correct mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pin notes by Suovula, see also http://hlt.media.mit.edu/?p=1229
|
||||||
|
*
|
||||||
|
* DIP and SOIC have same pinout, however the SOIC chips are much cheaper, especially if you buy more than 5 at a time
|
||||||
|
* For nice breakout boards see https://github.com/rambo/attiny_boards
|
||||||
|
*
|
||||||
|
* Basically the arduino pin numbers map directly to the PORTB bit numbers.
|
||||||
|
*
|
||||||
|
// I2C
|
||||||
|
arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
|
||||||
|
arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
|
||||||
|
// Timer1 -> PWM
|
||||||
|
arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
|
||||||
|
arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
|
||||||
|
arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
|
||||||
|
*/
|
||||||
|
#define I2C_SLAVE_ADDRESS 0x4 // the 7-bit address (remember to change this when adapting this example)
|
||||||
|
// Get this from https://github.com/rambo/TinyWire
|
||||||
|
#include <TinyWireS.h>
|
||||||
|
// The default buffer size, Can't recall the scope of defines right now
|
||||||
|
#ifndef TWI_RX_BUFFER_SIZE
|
||||||
|
#define TWI_RX_BUFFER_SIZE ( 16 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
volatile uint8_t i2c_regs[] =
|
||||||
|
{
|
||||||
|
0xDE,
|
||||||
|
0xAD,
|
||||||
|
0xBE,
|
||||||
|
0xEF,
|
||||||
|
};
|
||||||
|
// Tracks the current register pointer position
|
||||||
|
volatile byte reg_position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
|
||||||
|
* send-buffer when using this callback
|
||||||
|
*/
|
||||||
|
void requestEvent()
|
||||||
|
{
|
||||||
|
TinyWireS.send(i2c_regs[reg_position]);
|
||||||
|
// Increment the reg position on each read, and loop back to zero
|
||||||
|
reg_position = (reg_position+1) % sizeof(i2c_regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Either update this to use something smarter for timing or remove it alltogether
|
||||||
|
void blinkn(uint8_t blinks)
|
||||||
|
{
|
||||||
|
digitalWrite(3, HIGH);
|
||||||
|
while(blinks--)
|
||||||
|
{
|
||||||
|
digitalWrite(3, LOW);
|
||||||
|
tws_delay(50);
|
||||||
|
digitalWrite(3, HIGH);
|
||||||
|
tws_delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The I2C data received -handler
|
||||||
|
*
|
||||||
|
* This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
|
||||||
|
* so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
|
||||||
|
*/
|
||||||
|
void receiveEvent(uint8_t howMany)
|
||||||
|
{
|
||||||
|
if (howMany < 1)
|
||||||
|
{
|
||||||
|
// Sanity-check
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (howMany > TWI_RX_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
// Also insane number
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_position = TinyWireS.receive();
|
||||||
|
howMany--;
|
||||||
|
if (!howMany)
|
||||||
|
{
|
||||||
|
// This write was only to set the buffer for next read
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(howMany--)
|
||||||
|
{
|
||||||
|
i2c_regs[reg_position%sizeof(i2c_regs)] = TinyWireS.receive();
|
||||||
|
reg_position++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// TODO: Tri-state this and wait for input voltage to stabilize
|
||||||
|
pinMode(3, OUTPUT); // OC1B-, Arduino pin 3, ADC
|
||||||
|
digitalWrite(3, LOW); // Note that this makes the led turn on, it's wire this way to allow for the voltage sensing above.
|
||||||
|
|
||||||
|
pinMode(1, OUTPUT); // OC1A, also The only HW-PWM -pin supported by the tiny core analogWrite
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reminder: taking care of pull-ups is the masters job
|
||||||
|
*/
|
||||||
|
|
||||||
|
TinyWireS.begin(I2C_SLAVE_ADDRESS);
|
||||||
|
TinyWireS.onReceive(receiveEvent);
|
||||||
|
TinyWireS.onRequest(requestEvent);
|
||||||
|
|
||||||
|
|
||||||
|
// Whatever other setup routines ?
|
||||||
|
|
||||||
|
digitalWrite(3, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is the only way we can detect stop condition (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=984716&sid=82e9dc7299a8243b86cf7969dd41b5b5#984716)
|
||||||
|
* it needs to be called in a very tight loop in order not to miss any (REMINDER: Do *not* use delay() anywhere, use tws_delay() instead).
|
||||||
|
* It will call the function registered via TinyWireS.onReceive(); if there is data in the buffer on stop.
|
||||||
|
*/
|
||||||
|
TinyWireS_stop_check();
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* Example sketch for writing to and reading from a slave in transactional manner, it will also blink a led attached to pin 3 (which is the SOIC pin 2)
|
||||||
|
* (provided you're using one of my ATTiny85 boards from https://github.com/rambo/attiny_boards with the led soldered)
|
||||||
|
*
|
||||||
|
* NOTE: You must not use delay() or I2C communications will fail, use tws_delay() instead (or preferably some smarter timing system, like the Task library used in this example)
|
||||||
|
*
|
||||||
|
* On write the first byte received is considered the register addres to modify/read
|
||||||
|
* On each byte sent or read the register address is incremented (and it will loop back to 0)
|
||||||
|
*
|
||||||
|
* You can try this with the Arduino I2C REPL sketch at https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino
|
||||||
|
* If you have bus-pirate remember that the older revisions do not like the slave streching the clock, this leads to all sorts of weird behaviour
|
||||||
|
*
|
||||||
|
* By default this blinks the SOS morse pattern and then has long on/off time to indicate end of pattern, send [ 8 0 32 ] (using the REPL/bus-pirate
|
||||||
|
* semantics) to make the delay per bit smaller (and thus blinking faster). The pattern lenght is calculated from the register size, it would be fairly
|
||||||
|
* trivial to make it yet another variable changeable via I2C.
|
||||||
|
*
|
||||||
|
* You need to have at least 8MHz clock on the ATTiny for this to work (and in fact I have so far tested it only on ATTiny85 @8MHz using internal oscillator)
|
||||||
|
* Remember to "Burn bootloader" to make sure your chip is in correct mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pin notes by Suovula, see also http://hlt.media.mit.edu/?p=1229
|
||||||
|
*
|
||||||
|
* DIP and SOIC have same pinout, however the SOIC chips are much cheaper, especially if you buy more than 5 at a time
|
||||||
|
* For nice breakout boards see https://github.com/rambo/attiny_boards
|
||||||
|
*
|
||||||
|
* Basically the arduino pin numbers map directly to the PORTB bit numbers.
|
||||||
|
*
|
||||||
|
// I2C
|
||||||
|
arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
|
||||||
|
arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
|
||||||
|
// Timer1 -> PWM
|
||||||
|
arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
|
||||||
|
arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
|
||||||
|
arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
|
||||||
|
*/
|
||||||
|
#define I2C_SLAVE_ADDRESS 0x4 // the 7-bit address (remember to change this when adapting this example)
|
||||||
|
// Get this from https://github.com/rambo/TinyWire
|
||||||
|
#include <TinyWireS.h>
|
||||||
|
// The default buffer size, Can't recall the scope of defines right now
|
||||||
|
#ifndef TWI_RX_BUFFER_SIZE
|
||||||
|
#define TWI_RX_BUFFER_SIZE ( 16 )
|
||||||
|
#endif
|
||||||
|
// Get this library from http://bleaklow.com/files/2010/Task.tar.gz
|
||||||
|
// and read http://bleaklow.com/2010/07/20/a_very_simple_arduino_task_manager.html for background and instructions
|
||||||
|
#include <Task.h>
|
||||||
|
#include <TaskScheduler.h>
|
||||||
|
|
||||||
|
// The led is connected so that the tiny sinks current
|
||||||
|
#define LED_ON LOW
|
||||||
|
#define LED_OFF HIGH
|
||||||
|
|
||||||
|
// The I2C registers
|
||||||
|
volatile uint8_t i2c_regs[] =
|
||||||
|
{
|
||||||
|
150, // Delay between each position (ms, remeber that this isa byte so 255 is max)
|
||||||
|
B10101000, // SOS pattern
|
||||||
|
B01110111,
|
||||||
|
B01110001,
|
||||||
|
B01010000,
|
||||||
|
B00000000,
|
||||||
|
B11111111, // Long on and off to mark end of pattern
|
||||||
|
B00000000,
|
||||||
|
};
|
||||||
|
// Tracks the current register pointer position
|
||||||
|
volatile byte reg_position;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BEGIN: PatternBlinker task based on the Task library Blinker example
|
||||||
|
*/
|
||||||
|
// Timed task to blink a LED.
|
||||||
|
const byte pattern_lenght = (sizeof(i2c_regs)-1) * 8; // bits (first is the speed, rest is the pattern)
|
||||||
|
class PatternBlinker : public TimedTask
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Create a new blinker for the specified pin and rate.
|
||||||
|
PatternBlinker(uint8_t _pin);
|
||||||
|
virtual void run(uint32_t now);
|
||||||
|
private:
|
||||||
|
uint8_t pin; // LED pin.
|
||||||
|
uint8_t pattern_position; // Used to calcuate the register and bit offset
|
||||||
|
};
|
||||||
|
|
||||||
|
PatternBlinker::PatternBlinker(uint8_t _pin)
|
||||||
|
: TimedTask(millis()),
|
||||||
|
pin(_pin)
|
||||||
|
{
|
||||||
|
pinMode(pin, OUTPUT); // Set pin for output.
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternBlinker::run(uint32_t now)
|
||||||
|
{
|
||||||
|
// Start by setting the next runtime
|
||||||
|
incRunTime(i2c_regs[0]);
|
||||||
|
|
||||||
|
// Written out for clear code, the complier might optimize it to something more efficient even without it being unrolled into one line
|
||||||
|
byte reg = i2c_regs[1+(pattern_position/8)]; // Get the register where the bit pattern position is stored
|
||||||
|
byte shift_amount = 7 - (pattern_position % 7); // To have "natural" left-to-right pattern flow.
|
||||||
|
bool state = (reg >> shift_amount) & 0x1;
|
||||||
|
if (state) {
|
||||||
|
digitalWrite(pin, LED_ON);
|
||||||
|
} else {
|
||||||
|
digitalWrite(pin, LED_OFF);
|
||||||
|
}
|
||||||
|
// Calculate the next pattern position
|
||||||
|
pattern_position = (pattern_position+1) % pattern_lenght;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* END: PatternBlinker task copied from the Task library example
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* BEGIN: I2C Stop flag checker
|
||||||
|
*
|
||||||
|
* This task needs to run almost all the time due to the USI I2C implementation limitations
|
||||||
|
*
|
||||||
|
* So I2CStopCheck_YIELD_TICKS below is used to specify how often the task is run, not it's every 4 ticks
|
||||||
|
*/
|
||||||
|
#define I2CStopCheck_YIELD_TICKS 4
|
||||||
|
class I2CStopCheck : public Task
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
I2CStopCheck();
|
||||||
|
virtual void run(uint32_t now);
|
||||||
|
virtual bool canRun(uint32_t now);
|
||||||
|
private:
|
||||||
|
uint8_t yield_counter; // Incremented on each canRun call, used to yield to other tasks.
|
||||||
|
};
|
||||||
|
|
||||||
|
I2CStopCheck::I2CStopCheck()
|
||||||
|
: Task()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't just return true since then no other task could ever run (since we have the priority)
|
||||||
|
bool I2CStopCheck::canRun(uint32_t now)
|
||||||
|
{
|
||||||
|
yield_counter++;
|
||||||
|
return ((yield_counter % I2CStopCheck_YIELD_TICKS) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CStopCheck::run(uint32_t now)
|
||||||
|
{
|
||||||
|
TinyWireS_stop_check();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* END: I2C Stop flag checker
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create the tasks.
|
||||||
|
PatternBlinker blinker(3);
|
||||||
|
I2CStopCheck checker;
|
||||||
|
|
||||||
|
// Tasks are in priority order, only one task is run per tick
|
||||||
|
Task *tasks[] = { &checker, &blinker, };
|
||||||
|
TaskScheduler sched(tasks, NUM_TASKS(tasks));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
|
||||||
|
* send-buffer when using this callback
|
||||||
|
*/
|
||||||
|
void requestEvent()
|
||||||
|
{
|
||||||
|
TinyWireS.send(i2c_regs[reg_position]);
|
||||||
|
// Increment the reg position on each read, and loop back to zero
|
||||||
|
reg_position = (reg_position+1) % sizeof(i2c_regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The I2C data received -handler
|
||||||
|
*
|
||||||
|
* This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
|
||||||
|
* so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
|
||||||
|
*/
|
||||||
|
void receiveEvent(uint8_t howMany)
|
||||||
|
{
|
||||||
|
if (howMany < 1)
|
||||||
|
{
|
||||||
|
// Sanity-check
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (howMany > TWI_RX_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
// Also insane number
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_position = TinyWireS.receive();
|
||||||
|
howMany--;
|
||||||
|
if (!howMany)
|
||||||
|
{
|
||||||
|
// This write was only to set the buffer for next read
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(howMany--)
|
||||||
|
{
|
||||||
|
i2c_regs[reg_position%sizeof(i2c_regs)] = TinyWireS.receive();
|
||||||
|
reg_position++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// TODO: Tri-state this and wait for input voltage to stabilize
|
||||||
|
pinMode(3, OUTPUT); // OC1B-, Arduino pin 3, ADC
|
||||||
|
digitalWrite(3, LED_ON); // Note that this makes the led turn on, it's wire this way to allow for the voltage sensing above.
|
||||||
|
|
||||||
|
pinMode(1, OUTPUT); // OC1A, also The only HW-PWM -pin supported by the tiny core analogWrite
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reminder: taking care of pull-ups is the masters job
|
||||||
|
*/
|
||||||
|
|
||||||
|
TinyWireS.begin(I2C_SLAVE_ADDRESS);
|
||||||
|
TinyWireS.onReceive(receiveEvent);
|
||||||
|
TinyWireS.onRequest(requestEvent);
|
||||||
|
|
||||||
|
|
||||||
|
// Whatever other setup routines ?
|
||||||
|
|
||||||
|
digitalWrite(3, LED_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// Run the scheduler - never returns.
|
||||||
|
sched.run();
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For TinyWireS
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
begin KEYWORD2
|
||||||
|
send KEYWORD2
|
||||||
|
available KEYWORD2
|
||||||
|
receive KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Instances (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
TinyWireS KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
||||||
|
|
@ -0,0 +1,666 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
USI TWI Slave driver.
|
||||||
|
|
||||||
|
Created by Donald R. Blake. donblake at worldnet.att.net
|
||||||
|
Adapted by Jochen Toppe, jochen.toppe at jtoee.com
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Created from Atmel source files for Application Note AVR312: Using the USI Module
|
||||||
|
as an I2C slave.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Change Activity:
|
||||||
|
|
||||||
|
Date Description
|
||||||
|
------ -------------
|
||||||
|
16 Mar 2007 Created.
|
||||||
|
27 Mar 2007 Added support for ATtiny261, 461 and 861.
|
||||||
|
26 Apr 2007 Fixed ACK of slave address on a read.
|
||||||
|
04 Jul 2007 Fixed USISIF in ATtiny45 def
|
||||||
|
12 Dev 2009 Added callback functions for data requests
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
includes
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
#include "usiTwiSlave.h"
|
||||||
|
//#include "../common/util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
device dependent defines
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#if defined( __AVR_ATtiny2313__ )
|
||||||
|
# define DDR_USI DDRB
|
||||||
|
# define PORT_USI PORTB
|
||||||
|
# define PIN_USI PINB
|
||||||
|
# define PORT_USI_SDA PB5
|
||||||
|
# define PORT_USI_SCL PB7
|
||||||
|
# define PIN_USI_SDA PINB5
|
||||||
|
# define PIN_USI_SCL PINB7
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AVR_ATtiny84__) | \
|
||||||
|
defined(__AVR_ATtiny44__)
|
||||||
|
# define DDR_USI DDRA
|
||||||
|
# define PORT_USI PORTA
|
||||||
|
# define PIN_USI PINA
|
||||||
|
# define PORT_USI_SDA PORTA6
|
||||||
|
# define PORT_USI_SCL PORTA4
|
||||||
|
# define PIN_USI_SDA PINA6
|
||||||
|
# define PIN_USI_SCL PINA4
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVF_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( __AVR_ATtiny25__ ) | \
|
||||||
|
defined( __AVR_ATtiny45__ ) | \
|
||||||
|
defined( __AVR_ATtiny85__ )
|
||||||
|
# define DDR_USI DDRB
|
||||||
|
# define PORT_USI PORTB
|
||||||
|
# define PIN_USI PINB
|
||||||
|
# define PORT_USI_SDA PB0
|
||||||
|
# define PORT_USI_SCL PB2
|
||||||
|
# define PIN_USI_SDA PINB0
|
||||||
|
# define PIN_USI_SCL PINB2
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVF_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( __AVR_ATtiny26__ )
|
||||||
|
# define DDR_USI DDRB
|
||||||
|
# define PORT_USI PORTB
|
||||||
|
# define PIN_USI PINB
|
||||||
|
# define PORT_USI_SDA PB0
|
||||||
|
# define PORT_USI_SCL PB2
|
||||||
|
# define PIN_USI_SDA PINB0
|
||||||
|
# define PIN_USI_SCL PINB2
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_STRT_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVF_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( __AVR_ATtiny261__ ) | \
|
||||||
|
defined( __AVR_ATtiny461__ ) | \
|
||||||
|
defined( __AVR_ATtiny861__ )
|
||||||
|
# define DDR_USI DDRB
|
||||||
|
# define PORT_USI PORTB
|
||||||
|
# define PIN_USI PINB
|
||||||
|
# define PORT_USI_SDA PB0
|
||||||
|
# define PORT_USI_SCL PB2
|
||||||
|
# define PIN_USI_SDA PINB0
|
||||||
|
# define PIN_USI_SCL PINB2
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVF_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( __AVR_ATmega165__ ) | \
|
||||||
|
defined( __AVR_ATmega325__ ) | \
|
||||||
|
defined( __AVR_ATmega3250__ ) | \
|
||||||
|
defined( __AVR_ATmega645__ ) | \
|
||||||
|
defined( __AVR_ATmega6450__ ) | \
|
||||||
|
defined( __AVR_ATmega329__ ) | \
|
||||||
|
defined( __AVR_ATmega3290__ )
|
||||||
|
# define DDR_USI DDRE
|
||||||
|
# define PORT_USI PORTE
|
||||||
|
# define PIN_USI PINE
|
||||||
|
# define PORT_USI_SDA PE5
|
||||||
|
# define PORT_USI_SCL PE4
|
||||||
|
# define PIN_USI_SDA PINE5
|
||||||
|
# define PIN_USI_SCL PINE4
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( __AVR_ATmega169__ )
|
||||||
|
# define DDR_USI DDRE
|
||||||
|
# define PORT_USI PORTE
|
||||||
|
# define PIN_USI PINE
|
||||||
|
# define PORT_USI_SDA PE5
|
||||||
|
# define PORT_USI_SCL PE4
|
||||||
|
# define PIN_USI_SDA PINE5
|
||||||
|
# define PIN_USI_SCL PINE4
|
||||||
|
# define USI_START_COND_INT USISIF
|
||||||
|
# define USI_START_VECTOR USI_START_vect
|
||||||
|
# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
functions implemented as macros
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#define SET_USI_TO_SEND_ACK( ) \
|
||||||
|
{ \
|
||||||
|
/* prepare ACK */ \
|
||||||
|
USIDR = 0; \
|
||||||
|
/* set SDA as output */ \
|
||||||
|
DDR_USI |= ( 1 << PORT_USI_SDA ); \
|
||||||
|
/* clear all interrupt flags, except Start Cond */ \
|
||||||
|
USISR = \
|
||||||
|
( 0 << USI_START_COND_INT ) | \
|
||||||
|
( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
||||||
|
( 1 << USIDC )| \
|
||||||
|
/* set USI counter to shift 1 bit */ \
|
||||||
|
( 0x0E << USICNT0 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_USI_TO_READ_ACK( ) \
|
||||||
|
{ \
|
||||||
|
/* set SDA as input */ \
|
||||||
|
DDR_USI &= ~( 1 << PORT_USI_SDA ); \
|
||||||
|
/* prepare ACK */ \
|
||||||
|
USIDR = 0; \
|
||||||
|
/* clear all interrupt flags, except Start Cond */ \
|
||||||
|
USISR = \
|
||||||
|
( 0 << USI_START_COND_INT ) | \
|
||||||
|
( 1 << USIOIF ) | \
|
||||||
|
( 1 << USIPF ) | \
|
||||||
|
( 1 << USIDC ) | \
|
||||||
|
/* set USI counter to shift 1 bit */ \
|
||||||
|
( 0x0E << USICNT0 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_USI_TO_TWI_START_CONDITION_MODE( ) \
|
||||||
|
{ \
|
||||||
|
USICR = \
|
||||||
|
/* enable Start Condition Interrupt, disable Overflow Interrupt */ \
|
||||||
|
( 1 << USISIE ) | ( 0 << USIOIE ) | \
|
||||||
|
/* set USI in Two-wire mode, no USI Counter overflow hold */ \
|
||||||
|
( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \
|
||||||
|
/* Shift Register Clock Source = External, positive edge */ \
|
||||||
|
/* 4-Bit Counter Source = external, both edges */ \
|
||||||
|
( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \
|
||||||
|
/* no toggle clock-port pin */ \
|
||||||
|
( 0 << USITC ); \
|
||||||
|
USISR = \
|
||||||
|
/* clear all interrupt flags, except Start Cond */ \
|
||||||
|
( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
||||||
|
( 1 << USIDC ) | ( 0x0 << USICNT0 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_USI_TO_SEND_DATA( ) \
|
||||||
|
{ \
|
||||||
|
/* set SDA as output */ \
|
||||||
|
DDR_USI |= ( 1 << PORT_USI_SDA ); \
|
||||||
|
/* clear all interrupt flags, except Start Cond */ \
|
||||||
|
USISR = \
|
||||||
|
( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
||||||
|
( 1 << USIDC) | \
|
||||||
|
/* set USI to shift out 8 bits */ \
|
||||||
|
( 0x0 << USICNT0 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_USI_TO_READ_DATA( ) \
|
||||||
|
{ \
|
||||||
|
/* set SDA as input */ \
|
||||||
|
DDR_USI &= ~( 1 << PORT_USI_SDA ); \
|
||||||
|
/* clear all interrupt flags, except Start Cond */ \
|
||||||
|
USISR = \
|
||||||
|
( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \
|
||||||
|
( 1 << USIPF ) | ( 1 << USIDC ) | \
|
||||||
|
/* set USI to shift out 8 bits */ \
|
||||||
|
( 0x0 << USICNT0 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define USI_RECEIVE_CALLBACK() \
|
||||||
|
{ \
|
||||||
|
if (usi_onReceiverPtr) \
|
||||||
|
{ \
|
||||||
|
if (usiTwiDataInReceiveBuffer()) \
|
||||||
|
{ \
|
||||||
|
usi_onReceiverPtr(usiTwiAmountDataInReceiveBuffer()); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ONSTOP_USI_RECEIVE_CALLBACK() \
|
||||||
|
{ \
|
||||||
|
if (USISR & ( 1 << USIPF )) \
|
||||||
|
{ \
|
||||||
|
USI_RECEIVE_CALLBACK(); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define USI_REQUEST_CALLBACK() \
|
||||||
|
{ \
|
||||||
|
USI_RECEIVE_CALLBACK(); \
|
||||||
|
if(usi_onRequestPtr) usi_onRequestPtr(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
typedef's
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
USI_SLAVE_CHECK_ADDRESS = 0x00,
|
||||||
|
USI_SLAVE_SEND_DATA = 0x01,
|
||||||
|
USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02,
|
||||||
|
USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03,
|
||||||
|
USI_SLAVE_REQUEST_DATA = 0x04,
|
||||||
|
USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05
|
||||||
|
} overflowState_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
local variables
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
static uint8_t slaveAddress;
|
||||||
|
static volatile overflowState_t overflowState;
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t rxBuf[ TWI_RX_BUFFER_SIZE ];
|
||||||
|
static volatile uint8_t rxHead;
|
||||||
|
static volatile uint8_t rxTail;
|
||||||
|
|
||||||
|
static uint8_t txBuf[ TWI_TX_BUFFER_SIZE ];
|
||||||
|
static volatile uint8_t txHead;
|
||||||
|
static volatile uint8_t txTail;
|
||||||
|
|
||||||
|
// data requested callback
|
||||||
|
void (*_onTwiDataRequest)(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
local functions
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// flushes the TWI buffers
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
flushTwiBuffers(
|
||||||
|
void
|
||||||
|
)
|
||||||
|
{
|
||||||
|
rxTail = 0;
|
||||||
|
rxHead = 0;
|
||||||
|
txTail = 0;
|
||||||
|
txHead = 0;
|
||||||
|
} // end flushTwiBuffers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
public functions
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// initialise USI for TWI slave mode
|
||||||
|
|
||||||
|
void
|
||||||
|
usiTwiSlaveInit(
|
||||||
|
uint8_t ownAddress
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
flushTwiBuffers( );
|
||||||
|
|
||||||
|
slaveAddress = ownAddress;
|
||||||
|
|
||||||
|
// In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL
|
||||||
|
// low when a start condition is detected or a counter overflow (only
|
||||||
|
// for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released
|
||||||
|
// by the ISRs (USI_START_vect and USI_OVERFLOW_vect).
|
||||||
|
|
||||||
|
// Set SCL and SDA as output
|
||||||
|
DDR_USI |= ( 1 << PORT_USI_SCL ) | ( 1 << PORT_USI_SDA );
|
||||||
|
|
||||||
|
// set SCL high
|
||||||
|
PORT_USI |= ( 1 << PORT_USI_SCL );
|
||||||
|
|
||||||
|
// set SDA high
|
||||||
|
PORT_USI |= ( 1 << PORT_USI_SDA );
|
||||||
|
|
||||||
|
// Set SDA as input
|
||||||
|
DDR_USI &= ~( 1 << PORT_USI_SDA );
|
||||||
|
|
||||||
|
USICR =
|
||||||
|
// enable Start Condition Interrupt
|
||||||
|
( 1 << USISIE ) |
|
||||||
|
// disable Overflow Interrupt
|
||||||
|
( 0 << USIOIE ) |
|
||||||
|
// set USI in Two-wire mode, no USI Counter overflow hold
|
||||||
|
( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
|
||||||
|
// Shift Register Clock Source = external, positive edge
|
||||||
|
// 4-Bit Counter Source = external, both edges
|
||||||
|
( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
|
||||||
|
// no toggle clock-port pin
|
||||||
|
( 0 << USITC );
|
||||||
|
|
||||||
|
// clear all interrupt flags and reset overflow counter
|
||||||
|
|
||||||
|
USISR = ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | ( 1 << USIDC );
|
||||||
|
|
||||||
|
} // end usiTwiSlaveInit
|
||||||
|
|
||||||
|
|
||||||
|
bool usiTwiDataInTransmitBuffer(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
// return 0 (false) if the receive buffer is empty
|
||||||
|
return txHead != txTail;
|
||||||
|
|
||||||
|
} // end usiTwiDataInTransmitBuffer
|
||||||
|
|
||||||
|
|
||||||
|
// put data in the transmission buffer, wait if buffer is full
|
||||||
|
|
||||||
|
void
|
||||||
|
usiTwiTransmitByte(
|
||||||
|
uint8_t data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t tmphead;
|
||||||
|
|
||||||
|
// calculate buffer index
|
||||||
|
tmphead = ( txHead + 1 ) & TWI_TX_BUFFER_MASK;
|
||||||
|
|
||||||
|
// wait for free space in buffer
|
||||||
|
while ( tmphead == txTail );
|
||||||
|
|
||||||
|
// store data in buffer
|
||||||
|
txBuf[ tmphead ] = data;
|
||||||
|
|
||||||
|
// store new index
|
||||||
|
txHead = tmphead;
|
||||||
|
|
||||||
|
} // end usiTwiTransmitByte
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// return a byte from the receive buffer, wait if buffer is empty
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
usiTwiReceiveByte(
|
||||||
|
void
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
// wait for Rx data
|
||||||
|
while ( rxHead == rxTail );
|
||||||
|
|
||||||
|
// calculate buffer index
|
||||||
|
rxTail = ( rxTail + 1 ) & TWI_RX_BUFFER_MASK;
|
||||||
|
|
||||||
|
// return data from the buffer.
|
||||||
|
return rxBuf[ rxTail ];
|
||||||
|
|
||||||
|
} // end usiTwiReceiveByte
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// check if there is data in the receive buffer
|
||||||
|
|
||||||
|
bool
|
||||||
|
usiTwiDataInReceiveBuffer(
|
||||||
|
void
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
// return 0 (false) if the receive buffer is empty
|
||||||
|
return rxHead != rxTail;
|
||||||
|
|
||||||
|
} // end usiTwiDataInReceiveBuffer
|
||||||
|
|
||||||
|
uint8_t usiTwiAmountDataInReceiveBuffer(void)
|
||||||
|
{
|
||||||
|
if (rxHead == rxTail)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (rxHead < rxTail)
|
||||||
|
{
|
||||||
|
// Is there a better way ?
|
||||||
|
return ((int8_t)rxHead - (int8_t)rxTail) + TWI_RX_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
return rxHead - rxTail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
USI Start Condition ISR
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
ISR( USI_START_VECTOR )
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
// This triggers on second write, but claims to the callback there is only *one* byte in buffer
|
||||||
|
ONSTOP_USI_RECEIVE_CALLBACK();
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
// This triggers on second write, but claims to the callback there is only *one* byte in buffer
|
||||||
|
USI_RECEIVE_CALLBACK();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// set default starting conditions for new TWI package
|
||||||
|
overflowState = USI_SLAVE_CHECK_ADDRESS;
|
||||||
|
|
||||||
|
// set SDA as input
|
||||||
|
DDR_USI &= ~( 1 << PORT_USI_SDA );
|
||||||
|
|
||||||
|
// wait for SCL to go low to ensure the Start Condition has completed (the
|
||||||
|
// start detector will hold SCL low ) - if a Stop Condition arises then leave
|
||||||
|
// the interrupt to prevent waiting forever - don't use USISR to test for Stop
|
||||||
|
// Condition as in Application Note AVR312 because the Stop Condition Flag is
|
||||||
|
// going to be set from the last TWI sequence
|
||||||
|
while (
|
||||||
|
// SCL his high
|
||||||
|
( PIN_USI & ( 1 << PIN_USI_SCL ) ) &&
|
||||||
|
// and SDA is low
|
||||||
|
!( ( PIN_USI & ( 1 << PIN_USI_SDA ) ) )
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if ( !( PIN_USI & ( 1 << PIN_USI_SDA ) ) )
|
||||||
|
{
|
||||||
|
|
||||||
|
// a Stop Condition did not occur
|
||||||
|
|
||||||
|
USICR =
|
||||||
|
// keep Start Condition Interrupt enabled to detect RESTART
|
||||||
|
( 1 << USISIE ) |
|
||||||
|
// enable Overflow Interrupt
|
||||||
|
( 1 << USIOIE ) |
|
||||||
|
// set USI in Two-wire mode, hold SCL low on USI Counter overflow
|
||||||
|
( 1 << USIWM1 ) | ( 1 << USIWM0 ) |
|
||||||
|
// Shift Register Clock Source = External, positive edge
|
||||||
|
// 4-Bit Counter Source = external, both edges
|
||||||
|
( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
|
||||||
|
// no toggle clock-port pin
|
||||||
|
( 0 << USITC );
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// a Stop Condition did occur
|
||||||
|
|
||||||
|
USICR =
|
||||||
|
// enable Start Condition Interrupt
|
||||||
|
( 1 << USISIE ) |
|
||||||
|
// disable Overflow Interrupt
|
||||||
|
( 0 << USIOIE ) |
|
||||||
|
// set USI in Two-wire mode, no USI Counter overflow hold
|
||||||
|
( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
|
||||||
|
// Shift Register Clock Source = external, positive edge
|
||||||
|
// 4-Bit Counter Source = external, both edges
|
||||||
|
( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
|
||||||
|
// no toggle clock-port pin
|
||||||
|
( 0 << USITC );
|
||||||
|
|
||||||
|
} // end if
|
||||||
|
|
||||||
|
USISR =
|
||||||
|
// clear interrupt flags - resetting the Start Condition Flag will
|
||||||
|
// release SCL
|
||||||
|
( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) |
|
||||||
|
( 1 << USIPF ) |( 1 << USIDC ) |
|
||||||
|
// set USI to sample 8 bits (count 16 external SCL pin toggles)
|
||||||
|
( 0x0 << USICNT0);
|
||||||
|
|
||||||
|
|
||||||
|
} // end ISR( USI_START_VECTOR )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
USI Overflow ISR
|
||||||
|
|
||||||
|
Handles all the communication.
|
||||||
|
|
||||||
|
Only disabled when waiting for a new Start Condition.
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
volatile uint8_t last_unknown_address = 0;
|
||||||
|
ISR( USI_OVERFLOW_VECTOR )
|
||||||
|
{
|
||||||
|
|
||||||
|
switch ( overflowState )
|
||||||
|
{
|
||||||
|
|
||||||
|
// Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK,
|
||||||
|
// else reset USI
|
||||||
|
case USI_SLAVE_CHECK_ADDRESS:
|
||||||
|
if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) )
|
||||||
|
{
|
||||||
|
// callback
|
||||||
|
if(_onTwiDataRequest) _onTwiDataRequest();
|
||||||
|
if ( USIDR & 0x01 )
|
||||||
|
{
|
||||||
|
overflowState = USI_SLAVE_SEND_DATA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
overflowState = USI_SLAVE_REQUEST_DATA;
|
||||||
|
} // end if
|
||||||
|
SET_USI_TO_SEND_ACK( );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last_unknown_address = USIDR >> 1;
|
||||||
|
SET_USI_TO_TWI_START_CONDITION_MODE( );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Master write data mode: check reply and goto USI_SLAVE_SEND_DATA if OK,
|
||||||
|
// else reset USI
|
||||||
|
case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA:
|
||||||
|
if ( USIDR )
|
||||||
|
{
|
||||||
|
// if NACK, the master does not want more data
|
||||||
|
SET_USI_TO_TWI_START_CONDITION_MODE( );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// from here we just drop straight into USI_SLAVE_SEND_DATA if the
|
||||||
|
// master sent an ACK
|
||||||
|
|
||||||
|
// copy data from buffer to USIDR and set USI to shift byte
|
||||||
|
// next USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA
|
||||||
|
case USI_SLAVE_SEND_DATA:
|
||||||
|
USI_REQUEST_CALLBACK();
|
||||||
|
// Get data from Buffer
|
||||||
|
if ( txHead != txTail )
|
||||||
|
{
|
||||||
|
txTail = ( txTail + 1 ) & TWI_TX_BUFFER_MASK;
|
||||||
|
USIDR = txBuf[ txTail ];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the buffer is empty
|
||||||
|
SET_USI_TO_READ_ACK( ); // This might be neccessary sometimes see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=805227#805227
|
||||||
|
SET_USI_TO_TWI_START_CONDITION_MODE( );
|
||||||
|
return;
|
||||||
|
} // end if
|
||||||
|
overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA;
|
||||||
|
SET_USI_TO_SEND_DATA( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// set USI to sample reply from master
|
||||||
|
// next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA
|
||||||
|
case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA:
|
||||||
|
overflowState = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA;
|
||||||
|
SET_USI_TO_READ_ACK( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Master read data mode: set USI to sample data from master, next
|
||||||
|
// USI_SLAVE_GET_DATA_AND_SEND_ACK
|
||||||
|
case USI_SLAVE_REQUEST_DATA:
|
||||||
|
overflowState = USI_SLAVE_GET_DATA_AND_SEND_ACK;
|
||||||
|
SET_USI_TO_READ_DATA( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// copy data from USIDR and send ACK
|
||||||
|
// next USI_SLAVE_REQUEST_DATA
|
||||||
|
case USI_SLAVE_GET_DATA_AND_SEND_ACK:
|
||||||
|
// put data into buffer
|
||||||
|
// Not necessary, but prevents warnings
|
||||||
|
rxHead = ( rxHead + 1 ) & TWI_RX_BUFFER_MASK;
|
||||||
|
// check buffer size
|
||||||
|
if (rxHead == rxTail) {
|
||||||
|
// overrun
|
||||||
|
rxHead = (rxHead + TWI_RX_BUFFER_SIZE - 1) & TWI_RX_BUFFER_MASK;
|
||||||
|
} else {
|
||||||
|
rxBuf[ rxHead ] = USIDR;
|
||||||
|
}
|
||||||
|
// next USI_SLAVE_REQUEST_DATA
|
||||||
|
overflowState = USI_SLAVE_REQUEST_DATA;
|
||||||
|
SET_USI_TO_SEND_ACK( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
} // end switch
|
||||||
|
|
||||||
|
} // end ISR( USI_OVERFLOW_VECTOR )
|
@ -0,0 +1,93 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
Header file for the USI TWI Slave driver.
|
||||||
|
|
||||||
|
Created by Donald R. Blake
|
||||||
|
donblake at worldnet.att.net
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Created from Atmel source files for Application Note AVR312: Using the USI Module
|
||||||
|
as an I2C slave.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Change Activity:
|
||||||
|
|
||||||
|
Date Description
|
||||||
|
------ -------------
|
||||||
|
15 Mar 2007 Created.
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _USI_TWI_SLAVE_H_
|
||||||
|
#define _USI_TWI_SLAVE_H_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
includes
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
prototypes
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
void usiTwiSlaveInit( uint8_t );
|
||||||
|
void usiTwiTransmitByte( uint8_t );
|
||||||
|
uint8_t usiTwiReceiveByte( void );
|
||||||
|
bool usiTwiDataInReceiveBuffer( void );
|
||||||
|
void (*_onTwiDataRequest)(void);
|
||||||
|
bool usiTwiDataInTransmitBuffer(void);
|
||||||
|
uint8_t usiTwiAmountDataInReceiveBuffer(void);
|
||||||
|
// on_XXX handler pointers
|
||||||
|
void (*usi_onRequestPtr)(void);
|
||||||
|
void (*usi_onReceiverPtr)(uint8_t);
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
|
||||||
|
driver buffer definitions
|
||||||
|
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
// permitted RX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256
|
||||||
|
|
||||||
|
#define TWI_RX_BUFFER_SIZE ( 32 )
|
||||||
|
#define TWI_RX_BUFFER_MASK ( TWI_RX_BUFFER_SIZE - 1 )
|
||||||
|
|
||||||
|
#if ( TWI_RX_BUFFER_SIZE & TWI_RX_BUFFER_MASK )
|
||||||
|
# error TWI RX buffer size is not a power of 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// permitted TX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256
|
||||||
|
|
||||||
|
#define TWI_TX_BUFFER_SIZE ( 16 )
|
||||||
|
#define TWI_TX_BUFFER_MASK ( TWI_TX_BUFFER_SIZE - 1 )
|
||||||
|
|
||||||
|
#if ( TWI_TX_BUFFER_SIZE & TWI_TX_BUFFER_MASK )
|
||||||
|
# error TWI TX buffer size is not a power of 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ifndef _USI_TWI_SLAVE_H_
|
Loading…
Reference in new issue