Setting about UART0 Serial RX

General discussion about anything related to Ultibo.
gunwoo
Posts: 12
Joined: Fri Feb 02, 2018 1:15 am

Setting about UART0 Serial RX

Postby gunwoo » Tue May 15, 2018 2:57 am

Hello, I came in a long time today.
I have a question about UART0 serial.

I used uart0 as the code below.

Code: Select all


Const
  SerialBaudRate = 115200;
  SerialDataBits =SERIAL_DATA_8BIT;
  SerialStopBits =SERIAL_STOP_1BIT;
  SerialParity =SERIAL_PARITY_NONE;
  SerialFlowControl =SERIAL_FLOW_NONE;
  SerialReceiveDepth =100;//0; jwk180515
  SerialTransmitDepth =0;
 
type
  {HMI I/F Buffer RX}
  THMIIFBufRX = record
   BufferRX: array[0..HMIIFBufferRX_SIZE] of char;
   next_in:LongWord;
   next_out:LongWord;
   STX : char;
   ETX : char;
   CK_STX : Boolean;
   CK_ETX : Boolean;
   Flag : Boolean;
   //RXChar : char;
   RXChar : array[0..HMIIFBufferRX_SIZE] of char;
   RXCount:LongWord;
   CommandLine : string;
   x:LongWord;
  end;
 

//Serial Device Open
function HMIIF_DeviceOpen:PSerialDevice;
begin
   HMISerial:= SerialDeviceGetDefault;
   if (HMISerial <> nil) then
     if SerialDeviceOpen(HMISerial,HMIIFBaudRate,SerialDataBits,SerialStopBits,SerialParity,SerialFlowControl,SerialReceiveDepth,SerialTransmitDepth)= ERROR_SUCCESS then
       HMI_IF_state:=  stOpened
     else
       HMI_IF_state:=  stClosed;

   Result:= HMISerial;
end;

//function that stores received data in a buffer.
function HMIIF_RxDataAvailable: longword;
var
  i : integer;
begin
  Result:= 0;
  if (HMISerial <> nil) then
    if SerialDeviceRead(HMISerial, @HMIIFBufRX.RXChar, 1, SERIAL_READ_PEEK_BUFFER, HMIIFBufRX.RXCount) = ERROR_SUCCESS then
    begin
      if HMIIFBufRX.RXCount>0 then  begin

        DebugConsolBufferTX.Add('Recv Size : ' + Inttostr(HMIIFBufRX.RXCount));

        SerialDeviceRead(HMISerial, @HMIIFBufRX.RXChar, HMIIFBufRX.RXCount,SERIAL_READ_NON_BLOCK, HMIIFBufRX.RXCount);

        for i := 0 to HMIIFBufRX.RXCount -1 do begin
          HMIIFBufRX.BufferRX[HMIIFBufRX.next_in]:= HMIIFBufRX.RXChar[i];
          HMIIFBufRX.x:= HMIIFBufRX.next_in;
          HMIIFBufRX.next_in:= (HMIIFBufRX.next_in+1) mod HMIIFBufferRX_SIZE;

          if HMIIFBufRX.next_in = HMIIFBufferRX_SIZE then begin
             HMIIFBufRX.next_in := 0;
          end;
        end;
        inc(Result);

      end;

    end;
end;     



CM3L must receive data of at least 100 bytes.
However, data reception is cut off at 16 bytes.

Please let me know what the problem is.

Thanks.
User avatar
Ultibo
Site Admin
Posts: 2002
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Setting about UART0 Serial RX

Postby Ultibo » Tue May 15, 2018 11:01 am

gunwoo wrote:CM3L must receive data of at least 100 bytes.
However, data reception is cut off at 16 bytes.

Please let me know what the problem is.

Hi gunwoo,

Yes I see what the problem might be.

In your function HMIIF_RxDataAvailable you call SerialDeviceRead with the flag SERIAL_READ_PEEK_BUFFER which only tells you how many bytes are available now, there is no way to say how many bytes you want to receive.

If you are sure that you must receive 100 bytes then you could just do the same thing like this instead:

Code: Select all

