├── MiniProExtender └── MiniProExtender.ino ├── README.md └── TinyI2CWatchdog ├── TinyI2CWatchdog.ino ├── TinySerialDebug.ino └── TinyWireS_Custom ├── TinyWireS.cpp ├── TinyWireS_Custom.h ├── keywords.txt ├── usiTwiSlave.c └── usiTwiSlave.h /MiniProExtender/MiniProExtender.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************************************************************\ 2 | * Arduino project "ESP Easy" � Copyright www.esp8266.nu 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 6 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 7 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 8 | * You received a copy of the GNU General Public License along with this program in file 'License.txt'. 9 | * 10 | * IDE download : https://www.arduino.cc/en/Main/Software 11 | * ESP8266 Package : https://github.com/esp8266/Arduino 12 | * 13 | * Source Code : https://sourceforge.net/projects/espeasy/ 14 | * Support : http://www.esp8266.nu 15 | * Discussion : http://www.esp8266.nu/forum/ 16 | * 17 | * Additional information about licensing can be found at : http://www.gnu.org/licenses 18 | \*************************************************************************************************************************/ 19 | 20 | // This file is to be loaded onto an Arduino Pro Mini so it will act as a simple IO extender to the ESP module. 21 | // Communication between ESP and Arduino is using the I2C bus, so only two wires needed. 22 | // It best to run the Pro Mini on 3V3, although the 16MHz versions do not officially support this voltage level on this frequency. 23 | // That way, you can skip levelconverters on I2C. 24 | // Arduino Mini Pro uses A4 and A5 for I2C bus. ESP I2C can be configured but they are on GPIO-4 and GPIO-5 by default. 25 | 26 | #include 27 | 28 | #define I2C_MSG_IN_SIZE 4 29 | #define I2C_MSG_OUT_SIZE 4 30 | 31 | #define CMD_DIGITAL_WRITE 1 32 | #define CMD_DIGITAL_READ 2 33 | #define CMD_ANALOG_WRITE 3 34 | #define CMD_ANALOG_READ 4 35 | 36 | volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE]; 37 | 38 | void setup() 39 | { 40 | Wire.begin(0x7f); 41 | Wire.onReceive(receiveEvent); 42 | Wire.onRequest(requestEvent); 43 | } 44 | 45 | void loop() {} 46 | 47 | void receiveEvent(int count) 48 | { 49 | if (count == I2C_MSG_IN_SIZE) 50 | { 51 | byte cmd = Wire.read(); 52 | byte port = Wire.read(); 53 | int value = Wire.read(); 54 | value += Wire.read()*256; 55 | switch(cmd) 56 | { 57 | case CMD_DIGITAL_WRITE: 58 | pinMode(port,OUTPUT); 59 | digitalWrite(port,value); 60 | break; 61 | case CMD_DIGITAL_READ: 62 | pinMode(port,INPUT_PULLUP); 63 | clearSendBuffer(); 64 | sendBuffer[0] = digitalRead(port); 65 | break; 66 | case CMD_ANALOG_WRITE: 67 | analogWrite(port,value); 68 | break; 69 | case CMD_ANALOG_READ: 70 | clearSendBuffer(); 71 | int valueRead = analogRead(port); 72 | sendBuffer[0] = valueRead & 0xff; 73 | sendBuffer[1] = valueRead >> 8; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | void clearSendBuffer() 80 | { 81 | for(byte x=0; x < sizeof(sendBuffer); x++) 82 | sendBuffer[x]=0; 83 | } 84 | 85 | void requestEvent() 86 | { 87 | Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer)); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPEasySlaves 2 | Arduino (AVR/ATTiny) based slave projects for ESP Easy 3 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyI2CWatchdog.ino: -------------------------------------------------------------------------------- 1 | // Tiny85 as I2C Watchdog / Busmonitor 2 | // Version 0.1, 2016-01-08 3 | 4 | // Uses a customized TinyWire Slave library that does not hang on persistent SDA low conditions 5 | // - With this library, the MCU main sketch can still monitor I2C bus signal levels, 6 | // instead of hanging within the ISR routine 7 | // - Without this custom library, the MCU would not be able to reset the target if this happens 8 | // 9 | // This sketch also uses customized Software Serial code: 10 | // - Smaller size than the full librarym but TX only 11 | // - TX without IRQ blocking as it interferes sometimes with I2C traffic 12 | // - Serial debug can get garbled sometimes due to non IRQ blocking (but I2C is more important) 13 | //============================================================================================================== 14 | // ATMEL ATTINY85 GENERIC PINOUTS 15 | // o-\/-+ 16 | // Reset 1| |8 VCC 17 | // D3/A3 PB3 2| |7 PB2 D2/A1/I2C-SCL 18 | // D4/A2 PB4 3| |6 PB1 D1/PWM1 19 | // GND 4| |5 PB0 D0/PWM0/I2C-SDA 20 | // +----+ 21 | //============================================================================================================== 22 | 23 | //============================================================================================================== 24 | // ATMEL ATTINY85 PINOUTS used for watchdog purpose 25 | // o-\/-+ 26 | // Reset 1| |8 VCC 27 | // Factory Reset Pin 2| |7 I2C-SCL to ESP 28 | // (debug) Serial TX 3| |6 Reset output to ESP 29 | // GND 4| |5 I2C-SDA to ESP 30 | // +----+ 31 | //============================================================================================================== 32 | 33 | // I2C command structure uses one or two bytes: 34 | // 35 | // ,[value] 36 | // 37 | // Commands below 0x80 are used to write data to register in settings struct 38 | // 39 | // Commands starting at 0x80 are special commands: 40 | // Command 0x80 = store settings to EEPROM 41 | // Command 0x81 = reset to factory default settings 42 | // Command 0x82 = set I2C address, factory default = 0x26 43 | // Command 0x83 = set pointer to settings struct for I2C readings 44 | // Command 0x84 = reset statistics counters 45 | // Command 0x85 = simulate (test) watchdog timeout 46 | // Command 0xA5 = reset watchdog timer value to 0 47 | // 48 | 49 | #define SWSERIALDEBUG true 50 | #define SWSERIAL false 51 | 52 | #if SWSERIALDEBUG 53 | #define SERIAL_TX_PIN 4 54 | #endif 55 | 56 | #if SWSERIAL 57 | #include 58 | //SoftwareSerial Serial(3,4); // RX, TX 59 | #endif 60 | 61 | #include "TinyWireS_Custom.h" 62 | #include 63 | 64 | #define PROJECT_PID 2015123101L 65 | #define VERSION 1 66 | #define BUILD 1 67 | 68 | #define RESET_CAUSE_TIMEOUT 1 69 | 70 | struct SettingsStruct 71 | { 72 | unsigned long PID; // (B0-3) 4 byte ID, wipes eeprom if missing 73 | int Version; // (B4/5) Version, change if struct changes, wipes eeprom 74 | byte TimeOut; // (B6) Timeout in seconds 75 | byte Action; // (B7) Action on timeout, 1 = reset target 76 | byte ResetPin; // (B8) Reset output pin to toggle 77 | byte Sleep; // (B9) After reset, do nothing for x seconds 78 | byte I2CAddress; // (B10) Listen to this I2C Address (0x26 default) 79 | byte I2CLoadThreshold; // (B11) Threshold for I2C bus load 80 | byte ResetCounter; // (B12) Seconds counter for WD reset status 81 | byte SleepCounter; // (B13) Seconds counter for sleep time 82 | byte TargetResetCount; // (B14) Counts number of target resets 83 | byte I2CBusInitCount; // (B15) Counts I2C bus init calls 84 | byte I2CBusErrorCount; // (B16) Counts I2C bus threshold exceptions 85 | byte Status; // (B17) Status byte 86 | byte LastResetCause; // (B18) Last reset cause code 87 | } 88 | Settings; 89 | 90 | unsigned long timer1Sec=0; // Timer triggers each second for WD reset count 91 | unsigned long timer100mSec=0; // Timer triggers 10 times/second, check I2C bus levels 92 | volatile byte I2CBuffer[16]; // Buffer to store incoming I2C message 93 | volatile byte I2CReceived=0; // flag, will be set if I2C message received 94 | unsigned long I2CActive; // Count active signal on I2C bus 95 | unsigned long I2CIdle; // Count inactive signal on I2C bus 96 | byte settingsPointer = 0; // pointer to specifig location in the settings struct 97 | 98 | void(*Reset)(void)=0; 99 | volatile uint8_t *receivePortRegister; 100 | 101 | /********************************************************************************************\ 102 | * Main setup routine 103 | \*********************************************************************************************/ 104 | void setup() 105 | { 106 | #if SWSERIAL 107 | Serial.begin(9600); 108 | #endif 109 | 110 | #if SWSERIALDEBUG 111 | SoftwareSerial_init(SERIAL_TX_PIN); 112 | #endif 113 | 114 | LoadSettings(); 115 | if (Settings.Version != VERSION || Settings.PID != PROJECT_PID) 116 | { 117 | #if SWSERIAL 118 | Serial.print(F("\nPID:")); 119 | Serial.println(Settings.PID); 120 | Serial.print(F("Version:")); 121 | Serial.println(Settings.Version); 122 | Serial.println(F("INIT : Incorrect PID or version!")); 123 | #endif 124 | tws_delay(1000); 125 | ResetFactory(); 126 | } 127 | 128 | #if SWSERIAL 129 | Serial.println(F("\nTiny WD Boot...\n")); 130 | Serial.print(F("Free RAM : ")); 131 | Serial.println(freeRam()); 132 | Serial.print(F("Timeout : ")); 133 | Serial.println(Settings.TimeOut); 134 | Serial.print(F("Action : ")); 135 | Serial.println(Settings.Action); 136 | Serial.print(F("Resetpin : ")); 137 | Serial.println(Settings.ResetPin); 138 | Serial.print(F("I2CAddress: ")); 139 | Serial.println(Settings.I2CAddress, HEX); 140 | #endif 141 | 142 | #if SWSERIALDEBUG 143 | SoftwareSerial_println("Boot"); 144 | #endif 145 | 146 | pinMode(3, INPUT); 147 | pinMode(4, OUTPUT); 148 | 149 | // if pin 3 held low for > 10 seconds after boot, perform hard reset! 150 | byte factoryResetCount = 0; 151 | while (digitalRead(3) == 0) 152 | { 153 | factoryResetCount++; 154 | tws_delay(100); 155 | if (factoryResetCount > 100) 156 | { 157 | #if SWSERIALDEBUG 158 | SoftwareSerial_println("Hard Reset"); 159 | #endif 160 | ResetFactory(); 161 | } 162 | } 163 | 164 | TinyWireS.begin(Settings.I2CAddress); 165 | TinyWireS.onRequest(requestEvent); 166 | TinyWireS.onReceive(receiveEvent); 167 | 168 | uint8_t port = digitalPinToPort(0); 169 | receivePortRegister = portInputRegister(port); 170 | 171 | timer1Sec = millis() + 1000; 172 | timer100mSec = millis() + 100; 173 | 174 | Settings.ResetCounter = 0; 175 | Settings.SleepCounter = 0; 176 | Settings.Status = 0; 177 | } 178 | 179 | 180 | /********************************************************************************************\ 181 | * Main program loop 182 | \*********************************************************************************************/ 183 | void loop() 184 | { 185 | 186 | if (I2CReceived) 187 | I2CCommand(); 188 | 189 | if((*receivePortRegister & 0x5) != 5) 190 | I2CActive++; 191 | else 192 | I2CIdle++; 193 | 194 | if (millis() > timer100mSec) 195 | { 196 | timer100mSec = millis() + 100; 197 | checkI2Cbus(); 198 | } 199 | 200 | if (millis() > timer1Sec) 201 | { 202 | timer1Sec = millis() + 1000; 203 | 204 | Settings.I2CBusInitCount = TinyWireS.initCount(); 205 | 206 | if (Settings.SleepCounter > 0) 207 | { 208 | #if SWSERIAL 209 | Serial.print(F("Sleeping: ")); 210 | Serial.println(int(Settings.SleepCounter)); 211 | #endif 212 | Settings.SleepCounter--; 213 | } 214 | else 215 | { 216 | #if SWSERIAL 217 | Serial.print(F("ResetCounter: ")); 218 | Serial.println(Settings.ResetCounter); 219 | #endif 220 | #if SWSERIALDEBUG 221 | SoftwareSerial_print("RC "); 222 | SoftwareSerial_print(int2str(Settings.ResetCounter)); 223 | SoftwareSerial_print(" I "); 224 | SoftwareSerial_println(int2str(TinyWireS.initCount())); 225 | #endif 226 | 227 | Settings.ResetCounter++; 228 | if ((Settings.TimeOut != 0) && (Settings.ResetCounter > Settings.TimeOut)) 229 | timeOutAction(); 230 | } 231 | } 232 | TinyWireS_stop_check(); 233 | } 234 | 235 | 236 | /********************************************************************************************\ 237 | * Check I2C bus load 238 | \*********************************************************************************************/ 239 | void checkI2Cbus() 240 | { 241 | byte ratio = (100 * I2CActive) / (I2CIdle + I2CActive); 242 | if (ratio > 0) 243 | { 244 | #if SWSERIAL 245 | Serial.print(F("bus act: ")); 246 | Serial.print(I2CActive); 247 | Serial.print(F(" idle: ")); 248 | Serial.print(I2CIdle); 249 | Serial.print(F(" ratio: ")); 250 | Serial.print(ratio); 251 | Serial.print(F(" I2C inits: ")); 252 | Serial.println(TinyWireS.initCount()); 253 | #endif 254 | } 255 | 256 | I2CActive=0; 257 | I2CIdle=0; 258 | 259 | byte portState = *receivePortRegister & 0x5; 260 | 261 | if (ratio > Settings.I2CLoadThreshold) 262 | { 263 | #if SWSERIAL 264 | Serial.print(F("I2C bus: ")); 265 | Serial.println(portState); 266 | Serial.println(F("Reset I2C due to I2C threshold")); 267 | #endif 268 | #if SWSERIALDEBUG 269 | SoftwareSerial_println("I2C Err"); 270 | #endif 271 | Settings.I2CBusErrorCount++; 272 | TinyWireS.begin(Settings.I2CAddress); 273 | TinyWireS.onRequest(requestEvent); 274 | TinyWireS.onReceive(receiveEvent); 275 | } 276 | } 277 | 278 | 279 | /********************************************************************************************\ 280 | * Check I2C incoming command and perform actions 281 | \*********************************************************************************************/ 282 | void I2CCommand() 283 | { 284 | #if SWSERIAL 285 | Serial.print(F("I2C bytes: ")); 286 | Serial.print(I2CReceived); 287 | Serial.print(F( " Inits: ")); 288 | Serial.print(TinyWireS.initCount()); 289 | Serial.print(F( " cmd: ")); 290 | #endif 291 | 292 | if ((I2CBuffer[0] > 5) && (I2CBuffer[0] < sizeof(SettingsStruct))) 293 | { 294 | byte *pointerToByteToSave = (byte*)&Settings + I2CBuffer[0]; 295 | *pointerToByteToSave = I2CBuffer[1]; 296 | } 297 | else 298 | { 299 | switch(I2CBuffer[0]) 300 | { 301 | case 0x80: 302 | { 303 | SaveSettings(); 304 | break; 305 | } 306 | 307 | case 0x81: 308 | { 309 | ResetFactory(); 310 | break; 311 | } 312 | 313 | case 0x82: 314 | { 315 | Settings.I2CAddress = I2CBuffer[1]; 316 | TinyWireS.begin(Settings.I2CAddress); 317 | TinyWireS.onRequest(requestEvent); 318 | TinyWireS.onReceive(receiveEvent); 319 | break; 320 | } 321 | 322 | case 0x83: 323 | { 324 | settingsPointer = I2CBuffer[1]; 325 | break; 326 | } 327 | 328 | case 0x84: 329 | { 330 | Settings.TargetResetCount = 0; 331 | Settings.I2CBusInitCount = 0; 332 | Settings.I2CBusErrorCount = 0; 333 | break; 334 | } 335 | 336 | case 0x85: 337 | { 338 | timeOutAction(); 339 | break; 340 | } 341 | 342 | case 0xA5: 343 | { 344 | Settings.Status &= 0xFE; // reset bit 0, target reset state indicator 345 | Settings.ResetCounter = 0; 346 | break; 347 | } 348 | 349 | } // switch 350 | } // else 351 | 352 | I2CReceived = 0; 353 | } 354 | 355 | 356 | /********************************************************************************************\ 357 | * Perform preprogrammed actions due to timeout on watchdog feed 358 | \*********************************************************************************************/ 359 | void timeOutAction() 360 | { 361 | switch(Settings.Action) 362 | { 363 | case 1: 364 | { 365 | #if SWSERIAL 366 | Serial.println(F("Resetting target!")); 367 | #endif 368 | Settings.LastResetCause = RESET_CAUSE_TIMEOUT; 369 | resetTarget(); 370 | break; 371 | } 372 | } 373 | } 374 | 375 | 376 | /********************************************************************************************\ 377 | * Reply to I2C read requests 378 | \*********************************************************************************************/ 379 | void requestEvent() 380 | { 381 | byte *pointerToByteToRead = (byte*)&Settings + settingsPointer; 382 | TinyWireS.send(*pointerToByteToRead); 383 | } 384 | 385 | /********************************************************************************************\ 386 | * Receive and store incoming I2C writes 387 | \*********************************************************************************************/ 388 | void receiveEvent(uint8_t howMany) 389 | { 390 | 391 | if (howMany > 16) 392 | return; 393 | 394 | I2CReceived = howMany; 395 | 396 | for(byte x=0; x < howMany; x++) 397 | I2CBuffer[x] = TinyWireS.receive(); 398 | } 399 | 400 | 401 | /********************************************************************************************\ 402 | * Reset target device using digital output pin, should connect to target's reset input pin 403 | * Signal is active low 404 | \*********************************************************************************************/ 405 | void resetTarget() 406 | { 407 | Settings.TargetResetCount++; 408 | Settings.Status |= 0x01; // set bit 0, reset state 409 | pinMode(Settings.ResetPin,OUTPUT); 410 | digitalWrite(Settings.ResetPin,LOW); 411 | tws_delay(500); 412 | digitalWrite(Settings.ResetPin,HIGH); 413 | pinMode(Settings.ResetPin,INPUT); 414 | Settings.ResetCounter=0; 415 | Settings.SleepCounter=Settings.Sleep; 416 | } 417 | 418 | 419 | /*********************************************************************************************\ 420 | * Save settings to EEPROM memory. 421 | \*********************************************************************************************/ 422 | void SaveSettings(void) 423 | { 424 | char ByteToSave,*pointerToByteToSave=pointerToByteToSave=(char*)&Settings; 425 | 426 | for(int x=0; x0) 506 | { 507 | *--OutputLinePosPtr='0'+(x%10); 508 | x/=10; 509 | } 510 | } 511 | return OutputLinePosPtr; 512 | } 513 | 514 | /**********************************************************************************************\ 515 | * Converteert een unsigned long naar een hexadecimale string. 516 | \*********************************************************************************************/ 517 | char* int2strhex(unsigned long x) 518 | { 519 | //static char OutputLine[12]; 520 | char* OutputLinePosPtr=&OutputLine[10]; 521 | int y; 522 | 523 | *OutputLinePosPtr=0; 524 | 525 | if(x==0) 526 | { 527 | *--OutputLinePosPtr='0'; 528 | } 529 | else 530 | { 531 | while(x>0) 532 | { 533 | y=x&0xf; 534 | 535 | if(y<10) 536 | *--OutputLinePosPtr='0'+y; 537 | else 538 | *--OutputLinePosPtr='A'+(y-10); 539 | 540 | x=x>>4; 541 | ; 542 | } 543 | *--OutputLinePosPtr='x'; 544 | *--OutputLinePosPtr='0'; 545 | } 546 | return OutputLinePosPtr; 547 | } 548 | 549 | 550 | 551 | 552 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinySerialDebug.ino: -------------------------------------------------------------------------------- 1 | #ifdef SWSERIALDEBUG 2 | #define TX_DELAY 112 3 | #define XMIT_START_ADJUSTMENT 4 4 | 5 | uint8_t _transmitBitMask; 6 | volatile uint8_t *_transmitPortRegister; 7 | 8 | void SoftwareSerial_print(char* string) 9 | { 10 | byte x=0; 11 | while (string[x] != 0) 12 | { 13 | SoftwareSerial_write(string[x]); 14 | x++; 15 | } 16 | } 17 | 18 | void SoftwareSerial_println(char* string) 19 | { 20 | SoftwareSerial_print(string); 21 | SoftwareSerial_write(13); 22 | SoftwareSerial_write(10); 23 | } 24 | 25 | void SoftwareSerial_init(byte transmitPin) 26 | { 27 | SoftwareSerial_setTX(transmitPin); 28 | SoftwareSerial_tunedDelay(TX_DELAY); // if we were low this establishes the end 29 | } 30 | 31 | inline void SoftwareSerial_tunedDelay(uint16_t delay) { 32 | uint8_t tmp=0; 33 | 34 | asm volatile("sbiw %0, 0x01 \n\t" 35 | "ldi %1, 0xFF \n\t" 36 | "cpi %A0, 0xFF \n\t" 37 | "cpc %B0, %1 \n\t" 38 | "brne .-10 \n\t" 39 | : "+r" (delay), "+a" (tmp) 40 | : "0" (delay) 41 | ); 42 | } 43 | 44 | void SoftwareSerial_tx_pin_write(uint8_t pin_state) 45 | { 46 | if (pin_state == LOW) 47 | *_transmitPortRegister &= ~_transmitBitMask; 48 | else 49 | *_transmitPortRegister |= _transmitBitMask; 50 | } 51 | 52 | void SoftwareSerial_setTX(uint8_t tx) 53 | { 54 | pinMode(tx, OUTPUT); 55 | digitalWrite(tx, HIGH); 56 | _transmitBitMask = digitalPinToBitMask(tx); 57 | uint8_t port = digitalPinToPort(tx); 58 | _transmitPortRegister = portOutputRegister(port); 59 | } 60 | 61 | size_t SoftwareSerial_write(uint8_t b) 62 | { 63 | // DISABLED: conflicts with I2C 64 | //uint8_t oldSREG = SREG; 65 | //cli(); // turn off interrupts for a clean txmit 66 | 67 | // Write the start bit 68 | SoftwareSerial_tx_pin_write(LOW); 69 | SoftwareSerial_tunedDelay(TX_DELAY + XMIT_START_ADJUSTMENT); 70 | 71 | // Write each of the 8 bits 72 | for (byte mask = 0x01; mask; mask <<= 1) 73 | { 74 | if (b & mask) // choose bit 75 | SoftwareSerial_tx_pin_write(HIGH); // send 1 76 | else 77 | SoftwareSerial_tx_pin_write(LOW); // send 0 78 | 79 | SoftwareSerial_tunedDelay(TX_DELAY); 80 | } 81 | SoftwareSerial_tx_pin_write(HIGH); // restore pin to natural state 82 | 83 | // DISABLED: conflicts with I2C 84 | // SREG = oldSREG; // turn interrupts back on 85 | 86 | SoftwareSerial_tunedDelay(TX_DELAY); 87 | 88 | return 1; 89 | } 90 | #endif 91 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyWireS_Custom/TinyWireS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | TinyWireS.cpp - a wrapper class for Don Blake's usiTwiSlave routines. 3 | Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment. 4 | 1/23/2011 BroHogan - brohoganx10 at gmail dot com 5 | 6 | **** See TinyWireS.h for Credits and Usage information **** 7 | 8 | This library is free software; you can redistribute it and/or modify it under the 9 | terms of the GNU General Public License as published by the Free Software 10 | Foundation; either version 2.1 of the License, or any later version. 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | */ 15 | 16 | extern "C" { 17 | #include 18 | #include "usiTwiSlave.h" 19 | #include 20 | } 21 | 22 | #include "TinyWireS_Custom.h" 23 | #include "Arduino.h" 24 | 25 | // Constructors //////////////////////////////////////////////////////////////// 26 | 27 | USI_TWI_S::USI_TWI_S(){ 28 | } 29 | 30 | 31 | // Public Methods ////////////////////////////////////////////////////////////// 32 | 33 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 34 | uint16_t USI_TWI_S::initCount(void) 35 | { 36 | return twi_initCount(); 37 | } 38 | 39 | void USI_TWI_S::begin(uint8_t slaveAddr){ // initialize I2C lib 40 | usiTwiSlaveInit(slaveAddr); 41 | } 42 | 43 | void USI_TWI_S::send(uint8_t data){ // send it back to master 44 | usiTwiTransmitByte(data); 45 | } 46 | 47 | uint8_t USI_TWI_S::available(){ // the bytes available that haven't been read yet 48 | return usiTwiAmountDataInReceiveBuffer(); 49 | //return usiTwiDataInReceiveBuffer(); // This is wrong as far as the Wire API is concerned since it returns boolean and not amount 50 | } 51 | 52 | uint8_t USI_TWI_S::receive(){ // returns the bytes received one at a time 53 | return usiTwiReceiveByte(); 54 | } 55 | 56 | // sets function called on slave write 57 | void USI_TWI_S::onReceive( void (*function)(uint8_t) ) 58 | { 59 | usi_onReceiverPtr = function; 60 | } 61 | 62 | // sets function called on slave read 63 | void USI_TWI_S::onRequest( void (*function)(void) ) 64 | { 65 | usi_onRequestPtr = function; 66 | } 67 | 68 | void TinyWireS_stop_check() 69 | { 70 | if (!usi_onReceiverPtr) 71 | { 72 | // no onReceive callback, nothing to do... 73 | return; 74 | } 75 | if (!(USISR & ( 1 << USIPF ))) 76 | { 77 | // Stop not detected 78 | return; 79 | } 80 | uint8_t amount = usiTwiAmountDataInReceiveBuffer(); 81 | if (amount == 0) 82 | { 83 | // no data in buffer 84 | return; 85 | } 86 | usi_onReceiverPtr(amount); 87 | } 88 | 89 | // Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c) 90 | void tws_delay(unsigned long ms) 91 | { 92 | uint16_t start = (uint16_t)micros(); 93 | while (ms > 0) 94 | { 95 | TinyWireS_stop_check(); 96 | if (((uint16_t)micros() - start) >= 1000) 97 | { 98 | ms--; 99 | start += 1000; 100 | } 101 | } 102 | } 103 | 104 | // Preinstantiate Objects ////////////////////////////////////////////////////// 105 | 106 | USI_TWI_S TinyWireS = USI_TWI_S(); 107 | 108 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyWireS_Custom/TinyWireS_Custom.h: -------------------------------------------------------------------------------- 1 | /* 2 | TinyWireS.h - a wrapper class for Don Blake's usiTwiSlave routines. 3 | Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment. 4 | 1/23/2011 BroHogan - brohoganx10 at gmail dot com 5 | 6 | Major credit and thanks to Don Blake for his usiTwiSlave code which makes this possible 7 | http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=51467&start=all&postdays=0&postorder=asc 8 | (Changed #define USI_START_COND_INT USISIF (was USICIF) in usiTwiSlave.h) 9 | 10 | NOTE! - It's very important to use pullups on the SDA & SCL lines! More so than with the Wire lib. 11 | Current Rx & Tx buffers set at 32 bytes - see usiTwiSlave.h 12 | 13 | USAGE is modeled after the standard Wire library . . . 14 | Put in setup(): 15 | TinyWireS.begin(I2C_SLAVE_ADDR); // initialize I2C lib & setup slave's address (7 bit - same as Wire) 16 | 17 | To Receive: 18 | someByte = TinyWireS.available(){ // returns the number of bytes in the received buffer 19 | someByte = TinyWireS.receive(){ // returns the next byte in the received buffer 20 | 21 | To Send: 22 | TinyWireS.send(uint8_t data){ // sends a requested byte to master 23 | 24 | TODO: (by others!) 25 | - onReceive and onRequest handlers are not implimented. 26 | - merge this class with TinyWireM for master & slave support in one library 27 | 28 | This library is free software; you can redistribute it and/or modify it under the 29 | terms of the GNU General Public License as published by the Free Software 30 | Foundation; either version 2.1 of the License, or any later version. 31 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 32 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 33 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 34 | */ 35 | 36 | #ifndef TinyWireS_h 37 | #define TinyWireS_h 38 | 39 | #include 40 | 41 | 42 | class USI_TWI_S 43 | { 44 | private: 45 | //static uint8_t USI_BytesAvail; 46 | 47 | public: 48 | USI_TWI_S(); 49 | void begin(uint8_t I2C_SLAVE_ADDR); 50 | void send(uint8_t data); 51 | uint8_t available(); 52 | uint8_t receive(); 53 | void onReceive( void (*)(uint8_t) ); 54 | void onRequest( void (*)(void) ); 55 | 56 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 57 | uint16_t initCount(void); 58 | 59 | }; 60 | 61 | void TinyWireS_stop_check(); 62 | // Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c) 63 | void tws_delay(unsigned long); 64 | 65 | extern USI_TWI_S TinyWireS; 66 | 67 | #endif 68 | 69 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyWireS_Custom/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TinyWireS 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | 13 | begin KEYWORD2 14 | send KEYWORD2 15 | available KEYWORD2 16 | receive KEYWORD2 17 | 18 | ####################################### 19 | # Instances (KEYWORD2) 20 | ####################################### 21 | 22 | TinyWireS KEYWORD2 23 | 24 | ####################################### 25 | # Constants (LITERAL1) 26 | ####################################### 27 | 28 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyWireS_Custom/usiTwiSlave.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | 3 | USI TWI Slave driver. 4 | 5 | Created by Donald R. Blake. donblake at worldnet.att.net 6 | Adapted by Jochen Toppe, jochen.toppe at jtoee.com 7 | 8 | --------------------------------------------------------------------------------- 9 | 10 | Created from Atmel source files for Application Note AVR312: Using the USI Module 11 | as an I2C slave. 12 | 13 | This program is free software; you can redistribute it and/or modify it under the 14 | terms of the GNU General Public License as published by the Free Software 15 | Foundation; either version 2 of the License, or (at your option) any later 16 | version. 17 | 18 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 19 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 20 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 21 | 22 | --------------------------------------------------------------------------------- 23 | 24 | Change Activity: 25 | 26 | Date Description 27 | ------ ------------- 28 | 16 Mar 2007 Created. 29 | 27 Mar 2007 Added support for ATtiny261, 461 and 861. 30 | 26 Apr 2007 Fixed ACK of slave address on a read. 31 | 04 Jul 2007 Fixed USISIF in ATtiny45 def 32 | 12 Dev 2009 Added callback functions for data requests 33 | 34 | ********************************************************************************/ 35 | 36 | 37 | /******************************************************************************** 38 | includes 39 | ********************************************************************************/ 40 | 41 | #include 42 | #include 43 | 44 | #include "usiTwiSlave.h" 45 | //#include "../common/util.h" 46 | 47 | 48 | /******************************************************************************** 49 | device dependent defines 50 | ********************************************************************************/ 51 | 52 | #if defined( __AVR_ATtiny2313__ ) 53 | # define DDR_USI DDRB 54 | # define PORT_USI PORTB 55 | # define PIN_USI PINB 56 | # define PORT_USI_SDA PB5 57 | # define PORT_USI_SCL PB7 58 | # define PIN_USI_SDA PINB5 59 | # define PIN_USI_SCL PINB7 60 | # define USI_START_COND_INT USISIF 61 | # define USI_START_VECTOR USI_START_vect 62 | # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect 63 | #endif 64 | 65 | #if defined(__AVR_ATtiny84__) | \ 66 | defined(__AVR_ATtiny44__) 67 | # define DDR_USI DDRA 68 | # define PORT_USI PORTA 69 | # define PIN_USI PINA 70 | # define PORT_USI_SDA PORTA6 71 | # define PORT_USI_SCL PORTA4 72 | # define PIN_USI_SDA PINA6 73 | # define PIN_USI_SCL PINA4 74 | # define USI_START_COND_INT USISIF 75 | # define USI_START_VECTOR USI_START_vect 76 | # define USI_OVERFLOW_VECTOR USI_OVF_vect 77 | #endif 78 | 79 | #if defined( __AVR_ATtiny25__ ) | \ 80 | defined( __AVR_ATtiny45__ ) | \ 81 | defined( __AVR_ATtiny85__ ) 82 | # define DDR_USI DDRB 83 | # define PORT_USI PORTB 84 | # define PIN_USI PINB 85 | # define PORT_USI_SDA PB0 86 | # define PORT_USI_SCL PB2 87 | # define PIN_USI_SDA PINB0 88 | # define PIN_USI_SCL PINB2 89 | # define USI_START_COND_INT USISIF 90 | # define USI_START_VECTOR USI_START_vect 91 | # define USI_OVERFLOW_VECTOR USI_OVF_vect 92 | #endif 93 | 94 | #if defined( __AVR_ATtiny26__ ) 95 | # define DDR_USI DDRB 96 | # define PORT_USI PORTB 97 | # define PIN_USI PINB 98 | # define PORT_USI_SDA PB0 99 | # define PORT_USI_SCL PB2 100 | # define PIN_USI_SDA PINB0 101 | # define PIN_USI_SCL PINB2 102 | # define USI_START_COND_INT USISIF 103 | # define USI_START_VECTOR USI_STRT_vect 104 | # define USI_OVERFLOW_VECTOR USI_OVF_vect 105 | #endif 106 | 107 | #if defined( __AVR_ATtiny261__ ) | \ 108 | defined( __AVR_ATtiny461__ ) | \ 109 | defined( __AVR_ATtiny861__ ) 110 | # define DDR_USI DDRB 111 | # define PORT_USI PORTB 112 | # define PIN_USI PINB 113 | # define PORT_USI_SDA PB0 114 | # define PORT_USI_SCL PB2 115 | # define PIN_USI_SDA PINB0 116 | # define PIN_USI_SCL PINB2 117 | # define USI_START_COND_INT USISIF 118 | # define USI_START_VECTOR USI_START_vect 119 | # define USI_OVERFLOW_VECTOR USI_OVF_vect 120 | #endif 121 | 122 | #if defined( __AVR_ATmega165__ ) | \ 123 | defined( __AVR_ATmega325__ ) | \ 124 | defined( __AVR_ATmega3250__ ) | \ 125 | defined( __AVR_ATmega645__ ) | \ 126 | defined( __AVR_ATmega6450__ ) | \ 127 | defined( __AVR_ATmega329__ ) | \ 128 | defined( __AVR_ATmega3290__ ) 129 | # define DDR_USI DDRE 130 | # define PORT_USI PORTE 131 | # define PIN_USI PINE 132 | # define PORT_USI_SDA PE5 133 | # define PORT_USI_SCL PE4 134 | # define PIN_USI_SDA PINE5 135 | # define PIN_USI_SCL PINE4 136 | # define USI_START_COND_INT USISIF 137 | # define USI_START_VECTOR USI_START_vect 138 | # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect 139 | #endif 140 | 141 | #if defined( __AVR_ATmega169__ ) 142 | # define DDR_USI DDRE 143 | # define PORT_USI PORTE 144 | # define PIN_USI PINE 145 | # define PORT_USI_SDA PE5 146 | # define PORT_USI_SCL PE4 147 | # define PIN_USI_SDA PINE5 148 | # define PIN_USI_SCL PINE4 149 | # define USI_START_COND_INT USISIF 150 | # define USI_START_VECTOR USI_START_vect 151 | # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect 152 | #endif 153 | 154 | 155 | 156 | /******************************************************************************** 157 | 158 | functions implemented as macros 159 | 160 | ********************************************************************************/ 161 | 162 | #define SET_USI_TO_SEND_ACK( ) \ 163 | { \ 164 | /* prepare ACK */ \ 165 | USIDR = 0; \ 166 | /* set SDA as output */ \ 167 | DDR_USI |= ( 1 << PORT_USI_SDA ); \ 168 | /* clear all interrupt flags, except Start Cond */ \ 169 | USISR = \ 170 | ( 0 << USI_START_COND_INT ) | \ 171 | ( 1 << USIOIF ) | ( 1 << USIPF ) | \ 172 | ( 1 << USIDC )| \ 173 | /* set USI counter to shift 1 bit */ \ 174 | ( 0x0E << USICNT0 ); \ 175 | } 176 | 177 | #define SET_USI_TO_READ_ACK( ) \ 178 | { \ 179 | /* set SDA as input */ \ 180 | DDR_USI &= ~( 1 << PORT_USI_SDA ); \ 181 | /* prepare ACK */ \ 182 | USIDR = 0; \ 183 | /* clear all interrupt flags, except Start Cond */ \ 184 | USISR = \ 185 | ( 0 << USI_START_COND_INT ) | \ 186 | ( 1 << USIOIF ) | \ 187 | ( 1 << USIPF ) | \ 188 | ( 1 << USIDC ) | \ 189 | /* set USI counter to shift 1 bit */ \ 190 | ( 0x0E << USICNT0 ); \ 191 | } 192 | 193 | #define SET_USI_TO_TWI_START_CONDITION_MODE( ) \ 194 | { \ 195 | USICR = \ 196 | /* enable Start Condition Interrupt, disable Overflow Interrupt */ \ 197 | ( 1 << USISIE ) | ( 0 << USIOIE ) | \ 198 | /* set USI in Two-wire mode, no USI Counter overflow hold */ \ 199 | ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \ 200 | /* Shift Register Clock Source = External, positive edge */ \ 201 | /* 4-Bit Counter Source = external, both edges */ \ 202 | ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \ 203 | /* no toggle clock-port pin */ \ 204 | ( 0 << USITC ); \ 205 | USISR = \ 206 | /* clear all interrupt flags, except Start Cond */ \ 207 | ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \ 208 | ( 1 << USIDC ) | ( 0x0 << USICNT0 ); \ 209 | } 210 | 211 | #define SET_USI_TO_SEND_DATA( ) \ 212 | { \ 213 | /* set SDA as output */ \ 214 | DDR_USI |= ( 1 << PORT_USI_SDA ); \ 215 | /* clear all interrupt flags, except Start Cond */ \ 216 | USISR = \ 217 | ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \ 218 | ( 1 << USIDC) | \ 219 | /* set USI to shift out 8 bits */ \ 220 | ( 0x0 << USICNT0 ); \ 221 | } 222 | 223 | #define SET_USI_TO_READ_DATA( ) \ 224 | { \ 225 | /* set SDA as input */ \ 226 | DDR_USI &= ~( 1 << PORT_USI_SDA ); \ 227 | /* clear all interrupt flags, except Start Cond */ \ 228 | USISR = \ 229 | ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \ 230 | ( 1 << USIPF ) | ( 1 << USIDC ) | \ 231 | /* set USI to shift out 8 bits */ \ 232 | ( 0x0 << USICNT0 ); \ 233 | } 234 | 235 | #define USI_RECEIVE_CALLBACK() \ 236 | { \ 237 | if (usi_onReceiverPtr) \ 238 | { \ 239 | if (usiTwiDataInReceiveBuffer()) \ 240 | { \ 241 | usi_onReceiverPtr(usiTwiAmountDataInReceiveBuffer()); \ 242 | } \ 243 | } \ 244 | } 245 | 246 | #define ONSTOP_USI_RECEIVE_CALLBACK() \ 247 | { \ 248 | if (USISR & ( 1 << USIPF )) \ 249 | { \ 250 | USI_RECEIVE_CALLBACK(); \ 251 | } \ 252 | } 253 | 254 | 255 | #define USI_REQUEST_CALLBACK() \ 256 | { \ 257 | USI_RECEIVE_CALLBACK(); \ 258 | if(usi_onRequestPtr) usi_onRequestPtr(); \ 259 | } 260 | 261 | /******************************************************************************** 262 | 263 | typedef's 264 | 265 | ********************************************************************************/ 266 | 267 | typedef enum 268 | { 269 | USI_SLAVE_CHECK_ADDRESS = 0x00, 270 | USI_SLAVE_SEND_DATA = 0x01, 271 | USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02, 272 | USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03, 273 | USI_SLAVE_REQUEST_DATA = 0x04, 274 | USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05 275 | } overflowState_t; 276 | 277 | 278 | 279 | /******************************************************************************** 280 | 281 | local variables 282 | 283 | ********************************************************************************/ 284 | 285 | static uint8_t slaveAddress; 286 | static volatile overflowState_t overflowState; 287 | 288 | 289 | static uint8_t rxBuf[ TWI_RX_BUFFER_SIZE ]; 290 | static volatile uint8_t rxHead; 291 | static volatile uint8_t rxTail; 292 | 293 | static uint8_t txBuf[ TWI_TX_BUFFER_SIZE ]; 294 | static volatile uint8_t txHead; 295 | static volatile uint8_t txTail; 296 | 297 | // data requested callback 298 | void (*_onTwiDataRequest)(void); 299 | 300 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 301 | static volatile uint16_t twi_initCounter; 302 | 303 | /******************************************************************************** 304 | 305 | local functions 306 | 307 | ********************************************************************************/ 308 | 309 | 310 | 311 | // flushes the TWI buffers 312 | 313 | static 314 | void 315 | flushTwiBuffers( 316 | void 317 | ) 318 | { 319 | rxTail = 0; 320 | rxHead = 0; 321 | txTail = 0; 322 | txHead = 0; 323 | } // end flushTwiBuffers 324 | 325 | 326 | 327 | /******************************************************************************** 328 | 329 | public functions 330 | 331 | ********************************************************************************/ 332 | 333 | 334 | 335 | // initialise USI for TWI slave mode 336 | 337 | void 338 | usiTwiSlaveInit( 339 | uint8_t ownAddress 340 | ) 341 | { 342 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 343 | twi_initCounter++; 344 | 345 | flushTwiBuffers( ); 346 | 347 | slaveAddress = ownAddress; 348 | 349 | // In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL 350 | // low when a start condition is detected or a counter overflow (only 351 | // for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released 352 | // by the ISRs (USI_START_vect and USI_OVERFLOW_vect). 353 | 354 | // Set SCL and SDA as output 355 | DDR_USI |= ( 1 << PORT_USI_SCL ) | ( 1 << PORT_USI_SDA ); 356 | 357 | // set SCL high 358 | PORT_USI |= ( 1 << PORT_USI_SCL ); 359 | 360 | // set SDA high 361 | PORT_USI |= ( 1 << PORT_USI_SDA ); 362 | 363 | // Set SDA as input 364 | DDR_USI &= ~( 1 << PORT_USI_SDA ); 365 | 366 | USICR = 367 | // enable Start Condition Interrupt 368 | ( 1 << USISIE ) | 369 | // disable Overflow Interrupt 370 | ( 0 << USIOIE ) | 371 | // set USI in Two-wire mode, no USI Counter overflow hold 372 | ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | 373 | // Shift Register Clock Source = external, positive edge 374 | // 4-Bit Counter Source = external, both edges 375 | ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | 376 | // no toggle clock-port pin 377 | ( 0 << USITC ); 378 | 379 | // clear all interrupt flags and reset overflow counter 380 | 381 | USISR = ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | ( 1 << USIDC ); 382 | 383 | } // end usiTwiSlaveInit 384 | 385 | 386 | bool usiTwiDataInTransmitBuffer(void) 387 | { 388 | 389 | // return 0 (false) if the receive buffer is empty 390 | return txHead != txTail; 391 | 392 | } // end usiTwiDataInTransmitBuffer 393 | 394 | 395 | // put data in the transmission buffer, wait if buffer is full 396 | 397 | void 398 | usiTwiTransmitByte( 399 | uint8_t data 400 | ) 401 | { 402 | 403 | uint8_t tmphead; 404 | 405 | // calculate buffer index 406 | tmphead = ( txHead + 1 ) & TWI_TX_BUFFER_MASK; 407 | 408 | // wait for free space in buffer 409 | while ( tmphead == txTail ); 410 | 411 | // store data in buffer 412 | txBuf[ tmphead ] = data; 413 | 414 | // store new index 415 | txHead = tmphead; 416 | 417 | } // end usiTwiTransmitByte 418 | 419 | 420 | 421 | 422 | 423 | // return a byte from the receive buffer, wait if buffer is empty 424 | 425 | uint8_t 426 | usiTwiReceiveByte( 427 | void 428 | ) 429 | { 430 | 431 | // wait for Rx data 432 | while ( rxHead == rxTail ); 433 | 434 | // calculate buffer index 435 | rxTail = ( rxTail + 1 ) & TWI_RX_BUFFER_MASK; 436 | 437 | // return data from the buffer. 438 | return rxBuf[ rxTail ]; 439 | 440 | } // end usiTwiReceiveByte 441 | 442 | 443 | 444 | // check if there is data in the receive buffer 445 | 446 | bool 447 | usiTwiDataInReceiveBuffer( 448 | void 449 | ) 450 | { 451 | 452 | // return 0 (false) if the receive buffer is empty 453 | return rxHead != rxTail; 454 | 455 | } // end usiTwiDataInReceiveBuffer 456 | 457 | uint8_t usiTwiAmountDataInReceiveBuffer(void) 458 | { 459 | if (rxHead == rxTail) 460 | { 461 | return 0; 462 | } 463 | if (rxHead < rxTail) 464 | { 465 | // Is there a better way ? 466 | return ((int8_t)rxHead - (int8_t)rxTail) + TWI_RX_BUFFER_SIZE; 467 | } 468 | return rxHead - rxTail; 469 | } 470 | 471 | 472 | 473 | 474 | /******************************************************************************** 475 | 476 | USI Start Condition ISR 477 | 478 | ********************************************************************************/ 479 | 480 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 481 | uint16_t twi_initCount(void) 482 | { 483 | return twi_initCounter; 484 | } 485 | 486 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 487 | static volatile uint32_t twi_timeOutc; 488 | uint8_t twi_timeOut(uint8_t ini) 489 | { 490 | if (ini) twi_timeOutc=0; else twi_timeOutc++; 491 | if (twi_timeOutc>=100000UL) { 492 | twi_timeOutc=0; 493 | usiTwiSlaveInit(slaveAddress); 494 | return 1; 495 | } 496 | return 0; 497 | } 498 | 499 | 500 | ISR( USI_START_VECTOR ) 501 | { 502 | 503 | /* 504 | // This triggers on second write, but claims to the callback there is only *one* byte in buffer 505 | ONSTOP_USI_RECEIVE_CALLBACK(); 506 | */ 507 | /* 508 | // This triggers on second write, but claims to the callback there is only *one* byte in buffer 509 | USI_RECEIVE_CALLBACK(); 510 | */ 511 | 512 | // set default starting conditions for new TWI package 513 | overflowState = USI_SLAVE_CHECK_ADDRESS; 514 | 515 | // set SDA as input 516 | DDR_USI &= ~( 1 << PORT_USI_SDA ); 517 | 518 | // wait for SCL to go low to ensure the Start Condition has completed (the 519 | // start detector will hold SCL low ) - if a Stop Condition arises then leave 520 | // the interrupt to prevent waiting forever - don't use USISR to test for Stop 521 | // Condition as in Application Note AVR312 because the Stop Condition Flag is 522 | // going to be set from the last TWI sequence 523 | 524 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 525 | while ( 526 | // SCL is high 527 | ( PIN_USI & ( 1 << PIN_USI_SCL ) ) && 528 | // and SDA is low 529 | !( ( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) 530 | ){ 531 | if (twi_timeOut(0)) // this code to avoid that the wire libary hangs on SDA low without SCL signals. 532 | break; 533 | continue; 534 | } 535 | 536 | 537 | if ( !( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) 538 | { 539 | 540 | // a Stop Condition did not occur 541 | 542 | USICR = 543 | // keep Start Condition Interrupt enabled to detect RESTART 544 | ( 1 << USISIE ) | 545 | // enable Overflow Interrupt 546 | ( 1 << USIOIE ) | 547 | // set USI in Two-wire mode, hold SCL low on USI Counter overflow 548 | ( 1 << USIWM1 ) | ( 1 << USIWM0 ) | 549 | // Shift Register Clock Source = External, positive edge 550 | // 4-Bit Counter Source = external, both edges 551 | ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | 552 | // no toggle clock-port pin 553 | ( 0 << USITC ); 554 | 555 | } 556 | else 557 | { 558 | // a Stop Condition did occur 559 | 560 | USICR = 561 | // enable Start Condition Interrupt 562 | ( 1 << USISIE ) | 563 | // disable Overflow Interrupt 564 | ( 0 << USIOIE ) | 565 | // set USI in Two-wire mode, no USI Counter overflow hold 566 | ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | 567 | // Shift Register Clock Source = external, positive edge 568 | // 4-Bit Counter Source = external, both edges 569 | ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | 570 | // no toggle clock-port pin 571 | ( 0 << USITC ); 572 | 573 | } // end if 574 | 575 | USISR = 576 | // clear interrupt flags - resetting the Start Condition Flag will 577 | // release SCL 578 | ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | 579 | ( 1 << USIPF ) |( 1 << USIDC ) | 580 | // set USI to sample 8 bits (count 16 external SCL pin toggles) 581 | ( 0x0 << USICNT0); 582 | 583 | 584 | } // end ISR( USI_START_VECTOR ) 585 | 586 | 587 | 588 | /******************************************************************************** 589 | 590 | USI Overflow ISR 591 | 592 | Handles all the communication. 593 | 594 | Only disabled when waiting for a new Start Condition. 595 | 596 | ********************************************************************************/ 597 | 598 | ISR( USI_OVERFLOW_VECTOR ) 599 | { 600 | 601 | switch ( overflowState ) 602 | { 603 | 604 | // Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK, 605 | // else reset USI 606 | case USI_SLAVE_CHECK_ADDRESS: 607 | if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) ) 608 | { 609 | // callback 610 | if(_onTwiDataRequest) _onTwiDataRequest(); 611 | if ( USIDR & 0x01 ) 612 | { 613 | overflowState = USI_SLAVE_SEND_DATA; 614 | } 615 | else 616 | { 617 | overflowState = USI_SLAVE_REQUEST_DATA; 618 | } // end if 619 | SET_USI_TO_SEND_ACK( ); 620 | } 621 | else 622 | { 623 | SET_USI_TO_TWI_START_CONDITION_MODE( ); 624 | } 625 | break; 626 | 627 | // Master write data mode: check reply and goto USI_SLAVE_SEND_DATA if OK, 628 | // else reset USI 629 | case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA: 630 | if ( USIDR ) 631 | { 632 | // if NACK, the master does not want more data 633 | SET_USI_TO_TWI_START_CONDITION_MODE( ); 634 | return; 635 | } 636 | // from here we just drop straight into USI_SLAVE_SEND_DATA if the 637 | // master sent an ACK 638 | 639 | // copy data from buffer to USIDR and set USI to shift byte 640 | // next USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA 641 | case USI_SLAVE_SEND_DATA: 642 | USI_REQUEST_CALLBACK(); 643 | // Get data from Buffer 644 | if ( txHead != txTail ) 645 | { 646 | txTail = ( txTail + 1 ) & TWI_TX_BUFFER_MASK; 647 | USIDR = txBuf[ txTail ]; 648 | } 649 | else 650 | { 651 | // the buffer is empty 652 | SET_USI_TO_READ_ACK( ); // This might be neccessary sometimes see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=805227#805227 653 | SET_USI_TO_TWI_START_CONDITION_MODE( ); 654 | return; 655 | } // end if 656 | overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA; 657 | SET_USI_TO_SEND_DATA( ); 658 | break; 659 | 660 | // set USI to sample reply from master 661 | // next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA 662 | case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA: 663 | overflowState = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA; 664 | SET_USI_TO_READ_ACK( ); 665 | break; 666 | 667 | // Master read data mode: set USI to sample data from master, next 668 | // USI_SLAVE_GET_DATA_AND_SEND_ACK 669 | case USI_SLAVE_REQUEST_DATA: 670 | overflowState = USI_SLAVE_GET_DATA_AND_SEND_ACK; 671 | SET_USI_TO_READ_DATA( ); 672 | break; 673 | 674 | // copy data from USIDR and send ACK 675 | // next USI_SLAVE_REQUEST_DATA 676 | case USI_SLAVE_GET_DATA_AND_SEND_ACK: 677 | // put data into buffer 678 | // Not necessary, but prevents warnings 679 | rxHead = ( rxHead + 1 ) & TWI_RX_BUFFER_MASK; 680 | // check buffer size 681 | if (rxHead == rxTail) { 682 | // overrun 683 | rxHead = (rxHead + TWI_RX_BUFFER_SIZE - 1) & TWI_RX_BUFFER_MASK; 684 | } else { 685 | rxBuf[ rxHead ] = USIDR; 686 | } 687 | // next USI_SLAVE_REQUEST_DATA 688 | overflowState = USI_SLAVE_REQUEST_DATA; 689 | SET_USI_TO_SEND_ACK( ); 690 | break; 691 | 692 | } // end switch 693 | 694 | } // end ISR( USI_OVERFLOW_VECTOR ) 695 | -------------------------------------------------------------------------------- /TinyI2CWatchdog/TinyWireS_Custom/usiTwiSlave.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | 3 | Header file for the USI TWI Slave driver. 4 | 5 | Created by Donald R. Blake 6 | donblake at worldnet.att.net 7 | 8 | --------------------------------------------------------------------------------- 9 | 10 | Created from Atmel source files for Application Note AVR312: Using the USI Module 11 | as an I2C slave. 12 | 13 | This program is free software; you can redistribute it and/or modify it under the 14 | terms of the GNU General Public License as published by the Free Software 15 | Foundation; either version 2 of the License, or (at your option) any later 16 | version. 17 | 18 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 19 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 20 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 21 | 22 | --------------------------------------------------------------------------------- 23 | 24 | Change Activity: 25 | 26 | Date Description 27 | ------ ------------- 28 | 15 Mar 2007 Created. 29 | 30 | ********************************************************************************/ 31 | 32 | 33 | 34 | #ifndef _USI_TWI_SLAVE_H_ 35 | #define _USI_TWI_SLAVE_H_ 36 | 37 | 38 | 39 | /******************************************************************************** 40 | 41 | includes 42 | 43 | ********************************************************************************/ 44 | 45 | #include 46 | 47 | 48 | 49 | /******************************************************************************** 50 | 51 | prototypes 52 | 53 | ********************************************************************************/ 54 | 55 | void usiTwiSlaveInit( uint8_t ); 56 | void usiTwiTransmitByte( uint8_t ); 57 | uint8_t usiTwiReceiveByte( void ); 58 | bool usiTwiDataInReceiveBuffer( void ); 59 | void (*_onTwiDataRequest)(void); 60 | bool usiTwiDataInTransmitBuffer(void); 61 | uint8_t usiTwiAmountDataInReceiveBuffer(void); 62 | // on_XXX handler pointers 63 | void (*usi_onRequestPtr)(void); 64 | void (*usi_onReceiverPtr)(uint8_t); 65 | 66 | // mvdbro, 2016-01-01, avoid hang on bus error with SDA sustained low and SCL sustained high 67 | uint16_t twi_initCount(void); 68 | 69 | /******************************************************************************** 70 | 71 | driver buffer definitions 72 | 73 | ********************************************************************************/ 74 | 75 | // permitted RX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256 76 | 77 | #ifndef TWI_RX_BUFFER_SIZE 78 | #define TWI_RX_BUFFER_SIZE ( 16 ) 79 | #endif 80 | #define TWI_RX_BUFFER_MASK ( TWI_RX_BUFFER_SIZE - 1 ) 81 | 82 | #if ( TWI_RX_BUFFER_SIZE & TWI_RX_BUFFER_MASK ) 83 | # error TWI RX buffer size is not a power of 2 84 | #endif 85 | 86 | // permitted TX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256 87 | 88 | #ifndef TWI_TX_BUFFER_SIZE 89 | #define TWI_TX_BUFFER_SIZE ( 16 ) 90 | #endif 91 | #define TWI_TX_BUFFER_MASK ( TWI_TX_BUFFER_SIZE - 1 ) 92 | 93 | #if ( TWI_TX_BUFFER_SIZE & TWI_TX_BUFFER_MASK ) 94 | # error TWI TX buffer size is not a power of 2 95 | #endif 96 | 97 | 98 | 99 | #endif // ifndef _USI_TWI_SLAVE_H_ 100 | --------------------------------------------------------------------------------