Sign in Join
Wiki copytemporary

AVR Embedded Tutorial - I²C, TWI: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: AVR Embedded Tutorial - I²C, TWI

OldNewLine
1+# I²C / TWI
2+
3+This code is for an Atmega328/Arduino with 16MHz clock.
4+
5+How to control the UART can be found here: [[AVR Embedded Tutorial - UART|UARTInit and UARTSendString (...]]
6+
7+## Description
8+
9+This example demonstrates the communication with the I²C/TWI interface.
10+
11+Two AD1115 A/D converters are used as slave devices. These are very easy to use and are also very easy to obtain. What is very important is that with TWIStart you shift the address one bit to the left and then combine it with *or* using Write/Read. There are many tutorials on this.
12+
13+PulUp resistors are not necessary because they are already installed on the ADS1115 board (**Caution**: check *your* board for pullup resistors).
14+
15+## I²C functions
16+
17+### Constants
18+
19+``` pascal
20+ const
21+ CPU_Clock = 16000000; // Arduino clock frequency, default 16MHz.
22+ ADSaddr0 = $48; // Address converter 0
23+ ADSaddr1 = $49; // Address converter 1
24+ I2C_clock = 400000; // I²C clock (400KHz)
25+ TWI_Write = 0;
26+ TWI_Read = 1;
27+```
28+
29+### Initialize I²C
30+
31+``` pascal
32+ procedure TWIInit;
33+ const
34+ TWBR_val = byte((CPU_Clock div I2C_clock) - 16) div 2;
35+ begin
36+ TWSR := 0;
37+ TWBR := byte(TWBR_val);
38+ end;
39+```
40+
41+### Start I²C send/receive
42+
43+``` pascal
44+ procedure TWIStart(addr : byte);
45+ begin
46+ // Initiate sending / receiving
47+ TWCR := 0;
48+ TWCR := (1 shl TWINT) or (1 shl TWSTA) or (1 shl TWEN);
49+
50+ while ((TWCR and (1 shl TWINT)) = 0) do
51+ begin
52+ end;
53+
54+ // Send the address of the end device
55+ TWDR := addr;
56+ TWCR := (1 shl TWINT) or (1 shl TWEN);
57+
58+ while((TWCR and (1 shl TWINT)) = 0) do
59+ begin
60+ end;
61+ end;
62+```
63+
64+### I²C transmission / reception ended
65+
66+``` pascal
67+ procedure TWIStop ;
68+ begin
69+ TWCR := (1 shl TWINT) or (1 shl TWSTO) or (1 shl TWEN);
70+ end;
71+```
72+
73+### Send characters
74+
75+``` pascal
76+ procedure TWIWrite(u8data : byte);
77+ begin
78+ TWDR := u8data;
79+ TWCR := (1 shl TWINT) or (1 shl TWEN);
80+
81+ while((TWCR and (1 shl TWINT)) = 0) do
82+ begin
83+ end;
84+ end;
85+```
86+
87+### Receive characters
88+
89+Except for the last character, characters must be read with **TWIReadACK (...**. The last character must be read with **TWIReadNACK(...**
90+
91+``` pascal
92+ // Read to the penultimate character.
93+ function TWIReadACK : byte;
94+ begin
95+ TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA);
96+
97+ while (TWCR and (1 shl TWINT)) = 0 do
98+ begin
99+ end;
100+
101+ Result := TWDR;
102+ end;
103+
104+ // read last character.
105+ function TWIReadNACK : byte ;
106+ begin
107+ TWCR := (1 shl TWINT) or (1 shl TWEN);
108+
109+ while(TWCR and (1 shl TWINT)) = 0 do
110+ begin
111+ end;
112+
113+ Result := TWDR;
114+ end;
115+```
116+
117+> **Note:** These functions get stuck in the event of a hardware error or the slave address is wrong!
118+
119+### Character received with error query
120+
121+So that these functions do not get stuck, you have to install a counter in the while loop.
122+
123+When the counter reaches 255, the loop is interrupted and an error is output.
124+
125+255 is reached if no device is found at the I²C address.
126+
127+``` pascal
128+ // Read to the penultimate character.
129+ function TWIReadACK_Error : byte;
130+ var
131+ err : byte;
132+ begin
133+ err := 0;
134+ TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWA);
135+
136+ while ((TWCR and (1 shl TWINT)) = 0) and (err < 255) do
137+ begin
138+ Inc(err);
139+ end;
140+
141+ if err = 255 then
142+ begin
143+ UARTSendString('I²C-Timeout');
144+ Result := 0;
145+ end
146+ else
147+ begin
148+ Result := TWDR;
149+ end;
150+ end;
151+
152+ // read last character.
153+ function TWIReadNACK_Error : byte;
154+ var
155+ err : byte;
156+ begin
157+ err := 0;
158+ TWCR := (1 shl TWINT) or (1 shl TWEN);
159+
160+ while((TWCR and (1 shl TWINT)) = 0) and (err < 255) do
161+ begin
162+ Inc(err);
163+ end;
164+
165+ if err = 255 then
166+ begin
167+ UARTSendString('I²C-Timeout');
168+ Result := 0;
169+ end
170+ else
171+ begin
172+ Result := TWDR;
173+ end;
174+ end;
175+```
176+
177+## ADS1115 specific code
178+
179+This code is ADS1115 specific, for other I²C devices you will have to check their data sheets.
180+
181+``` pascal
182+ // Send parameters to ADS1115 and initiate measurement
183+ procedure WriteADS1115(addr : UInt16);
184+ begin
185+ TWIStart((addr shl 1) or TWI_Write);
186+ TWIWrite(1);
187+ TWIWrite(%11000011); // High
188+ TWIWrite(%11100011); // low
189+ TWIStop;
190+ end;
191+
192+ // Read out measurement from ADS1115.
193+ function ReadADS1115(addr : UInt16) : UInt16;
194+ begin
195+ TWIStart((addr shl 1) or TWI_Write);
196+ TWIWrite(0);
197+ TWIStop;
198+
199+ TWIStart((addr shl 1) or TWI_Read);
200+ Result := TWIReadACK * $100 + TWIReadNACK;
201+ TWIStop;
202+ end;
203+```
204+
205+## Example
206+
207+``` pascal
208+ var
209+ Data : Integer;
210+ s : ShortString;
211+
212+ begin
213+ asm cli end ; // block interrupt
214+ UARTInit;
215+ TWIInit;
216+ asm be end; // release interrupt
217+
218+ repeat
219+ WriteADS1115(ADSaddr0);
220+ Data := ReadADS1115(ADSaddr0);
221+
222+ str(Data : 6, s);
223+ UARTSendString('Channel 0:');
224+ UARTSendString(s);
225+
226+ WriteADS1115(ADSaddr1);
227+ Data := ReadADS1115(ADSaddr1);
228+
229+ str(Data : 6, s);
230+ UARTSendString('Channel 1:');
231+ UARTSendString(s);
232+ UARTSendString(#13#10);
233+ until 1 = 2;
234+ end.
235+```
236+
237+## ADS1115 parameters
238+
239+A list of the parameters for the ADS1115. More information is in the data sheet.
240+
241+Bit assignment of the ADS1115:
242+
243+**Low:** 11100011 **High:** 11000011
244+
245+<table>
246+<thead>
247+<tr>
248+<th>Bit</th>
249+<th>Name</th>
250+<th>Description</th>
251+</tr>
252+</thead>
253+<tbody>
254+<tr>
255+<td>15</td>
256+<td><strong>OS:</strong> Operational status or single-shot conversion start</td>
257+<td><strong>0</strong> : no effect<br />
258+<strong>1</strong> : Start a single conversion (when in power-down state)</td>
259+</tr>
260+<tr>
261+<td>14:12</td>
262+<td><strong>MUX[2:0]:</strong> Input multiplexer configuration (ADS1115 only)</td>
263+<td><strong>000</strong> : AINP = AIN0 and AINN = AIN1 (default)<br />
264+<strong>001</strong> : AINP = AIN0 and AINN = AIN3<br />
265+<strong>010</strong> : AINP = AIN1 and AINN = AIN3<br />
266+<strong>011</strong> : AINP = AIN2 and AINN = AIN3<br />
267+<strong>100</strong> : AINP = AIN0 and AINN = GND<br />
268+<strong>101</strong> : AINP = AIN1 and AINN = GND<br />
269+<strong>110</strong> : AINP = AIN2 and AINN = GND<br />
270+<strong>111</strong> : AINP = AIN3 and AINN = GND</td>
271+</tr>
272+<tr>
273+<td>11:9</td>
274+<td><strong>PGA[2:0]:</strong>Programmable gain amplifier configuration</td>
275+<td><strong>000</strong> : FSR = ±6.144 V<br />
276+<strong>001</strong> : FSR = ±4.096 V<br />
277+<strong>010</strong> : FSR = ±2.048 V (default)<br />
278+<strong>011</strong> : FSR = ±1.024 V<br />
279+<strong>100</strong> : FSR = ±0.512 V<br />
280+<strong>101</strong> : FSR = ±0.256 V<br />
281+<strong>110</strong> : FSR = ±0.256 V<br />
282+<strong>111</strong> : FSR = ±0.256 V<br />
283+</td>
284+</tr>
285+<tr>
286+<td>8</td>
287+<td><strong>MODE:</strong> Device operating mode</td>
288+<td><strong>0</strong> : Continuous-conversion mode<br />
289+<strong>1</strong> : Single-shot mode or power-down state (default)</td>
290+</tr>
291+<tr>
292+<td>7:5</td>
293+<td><strong>DR[2:0]:</strong> Data rate</td>
294+<td><strong>000</strong> : 8 SPS<br />
295+<strong>001</strong> : 16 SPS<br />
296+<strong>010</strong> : 32 SPS<br />
297+<strong>011</strong> : 64 SPS<br />
298+<strong>100</strong> : 128 SPS (default)<br />
299+<strong>101</strong> : 250 SPS<br />
300+<strong>110</strong> : 475 SPS<br />
301+<strong>111</strong> : 860 SPS<br />
302+</td>
303+</tr>
304+<tr>
305+<td>4</td>
306+<td><strong>COMP_MODE:</strong> Comparator mode</td>
307+<td><strong>0</strong> : Traditional comparator (default)<br />
308+<strong>1</strong> : Window comparator</td>
309+</tr>
310+<tr>
311+<td>3</td>
312+<td><strong>COMP_POL:</strong> Comparator polarity</td>
313+<td><strong>0</strong> : Active low (default)<br />
314+<strong>1</strong> : Active high</td>
315+</tr>
316+<tr>
317+<td>2</td>
318+<td><strong>COMP_LAT:</strong> Latching comparator</td>
319+<td><strong>0</strong> : Nonlatching comparator (default)<br />
320+<strong>1</strong> : Latching comparator</td>
321+</tr>
322+<tr>
323+<td>1:0</td>
324+<td><strong>COMP_QUE[1:0]:</strong> Comparator queue and disable</td>
325+<td><strong>00</strong> : Assert after one conversion<br />
326+<strong>01</strong> : Assert after two conversions<br />
327+<strong>10</strong> : Assert after four conversions<br />
328+<strong>11</strong> : Disable comparator and set ALERT/RDY pin to high-impedance (default)</td>
329+</tr>
330+</tbody>
331+</table>
332+
333+# See also
334+
335+- Overview page - [[AVR Embedded Tutorial]]
336+
337+---
338+*From the Free Pascal wiki, page "AVR Embedded Tutorial - I²C, TWI", as it stood on 2020-03-06 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history