function HMIIF_RxDataAvailable2: longword;
begin
  Result:= 0;
  if (HMISerial <> nil) then
    if SerialDeviceRead(HMISerial, @HMIIFBufRX.RXChar, 100, SERIAL_READ_NONE, HMIIFBufRX.RXCount) = ERROR_SUCCESS then
    begin
      if HMIIFBufRX.RXCount>0 then
      begin

        DebugConsolBufferTX.Add('Recv Size : ' + Inttostr(HMIIFBufRX.RXCount));

        Result:=HMIIFBufRX.RXCount;

      end;
    end;
end;

This will wait until exactly 100 bytes has been received in your buffer and will never return until that happens.

If you want to have a timeout so that the function returns after a certain amount of time even if the 100 bytes has not been received then you could do it like this:

Code: Select all

function HMIIF_RxDataAvailable3: longword;
var
  i : integer;
  StartTime:Int64;
  Timeout:LongWord;
begin
  Result:=0;

  //Get tick count in milliseconds
  StartTime:=GetTickCount64;

  //Set timeout to 5 seconds
  Timeout:=5000;

  if (HMISerial <> nil) then
   begin
    while Result < 100 do
     begin
       if SerialDeviceRead(HMISerial, @HMIIFBufRX.RXChar, 1, SERIAL_READ_PEEK_BUFFER, HMIIFBufRX.RXCount) = ERROR_SUCCESS then
       begin
         if HMIIFBufRX.RXCount>0 then
         begin

          DebugConsolBufferTX.Add('Recv Size : ' + Inttostr(HMIIFBufRX.RXCount));

          SerialDeviceRead(HMISerial, @HMIIFBufRX.RXChar, HMIIFBufRX.RXCount,SERIAL_READ_NON_BLOCK, HMIIFBufRX.RXCount);

          for i := 0 to HMIIFBufRX.RXCount -1 do begin
            HMIIFBufRX.BufferRX[HMIIFBufRX.next_in]:= HMIIFBufRX.RXChar[i];
            HMIIFBufRX.x:= HMIIFBufRX.next_in;
            HMIIFBufRX.next_in:= (HMIIFBufRX.next_in+1) mod HMIIFBufferRX_SIZE;

            if HMIIFBufRX.next_in = HMIIFBufferRX_SIZE then begin
               HMIIFBufRX.next_in := 0;
            end;
          end;
          inc(Result);

         end
         else
         begin
          //Yield the CPU if no data available
          Sleep(0);
         end;
       end;

       if GetTickCount64 > (StartTime + Timeout) then Break;
     end;
   end;

  DebugConsolBufferTX.Add('Total Received : ' + Inttostr(Result));
end;

There are many ways you could do this but the important thing is to keep calling SerialDeviceRead until you have received all the bytes you are expecting.

Hope that helps you get it working.
Ultibo.org | Make something amazing
https://ultibo.org
User avatar
Ultibo
Site Admin
Posts: 2002
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Setting about UART0 Serial RX

Postby Ultibo » Tue May 15, 2018 11:11 am

I forgot to mention that you should not do this in your code:
gunwoo wrote:

Code: Select all


Const

  SerialReceiveDepth =100;//0; jwk180515



Because that will set the RX FIFO to 100 bytes when it would normally be 2048 bytes if you left that as 0 instead. Changing the receive depth should not be needed unless you want to make it bigger.
Ultibo.org | Make something amazing
https://ultibo.org
gunwoo
Posts: 12
Joined: Fri Feb 02, 2018 1:15 am

Re: Setting about UART0 Serial RX

Postby gunwoo » Wed May 16, 2018 3:45 am

I try that the function returns after a certain amount of time even if the 100 bytes has not been received.
But, After the 17th byte, some data is lost or the next frame is read.

On the sending side, send 100 bytes to CM3L every 500 milliseconds.
(100Bytes per 1frame)

I think it is necessary to use interrupt when receiving serial.
Please advise how to use Serial Receive Interrupt.
User avatar
Ultibo
Site Admin
Posts: 2002
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Setting about UART0 Serial RX

Postby Ultibo » Wed May 16, 2018 4:23 am

