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