Simplification of I2C communication

Added backlight and contrast features
Added fixed TinyWireS as a needed library
Extended example to use new features
master
Philipp Klaus 12 years ago
parent bd29ac70ca
commit 4a4e1f7e65

@ -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);
}

@ -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
lcd.setCursor(col, row);
break;
case LCD_SHOWFIRMWAREREVISION:

@ -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

@ -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_

@ -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);
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){}

@ -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[]);
@ -110,9 +111,11 @@ public:
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

Loading…
Cancel
Save