Page 1 of 1

More than 3 Receiving UARTs

Posted: Thu Mar 22, 2018 6:25 am
by Vladis
Hello,

I have a necessity for more than 3 receiving uarts. Using the hardware timers I am able to increase the number of uarts. The sketch uses the falling edge of a start bit to produce an interrupt on the declared receiving pin. The ISR to this interrupt disables the interrupt on the pin and sets a one-shoot timer that fires 1/2 bit time after. On the the service to the one-shoot timer, the timer is set as a repeat-timer firing with period equal to the bit time. Then it reads the state of the pin on the first 8 firings and on the 9th the received byte is placed in a buffer and the pin is placed again in its initial waiting state.

My experience of C and C++ is very limited and I am not able to write a library or a class for the code I include here. I would like to have a library so that a sketch would look like this

Code: Select all

HardwareSerial SerialOut(2);
SoftwareSerial SerialIn(0)

void setup() {
  Serial.begin(115200);
  SerialOut.begin(9600);
  SerialIn_begin(12, 256, 9600);
}

void loop() {
  delay (500);
  SerialOut.println("Send this out on pin 17 to be read in on pin 12!");
  byte b;
  int n = SerialIn_available();
  for (int i=0; i < n; i++){
     b = SerialIn.read();
     Serial.print(char(b));
   }
}
I tried for several hours without success. So I leave here a working sketch hoping that someone could give it a look and have it working in a properly way. Thanks in advance.

Code: Select all

// Sketch to test up to extra 4 receiving UARTs using the 4
// ESP32 hardware timers. Tested with timer0 and timer1 only

// Works without errors up to 38400 baud and tested on
// NodeMCU-32S with pins pins 25 and 34 connected
// together and pins 17 and 12 also connected together

HardwareSerial Serial1(1);
HardwareSerial Serial2(2);

void setup() {
  Serial.begin(115200);
  // send on pin 25 to be received on pin 34
  Serial1.begin(9600, SERIAL_8N1, 26, 25);
  Serial0_begin(34, 256, 9600);
  // send on pin 17 to be received on pin 12
  Serial2.begin(9600);
  Serial1_begin(12, 256, 9600);
}

void loop() {
  delay (500);
  Serial1.println("This is transmitted on pin 25 and is received on pin 34");
  Serial2.println("This is transmitted on pin 17 and is received on pin 12");
  delay(500);
  byte b0;
  int nnn0 = Serial0_available();
  for (int i0=0; i0 < nnn0; i0++){
     b0 = Serial0_read();
     Serial.print(char(b0));
   }
  byte b1;
  int nnn1 = Serial1_available();
  for (int i1=0; i1 < nnn1; i1++){
     b1 = Serial1_read();
     Serial.print(char(b1));
   } 
}

// I am not able to put the following in a class or library!
// *********************************************************
// please help!

// for timer 0
uint32_t bit_ticks0;        // duration of bit in ticks, where each tick is 0.1 us 
uint32_t init_ticks0;       // ticks from falling edge to the middle of start bit

int rxbuffSize0;
int rxPin0;
byte bitCounter0;
uint8_t rx0;
unsigned int inPos0;
unsigned int outPos0;
uint8_t *rxBuffer0;

hw_timer_t * timer0 = NULL;

void IRAM_ATTR rxISR0();
void IRAM_ATTR OnceTimerISR0();
void IRAM_ATTR RepeatTimerISR0();

// for timer 1
uint32_t bit_ticks1;        // duration of bit in ticks, where each tick is 0.1 us 
uint32_t init_ticks1;       // ticks from falling edge to the middle of start bit

int rxbuffSize1;
int rxPin1;
byte bitCounter1;
uint8_t rx1;
unsigned int inPos1;
unsigned int outPos1;
uint8_t *rxBuffer1;

hw_timer_t * timer1 = NULL;

void IRAM_ATTR rxISR1();
void IRAM_ATTR OnceTimerISR1();
void IRAM_ATTR RepeatTimerISR1();

//portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
                      
void Serial0_begin(int Pin, int bSize, long baud) {
  rxPin0 = Pin;
  pinMode(rxPin0, INPUT_PULLUP);  
  rxBuffer0 = (uint8_t*)malloc(bSize);
  rxbuffSize0 = bSize;
  inPos0 = outPos0 = 0;
  bit_ticks0 = 10000000 / baud;         // units = 0.1 micro seconds
  init_ticks0 = (bit_ticks0 / 2);
  timer0 = timerBegin(0, 8, true);
  attachInterrupt(rxPin0, rxISR0, FALLING);
}
void Serial1_begin(int Pin, int bSize, long baud) {
  rxPin1 = Pin;
  pinMode(rxPin1, INPUT_PULLUP);  
  rxBuffer1 = (uint8_t*)malloc(bSize);
  rxbuffSize1 = bSize;
  inPos1 = outPos1 = 0;
  bit_ticks1 = 10000000 / baud;         // units = 0.1 micro seconds
  init_ticks1 = (bit_ticks1 / 2);
  timer1 = timerBegin(1, 8, true);
  attachInterrupt(rxPin1, rxISR1, FALLING);
}

