// This file v1ss_vX.ino is a very simple implementation of Software Serial intended for use on // an Arduino Uno. // // It's intended to be included in the same directory as the Arduino program that uses it (V1SoftSerial). // It will then appear as a second tab in the Arduino IDE. // // This code is written for the Uno or an Atmega 328 // it assumes a clock speed of 16MHz // it works at 9600 baud and probably also at 4800 and 19200 // it is only designed for the 8N1 protocol // // it can only Listen // // Rx is pin 3 (INT 1) // // The user functions are // char v1ssBegin() // void v1ssEnd() // byte v1ssAvailable() // returns number of bytes in buffer // int v1ssRead() // returns next byte or -1 if buffer is empty // // Calling any other function will likely cause problems // // // Arduino Resources used ... // Pin 3 and INT1 -- Rx // Timer2A // Timer2 prescaler //===================================== // variables - not accessible by user const unsigned long v1ssBaudRate = 57600; const byte v1ssBufSize = 32; const unsigned long v1ssIdleTimeoutMillis = 10; const boolean v1ssdebugging = 0; // controls printing of debug information const byte v1ssRxPin = 3; // the pin for INT1 byte v1ssBufTail = 0; volatile byte v1ssBufHead = 0; volatile byte v1ssBuffer[v1ssBufSize]; // use same buffer for send and receive byte v1ssBaudTimerCount; byte v1ssBaudTimerFirstCount; volatile byte v1ssBitCount = 0; // counts bits as they are received volatile byte v1ssRecvByte = 0; // holder for received byte volatile boolean v1ssIsListening = false; boolean v1ssReady = false; byte v1ssNumBytesToSend = 0; byte v1ssBufferPos = 0; // keeps track of byte to send within the buffer volatile byte v1ssNextByteToSend; volatile byte v1ssNextBit; volatile byte v1ssBitPos = 0; // position of bit within byte being sent void (*v1ssTimerFunctionP)(void); // function pointer for use with ISR(TIMER2_COMPA_vect) volatile unsigned long v1ssCurIdleMicros; volatile unsigned long v1ssPrevIdleMicros; //unsigned long v1ssIdleIntervalMicros; // this didn't get used anywhere?? int sensorPin = A5; volatile int sensorValue = 0; //===================================== // user accessible functions //=========== char v1ssBegin() { v1ssDbg("Beginning ", 0); v1ssStopAll(); // prepare Rx pin pinMode(v1ssRxPin, INPUT); //had , INPUT_PULLUP // prepare Timer2 TCCR2A = B00000000; // set normal mode (not fast PWM) // with fast PWM the clock resets when it matches and gives the wrong timing TCCR2B = B0000010; // set the prescaler to 32 - this gives counts at 2usec intervals. B00000011 // set the prescaler to 8 - this gives counts at 0.5usec intervals. B00000010 //v1ssBaudTimerCount = 1000000UL / v1ssBaudRate / 0.5; v1ssBaudTimerCount = 28; //Serial.print("BaudTimer = "); //Serial.println(v1ssBaudTimerCount); // Number of counts per baud at 2usecs per count, // for the faster interupt speed, number of count per baud is at 0.5us // 16MHz / 32 (prescaler) = 0.5MHz => 2us is a single Interupt cycle time, // so if we have 52 counts of 2us we would have waited 104us or one baud cyce (1/9600) // 16MHz / 8 (prescaler) = 2MHz => 0.5us per count, // We need 35 counts at 0.5us to have waited 17us (1/57600) // set baud rate timing v1ssBaudTimerFirstCount = v1ssBaudTimerCount * 1; // I thought a longer period might be needed to get into the first bit after the start bit - but apparently not // Normally an interrupt occurs after v1ssBaudTimerCount * 2 usecs (104usecs for 9600 baud) // For the V1 an interrupt occurs after v1ssBaudTimerCount * 0.5 usecs (17usecs for 57600 baud) v1ssDbg("baudTimer ", v1ssBaudTimerCount); // length of required idle period to synchronize start bit detection //v1ssIdleIntervalMicros = 1000000UL / v1ssBaudRate * 25 ; // 2.5 byte lengths - this didn't seem to be used at all?? //v1ssDbg("idleCount ", v1ssIdleIntervalMicros); v1ssPrepareToListen(); v1ssReady = true; } //============ void v1ssEnd() { v1ssStopAll(); v1ssReady = false; } //============ int v1ssAvailable() { if (! v1ssReady) // true at end of begin() { return -1; } if (v1ssNumBytesToSend > 0) // only accept it if sender is idle { return -1; } if (! v1ssIsListening) // true if (v1ssIdleCount >= v1ssMinIdleCount) { char ok = v1ssPrepareToListen(); if (! ok) { return -1; } } char v1ssByteCount = v1ssBufHead - v1ssBufTail; if (v1ssByteCount < 0) { v1ssByteCount += v1ssBufSize; } return v1ssByteCount; } //============ int v1ssRead() { if (! v1ssReady) { return -1; } if (v1ssNumBytesToSend > 0) // only accept read if sender is idle { return -1; } if (! v1ssIsListening) { char ok = v1ssPrepareToListen(); // calling ok if (! ok) { return -1; } } if (v1ssBufTail == v1ssBufHead) { return -1; } else { v1ssBufTail = (v1ssBufTail + 1) % v1ssBufSize; // basically v1ssBufTail++ return v1ssBuffer[v1ssBufTail]; } } //============================ // Internal Functions //============ void v1ssStopAll() { TIMSK2 &= B11111100; // Disable Timer2 Compare Match A Interrupt EIMSK &= B11111101; // Disable Interrupt 1 v1ssIsListening = false; } //============ char v1ssPrepareToListen() // This is the business function re setup :) { v1ssStopAll(); // check for idle period //unsigned long v1ssIdleDelay = 1000000UL / v1ssBaudRate / 0.5; // usecs between checks - half the baud interval 17 / 2 = 8us unsigned long v1ssIdleDelay = 10; // usecs between checks - half the baud interval 17 / 2 = 8us unsigned long v1ssStartMillis = millis(); byte v1ssIdleCount = 0; byte v1ssMinIdleCount = 10; // 20 bits checked at half the bit interval plus a little extra // (2.5 bytes or 20 bits @ 2us = 40us period -> 16MHz clock / 32) // (2.5 bytes or 20 bits @ 0.5us = 10us period -> 16MHz clock / 8) v1ssDbg("idleDelay ", v1ssIdleDelay); v1ssDbg("baudTimerCount ",v1ssBaudTimerCount); v1ssDbg("PIND ", PIND & B00001000); // this is testing for a high on Pin3 (if you were wondering) while (millis() - v1ssStartMillis < v1ssIdleTimeoutMillis) { v1ssIdleCount++; v1ssDbg("v1ssStartMillis ", millis() - v1ssStartMillis); if ( ! (PIND & B00001000) ) // if pin3 is low (not high) { v1ssIdleCount = 0; // start counting again } v1ssDbg("v1ssIdleCount ", v1ssIdleCount); // if pin3 has been high for long enough if (v1ssIdleCount >= v1ssMinIdleCount) { // reset receive buffer v1ssBufHead = 0; v1ssBufTail = 0; // set startBit interrupt EICRA |= B0001000; // External Interrupt 1 FALLING EIFR |= B00000010; // clear interrupt 1 flag - prevents any spurious carry-over EIMSK |= B0000010; // enable Interrupt 1 v1ssTimerFunctionP = &v1ssTimerGetBitsISR; v1ssIsListening = true; v1ssDbg("v1ssIsListening = ", v1ssIsListening); } delayMicroseconds(v1ssIdleDelay); // what exactly is this delay for...start bit? } v1ssDbg("LISTENING = ", v1ssIsListening); return v1ssIsListening; } //============ void v1ssDbg( char dbgStr[], unsigned long dbgVal) { if (v1ssdebugging) { Serial.print(dbgStr); Serial.println(dbgVal); } } //===================================== // Interrupt Routines ISR(INT1_vect) { // this is called when a start bit is detected // turn off the startBit interrupt EIMSK &= B11111101; // Disable Interrupt 1 // start the bitInterval timer OCR2A = TCNT2 + (v1ssBaudTimerFirstCount); // This sets up Timer2A to run at the baud rate plus shifts us 1/2 a bit value // This is so when we sample, we are in the middle of the bit TIMSK2 &= B11111100; // Disable compareMatchA and Overflow TIFR2 |= B00000010; // Clear Timer2 Compare Match A Flag - not sure this is essential TIMSK2 |= B00000010; // Enable Timer2 Compare Match A Interrupt // set counters and buffer index v1ssBitCount = 0; v1ssRecvByte = 0; } //============= ISR(TIMER2_COMPA_vect) { // this is called by the bitInterval timer v1ssTimerFunctionP(); // this is a function pointer and will call different functions // Points to v1ssTimerGetBitsISR() // Was for when send code was present // left it in to add send code later } //============= void v1ssTimerGetBitsISR() // This is the business function for receiving each bit :) { // This is one of the functions called by TimerFunctionP() // read the bit value first byte v1ssNewBit = PIND & B00001000; // Arduino Pin 3 is pin3 in Port D // update the counter for the next bit interval OCR2A = TCNT2 + v1ssBaudTimerCount; if (v1ssBitCount == 8) // this is the stop bit { // stop the bitIntervalTimer TIMSK2 &= B11111100; // Disable Timer2 Compare Match A and overflow Interrupts // enable Interrupt 0 EIFR |= B00000010; // Clear interrupt 1 flag - prevents any spurious carry-over EIMSK |= B00000010; // Enable Interrupt 1 to detect the next start bit } v1ssNewBit = v1ssNewBit >> 3; // gets the bit into posn 0 v1ssNewBit = v1ssNewBit << v1ssBitCount; // moves it to the correct place v1ssRecvByte += v1ssNewBit; if (v1ssBitCount == 7) { // update the bufHead index // but prevent overwriting the tail of the buffer byte v1ssTestHead = (v1ssBufHead + 1) % v1ssBufSize; if (v1ssTestHead != v1ssBufTail) // otherwise v1ssBufHead is unchanged { v1ssBufHead = v1ssTestHead; } // and save the byte v1ssBuffer[v1ssBufHead] = v1ssRecvByte; } v1ssBitCount++; } //========END=============