-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathMultiSerial.cpp
More file actions
executable file
·323 lines (276 loc) · 11.7 KB
/
MultiSerial.cpp
File metadata and controls
executable file
·323 lines (276 loc) · 11.7 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
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
MultiSerial.cpp - Arduino library for the StrichLabs MultiSerial Shield
Copyright (c) 2010 Sarah Nordstrom. Licensed under Creative Commons BY-NC-SA.
*/
// include this library's description file
#include "MultiSerial.h"
// include description files for Wire, as the shield uses I2C
#include "Wire.h"
// Revision 1 boards have a 1.8432MHz crystal on them
//#define MS_HZ 1843200L
// Revision 2 boards have a 3.6864MHz crystal on them
//#define MS_HZ 3686400L
// Revision 3 and above boards have a 11.0592MHz crystal on them
#define MS_HZ 11059200L
// track if the shield has been reset, as we only ever want to do this once
static int needsReset=1;
// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances
// Description: Creates a variable of type MultiSerial, to communciate with the MultiSerial Shield.
// Syntax: MultiSerial(address, port)
// Parameter: address - Address on the I2C bus of the shield to communicate with
// Parameter: port - Which port (0 or 1) on the shield to associate this instance with
// Returns: Instance of MultiSerial associated with a specific port on a specific shield.
MultiSerial::MultiSerial(byte shieldAddress, byte myChannel) {
addr = shieldAddress;
chan = myChannel;
// each channel gets a one byte 'peek buffer', to support peek() even though
// the hardware itself has no ability to peek into the buffer
peek_buf_valid[0] = 0;
peek_buf_valid[1] = 0;
}
MultiSerial::MultiSerial(void) {
// this 'fake constructor' allows creating an 'empty' MultiSerial type variable
// later on, the 'real constructor' can be called to actually begin using it.
}
// Public Methods //////////////////////////////////////////////////////////////
// Functions available in Wiring sketches, this library, and other libraries
// Description: Sets the data rate in bits per second for serial data transmission. This is the normal constructor used.
// Syntax: MultiSerialInstance.begin(speed)
// Parameter: speed - communications speed in bits per second
// Returns: nothing
void MultiSerial::begin(unsigned long baud) {
// just call the other constructor, using the standard crystal frequency
begin(baud, MS_HZ);
}
// Description: Sets the data rate in bits per second for serial data transmission. This constructor should be used if you are using a non-standard crystal.
// Syntax: MultiSerialInstance.begin(speed, crystalHz)
// Parameter: speed - communications speed in bits per second
// Parameter: crystalHz - Speed of the crystal on the board, in Hz
// Returns: nothing
void MultiSerial::begin(unsigned long baud, unsigned long crystalHz) {
// join i2c bus (no address as we want to be the master)
Wire.begin();
// if this is the first time the shield is used, reset the UART so
// that we can start off in a known state
// FIXME: will this work for >1 shield though?
if(needsReset==1) {
msWriteRegister(IOControl, 0x8);
needsReset=0;
}
delay(25);
// switch to the special register bank
msWriteRegister(LCR, 128);
// set the baud rate
unsigned short baudDivisor;
baudDivisor = (crystalHz + baud * 8L) / (baud * 16L);
msWriteRegister(DLL, baudDivisor & 0xFF);
msWriteRegister(DLH, baudDivisor >> 8);
// switch back to the normal register bank
msWriteRegister(LCR, 0);
// set the word size to 8 bits
setWordSize(8);
// enable the TX/RX fifos
msWriteRegister(FCR, 7);
}
// Description: Transmit a byte of data out the serial port.
// Syntax: MultiSerialInstance.write(data)
// Parameter: data - byte of data to write
// Returns: nothing
// FIXME: This function should return something useful
size_t MultiSerial::write(uint8_t val) {
msWriteRegister(THR, val);
}
// Description: Transmit a series of bytes of data out the serial port.
// Syntax: MultiSerialInstance.write(data, nbrBytes)
// Parameter: data - array of bytes of data to write
// Parameter: nbrBytes - number of elements in the data array
// Returns: nothing
// FIXME: This function should return something useful
void MultiSerial::write(uint8_t val[], uint8_t nbrBytes) {
msWriteRegister(THR, val, nbrBytes);
}
// Description: Read the next available byte of available serial data.
// Syntax: MultiSerialInstance.read()
// Parameter: none
// Returns: first available byte of serial data, or -1 if no data is available
int MultiSerial::read(void) {
if(peek_buf_valid[chan]) {
peek_buf_valid[chan] = 0;
return peek_buf[chan];
} else {
return msReadRegister(RHR);
}
}
// Description: Get the number of bytes available for reading from the serial port.
// Syntax: MultiSerialInstance.available()
// Parameter: none
// Returns: number of bytes available to read
int MultiSerial::available(void) {
if(peek_buf_valid[chan]) return 1;
return msReadRegister(RXLVL);
}
// Description: Store one byte of data associated with a specific shield and port. This byte is stored on the shield itself, not in the Arduino's memory. Each successive call to store() will overwrite any previous byte stored.
// Syntax: MultiSerialInstance.store(data)
// Parameter: one byte of data to associate with a specific port on a specific shield
// Returns: nothing
void MultiSerial::store(byte val) {
msWriteRegister(SPR, val);
}
// Description: Retrieve the byte of data aassociated with a specific shield and port, that had been stored earlier.
// Syntax: MultiSerialInstance.retrieve()
// Parameter: none
// Returns: one byte of data that is associated with a specific port on a specific shield
byte MultiSerial::retrieve(void) {
return msReadRegister(SPR);
}
// Description: Flush all waiting data out of the buffer.
// Syntax: MultiSerialInstance.flush()
// Parameter: none
// Returns: nothing
void MultiSerial::flush() {
msWriteRegister(FCR, 6); // flush receive FIFO
peek_buf_valid[chan] = 0; // invalidate peek buffer
}
// Description: Read the next available byte of available serial data, but leave it in the buffer. Every call to peek() will return the same byte of data, until read() is called to return/remove it from the buffer or flush() is called to purge it.
// Syntax: MultiSerialInstance.peek()
// Parameter: none
// Returns: first available byte of serial data, or -1 if no data is available
int MultiSerial::peek() {
if(peek_buf_valid[chan]) return peek_buf[chan];
if(msReadRegister(RXLVL) < 1) return -1;
peek_buf[chan] = msReadRegister(RHR);
peek_buf_valid[chan] = 1;
return peek_buf[chan];
}
// Description: Configured the specified GPIO pin to be an input or an output.
// Syntax: MultiSerialInstance.pinMode(pin, mode);
// Parameter: pin - GPIO pin number to change
// Parameter: mode - either INPUT or OUTPUT
// Returns: nothing
void MultiSerial::pinMode(byte pin, byte direction) {
byte regDirs;
regDirs = msReadRegister(IODir);
if(direction == OUTPUT) {
regDirs = regDirs | (1 << pin);
} else {
msWriteRegister(IODir, 0);
regDirs = regDirs & ~(1 << pin);
}
msWriteRegister(IODir, regDirs);
}
// Description: Set a specified GPIO pin to a HIGH or LOW state. Unlike the Arduino digital pins, these do not have pullup resistors, so digitalWrite when the pin is set to an input has no effect.
// Syntax: MultiSerialInstance.digitalWrite(pin, value)
// Parameter: pin - GPIO pin number to change
// Parameter: value - HIGH or LOW
// Returns: nothing
void MultiSerial::digitalWrite(byte pin, byte value) {
byte regValues;
regValues = msReadRegister(IOState);
if(value == HIGH) {
regValues = regValues | (1 << pin);
} else {
regValues = regValues & ~(1 << pin);
}
msWriteRegister(IOState, regValues);
}
// Description: Read the current value of a specific GPIO pin.
// Syntax: MultiSerialInstance.digitalRead(pin)
// Parameter: pin - GPIO pin number to read the value of
// Returns: HIGH or LOW
byte MultiSerial::digitalRead(byte pin) {
byte regValues;
regValues = msReadRegister(IOState);
if((regValues & (1 << pin)) > 0) {
return HIGH;
} else {
return LOW;
}
}
// Description: Set the data word size to use on a specific serial port. Defaults to 8 bits when begin() is called, but can be changed if required.
// Syntax: MultiSerialInstance.setWordSize(size)
// Parameter: size - word size to use on the port, valid values are 5, 6, 7, or 8
// Returns: nothing
void MultiSerial::setWordSize(byte wordSize) {
byte regValue;
wordSize -= 5;
regValue = msReadRegister(LCR);
regValue = (regValue & ~(1|2)) | (wordSize & (1|2));
msWriteRegister(LCR, regValue);
}
// Description: Set the number of stop bits. Defaults to 1 when begin() is called, but can be changed if required.
// Syntax: MultiSerialInstance.setStop(size)
// Parameter: stopBits - 0 -> 1 stop bit; 1 -> 1.5 stop bits if word size == 5, 2 stop bits if word size != 5
// Returns: nothing
void MultiSerial::setStopBits(byte stopBits) {
byte regValue;
regValue = msReadRegister(LCR);
bitWrite(regValue, 2, stopBits);
msWriteRegister(LCR, regValue);
}
// Description: Enable specific interrupt types.
// Syntax: MultiSerialInstance.enableInterrupt(types)
// Parameter: types - bitmask of INT_RX and/or INT_TX for which interrupt(s) to enable
// Returns: nothing
void MultiSerial::enableInterrupt(byte interruptTypes) {
byte curIntFlags = msReadRegister(IER) & (INT_RX | INT_TX);
msWriteRegister(IER, (interruptTypes | curIntFlags) & (INT_RX | INT_TX));
}
// Description: Disable specific interrupt types.
// Syntax: MultiSerialInstance.disableInterrupt(types)
// Parameter: types - bitmask of INT_RX and/or INT_TX for which interrupt(s) to disable
// Returns: nothing
void MultiSerial::disableInterrupt(byte interruptTypes) {
byte curIntFlags = msReadRegister(IER) & (INT_RX | INT_TX);
msWriteRegister(IER, (interruptTypes ^ curIntFlags) & (INT_RX | INT_TX));
}
// Private Methods /////////////////////////////////////////////////////////////
// Functions only available to other functions in this library
// send the subaddress required to access one of the controller chip's registers
void MultiSerial::msSendSubAddr(byte reg) {
int subAddr = 0;
subAddr |= (chan << 1);
subAddr |= (reg << 3);
Wire.write(subAddr);
}
// write a value to one of the controller chip's registers
void MultiSerial::msWriteRegister(byte reg, byte val) {
// begin the i2c transmission to the chip
Wire.beginTransmission(addr);
// send the register address we want to write to
msSendSubAddr(reg);
// send the actual data we want to write and commit the transaction
Wire.write(val);
Wire.endTransmission();
}
// write a series of values to sequential addresses in the the controller chip's registers
void MultiSerial::msWriteRegister(byte reg, byte val[], byte nbrValues) {
byte i;
// begin the i2c transmission to the chip
Wire.beginTransmission(addr);
// send the register address we want to write to
msSendSubAddr(reg);
// send the actual data we want to write and commit the transaction
for(i=0; i<nbrValues; i++) {
Wire.write(val[i]);
}
Wire.endTransmission();
}
// read a value from one of the controller chip's registers and return it
byte MultiSerial::msReadRegister(byte reg) {
Wire.beginTransmission(addr);
msSendSubAddr(reg);
Wire.endTransmission();
Wire.requestFrom((byte)addr, (byte)1);
return Wire.read();
}
// Documentation ///////////////////////////////////////////////////////////////
// Description: Outputs data to the serial port, converting numbers to their human-readable ASCII representations.
// Syntax: MultiSerialInstance.print(value)
// Syntax: MultiSerialInstance.print(value, base)
// Syntax: MultiSerialInstance.print(string)
// Parameter: value - Variable or literal to output
// Parameter: base - Base to display number in (OCT, DEC, BIN, HEX) or number of decimal places for floating point values, DEC assumed if not supplied
// Parameter: string - String to output
// Returns: nothing
// void MultiSerial::print(int value, byte base) {}