AVR Embedded Tutorial - I²C, TWI/de: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | # I²C / TWI | |
| 2 | 2 | ||
| 3 | 3 | ## Vorwort | |
| 4 | 4 | ||
| 5 | 5 | Dieser Code ist für einen Atmega328/Arduino mit 16MHz. | |
| 6 | 6 | ||
| 7 | 7 | Wie man die UART ansteuert, steht hier: | |
| 8 | 8 | ||
| 9 | 9 | - [[AVR Embedded Tutorial - UART/de|UART]] *UARTInit* und *UARTSendString(...* | |
| 10 | 10 | ||
| 11 | 11 | ## Beschreibung | |
| 12 | 12 | ||
| 13 | 13 | Dieses Beispiel demonstriert die Kommunikation mit der I²C/TWI - Schnittstelle. | |
| 14 | 14 | ||
| 15 | 15 | Als Slave-Geräte werden 2 AD1115 A/D-Wandler verwendet. Diese sind sehr einfach in der Handhabung und sind auch sehr einfach erhältlich. Was sehr wichtig ist, dass man bei TWIStart die Adresse um ein Bit nach links verschiebt und anschließend mittels Write/Read mit **or** verknüpft. Es gibt viele Tutorials dazu. Dort sieht man das leider sehr schlecht. | |
| 16 | 16 | ||
| 17 | 17 | PulUp-Widerstände sind nicht nötig, da diese schon auf der Platine des ADS1115 verbaut sind. | |
| 18 | 18 | ||
| 19 | 19 | ## I²C Funktionen | |
| 20 | 20 | ||
| 21 | 21 | ### Konstanten | |
| 22 | 22 | ||
| 23 | 23 | ``` pascal | |
| 24 | 24 | const | |
| 25 | 25 | CPU_Clock = 16000000; // Taktfrequenz Arduino, default 16MHz. | |
| 26 | 26 | ADSaddr0 = $48; // Adresse Wandler 0 | |
| 27 | 27 | ADSaddr1 = $49; // Adresse Wandler 1 | |
| 28 | 28 | I2C_Takt = 400000; // I²C-Takt (400KHz) | |
| 29 | 29 | TWI_Write = 0; | |
| 30 | 30 | TWI_Read = 1; | |
| 31 | 31 | ``` | |
| 32 | 32 | ||
| 33 | 33 | ### I²C inizialisieren | |
| 34 | 34 | ||
| 35 | 35 | ``` pascal | |
| 36 | 36 | procedure TWIInit; | |
| 37 | 37 | const | |
| 38 | 38 | TWBR_val = byte((CPU_Clock div I2C_Takt) - 16) div 2; | |
| 39 | 39 | begin | |
| 40 | 40 | TWSR := 0; | |
| 41 | 41 | TWBR := byte(TWBR_val); | |
| 42 | 42 | end; | |
| 43 | 43 | ``` | |
| 44 | 44 | ||
| 45 | 45 | ### I²C Senden/Empfangen starten | |
| 46 | 46 | ||
| 47 | 47 | ``` pascal | |
| 48 | 48 | procedure TWIStart(addr: byte); | |
| 49 | 49 | begin | |
| 50 | 50 | // Senden/Empfangen einleiten | |
| 51 | 51 | TWCR := 0; | |
| 52 | 52 | TWCR := (1 shl TWINT) or (1 shl TWSTA) or (1 shl TWEN); | |
| 53 | 53 | while ((TWCR and (1 shl TWINT)) = 0) do begin | |
| 54 | 54 | end; | |
| 55 | 55 | ||
| 56 | 56 | // Adresse des Endgerätes senden | |
| 57 | 57 | TWDR := addr; | |
| 58 | 58 | TWCR := (1 shl TWINT) or (1 shl TWEN); | |
| 59 | 59 | while ((TWCR and (1 shl TWINT)) = 0) do begin | |
| 60 | 60 | end; | |
| 61 | 61 | end; | |
| 62 | 62 | ``` | |
| 63 | 63 | ||
| 64 | 64 | ### I²C Senden/Empfang beendet | |
| 65 | 65 | ||
| 66 | 66 | ``` pascal | |
| 67 | 67 | procedure TWIStop; | |
| 68 | 68 | begin | |
| 69 | 69 | TWCR := (1 shl TWINT) or (1 shl TWSTO) or (1 shl TWEN); | |
| 70 | 70 | end; | |
| 71 | 71 | ``` | |
| 72 | 72 | ||
| 73 | 73 | ### Zeichen Senden | |
| 74 | 74 | ||
| 75 | 75 | ``` pascal | |
| 76 | 76 | procedure TWIWrite(u8data: byte); | |
| 77 | 77 | begin | |
| 78 | 78 | TWDR := u8data; | |
| 79 | 79 | TWCR := (1 shl TWINT) or (1 shl TWEN); | |
| 80 | 80 | while ((TWCR and (1 shl TWINT)) = 0) do begin | |
| 81 | 81 | end; | |
| 82 | 82 | end; | |
| 83 | 83 | ``` | |
| 84 | 84 | ||
| 85 | 85 | ### Zeichen Empfangen | |
| 86 | 86 | ||
| 87 | 87 | Bis auf das vorletzte Zeichen muss mit **TWIReadACK(...** gelesen werden. Das letzte Zeichen muss mit **TWIReadNACK(...** gelesen werden. | |
| 88 | 88 | ||
| 89 | 89 | ``` pascal | |
| 90 | 90 | // Lesen bis zum vorletzten Zeichen. | |
| 91 | 91 | function TWIReadACK: byte; | |
| 92 | 92 | begin | |
| 93 | 93 | TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA); | |
| 94 | 94 | while (TWCR and (1 shl TWINT)) = 0 do begin | |
| 95 | 95 | end; | |
| 96 | 96 | Result := TWDR; | |
| 97 | 97 | end; | |
| 98 | 98 | ||
| 99 | 99 | // Letztes Zeichen lesen. | |
| 100 | 100 | function TWIReadNACK: byte; | |
| 101 | 101 | begin | |
| 102 | 102 | TWCR := (1 shl TWINT) or (1 shl TWEN); | |
| 103 | 103 | while (TWCR and (1 shl TWINT)) = 0 do begin | |
| 104 | 104 | end; | |
| 105 | 105 | Result := TWDR; | |
| 106 | 106 | end; | |
| 107 | 107 | ``` | |
| 108 | 108 | ||
| 109 | 109 | > **Note:** Anmerkung: Diese Funktionen bleibt bei einem Hardware-Fehler, oder die Slave-Adresse falsch ist, hängen ! | |
| 110 | 110 | ||
| 111 | 111 | ### Zeichen Empfangen mit Fehlerabfrage | |
| 112 | 112 | ||
| 113 | 113 | Das diese Funktionen nicht hängen bleiben, muss man in der while-Schleife einen Zähler einbauen. | |
| 114 | 114 | ||
| 115 | 115 | Wen der Zähler 255 erreicht, wird die Schleife unterbrochen und ein Fehler ausgegeben. | |
| 116 | 116 | ||
| 117 | 117 | 255 wird erreicht, wen an der I²C-Adresse kein Gerät gefunden wird. | |
| 118 | 118 | ||
| 119 | 119 | ``` pascal | |
| 120 | 120 | // Lesen bis zum vorletzten Zeichen. | |
| 121 | 121 | function TWIReadACK_Error: byte; | |
| 122 | 122 | var | |
| 123 | 123 | err:byte; | |
| 124 | 124 | begin | |
| 125 | 125 | err := 0; | |
| 126 | 126 | TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA); | |
| 127 | 127 | while ((TWCR and (1 shl TWINT)) = 0) and (err < 255) do begin | |
| 128 | 128 | Inc(err); | |
| 129 | 129 | end; | |
| 130 | 130 | if err = 255 then begin | |
| 131 | 131 | UARTSendString('I²C-Timeout'); | |
| 132 | 132 | Result := 0; | |
| 133 | 133 | end else begin | |
| 134 | 134 | Result := TWDR; | |
| 135 | 135 | end; | |
| 136 | 136 | end; | |
| 137 | 137 | ||
| 138 | 138 | // Letztes Zeichen lesen. | |
| 139 | 139 | function TWIReadNACK_Error: byte; | |
| 140 | 140 | var | |
| 141 | 141 | err:byte; | |
| 142 | 142 | begin | |
| 143 | 143 | err := 0; | |
| 144 | 144 | TWCR := (1 shl TWINT) or (1 shl TWEN); | |
| 145 | 145 | while ((TWCR and (1 shl TWINT)) = 0) and (err < 255) do begin | |
| 146 | 146 | Inc(err); | |
| 147 | 147 | end; | |
| 148 | 148 | if err = 255 then begin | |
| 149 | 149 | UARTSendString('I²C-Timeout'); | |
| 150 | 150 | Result := 0; | |
| 151 | 151 | end else begin | |
| 152 | 152 | Result := TWDR; | |
| 153 | 153 | end; | |
| 154 | 154 | end; | |
| 155 | 155 | ``` | |
| 156 | 156 | ||
| 157 | 157 | ## ADS1115 spezifischen Code | |
| 158 | 158 | ||
| 159 | 159 | Dieser Code ist ADS1115 spezifischen, andere I²C-Geräte müssen im Datenblatt nachgeguckt werden. | |
| 160 | 160 | ||
| 161 | 161 | ``` pascal | |
| 162 | 162 | // Parameter an ADS1115 schicken und Messen einleiten | |
| 163 | 163 | procedure WriteADS1115(addr: UInt16); | |
| 164 | 164 | begin | |
| 165 | 165 | TWIStart((addr shl 1) or TWI_Write); | |
| 166 | 166 | TWIWrite(1); | |
| 167 | 167 | TWIWrite(%11000011); // High | |
| 168 | 168 | TWIWrite(%11100011); // Low | |
| 169 | 169 | TWIStop; | |
| 170 | 170 | end; | |
| 171 | 171 | ||
| 172 | 172 | // Messung von ADS1115 auslesen. | |
| 173 | 173 | function ReadADS1115(addr: UInt16): UInt16; | |
| 174 | 174 | begin | |
| 175 | 175 | TWIStart((addr shl 1) or TWI_Write); | |
| 176 | 176 | TWIWrite(0); | |
| 177 | 177 | TWIStop; | |
| 178 | 178 | ||
| 179 | 179 | TWIStart((addr shl 1) or TWI_Read); | |
| 180 | 180 | Result := TWIReadACK * $100 + TWIReadNACK; | |
| 181 | 181 | TWIStop; | |
| 182 | 182 | end; | |
| 183 | 183 | ``` | |
| 184 | 184 | ||
| 185 | 185 | ## Beispiel | |
| 186 | 186 | ||
| 187 | 187 | ``` pascal | |
| 188 | 188 | var | |
| 189 | 189 | Data: Integer; | |
| 190 | 190 | s: ShortString; | |
| 191 | 191 | ||
| 192 | 192 | begin | |
| 193 | 193 | asm cli end; // Interrupt sperren. | |
| 194 | 194 | UARTInit; | |
| 195 | 195 | TWIInit; | |
| 196 | 196 | asm sei end; // Interrupt frei geben. | |
| 197 | 197 | ||
| 198 | 198 | repeat | |
| 199 | 199 | WriteADS1115(ADSaddr0); | |
| 200 | 200 | Data := ReadADS1115(ADSaddr0); | |
| 201 | 201 | ||
| 202 | 202 | str(Data: 6, s); | |
| 203 | 203 | UARTSendString('Kanal 0: '); | |
| 204 | 204 | UARTSendString(s); | |
| 205 | 205 | ||
| 206 | 206 | WriteADS1115(ADSaddr1); | |
| 207 | 207 | Data := ReadADS1115(ADSaddr1); | |
| 208 | 208 | ||
| 209 | 209 | str(Data: 6, s); | |
| 210 | 210 | UARTSendString(' Kanal 1: '); | |
| 211 | 211 | UARTSendString(s); | |
| 212 | 212 | UARTSendString(#13#10); | |
| 213 | 213 | until 1 = 2; | |
| 214 | 214 | end. | |
| 215 | 215 | ``` | |
| 216 | 216 | ||
| 217 | 217 | ## Parameter ADS1115 | |
| 218 | 218 | ||
| 219 | 219 | Eine Auflistung der Parameter des ADS1115. Mehr Infos im Datenblatt. | |
| 220 | 220 | ||
| 221 | 221 | **Bit-Belegung des ADS1115:** | |
| 222 | 222 | ||
| 223 | 223 | **Low:** 11100011 **High:** 11000011 | |
| 224 | 224 | ||
| 225 | - | ||
| 226 | 225 | <thead> | |
| 227 | - | ||
| 228 | - | ||
| 229 | - | ||
| 230 | - | ||
| 231 | - | ||
| 226 | + | ||
| 232 | 227 | </thead> | |
| 233 | 228 | <tbody> | |
| 234 | - | ||
| 235 | - | ||
| 236 | - | ||
| 237 | - | ||
| 229 | + | ||
| 238 | 230 | <strong>1</strong> : Start a single conversion (when in power-down state)</td> | |
| 239 | - | ||
| 240 | - | ||
| 241 | - | ||
| 242 | - | ||
| 243 | - | ||
| 231 | + | ||
| 244 | 232 | <strong>001</strong> : AINP = AIN0 and AINN = AIN3<br /> | |
| 245 | 233 | <strong>010</strong> : AINP = AIN1 and AINN = AIN3<br /> | |
| 246 | 234 | <strong>011</strong> : AINP = AIN2 and AINN = AIN3<br /> | |
| 247 | 235 | <strong>100</strong> : AINP = AIN0 and AINN = GND<br /> | |
| 248 | 236 | <strong>101</strong> : AINP = AIN1 and AINN = GND<br /> | |
| 249 | 237 | <strong>110</strong> : AINP = AIN2 and AINN = GND<br /> | |
| 250 | 238 | <strong>111</strong> : AINP = AIN3 and AINN = GND</td> | |
| 251 | - | ||
| 252 | - | ||
| 253 | - | ||
| 254 | - | ||
| 255 | - | ||
| 239 | + | ||
| 256 | 240 | <strong>001</strong> : FSR = ±4.096 V<br /> | |
| 257 | 241 | <strong>010</strong> : FSR = ±2.048 V (default)<br /> | |
| 258 | 242 | <strong>011</strong> : FSR = ±1.024 V<br /> | |
| 259 | 243 | <strong>100</strong> : FSR = ±0.512 V<br /> | |
| 260 | 244 | <strong>101</strong> : FSR = ±0.256 V<br /> | |
| 261 | 245 | <strong>110</strong> : FSR = ±0.256 V<br /> | |
| 262 | 246 | <strong>111</strong> : FSR = ±0.256 V<br /> | |
| 263 | - | ||
| 264 | - | ||
| 265 | - | ||
| 266 | - | ||
| 267 | - | ||
| 268 | - | ||
| 247 | + | ||
| 269 | 248 | <strong>1</strong> : Single-shot mode or power-down state (default)</td> | |
| 270 | - | ||
| 271 | - | ||
| 272 | - | ||
| 273 | - | ||
| 274 | - | ||
| 249 | + | ||
| 275 | 250 | <strong>001</strong> : 16 SPS<br /> | |
| 276 | 251 | <strong>010</strong> : 32 SPS<br /> | |
| 277 | 252 | <strong>011</strong> : 64 SPS<br /> | |
| 278 | 253 | <strong>100</strong> : 128 SPS (default)<br /> | |
| 279 | 254 | <strong>101</strong> : 250 SPS<br /> | |
| 280 | 255 | <strong>110</strong> : 475 SPS<br /> | |
| 281 | 256 | <strong>111</strong> : 860 SPS<br /> | |
| 282 | - | ||
| 283 | - | ||
| 284 | - | ||
| 285 | - | ||
| 286 | - | ||
| 287 | - | ||
| 257 | + | ||
| 288 | 258 | <strong>1</strong> : Window comparator</td> | |
| 289 | - | ||
| 290 | - | ||
| 291 | - | ||
| 292 | - | ||
| 293 | - | ||
| 259 | + | ||
| 294 | 260 | <strong>1</strong> : Actice high</td> | |
| 295 | - | ||
| 296 | - | ||
| 297 | - | ||
| 298 | - | ||
| 299 | - | ||
| 261 | + | ||
| 300 | 262 | <strong>1</strong> : Latching comparator</td> | |
| 301 | - | ||
| 302 | - | ||
| 303 | - | ||
| 304 | - | ||
| 305 | - | ||
| 263 | + | ||
| 306 | 264 | <strong>01</strong> : Assert after two conversions<br /> | |
| 307 | 265 | <strong>10</strong> : Assert after four conversions<br /> | |
| 308 | 266 | <strong>11</strong> : Disable comparator and set ALERT/RDY pin to high-impedance (default)</td> | |
| 309 | - | ||
| 267 | + | ||
| 310 | 268 | </tbody> | |
| 311 | - | ||
| 312 | 269 | ||
| 313 | 270 | # Siehe auch | |
| 314 | 271 | ||
| 315 | 272 | - Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]] | |
| 316 | 273 | ||
| 317 | 274 | Autor: Mathias | |
| 318 | 275 | ||
| 319 | 276 | --- | |
| 320 | 277 | *From the Free Pascal wiki, page "AVR Embedded Tutorial - I²C, TWI/de", 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).* |
