-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathattiny.ino
More file actions
168 lines (150 loc) · 5.26 KB
/
attiny.ino
File metadata and controls
168 lines (150 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#define I2C_SLAVE_ADDRESS 0x3D // the 7-bit address (remember to change this when adapting this example)
/**
* 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)
*/
// 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[] =
{
// High byte first, then low byte (we will increment i2c_regs[1] more)
0x00,
0x00,
};
// Tracks the current register pointer position
volatile byte reg_position;
const byte reg_size = sizeof(i2c_regs);
volatile int16_t ticks = 0;
volatile uint8_t oldvalues;
ISR( PCINT0_vect ) {
uint8_t newvalues = (PINB & 0b00011000);
// If the interrupt was on pin 3...
if ( (newvalues ^ oldvalues) & 0b00001000 ) {
switch (newvalues) {
case 0b00000000: ticks--; break;
case 0b00011000: ticks--; break;
case 0b00001000: ticks++; break;
case 0b00010000: ticks++; break;
}
} else {
switch (newvalues) {
case 0b00000000: ticks++; break;
case 0b00011000: ticks++; break;
case 0b00001000: ticks--; break;
case 0b00010000: ticks--; break;
}
}
oldvalues = newvalues;
i2c_regs[0] = ticks >> 8;
i2c_regs[1] = ticks & 0x00FF;
}
/**
* 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++;
if (reg_position >= reg_size)
{
reg_position = 0;
ticks = 0;
i2c_regs[0] = 0;
i2c_regs[1] = 0;
}
}
/**
* 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--)
{
TinyWireS.receive();
reg_position++;
if (reg_position >= reg_size)
{
reg_position = 0;
}
}
}
void setup()
{
/**
* Reminder: taking care of pull-ups is the masters job
*/
TinyWireS.begin(I2C_SLAVE_ADDRESS);
TinyWireS.onReceive(receiveEvent);
TinyWireS.onRequest(requestEvent);
// Make the pins inputs and pullups
DDRB = ~(0b00011000);
PORTB |= 0b00011000;
// Whatever other setup routines ?
GIMSK = 0b00100000; // turns on pin change interrupts
PCMSK |= 0b00011000;
sei();
}
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();
}