diff --git a/examples/tinyLCD_I2C_Test/tinyLCD_I2C_Test.ino b/examples/tinyLCD_I2C_Test/tinyLCD_I2C_Test.ino index a7354c1..e3fd9f4 100644 --- a/examples/tinyLCD_I2C_Test/tinyLCD_I2C_Test.ino +++ b/examples/tinyLCD_I2C_Test/tinyLCD_I2C_Test.ino @@ -17,6 +17,29 @@ void setup() lcd.showFirmware(); delay(1500); lcd.clear(); + lcd.print("Backlight"); + for (int i = 255; i >= 0; i--) { + lcd.setBacklight(i); + delay(10); + } + for (int i = 0; i < 256; i++) { + lcd.setBacklight(i); + delay(10); + } + delay(1000); + lcd.clear(); + lcd.print("Contrast"); + for (int i = 0; i < 256; i++) { + lcd.setContrast(i); + delay(10); + } + for (int i = 255; i >= 0; i--) { + lcd.setContrast(i); + delay(10); + } + lcd.setContrast(10); + delay(1000); + lcd.clear(); lcd.print("Uptime now (us):"); } @@ -24,5 +47,5 @@ void loop() { lcd.setCursor(0,1); lcd.print(millis()/1); lcd.print(" ms"); - delay(100); + delay(10); } diff --git a/firmware/tinyLCD_I2C/tinyLCD_I2C.ino b/firmware/tinyLCD_I2C/tinyLCD_I2C.ino index 0eea42a..86a6793 100644 --- a/firmware/tinyLCD_I2C/tinyLCD_I2C.ino +++ b/firmware/tinyLCD_I2C/tinyLCD_I2C.ino @@ -15,20 +15,22 @@ #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 -#define LCD_SETDDRAMADDR 0x80 #define LCD_SETDIMENSION 0x9f #define LCD_SETCURSOR 0x9e #define LCD_SHOWFIRMWAREREVISION 0x9d #define LCD_SETADDRESS 0x9c +#define LCD_SETCONTRAST 0x9b // flags for backlight control #define LCD_BACKLIGHT 0x81 #define LCD_NOBACKLIGHT 0x80 +#define LCD_SETBACKLIGHT 0x82 // I2C default address #define I2C_SLAVE_ADDRESS 0x50 #define CONTRAST_PIN 2 +#define BACKLIGHT_PIN 3 #define EEBASE_ADDR 24 @@ -68,7 +70,9 @@ void setup() { TinyWireS.begin(slave_address); TinyWireS.onReceive(receive_event); //TinyWireS.onRequest(requestEvent); + analogWrite(CONTRAST_PIN, 10); + analogWrite(BACKLIGHT_PIN, 255); lcd.begin(16,2); lcd.clear(); //test_lcd(); @@ -108,59 +112,47 @@ void receive_event(uint8_t howMany) { } void command_byte(char c, byte bytesInBuffer) { - uint8_t cols, rows, col, row, addr; + uint8_t col, row, addr, val; + if (c & 0xC0 == LCD_SETCGRAMADDR) { + // construct character + uint8_t cdata[8]; + uint8_t i; + for (i = 0; i < 8; i++) { + cdata[i] = TinyWireS.receive(); + } + lcd.createChar((c & 0x38) >> 3, cdata); + } else if (c & 0xF8 == LCD_DISPLAYCONTROL || c & 0xF0 == LCD_CURSORSHIFT || c & 0xFC == LCD_ENTRYMODESET) { + lcd.command(c); + } switch (c) { case LCD_CLEARDISPLAY: - #ifdef DBGME - lcd.print("Clear Display!"); - #endif lcd.clear(); break; case LCD_RETURNHOME: - #ifdef DBGME - lcd.print("Return Home!"); - #endif lcd.home(); break; case LCD_BACKLIGHT: - #ifdef DBGME - lcd.print("Backlight!"); - #endif + analogWrite(BACKLIGHT_PIN, 255); break; case LCD_NOBACKLIGHT: - #ifdef DBGME - lcd.print("No Backlight!"); - #endif + analogWrite(BACKLIGHT_PIN, 0); + break; + case LCD_SETBACKLIGHT: + val = TinyWireS.receive(); + analogWrite(BACKLIGHT_PIN, val); + break; + case LCD_SETCONTRAST: + val = TinyWireS.receive(); + analogWrite(CONTRAST_PIN, val); break; case LCD_SETDIMENSION: - TinyWireS.receive(); - cols = TinyWireS.receive(); - TinyWireS.receive(); - rows = TinyWireS.receive(); - #ifdef DBGME - lcd.print("Set Dim:"); - lcd.print(int(cols)); - lcd.print(" / "); - lcd.print(int(rows)); - tws_delay(660); - #endif - lcd_begin(cols, rows); + col = TinyWireS.receive(); + row = TinyWireS.receive(); + lcd_begin(col, row); break; case LCD_SETCURSOR: - if ( TinyWireS.receive() == 0 ) { - col = TinyWireS.receive(); - if ( TinyWireS.receive() == 0 ) { - row = TinyWireS.receive(); - } - } - #ifdef DBGME - lcd.print("Set Cursor:"); - lcd.setCursor(0,1); - lcd.print(int(col)); - lcd.print(" - "); - lcd.print(int(row)); - tws_delay(660); - #endif + col = TinyWireS.receive(); + row = TinyWireS.receive(); lcd.setCursor(col, row); break; case LCD_SHOWFIRMWAREREVISION: diff --git a/libraries/TinyWireS/TinyWireS.cpp b/libraries/TinyWireS/TinyWireS.cpp new file mode 100644 index 0000000..88fbcdb --- /dev/null +++ b/libraries/TinyWireS/TinyWireS.cpp @@ -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 + #include "usiTwiSlave.h" + #include + } + +#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(); + diff --git a/libraries/TinyWireS/TinyWireS.h b/libraries/TinyWireS/TinyWireS.h new file mode 100644 index 0000000..ccef042 --- /dev/null +++ b/libraries/TinyWireS/TinyWireS.h @@ -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 + + +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 + diff --git a/libraries/TinyWireS/avr_usi_i2c_master.pdf b/libraries/TinyWireS/avr_usi_i2c_master.pdf new file mode 100644 index 0000000..dc8f460 Binary files /dev/null and b/libraries/TinyWireS/avr_usi_i2c_master.pdf differ diff --git a/libraries/TinyWireS/avr_usi_i2c_slave.pdf b/libraries/TinyWireS/avr_usi_i2c_slave.pdf new file mode 100644 index 0000000..e4704c7 Binary files /dev/null and b/libraries/TinyWireS/avr_usi_i2c_slave.pdf differ diff --git a/libraries/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino b/libraries/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino new file mode 100644 index 0000000..2d5de94 --- /dev/null +++ b/libraries/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino @@ -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 +// 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(); +} diff --git a/libraries/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino b/libraries/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino new file mode 100644 index 0000000..403eee1 --- /dev/null +++ b/libraries/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino @@ -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 +// 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 +#include + +// 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(); +} diff --git a/libraries/TinyWireS/keywords.txt b/libraries/TinyWireS/keywords.txt new file mode 100644 index 0000000..ccc0b46 --- /dev/null +++ b/libraries/TinyWireS/keywords.txt @@ -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) +####################################### + diff --git a/libraries/TinyWireS/usiTwiSlave.c b/libraries/TinyWireS/usiTwiSlave.c new file mode 100644 index 0000000..4fd3507 --- /dev/null +++ b/libraries/TinyWireS/usiTwiSlave.c @@ -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 +#include + +#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 ) diff --git a/libraries/TinyWireS/usiTwiSlave.h b/libraries/TinyWireS/usiTwiSlave.h new file mode 100644 index 0000000..879d3a6 --- /dev/null +++ b/libraries/TinyWireS/usiTwiSlave.h @@ -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 + + + +/******************************************************************************** + + 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_ diff --git a/src/tinyLCD_I2C/tinyLCD_I2C.cpp b/src/tinyLCD_I2C/tinyLCD_I2C.cpp index 110740c..67642d9 100644 --- a/src/tinyLCD_I2C/tinyLCD_I2C.cpp +++ b/src/tinyLCD_I2C/tinyLCD_I2C.cpp @@ -15,7 +15,6 @@ tinyLCD_I2C::tinyLCD_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows) _Addr = lcd_addr; _cols = lcd_cols; _rows = lcd_rows; - _backlightval = LCD_NOBACKLIGHT; } void tinyLCD_I2C::init(){ @@ -30,44 +29,33 @@ void tinyLCD_I2C::init_priv() void tinyLCD_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { displayDimension(cols, lines); - delayMicroseconds(550*1000); clear(); - delayMicroseconds(850*1000); home(); - delayMicroseconds(650*1000); } void tinyLCD_I2C::displayDimension(uint8_t cols, uint8_t lines) { - command(LCD_SETDIMENSION); - command(cols); - command(lines); + uint8_t data[2]; + data[0] = cols; + data[1] = lines; + send(LCD_SETDIMENSION, 0, 2, data); } // clear display void tinyLCD_I2C::clear(){ command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero - delayMicroseconds(400); // this command takes a long time! } // jump to 0,0 void tinyLCD_I2C::home(){ command(LCD_RETURNHOME); // set cursor position to zero - delayMicroseconds(400); // this command takes a long time! } // set cursor position -void tinyLCD_I2C::setCursor(uint8_t col, uint8_t row){ - /* int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; - if ( row > _numlines ) { - row = _numlines-1; // we count rows starting w/0 - } - command(LCD_SETDDRAMADDR | (col + row_offsets[row])); */ - command(LCD_SETCURSOR); - delayMicroseconds(2500); - command(col); - delayMicroseconds(2500); - command(row); - delayMicroseconds(2500); +void tinyLCD_I2C::setCursor(uint8_t col, uint8_t row) { + uint8_t data[2]; + data[0] = col; + data[1] = row; + send(LCD_SETCURSOR, 0, 2, data); } @@ -137,24 +125,29 @@ void tinyLCD_I2C::noAutoscroll(void) { // with custom characters void tinyLCD_I2C::createChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; // we only have 8 locations 0-7 - command(LCD_SETCGRAMADDR | (location << 3)); - for (int i=0; i<8; i++) { - write(charmap[i]); - } + send(LCD_SETCGRAMADDR | (location << 3), 0, 8, charmap); } // Turn the (optional) backlight off/on void tinyLCD_I2C::noBacklight(void) { - _backlightval=LCD_NOBACKLIGHT; command(LCD_NOBACKLIGHT); } void tinyLCD_I2C::backlight(void) { - _backlightval=LCD_BACKLIGHT; command(LCD_BACKLIGHT); } +void tinyLCD_I2C::setBacklight(uint8_t value) { + send(LCD_SETBACKLIGHT, 0, 1, &value); +} + +void tinyLCD_I2C::setContrast(uint8_t new_val) { + send(LCD_SETCONTRAST, 0, 1, &new_val); +} +void tinyLCD_I2C::changeI2CAddress(uint8_t new_address) { + send(LCD_SETADDRESS, 0, 1, &new_address); +} /*********** mid level commands, for sending data/cmds */ @@ -172,7 +165,7 @@ inline size_t tinyLCD_I2C::write(uint8_t value) { /************ low level data pushing commands **********/ // write either command or data -void tinyLCD_I2C::send(uint8_t value, uint8_t mode) { +void tinyLCD_I2C::send(uint8_t value, uint8_t mode, uint8_t len, uint8_t *data) { int ret = 0; uint8_t repeat = 4; do { @@ -181,11 +174,16 @@ void tinyLCD_I2C::send(uint8_t value, uint8_t mode) { Wire.write(mode); } Wire.write((int)(value)); + uint8_t i = 0; + while (len--) { + Wire.write(data[i++]); + } ret = Wire.endTransmission(); } while (ret && repeat--); } + // Alias functions void tinyLCD_I2C::cursor_on(){ @@ -208,13 +206,6 @@ void tinyLCD_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){ createChar(char_num, rows); } -void tinyLCD_I2C::setBacklight(uint8_t new_val){ - if(new_val){ - backlight(); // turn backlight on - }else{ - noBacklight(); // turn backlight off - } -} void tinyLCD_I2C::printstr(const char c[]){ //This function is not identical to the function used for "real" I2C displays @@ -236,5 +227,4 @@ uint8_t tinyLCD_I2C::keypad (){return 0;} uint8_t tinyLCD_I2C::init_bargraph(uint8_t graphtype){return 0;} void tinyLCD_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end){} void tinyLCD_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end){} -void tinyLCD_I2C::setContrast(uint8_t new_val){} diff --git a/src/tinyLCD_I2C/tinyLCD_I2C.h b/src/tinyLCD_I2C/tinyLCD_I2C.h index 59b4029..82e5f1a 100644 --- a/src/tinyLCD_I2C/tinyLCD_I2C.h +++ b/src/tinyLCD_I2C/tinyLCD_I2C.h @@ -24,11 +24,10 @@ #define LCD_SETDIMENSION 0x9f #define LCD_SETCURSOR 0x9e #define LCD_SHOWFIRMWAREREVISION 0x9d -#define LCD_SETDIMENSION 0x9f -#define LCD_SETCURSOR 0x9e +#define LCD_SETADDRESS 0x9c +#define LCD_SETCONTRAST 0x9b #define LCD_AUTOSCROLL 0x17 #define LCD_NOAUTOSCROLL 0x1a -#define LCD_SHOWFIRMWAREREVISION 0x1b // flags for display entry mode @@ -58,6 +57,7 @@ // flags for backlight control #define LCD_BACKLIGHT 0x81 #define LCD_NOBACKLIGHT 0x80 +#define LCD_SETBACKLIGHT 0x82 #define En B00000100 // Enable bit #define Rw B00000010 // Read/Write bit @@ -88,6 +88,7 @@ public: void shiftDecrement(); void noBacklight(); void backlight(); + void setContrast(uint8_t new_val); void autoscroll(); void noAutoscroll(); void createChar(uint8_t, uint8_t[]); @@ -109,10 +110,12 @@ public: void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar() void printstr(const char[]); + + ////special API + void changeI2CAddress(uint8_t new_address); ////Unsupported API functions (not implemented in this library) uint8_t status(); -void setContrast(uint8_t new_val); uint8_t keypad(); void setDelay(int,int); void on(); @@ -124,7 +127,7 @@ void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixe private: void init_priv(); - void send(uint8_t, uint8_t); + void send(uint8_t cmd, uint8_t value, uint8_t len = 0, uint8_t *data = NULL); //void write4bits(uint8_t); //void expanderWrite(uint8_t); //void pulseEnable(uint8_t); @@ -135,7 +138,6 @@ private: uint8_t _numlines; uint8_t _cols; uint8_t _rows; - uint8_t _backlightval; }; #endif