ledongthuc / CustomSoftwareSerial

Alternative SoftwareSerial in Arduino. CustomSoftwareSerial library allow to configure and custom Parity Bit and Stop Bit.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Garbage @ 115200

anfebit opened this issue · comments

First of all, I wanted to thank you for the library, very helpful. It is unbelievable that Arduino has so many constraints in terms of communication.

Right now, I'm using your library, but I'm having troubles with the baudrate. I can only have good data a 9600 bps. Changing to 115200 everything is just garbage. Unfortunately, my application does not allow something less that 115200 bps. In order to test the library, I did the following program. The following code uses the default pins (0,1) to transmit the word "Hello world". But I only get reliable data at 9600. Any answer will be much appreciated it.

#include <CustomSoftwareSerial.h>
CustomSoftwareSerial* customSerial;               // Declare serial
CustomSoftwareSerial* customSerial1;


//#include <SoftwareSerial.h>
#include <stdint.h>

//SoftwareSerial portOne(10, 11); // RX, TX

unsigned int read_val=0;
void setup() 
{
  //Serial.begin(115200);
  while (!Serial) 
  {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  delay(10);

  customSerial = new CustomSoftwareSerial(10, 11); // rx, tx
  customSerial1 = new CustomSoftwareSerial(0, 1); // rx, tx
  customSerial1->begin(14400, CSERIAL_8N1);
  customSerial->begin(115200, CSERIAL_8N1);         // Baud rate: 9600, configuration: CSERIAL_8N1

  //portOne.begin(115200);
  delay(1000);
  //portOne.print(50,HEX);
  customSerial->print(50,HEX);
  customSerial1->print("Hello world");
}

void loop()
{
  while(true)
  {

    if (customSerial->available()) 
    {  
        read_val=customSerial->read();
        Serial.println(read_val);
        //Serial.println("Hello");  
     }  
   }  
 }

Yup, I can 100% confirm the bug now.

Today I am testing HLK-LD1155H-24G sensor using Arduino UNO, at Baud rate 115200.
Even after setting parity and stop bit correctly, output is still all garbage.
However, if I connect Port 0 as RX to sensor's TX port (since I only need to input from the sensor and output to PC, i.e., input from PC and output to sensor are not needed), and modify the code to pass-through back to the same Serial port as below:

//#include <CustomSoftwareSerial.h>

//CustomSoftwareSerial Serial1(10, 11);

void setup() {
  Serial.begin(115200, SERIAL_8N1);
  //Serial1.begin(115200, CSERIAL_8N1);
  Serial.println("Custom software serial passthrough started on Port 10 and 11");
}

void loop() {
  if (Serial.available()) {      // If anything comes in Serial PIN 0
    Serial.write(Serial.read());   // read it and send it out Serial PIN 1
  }
  
//  if (Serial.available()) {      // If anything comes in Serial PIN 0 (PC USB)
//    Serial1.write(Serial.read());   // read it and send it out Serial1 PIN 11
//  }

//  if (Serial1.available()) {     // If anything comes in Serial1 PIN 10
//    Serial.write(Serial1.read());   // read it and send it out Serial PIN 1 (PC USB)
//  }
}

, I get correct output which means that the built-in Serial is working at 115200 baud rate, but not the CustomSoftwareSerial.

I have successfully figured out how to read UART at 115200 baud rate for CSERIAL_8N1 on Arduino UNO, other configurations like 8N2/8E1/8O1/etc should similarly apply.

The main difficulty is that since 16000000/115200=138.889, you need to read the RX pin every ~139 clock cycles which is not a lot. Every call to digitalRead() takes about 64 clock cycles, so you cannot think about it. And there is too little time to check for how many data bits, stop bits, or parity bit in different configurations. In addition, you need to be clear about how much time every line of code will take, and function calls must be avoided as much as possible because stack pushing/popping takes many clock cycles.

#define RX_PIN 3
#define TX_PIN 2
#define MAX_BUF 256
#define readPIN (PIND&8?1:0)
#define CYCLES_PER_BIT 139
#define GRACE_CYCLES 40

int RX_head = 0, RX_tail = 0;
char x, RX_BUF[MAX_BUF];
uint16_t RX_packet = 0;
uint16_t next_tick = CYCLES_PER_BIT-GRACE_CYCLES;

void wait_for_nodata(){ // when not sending data, RX PIN is hold high
  TCNT1 = 0;
  while(TCNT1< CYCLES_PER_BIT*10)
    if(!readPIN)TCNT1=0;
}

void uart_start_vect(){
  cli();  // clock-cycle critical session, must disable all interrupts
  TCNT1 = 0;  // reset counter
  while(TCNT1<next_tick);  // wait for the start bit to finish

  // start reading packet
re:
  while(1){
    RX_packet |= (readPIN<<x);
    if(++x>8)break;
    next_tick += CYCLES_PER_BIT;
    while(TCNT1<next_tick);
  }

  // save the byte into circular buffer
  RX_BUF[RX_head] = RX_packet;
  if(++RX_head>=MAX_BUF)
    RX_head = 0;

  // 16000000/115200=138.888 that is 139 for 8/9 of the time and 1/9 of the time
  next_tick += CYCLES_PER_BIT-1;
  RX_packet = 0;
  x = 0;
  while(TCNT1<next_tick); // wait for stop bit to finish
  if(!readPIN){ // another start bit => consective packet
    next_tick += CYCLES_PER_BIT;
    while(TCNT1<next_tick); // wait for start bit to finish
    goto re;
  }

  // prepare for the next
  next_tick = CYCLES_PER_BIT-GRACE_CYCLES;
  
  sei();  // resume all interrupts
}

void setup() {
  delay(200); // prevent running twice when uploading while console is open
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(RX_PIN, INPUT_PULLUP);
  pinMode(TX_PIN, INPUT_PULLUP);
  
  // setup serial
  digitalWrite(LED_BUILTIN, 1);
  Serial.begin(115200);
  Serial.println("TimerSoftwareSerial started:");

  // setup timer1 in normal mode for gettickcount()
  cli();
  TCCR1A = 0;
  TCCR1B = 1;
  OCR1A = 0xffff;

  // setup external interrupt
  attachInterrupt(digitalPinToInterrupt(RX_PIN), uart_start_vect, FALLING);

  wait_for_nodata();

  // start all interrupts
  sei();
}

bool pool(){
  return RX_head!=RX_tail;
}

char *recv(char*buf){
  int p=0;
  while(RX_head!=RX_tail){
    buf[p++] = RX_BUF[RX_tail++]&0b01111111;
    if(RX_tail>=MAX_BUF)
      RX_tail=0;
    if(p>=MAX_BUF)
      Serial.println("\nrecv: buffer overflow");
  }
  buf[p] = 0;
  return buf;
}

char arr[2*256];
void print_bits(char *buf){
  int posi=0;
  char ch;
  for(unsigned char x=0; buf[x]!=0; ++x){
    ch = buf[x];
    for(unsigned char y=0; y<8; ++y)
      arr[posi++] = ((ch<<y)&0b10000000)?'1':'0';
    arr[posi++] = ' ';
    if(posi>100)break;
  }
  arr[posi]=0;
  Serial.println(arr);
}

char buf[MAX_BUF];
void loop() {
  // put your main code here, to run repeatedly:
  if(pool()){
    recv(buf);
    Serial.println(buf);
    print_bits(buf);
  }
}

The above code is tested working on reading UART data from HLK-LD1155H-24G sensor, the idea is to use Timer1 counter to wait until the arrival of the next bit, avoid calling functions and try to use constants instead of variables.