Trying to receive characters from Putty to Arduino

So, I’m making an own “UART” on Arduino Uno pin 7 and 8. I have USB to 3-pin TTL from my laptop to my Arduino to send the characters. I’m working on the receive functionality right now. I have a pin change interrupt running and when it is detected I enable the timer2 interrupt to receive the bits at 9600 baud. But for some reason I can’t seem to receive the data I want.
My code:

#include "arduino.h"
#include <stdint.h>

#define Fosc 16000000

int rxPin = 7;
int txPin = 8;

uint16_t data;
int recvBits = 0;

uint8_t rxBuffer[64];
volatile int rxFirst = 0;
volatile int rxLast = 0;

void setup() {
  Serial.begin(9600);
  pinMode(txPin, OUTPUT);
  digitalWrite(txPin, HIGH);
  pinMode(rxPin, INPUT);
  digitalWrite(rxPin, HIGH);
  setupRXInterrupt(28800);
}

void loop() {
  Serial.println(data,BIN);
  delay(1000);
}


void setupRXInterrupt(long sampleSpeed)
{
  //long cmr = ((Fosc / (8 * sampleSpeed)) - 1);
  long cmr = ((Fosc / (8 * 9600)) - 1);
  noInterrupts();
  TCCR2A = 0;
  TCCR1B = 0;
  TCNT2 = 0;
  OCR2A = cmr;
  TCCR2A |= (1 << WGM21);  //ctc
  TCCR2B |= (1 << CS21); //8 prescaler

  PCICR |= (1 << PCIE2);  //enable interrupt port
  PCMSK2 |= (1 << PCINT23); //enable pin 7 for pc interrupt
  interrupts();
}


ISR(TIMER2_COMPA_vect)
{
  PCMSK2 &= ~_BV(PCINT23);
  receive();
}

ISR(PCINT0_vect)
{
  TIMSK2 |= (1 << OCIE2A);
  data = 0;
}

void receive()
{
  if (recvBits < 10)
  {
    data >>= 1;
    if (digitalRead(rxPin) == HIGH)
    {
      data |= 0b10000000;
    }
  }
  if (recvBits = 10)
  {
    int next = (rxLast + 1);
    rxBuffer[rxLast] = data;
    rxLast = next;
    recvBits = 0;
    TIMSK2 &= ~_BV(OCIE2A);
    PCMSK2 |= (1 << PCINT23);
  }
}

When I send a character from putty on the COM port I do receive something but it has question marks and different characters(see attached image). Putty setting (for now) are: 9600 baud, 8 data bits, no parity, one stop bit, local echo force on.

I’m trying to receive the bits at 9600 baud but when I have that working I want to work on setting the baud to 3x9600 so I can sample 3 times per bit to get the correct bit. But I wanted to get an easier version with the interrupts working first.

Does anyone see what I’m doing wrong I have done a lot of research and trying but can’t seem to get it right.

SerialMonitorOutput.PNG

Wouldn't using one of the Software Serial librarys work better?

CrossRoads: Wouldn't using one of the Software Serial librarys work better?

Yes, but i want to create my own just for learning. And I want to use to pin change interrupt in combination with the timer interrupt and not the delay like in the Software Serial library.

can you explain how your code is intended to work?

You seem to be enabling and cancelling the timer2 interrupt all over the place.

Don't do that, leave it enabled all the time - you will lose interrupts and get spurious ones doing this sort of thing. The only thing you should do with enabling / disabling interrupts in code after setup() is to use noInterrupts() and interrupts() around critical sections.

The way to inhibit interrupt processing once the code's running is to tell the ISR using a boolean variable:

volatile bool enable = true ;

ISR (.....)
{
  if (!enable)
    return ;

  ..
  ..
}

Then the main program can control the ISR as it sees fit without risk of spurious or missed interrupts.

The noInterrupts()/interrupts() technique is guaranteed to queue pending interrupts without error, so long as only one of each type is outstanding.

Disabling and re-enabling a particular interrupt is not guaranteed to do this at all, it all depends on the particular interrupt in fact.

gcjr: can you explain how your code is intended to work?

So, when I receive the start bit, pin 7 goes from high to low so the pin change interrupt is done. In the PC interrupt I turn on the timer interrupt to receive the bits at the 9600 baudrate I set. For every time the timer interrupt is done I shift the bit and check if the pin is High or low to check what the bit is. When I received all bits I add the corresponding character to the buffer. When the PC interrupt is first done I disable it so i can receive the bits using the timer interrupt and the PC interrupt doesn't interfere.

MarkT:
The way to inhibit interrupt processing once the code’s running is to tell the ISR using a boolean
variable.