void IRAM_ATTR rxISR0() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  detachInterrupt(rxPin0);
  timerAttachInterrupt(timer0, &OnceTimerISR0, true);
  timerAlarmWrite(timer0, init_ticks0, true);
  timerAlarmEnable(timer0);
  rx0 = 0;
//  portEXIT_CRITICAL_ISR(&timerMux);
}
void IRAM_ATTR rxISR1() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  detachInterrupt(rxPin1);
  timerAttachInterrupt(timer1, &OnceTimerISR1, true);
  timerAlarmWrite(timer1, init_ticks1, true);
  timerAlarmEnable(timer1);
  rx1 = 0;
//  portEXIT_CRITICAL_ISR(&timerMux);
}

void IRAM_ATTR OnceTimerISR0() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  timerAttachInterrupt(timer0, &RepeatTimerISR0, true);
  timerAlarmWrite(timer0, bit_ticks0, true);
  timerAlarmEnable(timer0);
  bitCounter0 = 1;
//  portEXIT_CRITICAL_ISR(&timerMux);
}
void IRAM_ATTR OnceTimerISR1() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  timerAttachInterrupt(timer1, &RepeatTimerISR1, true);
  timerAlarmWrite(timer1, bit_ticks1, true);
  timerAlarmEnable(timer1);
  bitCounter1 = 1;
//  portEXIT_CRITICAL_ISR(&timerMux);
}

void IRAM_ATTR RepeatTimerISR0() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  if (bitCounter0 == 9) {
    timerAlarmDisable(timer0);
    if (digitalRead(rxPin0)) {
      int next = (inPos0+1) % rxbuffSize0;
      if (next != outPos0) {
        rxBuffer0[inPos0] = rx0;
        inPos0 = next;
      }
    }
    attachInterrupt(rxPin0, rxISR0, FALLING); 
  }
  rx0 >>= 1;
  if (digitalRead(rxPin0)) { rx0 |= 0x80; }
  bitCounter0 = bitCounter0 + 1;
//  portEXIT_CRITICAL_ISR(&timerMux);
}
void IRAM_ATTR RepeatTimerISR1() { 
//  portENTER_CRITICAL_ISR(&timerMux);
  if (bitCounter1 == 9) {
    timerAlarmDisable(timer1);
    if (digitalRead(rxPin1)) {
      int next = (inPos1+1) % rxbuffSize1;
      if (next != outPos1) {
        rxBuffer1[inPos1] = rx1;
        inPos1 = next;
      }
    }
    attachInterrupt(rxPin1, rxISR1, FALLING); 
  }
  rx1 >>= 1;
  if (digitalRead(rxPin1)) { rx1 |= 0x80; }
  bitCounter1 = bitCounter1 + 1;
//  portEXIT_CRITICAL_ISR(&timerMux);
}

int Serial0_available() {
  int avail = inPos0 - outPos0;
  if (avail < 0) avail += rxbuffSize0;
  return avail;
}
int Serial1_available() {
  int avail = inPos1 - outPos1;
  if (avail < 0) avail += rxbuffSize1;
  return avail;
}

uint8_t Serial0_read() {
   if ( inPos0 == outPos0) return -1;
   uint8_t ch = rxBuffer0[outPos0];
   outPos0 = (outPos0 + 1) % rxbuffSize0;
   return ch;
}
uint8_t Serial1_read() {
   if ( inPos1 == outPos1) return -1;
   uint8_t ch = rxBuffer1[outPos1];
   outPos1 = (outPos1 + 1) % rxbuffSize1;
   return ch;
}

void Serial0_end() {
  detachInterrupt(rxPin0);   
  timerEnd(timer0);
  timer0 = NULL;  
}

void Serial1_end() {
  detachInterrupt(rxPin1);   
  timerEnd(timer1);
  timer1 = NULL;  
}


Re: More than 3 Receiving UARTs

Posted: Sat Mar 24, 2018 5:43 am
by Vladis
Hello again,

I think that my problem with in creating a class (library) is that the attachinterrupt() can not be easily implemented inside a class. I saw some discussions to bypass this (one example being the SoftwareSerial() library for the arduino) but I will just place my code in the main sketch or even better I will write 4 ino files (one for each timer) and when I need to use one of these, I go for "Add File ..." instead of "Include Library >"

Regards, Vladis