Page 1 of 1

write in C to uart only outputs 16 bytes? [solved]

Posted: Wed Aug 06, 2014 8:25 pm
by Gert van Loo
write(uart_file_pnt,data,bytes) (bytes=132)
only outputs 16 bytes. Yes, the uart_file_pnt is from a previously correctly opened /dev/ttyAMA0
yes, the data output is what I expect but only the first 16 bytes come out. (I have scope on the line)
When I send a big file to the device from the command prompt that works.
So this is OK: cat bigfile >/dev/ttyAMA0
In the end I DID get my data out but I have to do a loop where I send max 16 bytes and wait in between.
That suggest it is not my code...
My code now looks like below (There may be typos but you should get the idea)

Code: Select all

send_uart(void *data,int bytes)
{ int b;
  while (bytes)
  { if (bytes>=16)
      b=16;
    else
      b = bytes;
    bytes -= b;
   write(uart0_filepnt,data,b);
   // Flush did not help
   usleep(100000); // wait 0.1 seconds
   data = (void *)((char *)data+b);
  } // while have bytes 
} // write_uart 

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:30 pm
by PeterO
What is the return value from write ?
And if it is -1 what is the value of errno ?
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:31 pm
by Gert van Loo
I check if it is NOT -1
I did not check for other values.
Humm, maybe it does return 16.
I have to try that.

Yep! it returns 16.
So my error is that I ignored the return value.

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:42 pm
by PeterO
Gert van Loo wrote:I check if it is NOT -1
I did not check for other values.
Humm, maybe it does return 16.
I have to try that.

Yep! it returns 16.
So my error is that I ignored the return value.
Interesting, because the little bit of test code I just wrote here returns 132 :o Which flags did you pass to the open call ?
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:51 pm
by Gert van Loo
c_cflag = B57600 | CS8 | CLOCAL | CREAD ;
c_iflag = IGNPAR;
c_oflag = 0;
c_lflag = 0;

Post edit: my file is a .cpp file as I am linking to QT4.

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:55 pm
by Gert van Loo
If I keep pumping in bytes with no delay it looses bytes.
16, then 8 then nothing. I suspect the 8 depends on how much printf I have between.

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 8:56 pm
by PeterO
Those are to tcsetattr ?
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 9:04 pm
by PeterO
I guess you are repeatedly calling write ? I just tried that , First 30 writes return 132, then......
n= 19 loops=31
n= 8 loops=32
n= 16 loops=33
n= 8 loops=34
n= 8 loops=35
n= 16 loops=36
n= 8 loops=37
n= 16 loops=38
n= 8 loops=39
n= 8 loops=40
n= 8 loops=41
n= 16 loops=42
n= 16 loops=43
n= 16 loops=44
n= 16 loops=45
n= -1 loops=46
n= -1 loops=47
n= -1 loops=48
n= -1 loops=49
n= -1 loops=50

31x132 = 4092
If I write 80 bytes at a time it works for first 50 times... 50 * 80 = 4000, so it looks l ike there is a 4096 byte buffer some where !
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Wed Aug 06, 2014 9:06 pm
by Gert van Loo
Yes,
I just checked my rasbian version: 3.6.11+ (Aug 2013)
Seems this is a very old version.
My SD-card failed and I randomly grabbed another I had laying around.
Time for an upgrade!

But first time for bed. It is late here.

Re: write in C to uart only outputs 16 bytes ?

Posted: Mon Aug 11, 2014 7:31 pm
by Gert van Loo
I have given up on trying to solve this.
I can't find anything wrong with what I am doing but the Pi keeps outputting only 16 bytes
although it reports it writes all of them. (it prints a=64 but only 16 bytes go out)
I even went in the minicom source code to see what they do
(as it works for them) but their open and init code gives me the same result.
I'll use my workaround which uses a usleep and is good enough for my application.
I can't spend anymore time on this

Code below: write_uart1 fails, write_uart2 is my workaround and is fine.

Code: Select all

//
// Raspberry-Pi UART access from C
//
// Code based on
//  http://www.raspberrypi.org/phpBB3/viewtopic.php?t=7500&p=93257
//  mixed with minicom code 
//

#include <unistd.h>   //Used for UART
#include <fcntl.h>    //Used for UART
#include <termios.h>  //Used for UART

int  open_uart(int low);
int  write_uart1(void *data,int bytes);
int  write_uart2(void *data,int bytes);
void close_uart();

// debug:
#include <stdio.h>