Thanks for your answer, I didn’t know that so, I implemented this so the code looks like this now:

#include "arduino.h"
#include <stdint.h>

#define Fosc 16000000

int rxPin = 7;
int txPin = 8;

uint16_t data;
int recvBits = 0;
volatile bool rxEnabled = false;
volatile bool pcEnabled = false;

uint8_t rxBuffer[64];
volatile int rxFirst = 0;
volatile int rxLast = 0;

void setup() {
  Serial.begin(9600);
  pinMode(txPin, OUTPUT);
  digitalWrite(txPin, HIGH);
  pinMode(rxPin, INPUT);
  digitalWrite(rxPin, HIGH);
  setupRXInterrupt(28800); //actually 9600 for now
}

void loop() {
  Serial.println(data, BIN);
  delay(1000);
}

void setupRXInterrupt(long sampleSpeed)
{
  //long cmr = ((Fosc / (8 * sampleSpeed)) - 1);
  long cmr = ((Fosc / (8 * 9600)) - 1);
  noInterrupts();
  TCCR2A = 0;
  TCCR1B = 0;
  TCNT2 = 0;
  OCR2A = cmr;
  TCCR2A |= (1 << WGM21);  //ctc
  TCCR2B |= (1 << CS21); //8 prescaler
  TIMSK2 |= (1 << OCIE2A); //enable timer 2

  PCICR |= (1 << PCIE2);  //enable interrupt port
  PCMSK2 |= (1 << PCINT23); //enable pin 7 for pc interrupt
  interrupts();
}

ISR(TIMER2_COMPA_vect)
{
  if (!rxEnabled)
  {
    return;
  }
  receive();
}

ISR(PCINT0_vect)
{
  if (!pcEnabled)
  {
    return;
  }
  rxEnabled = true;
  pcEnabled = false;
  data = 0;
}

void receive()
{
  if (recvBits < 10)
  {
    data >>= 1;
    if (digitalRead(rxPin) == HIGH)
    {
      data |= 0b10000000;
    }
  }
  if (recvBits = 10)
  {
    int next = (rxLast + 1);
    rxBuffer[rxLast] = data;
    rxLast = next;
    recvBits = 0;
    rxEnabled = false;
    pcEnabled = true;
  }
}

Still experiencing the problem with the output though.

casperm10: So, when I receive the start bit, pin 7 goes from high to low so the pin change interrupt is done. In the PC interrupt I turn on the timer interrupt to receive the bits at the 9600 baudrate I set. For every time the timer interrupt is done I shift the bit and check if the pin is High or low to check what the bit is. When I received all bits I add the corresponding character to the buffer.

do you start sampling after 1.5 bit periods and then each bit period for the next 7 bits so that you sample in the middle of the bit period?

gcjr: do you start sampling after 1.5 bit periods and then each bit period for the next 7 bits so that you sample in the middle of the bit period?

So, I fixed some bugs and started testing some more, and came to the conclusion that the Pin Change interrupt wasn't not called because I had the wrong PCint I used pcint0 instead of pcint2, because i used a different pin before to receive face palm. I am receiving now, still question marks but it's about the timing now, like you said so, I will get going on that.

Did you use digitalPinToInterrupt(pin)?

I’ve chnaged to code a bit to sample 3 times per bit. I think I did it correct but I’m stuck on a high pin. The digitalRead(rxPin) is always high when i check it, although the pin change interrupt does work so it has to change. So, I don’t really now why it doesn’t go low when a bit is low. The output of data is always just 11111111. Does anyone see something that I’m doing wrong especially with the pin not changing.
Code now:

#include "arduino.h"
#include <stdint.h>

#define Fosc 16000000

int rxPin = 7;
int txPin = 8;

uint8_t data = 0;
int recvBits = 0;
volatile bool rxEnabled = false;
volatile bool pcEnabled = true;
volatile int rxSample = 0;

uint8_t rxBuffer[64];
volatile int rxFirst = 0;
volatile int rxLast = 0;

void setup() {
  Serial.begin(9600);
  pinMode(txPin, OUTPUT);
  digitalWrite(txPin, HIGH);
  pinMode(rxPin, INPUT);
  digitalWrite(rxPin, HIGH);
  setupRXInterrupt(28800);
}

void loop() {
}

void setupRXInterrupt(long sampleSpeed)
{
  long cmr = ((Fosc / (8 * sampleSpeed)) - 1);
  noInterrupts();
  TCCR2A = 0;
  TCCR1B = 0;
  TCNT2 = 0;
  OCR2A = cmr;
  TCCR2A |= (1 << WGM21);  //ctc
  TCCR2B |= (1 << CS21); //8 prescaler
  TIMSK2 |= (1 << OCIE2A); //enable timer 2

  PCICR |= (1 << PCIE2);  //enable interrupt port
  PCMSK2 |= (1 << PCINT23); //enable pin 7 for pc interrupt
  interrupts();
}

