-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
300 lines (246 loc) · 11.3 KB
/
main.cpp
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
//======================================================================
// main.c
//
// demonstrate twi0 usage, both master and slave communicating on same
// device, tested with an ATtiny416 Xplained Nano (led is PB5, inverted)
//======================================================================
#include "Twis.hpp"
#include "Twim.hpp"
/*------------------------------------------------------------------------------
wait - uses _delay_ms (so we can have a simple callable delay with
variable runtime values)
------------------------------------------------------------------------------*/
static void
waitMS (u16 ms){ while( ms-- ) _delay_ms(1); }
/*------------------------------------------------------------------------------
Led
------------------------------------------------------------------------------*/
class
Led {
PORT_t& port_;
u8 pinbm_;
public:
Led (PORT_t& port, u8 pin, bool invert = false)
: port_(port), pinbm_(1<<pin)
{
(&port.PIN0CTRL)[pin] = invert ? 0x80 : 0; //enable invert if needed
port_.DIRSET = pinbm_; //output
}
//constructor invert took care of high/low state needed for
//on/off, so user can now simply use on/off
auto
on () { port_.OUTSET = pinbm_; }
auto
off () { port_.OUTCLR = pinbm_; }
auto
toggle () { port_.OUTTGL = pinbm_; }
}; //Led
/*------------------------------------------------------------------------------
Blinker I2C device - example i2c device on this mcu
twi slave address 0x51
our slave device has registers from address 0x00 to 0x07 named ram,
which can be used to store byte values
our slave device has a register at address 0x08 named onTime,
and an address of 0x09 named offTime
the onTime value will determine the led blink on time-
0 = off
1 = 10ms on
255 = 2550ms on
the offTime value will determine the led off time-
0 = off
1 = 10ms off
255 = 2550ms off
register address will auto increment after a write or read (if write
was not a register address write), will roll over from end to beginning
to write a new value to onTime or offTime
write 0x08 or 0x09, write new value
to read the current value of onTime or offTime
write 0x08 or 0x09, read value
to write a new value to onTime and offTime
write 0x08, write new onTime value, write new offTime value
to read the current value of onTime and offTime
write 0x08, read onTime value, read offTime value
------------------------------------------------------------------------------*/
class
BlinkerI2C {
//device registers 0-9
u8 ram_[8];
u8 onTime_; //[8], MS x10
u8 offTime_; //[9], MS x10
u8* regPtr_; //current device register address (pointer)
Twis& twi_; //Twis instance
const u8 myAddress_;//slave address
Led& led_; //led to blink
//so static callback can access this object, store object in static array
//in this case we only allow 1 instance, but could have 2 instances
//if device also has TWI1 available
#if defined(TWI1)
static inline BlinkerI2C* instance_[2];
#else
static inline BlinkerI2C* instance_[1];
#endif
public:
BlinkerI2C (Twis& twis, u8 myAddress, Led& led)
: twi_(twis), myAddress_(myAddress), led_(led)
{
instance_[twis.twiN()] = this;
on();
}
void
on () { twi_.on( myAddress_, twiCallback ); }
auto
off () { twi_.off(); }
static bool
twiCallback (Twis& twis, Twis::IRQ_STATE state, u8 statusReg)
{
static bool is1stWrite;
BlinkerI2C& my{ *instance_[twis.twiN()] }; //get our object, as a reference
//keep regPtr inside the range of registers that can write/read
if( my.regPtr_ > &my.offTime_ or
my.regPtr_ < &my.ram_[0] ) my.regPtr_ = &my.ram_[0];
bool ret = true; //assume ok to continue transaction
switch( state ) {
//check address here, could be general call (0) or maybe we
//have a second address or address mask
case Twis::TWIS_ADDRESSED:
if( twis.lastAddress() != my.myAddress_ ) ret = false; //for us?
else is1stWrite = true; //yes, expect a register address write
break;
case Twis::TWIS_MREAD: //master read, so slave writes
twis.write( *my.regPtr_++ );
break;
case Twis::TWIS_MWRITE: { //parens so we can create a var inside case without error
u8 v = twis.read();
if( is1stWrite ){ //if first write, is a register address write
is1stWrite = false;
my.regPtr_ = &my.ram_[v]; //ram is base address 0, so v is offset from that
break; //regPtr will be validated in the next isr
}
//else is a register write
*my.regPtr_++ = v;
}
break;
case Twis::TWIS_STOPPED:
case Twis::TWIS_ERROR:
ret = false;
break;
}
return ret;
}
void
run (u8 n) //do what we are designed to do- blink led
{
for( u8 i = 0; i < n; i++ ){
led_.on(); waitMS( onTime_ * 10 );
led_.off(); waitMS( offTime_ * 10 );
}
}
};
/*------------------------------------------------------------------------------
BlinkerCmd - twi master communications to slave device
------------------------------------------------------------------------------*/
class
BlinkerCmd {
Twim& twi_;
u8 address_;
auto
twiOn () { twi_.baud( 100000ul ).on(address_); }
public:
//register addresses of BlinkerI2C device
enum
REGISTERS { RAM, ONTIME = 8, OFFTIME };
BlinkerCmd (Twim& twim, u8 address )
: twi_(twim), address_(address)
{
twiOn();
}
void
reset ()
{
//special case for bus recovery since we are using the same pins
//for master/slave (in this example), so caller will have to disable
//the slave to also release the pins (and re-enable slave when done)
twi_.busRecovery();
twiOn();
}
//caller will check return value, and if false
//they are in charge of calling reset if needed
template<int N> bool
write (const u8 reg, const u8(&wbuf)[N]) //multi-byte write starting at reg
{ //write register address, write value(s)
twi_.writeWrite(reg, wbuf);
return twi_.waitUS( 3000 );
}
bool //single byte write to reg
write (u8 reg, const u8& wbyte)
{
u8 wbuf[1] = { wbyte }; //convert to a 1 byte array for above write
return write( reg, wbuf );
}
template<int N> bool
read (u8 reg, u8(&rbuf)[N]) //multi-byte read starting at reg
{ //write register address, read value(s)
u8 wr[1] = { reg }; //make it a 1 byte array for writeRead
twi_.writeRead( wr, rbuf );
return twi_.waitUS( 3000 );
}
bool //single byte read from reg
read (u8 reg, u8& rbyte)
{
u8 rbuf[1] = { rbyte }; //convert to a 1 byte array for above read
return read( reg, rbuf );
}
};
/*------------------------------------------------------------------------------
main
------------------------------------------------------------------------------*/
static Twim twim0 { TWI0_pins };
static Twis twis0 { TWI0_pins };
static Led led { PORTB, 5, true }; //tiny416 xplained nano led
static BlinkerCmd blinkercmd { twim0, 0x51 };
static BlinkerI2C blinkeri2c { twis0, 0x51, led };
int
main ()
{
sei();
//lambda function for any errors w/twi, used twice below
auto error = []{
blinkeri2c.off(); //need slave to also release pins (in this example)
blinkercmd.reset();
blinkeri2c.on();
led.on();
waitMS(10000);
led.off();
};
//blinker device has ram registers, will use ram[0]
//to store a value so we can test reading the slave also
//if fails, keep trying
u8 blinkN = 5; //blink N times in loop below
while( not blinkercmd.write(blinkercmd.RAM, blinkN) ){ //write 1 byte, 5 -> ram[0]
error();
}
//table of led on/off value pairs
using TblT = struct { u8 ms10[2]; };
const TblT onOffTbl[] = {
{ 2, 40 }, //20ms on, 400ms off
{ 100, 100 }, //1000ms on, 1000ms off
{ 10, 200 } //100ms on, 2000ms off
};
while( 1 ) {
for( auto& tbl : onOffTbl ){
//write 2 values starting at ONTIME register (master->slave)
//then get value from register ram[0] (should be same as blinkN initial value above)
if( blinkercmd.write(blinkercmd.ONTIME, tbl.ms10) and
blinkercmd.read(blinkercmd.RAM, blinkN) ) {
//then blink N times (blinkeri2c is doing this)
blinkeri2c.run( blinkN );
waitMS( 2000 ); //2 seconds between blink 'sets'
continue; //next pair
}
//if any errors, do bus recovery (also turns on led for 10 seconds)
error();
//and start over
break;
}
} //while
} //main