char pattern[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\015\012";
// test
int main()
{
  if (open_uart(0)!=0)
  { fprintf(stderr,"Could not open UART\n");
    return 1;
  }
  while (1)
  {
    write_uart2(pattern,64);
    sleep(2);
  }
  return 0;
} // main 

int uart0_filestream = -1;

//
// Setup Raspberry-Pi UART
// return 0 on success
// return 1 on failure
//
// Code based on http://www.raspberrypi.org/phpBB3/viewtopic.php?t=7500&p=93257
//
int open_uart(int low)
{ int n;
  struct termios options;
  if (uart0_filestream!=-1)
    return 0; // assume already opened 
  uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);  //Open in non blocking read/write mode
  if (uart0_filestream == -1)
  {
    fprintf(stderr,"Error - Unable to open UART. Ensure it is not in use by another application\n");
    fflush(stderr);
    return 1;
  }
  // cancel the O_NDELAY flag
  // (copied from minicom)
  n = fcntl(uart0_filestream, F_GETFL, 0);
  fcntl(uart0_filestream, F_SETFL, n & ~O_NDELAY);

  // CONFIGURE THE UART
  // The flags (defined in /usr/include/termios.h - 
  // see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
  tcgetattr(uart0_filestream, &options);
  cfsetospeed(&options,B57600);
  cfsetispeed(&options,B57600);
  options.c_cflag =  (options.c_cflag & ~CSIZE) | CS8; // 8 bits
  options.c_cflag |=  CLOCAL | CREAD;  // ignore mode status, enable rec.
  options.c_cflag &=  ~(PARENB | PARODD | CSTOPB); // No parity, 1 stop bit
  
  options.c_iflag = IGNBRK;
  options.c_iflag &= ~(IXON|IXOFF|IXANY);

  options.c_oflag = 0;
  options.c_lflag = 0;
  tcsetattr(uart0_filestream, TCSANOW, &options); // set the options NOW
  return 0;
} // setup_uart

// what it says 
void close_uart()
{
  if (uart0_filestream!=-1)
    close(uart0_filestream);
}


//
// transmit data to uart 
// This is how it should work 
//
// return 0 on failure
// return 1 on success
//
int write_uart1(void *data,int bytes)
{ int a;
  while (bytes)
  { 
    a = write(uart0_filestream, data, bytes);
    if (a==-1)
    { fprintf(stderr,"UART WRITE ERRROR!!\n");
      return 0;
    }
    bytes -= a;
    printf("a=%d\n",a);
    data = (void *)((char *)data + a);
  }
  tcflush(uart0_filestream,TCOFLUSH); 
  return 1;
} // tx_uart


//
// transmit data to uart 
// max 16 at a time 
// wait for 16 charac to leave before pushing more out
//
// return 0 on failure
// return 1 on success
//
int write_uart2(void *data,int bytes)
{ int b,a;
  while (bytes)
  { 
    b = bytes>=16 ? 16 : bytes;
    a = write(uart0_filestream, data, b);
    if (a==-1)
    { fprintf(stderr,"UART WRITE ERRROR!!\n");
      return 0;
    }
    bytes -= a;
    printf("a=%d\n",a);
    data = (void *)((char *)data + a);
    usleep(30000); // > 16charsx10bits/57600
  }
  tcflush(uart0_filestream,TCOFLUSH); 
  return 1;
} // tx_uart

Re: write in C to uart only outputs 16 bytes ?

Posted: Mon Aug 11, 2014 8:08 pm
by PeterO
Bit busy at the moment (trying to find a missing vertex in a 3D object !) but I'll try your code when I get a few minutes....
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Mon Aug 11, 2014 8:53 pm
by RaspISteve
Guys,

I remember years ago there was a UART which came with a 16 byte FIFO buffer. Very useful if you couldn't always get to it on time as you could whack a load of characters at it then go off and do something else.

If you didn't check the right flags and waited for characters to flush out correctly via the serial bit any character(s) written after the 17th all went to the same place and corrupted and I guess confused the thing. Maybe the Pi chip set has a similar feature.

Check you are testing the right status flags.

Re: write in C to uart only outputs 16 bytes ?

Posted: Mon Aug 11, 2014 9:21 pm
by jojopi
Gert van Loo wrote:tcflush(uart0_filestream,TCOFLUSH);
This flushes data written but not transmitted. So yes, you can successfully write 64 bytes but then discard most of them, sending only the 16 that were already in the FIFO.

Perhaps you meant tcdrain(), though in most cases you do not need that. Writes will start to block or return short once the output buffer is full.

Re: write in C to uart only outputs 16 bytes ?

Posted: Mon Aug 11, 2014 9:37 pm
by PeterO
Just got to look at this , and came to same conclusion and jojopi..... tcflush is not what you want .... "man termios" is your friend :D

Code: Select all

   tcflush() discards data written to the object referred to by fd but not
       transmitted,  or  data received but not read, depending on the value of
       queue_selector:

Code: Select all

     tcdrain()  waits  until all output written to the object referred to by
       fd has been transmitted.
PeterO

Re: write in C to uart only outputs 16 bytes ?

Posted: Tue Aug 12, 2014 8:26 am
by Gert van Loo
Yes! thanks guys. It is now working.
My error was the usual: knowing fflush I assumed tcflush was the same.