Hi there,
I'm working on a little project then needs to have two serial devices connected to it, it basically reads analog voltages from A0-5, and a gps module connected via a serial port at 9600 baud, does a little bit of processing and sends this data over a second serial port.
The second serial port needs be full duplex, as it is used to also receive control commands from a pc, so I have naturally used the hardware serial port for this. The GPS module therefore needs to be connected using some sort of software UART.
I looked at first at the Software Serial library, but the problem with that is that it from what I can tell from the code, it blocks the processor during receive. The module is fixed at 9600 b/s, a full set of NMEA messages could easily be 4-500 bytes and so this will block the processor for nearly half a second, time that needs to be spent measuring A0-5, which I want to be happening about 10 times a second.
I therefore decided to build my own software serial implementation using an external interrupt on pin 2, and timer1, I know this is a very resource heavy way to do it, but I don't need these functions for anything else so that doesn't concern me.
I've written the code below to test my iplementation, using an uno, with pin 0 (HW UART Rx) connected directly to pin 2. I then just send it characters from realterm and it should echo them back to me. The problem is that it seems to miss the first bit every time, so for example 'a' (B01100001) gets returned as 0xC2,0xFF (B11000010 B11111111) i.e. it reads the start bit as the LSB of 'a' reads bits 0-6 as 1-7 then the last bit, a 0, is recognised as a new byte, then it gets all 1's (no transmission).
I cannot understand at all from my code why it reads the start bit as bit 0, but I've been trying to get it to work for ages now.
Would someone more experienced than myself mind having a look at my code and seeing if they can work out the problem.
I have made this to work with an uno, though I think should work with any 16MHz processor.
Thanks,
Tobyb121
EDIT: One other thing I should mention, in debugging this I added an array that would store value of micros() at each timer interrupt and the initial falling edge, this showed that the first timer interrupt happens about very quickly (<20us I think) after the falling edge, implying that it tirggers immediately after exiting the ext interrupt routine
#define BUFFER_SIZE 128 //Number of bytes to store in a buffer
byte _i; //current bit
char _b; //shift byte to store current byte
char buf[BUFFER_SIZE]; //buffer to store received bytes
byte _ptrRead; //position in buffer to read from
byte _ptrWrite; //position in buffer to write to
int _baud; //period of each bit in instruction cycles
void initialiseSerial(int baud){
_ptrRead=0;
_ptrWrite=0;
pinMode(PIN2,INPUT);
_baud=2000000/baud; //Calculate baud (based on 16MHz processor)
SREG|=B10000000; //Global interrupt enable
// Setup timer1 to interrupt on overflow, internal clock, 1:8 prescaler
TCCR1A=0;
TCCR1B=B00000010;
TIMSK1=B00000000;
//External Interrupt enable on pin2 falling edge
EICRA=B00000010;
EIMSK=B00000001;
}
void Timer1InterruptRoutine(){
// reset timer to trigger in next bit
TCNT1=0xFFFF-_baud;
//if less than eight bytes have been written
if(_i<8){
_b|=digitalRead(PIN2)<<_i; //read the pin and add it to the temp byte
_i++;
}
else{ //when all bytes have been received
buf[_ptrWrite++]=_b; //Add the temp byte to the buffer
TIMSK1=B00000000; //Switch off the timer
EIMSK=B00000001; //Re-enable the falling edge interrupt
}
}
ISR(TIMER1_OVF_vect){
Timer1InterruptRoutine();
}
//Fired when falling edge detected on pin 2
void Int0InterruptRoutine(){
//reset temporary storage variables
_b=0;
_i=0;
//Disable external interrupt for now
EIMSK=B00000000;
//Enable timer and set it to overflow in 1.5*baud ticks, in middle of bit0
TCNT1=0xFFFF-3*_baud/2;
TIMSK1=B00000001;
}
ISR(INT0_vect){
Int0InterruptRoutine();
}
// Read a byte from the buffer and increment the pointer
char readByte(){
char b;
b=buf[_ptrRead++];
_ptrRead%=BUFFER_SIZE;
return b;
}
// Check if there are bytes available to read
byte bytesAvailable(){
if(_ptrWrite<_ptrRead)
return _ptrWrite+BUFFER_SIZE-_ptrRead;
else
return _ptrWrite-_ptrRead;
}
void setup()
{
pinMode(PIN3,OUTPUT);
pinMode(PIN2,INPUT);
Serial.begin(9600);
initialiseSerial(9600);
}
void loop()
{
if(bytesAvailable())
Serial.print((char)readByte()); //Continuously check the buffer and write any received bytes to the hardware serial
}