gunwoo wrote:I try that the function returns after a certain amount of time even if the 100 bytes has not been received.
But, After the 17th byte, some data is lost or the next frame is read.

Are you saying that with the HMIIF_RxDataAvailable2 it returns only when part of the next frame is read or with HMIIF_RxDataAvailable3 it returns before 100 bytes is read ?

If you are using 115200 baud or higher with no flow control have you tried downloading and applying the latest Ultibo RTL from GitHub that contains this commit? Those changes make the driver handle higher throughput receive much better than before.

gunwoo wrote:I think it is necessary to use interrupt when receiving serial.
Please advise how to use Serial Receive Interrupt.

The driver is using receive interrupts, make sure you are using the latest version and that you are not setting SerialReceiveDepth to 100 as noted above.
Ultibo.org | Make something amazing
https://ultibo.org
gunwoo
Posts: 12
Joined: Fri Feb 02, 2018 1:15 am

Re: Setting about UART0 Serial RX

Postby gunwoo » Wed May 16, 2018 10:13 am

It returned 100 bytes before or returned 100 bytes containing part of the next frame.
Both HMIIF_RxDataAvailable2 and HMIIF_RxDataAvailable3 have the same result.
(I set SerialReceiveDepth back to 0.)

I used Baud Rate 921600, but it works well after I change it to 9600.

I will find the appropriate baud rate using the lastest version.

Thanks for you help.

I have one more question.
Is SerialDeviceRead() using Rx interrupts?
User avatar
Ultibo
Site Admin
Posts: 2002
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Setting about UART0 Serial RX

Postby Ultibo » Wed May 16, 2018 11:18 am

gunwoo wrote:I used Baud Rate 921600, but it works well after I change it to 9600.

When using any rate higher than 115200 we recommend connecting the RTS/CTS lines and setting flow control to SERIAL_FLOW_RTS_CTS, the RX FIFO in the Pi UART is only 16 bytes deep so it is very easy to overrun.

The UART also has no DMA support (a hardware limitation) so high baud rates will almost certainly lose some bytes if there is no RTS/CTS enabled.

gunwoo wrote:I have one more question.
Is SerialDeviceRead() using Rx interrupts?

Yes, the driver is interrupt enabled for both RX and TX.
Ultibo.org | Make something amazing
https://ultibo.org
gunwoo
Posts: 12
Joined: Fri Feb 02, 2018 1:15 am

Re: Setting about UART0 Serial RX

Postby gunwoo » Thu May 17, 2018 1:10 am

The flow control can not be used because the hardware change is difficult.
Thanks for your advice.

I have another question about serial RX interrupt.

Can I generate a callback event when data is written to CM3's serial Rx FIFO?
User avatar
Ultibo
Site Admin
Posts: 2002
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Setting about UART0 Serial RX

Postby Ultibo » Thu May 17, 2018 7:55 am

gunwoo wrote:The flow control can not be used because the hardware change is difficult.
Thanks for your advice.

You're welcome.

gunwoo wrote:I have another question about serial RX interrupt.

Can I generate a callback event when data is written to CM3's serial Rx FIFO?

This is not something that is supported by the current serial API or drivers but if you can tell us how you are thinking of using it we might consider adding the feature if it is something that other people might also want to use.

The only way for you to do this currently would be to take the source of the UART driver and copy it into a new unit to make your own customized version of the driver, the UART driver for the Pi can be found in one of three units depending on which model you are using.

  • Pi A/B/A+/B/Zero = BCM2708
  • Pi 2B = BCM2709
  • Pi 3B/3B+ = BCM2710
There are a lot of drivers contained in each of those units but the one you are interested in is the UART0 device and all of the relevant constants, types and functions will contains BCM270XUART0 or BCM270X_UART0 in their name. Once you narrow it down that far there are only 11 functions in the UART0 driver.

You can prevent the built in driver from loading (to allow your custom version to work) by setting the BCM270X_REGISTER_UART0 variable to False either in the GlobalConfig unit (you must rebuild the RTL after changing that) or in an init unit during boot.
Ultibo.org | Make something amazing
https://ultibo.org

Return to “Discussion”

Who is online

Users browsing this forum: No registered users and 3 guests