ISR(TIMER2_COMPA_vect)
{
  if (!rxEnabled)
  {
    return;
  }
  receive();
}

ISR(PCINT2_vect)
{
  if (!pcEnabled)
  {
    return;
  }
  rxEnabled = true;
  pcEnabled = false;
}

void receive()
{
  if (recvBits > 3 && recvBits < 28)
  {
    if (digitalRead(rxPin) == HIGH)
    {
      rxSample += 1;
    }
    if (recvBits % 3 == 0)
    {
      data >>= 1;
      if (rxSample == 2 || rxSample == 3)
      {
        data |= 0b10000000;
      }
      rxSample = 0;
    }
  }
  if (recvBits == 28)
  {
    int next = (rxLast + 1);
    rxBuffer[rxLast] = data;
    rxLast = next;
    recvBits = 0;
    rxSample = 0;
    Serial.println(data, BIN);
    data = 0;
    rxEnabled = false;
    pcEnabled = true;
  }
  recvBits++;
}

aarg:
Did you use digitalPinToInterrupt(pin)?

And no I don’t use digitalPinToInterrupt, I use the pin change interrupt of the arduino to detect a interrupt.

are you sure of your sampling times?

i would capture a timestamp (micros()) as well as the the pin value. have a routine to dump the results after a sufficient # of sample (3 * 12 bits)

gcjr:
are you sure of your sampling times?

i would capture a timestamp (micros()) as well as the the pin value. have a routine to dump the results after a sufficient # of sample (3 * 12 bits)

So, when i do this the first 9 bits are 1100 micros difference and after that it’s around 160 micros difference. The pin is always high except for the first 2 these are sometime 0 but the first 4 have to be 0 because of the start bit. But the sampling times are off so wouldn’t expect it from the pin read. I have no clue why the times are so different.
I have 28800 baudrate so, 28800 bits per second so 1 bit is ((1/28800)*1000000) = 35 right? But it’s nowhere near. Is the receive code too long or something i have no idea.

shouldn't our ISR code be pretty simple? something like

recvBits = recBits >>1 | digitalRead (rxPin) ? 0x8000 : 0;
if (isrCnt++ >= MAX)
     // disable timer interrupt

capture enough bits including start, par and stop and shift data into LSB

So, I’ve started simplifying again so, I just want to receive at 9600 baud without sampling 3 timer per bit for now. I’ve got this:

#include "arduino.h"
#include <stdint.h>

#define Fosc 16000000

int rxPin = 7;
int txPin = 8;

uint8_t data = 0;
volatile int recvIt = 0;
volatile bool rxEnabled = false;
volatile bool pcEnabled = true;

void setup() {
  Serial.begin(9600);
  pinMode(rxPin, INPUT);
  digitalWrite(rxPin, HIGH);
  setupRXInterrupt(9600);
}

void loop() {
  Serial.println(data,BIN);
  delay(1000);
}

void setupRXInterrupt(long baud)
{
  long cmr = ((Fosc / (8 * baud)) - 1);
  noInterrupts();
  TCCR2A = 0;
  TCCR1B = 0;
  TCNT2 = 0;
  OCR2A = cmr;
  TCCR2A |= (1 << WGM21);  //ctc
  TCCR2B |= (1 << CS21);   //8 prescaler
  TIMSK2 |= (1 << OCIE2A); //enable timer 2
  
  PCICR |= (1 << PCIE2);  //enable interrupt port
  PCMSK2 |= (1 << PCINT23); //enable pin 7 for pc interrupt
  interrupts();
}


ISR(TIMER2_COMPA_vect)
{
  if (!rxEnabled)
  {
    return;
  }
  receive();
}

ISR(PCINT2_vect)
{
  if (!pcEnabled)
  {
    return;
  }
  rxEnabled = true;
  pcEnabled = false;
}

void receive()
{
  if (recvIt > 0 && recvIt < 9)
  {
    data >>= 1;
    if (digitalRead(rxPin) == HIGH)
    {
      data |= 0b10000000;
    }
  }
  if (recvIt == 9)
  {
    recvIt = 0;
    rxEnabled = false;
    pcEnabled = true;
  }
  recvIt++;
}

I setup timer 2 on 9600 baud but when I start printing values like this, when recvIt ==9 the stop bit should be on the line so no more pin changes but it does pin change still after that, so the baud rate is of? But i set it correctly. For some reason the timing is of, I don’t know why. Maybe something is interfering but I don’t know.