Beijer Electronics (formerly QSI Corporation)

Manufacturer of Mobile Data and Human Machine Interface Terminals.
It is currently Tue Nov 21, 2017 8:34 pm

All times are UTC - 7 hours




Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Fri Feb 26, 2010 1:18 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Hello,

Firstly please excuse me for my English.

I'm new in Qlarity language and I'm trying to implement a communication protocol that first I implemented in a Visual Basic application. In this application, I could send and receive data via the serial port. In Qlarity, I can control reception events with the datareceived function of BasicSerialV2, but I can't control sending events. In chapter 3 of Qlarity programer's reference I've read that I can control sending events with the message MSG_COMM_TRANSMIT. In Chapter 3 of QSI Qlarity-based Terminal Programmer’s Reference, I read that I can control the sending events with MSG_COMM_TRANSMIT message, which is generated by the Send() API function. When I try to send data with Send() API function I don't receipt nothing in my DSP, but when I use the senddata function of BasicSerialV2 I receipt data correctly in my DSP, but I'm not be able to detect the MSG_COMM_TRANSMIT message.

What can I do to control and detect the sending events?


Top
 Profile  
 
PostPosted: Fri Feb 26, 2010 8:12 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
In general, I recommend avoiding the Send API and the MSG_COMM_TRANSMIT function. It doesn't quite do what most people want. If this is something you really want to use, let me know and I can give step-by-step instructions on how to use it. However, I have been using Qlarity for a very long time and find that the senddata method of your communication object will let you do what you want and is easier to use.

Instead of Send/MSG_COMM_TRANSMIT, I recommend creating your own function (perhaps SendMessage if you are trying to send messages to the unit). This can perform any operations you need to on the data then transmit it to the unit.

Here is an example. In this example there is a BasicSerialv2 object called myComm. This is global code so any object in the workspace can use it. I am simply using a made-up protocol to illustrate the technique. Obviously you will need to adapt this to your needs:
Code:
dim seqNo as integer
func SendMessage (msgData[] as byte)
    'Format the message to send
    'Add a sequence number and carriage return:
    msgData = str(seqNo) + ":" + msgData + "\r"
    seqNo = seqNo + 1
    myComm.SendData = msgData
endfunc

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
PostPosted: Mon Mar 01, 2010 5:34 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Thanks for your reply, but your solution isn't good for my problem. The output buffer size must be a byte. I need that each byte sent generates a sending event.

Anyway, I have been able to solve my problem. I've used the code of BasicSerialV2 and I've modified and added the following code.

Code:
#doc prop senddata
~Set this property in order to send data to the serial port.
~This property is used at run-time and not in Layout View.
dim senddata[] as byte
func senddata(newval[] as byte)
    if comport <> COM_INVALID then
        'register globals to receive the MSG_COMM_TRANSMIT message
        registerMsgHandler(me, MSG_COMM_TRANSMIT, comport)
        'send a connection resource and transmission data
        Send(comport, newval)
        return
    endif
endfunc

private func CommSend (data[] as byte) returns boolean
    handles MSG_COMM_TRANSMIT
    dim comport as comm
    'get comport used in the Send and registerMsgHandler functions
    comport = GetComMessageSource()
    'transmit data used in the Send Function
    transmit(comport, data, false)
    datasent(data)
    return true
endfunc

#doc override DataSent
#param data:Array of bytes sent by the serial port.
~This function is called each time data is sent by the serial port.
func datasent(data[] as byte)
    return
endfunc


Thanks again.


Top
 Profile  
 
PostPosted: Mon Mar 01, 2010 8:06 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
Well a couple of thoughts here.

Calling Send, simply invokes your MSG_COMM_TRANSMIT handler. There is nothing that would prevent you from simply directly calling that function from SendData, or even to simply embed that code in your SendData function. (This is what I meant by saying that it isn't a very useful message. I have never encountered a situation that you couldn't directly call a user function to perform the same action).

Unless I have missed something, In your case, the code you wrote is effectively the same as:
Code:
#doc prop senddata
~Set this property in order to send data to the serial port.
~This property is used at run-time and not in Layout View.
dim senddata[] as byte
func senddata(newval[] as byte)
    if comport <> COM_INVALID then
        'send a connection resource and transmission data
        transmit(comport, data, false)
        datasent(data)
        return
    endif
endfunc

#doc override DataSent
#param data:Array of bytes sent by the serial port.
~This function is called each time data is sent by the serial port.
func datasent(data[] as byte)
    return
endfunc


Now I am a bit intrigued by your statement
Quote:
I need that each byte sent generates a sending event.
I am not quite sure what you mean by that. Your workaround code doesn't seem to do this (unless you are calling SendData on single element byte arrays).

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
PostPosted: Mon Mar 01, 2010 9:50 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Hello again,

En mi anterior declaración:
Quote:
I need that each byte sent generates a sending event.

I mean I need that to be generated a sending event each time a byte is sent by the serial port, because the size of my output buffer has to be a single byte. My communication protocol is based on events. These events allow a sequential progress in two state machines: emission and reception. For this reason, I need the MSG_COMM_TRANSMIT handler or another event that tells me that a byte has been sent through the serial port.

M.L.G.


Top
 Profile  
 
PostPosted: Mon Mar 01, 2010 9:58 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Sorry, excuse me for my bad English.

In my previous statement:
Quote:
I need that each byte sent generates a sending event.

I mean I need that to be generated a sending event each time a byte is sent by the serial port, because the size of my output buffer has to be a single byte. My communication protocol is based on events. These events allow a sequential progress in two state machines: emission and reception. For this reason, I need the MSG_COMM_TRANSMIT handler or another event that tells me that a byte has been sent through the serial port.

M.L.G.


Top
 Profile  
 
PostPosted: Wed Mar 03, 2010 10:13 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
There is no event that lets you know that a byte has been successfully transmitted out the serial port.

You queue bytes to transmission using the Transmit API. However, no event is fired when that data has been physically placed on the wire(s).

Transmit, DOES have a "block" 3rd parameter. If you set that to true, your program will block until the data has been successfully sent. Normally you would set that to false, however, in your case you may wish to set that value to true.

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
PostPosted: Thu Mar 04, 2010 1:57 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Many thanks!

I'll take into account your last reply.

However, as I said in a previous reply, I has been able to solve my problem. I'm able to generate sending event with the code posted in that reply. I added this code into the BasicSerialV2 code. Maybe "sending event" it's not a right name for this, but it does just what I want.

I'll try to explain the code works.

I put a byte on the modified "senddata" function as shown below.
Code:
send_status = SEND_EMITTER
output_data[0]=ENQ
BasicSerial_1.senddata=output_data

Then the modified "senddata" function invokes the MSG_COMM_TRANSMIT handler through the Send() API function.
Code:
func senddata(newval[] as byte)
    if comport <> COM_INVALID then
        'register globals to receive the MSG_COMM_TRANSMIT message
        registerMsgHandler(me, MSG_COMM_TRANSMIT, comport)
        'send a connection resource and transmission data
        Send(comport, newval)
        return
    endif
endfunc

Next, thanks the MSG_COMM_TRANSMIT handler, it starts the "CommSend" function.
Code:
private func CommSend (data[] as byte) returns boolean
    handles MSG_COMM_TRANSMIT
    dim comport as comm
    'get comport used in the Send and registerMsgHandler functions
    comport = GetComMessageSource()
    'transmit data used in the Send Function
    transmit(comport, data, true)
    datasent(data)
    return true
endfunc

Where "datasent" is defined as:
Code:
#doc override DataSent
#param data:Array of bytes sent by the serial port.
~This function is called each time data is sent by the serial port.
func datasent(data[] as byte)
    return
endfunc

Finally, through the "datasent" function, begins the communication protocol. For example:
Code:
func datasent(data[] as byte)
    If send_status == SEND_EMITTER Then
        send_status = SEND_RECEIVER
        output_data[0]=identity
        BasicSerial_1.senddata=output_data
...


I hope I have explained this properly.

Regards,

M.L.G.


Top
 Profile  
 
PostPosted: Fri Mar 05, 2010 7:54 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
As long as it works, you are in good shape. One performance improvement you might add is only calling RegisterMsgHandler once, as there is some performance issues involved in repeatedly calling it (although there is a good chance you will never notice them).

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
PostPosted: Fri Apr 16, 2010 1:54 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Hi,

I've a new problem with the serial comunications.

My application receives 800 bytes (200 floats of a waveform points) from a DSP in my Z60 terminal. As I mentioned in previous replies, my communication protocol is based on events and receives data like this:
Code:
func datareceived(data[] as byte)
    setserialrecvsize(BasicSerial_1.Comport,1)
    input_data = data[0]

    If receive_status == IDLE Then
        If input_data == ENQ Then
            receive_status = WAIT_EMITTER
            comm_status = RX
            Timer1.Enabled = True
        EndIf

    ElseIf receive_status == WAIT_EMITTER Then
        emitter_R = input_data
        receive_status = WAIT_RECEIVER

    ElseIf receive_status == WAIT_RECEIVER Then
        If input_data == identity Then
            output_data[0]=ACK
            BasicSerial_1.senddata=output_data
            receive_status = WAIT_CMD
            cmd_index = 0
        ElseIf input_data == BROADCAST Then
            receiver_R = input_data
            receive_status = WAIT_CMD
            cmd_index = 0
        Else
            Timer1.Enabled = False
            Timer2.Enabled = True
            receive_status = WAIT_ACK
        EndIf

    ElseIf receive_status == WAIT_CMD Then
        If cmd_index < CMD_LENGTH Then
            cmd0_R = input_data
            cmd_index = cmd_index + 1
        Else
            cmd1_R = input_data
            comando_R = cmd0_R*256 + cmd1_R
            receive_status = WAIT_NUMDATA
        EndIf

    ElseIf receive_status == WAIT_NUMDATA Then
        numdatos_R = input_data
        numbyte = numdatos_R * 4
        redim(datos_R,numbyte)
        receive_status = WAIT_DATA
        crc_Int = 0
        byte_index = 0
        data_index = 0
        data_length_index = 0

    ElseIf receive_status == WAIT_DATA Then
        Timer1.Enabled = False
        Timer1.Enabled = True
        If byte_index < numbyte - 1 Then
            datos_R[byte_index] = input_data
            byte_index = byte_index + 1
            crc_Int = crc_Int + input_data
        Else
            datos_R[byte_index] = input_data
            crc_Int = crc_Int + input_data
            receive_status = WAIT_CRC
        EndIf

    ElseIf receive_status == WAIT_CRC Then
        crc_R = crc_Int Mod 256
        If input_data == crc_R And receiver_R <> BROADCAST Then
            output_data[0]=ACK
            BasicSerial_1.senddata=output_data
            receive_status = IDLE
            comm_status = IDLE
            input_data = 0
            Timer1.Enabled = False
            timer1_counter = 0
            Timer2.Enabled = False
            timer2_counter = 0

        ElseIf input_data <> crc_R And receiver_R <> BROADCAST Then
            output_data[0]=NAK
            BasicSerial_1.senddata=output_data
            receive_status = IDLE
            comm_status = IDLE
            input_data = 0
            Timer1.Enabled = False
            timer1_counter = 0
            Timer2.Enabled = False
            timer2_counter = 0
        Else
            receive_status = IDLE
            comm_status = IDLE
            input_data = 0
            Timer1.Enabled = False
            timer1_counter = 0
            Timer2.Enabled = False
            timer2_counter = 0
        EndIf

    ElseIf receive_status == WAIT_ACK Then
        If input_data == ACK Then
            receive_status = WAIT_EOT  ' Espero el segundo ACK
        EndIf

    ElseIf receive_status == WAIT_EOT Then
        If input_data == ACK Then
            receive_status = IDLE
            comm_status = IDLE
            input_data = 0
            Timer1.Enabled = False
            timer1_counter = 0
            Timer2.Enabled = False
            timer2_counter = 0
        EndIf
    Endif
endfunc


The problem is that when I run my application in the Qlaryti simulation view this run correctly, but when I download this application into my Z60 terminal, this run more slowly, so it gives the impression that the teminal is very slow.

Is this normal? Is it possible to speed up the receipt in any way?

MLG


Top
 Profile  
 
PostPosted: Fri Apr 16, 2010 7:16 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
The short answer to your question: probably.

The long answer is a bit more complicated.

Qlarity Foundry's Simulation view does not attempt to simulate the processor speed of the terminal. It will run at the native speed of your CPU -- i.e. something like 2-3 GHz with a whole lot of optimized hardware. Your Z60 is our extra low cost Qlarity terminal, which has a much lower performance processor -- about 75 MHz, and a much less optimized memory subsystem. So slower processing speeds are to be expected on the Z60. If you need more processing power you might consider the G70 terminal, which will run the same Qlarity code but at 2-6x the Z60's speeds depending on what you are doing.

Having said that there probably are things that can be done to optimize the situation. The #1 big one that I can see based on the limited code you have posted is: setserialrecvsize(BasicSerial_1.Comport,1). This line of code is brutal in two ways.

First of all, it is executed for every single byte that is received on the serial port, but only really needs to run once at program startup. Try moving it to the program startup like this:

Code:
'Global code section
func Startup ()
    handles MSG_INIT
    'Add any code needed to prepare the workspace here.

    'Code that needs to intereact with other objects, or that needs the
    'Z-order to be established should be placed in PostInit function
    UserDirectMsg(default, _MSG_POST_INIT, 0, false)
    return
endfunc

func PostInit(parm as integer) returns boolean
    handles _MSG_POST_INIT
    'This function will be called after all objects have processed their Startup function

    'Add any startup code that needs to interact with other objects here

    SetSerialRecvSize(BasicSerial_1.Commport, 1)

    return true
endfunc


This may produce a minor speed increase. However by far the worst problem in your serial receive code is that you are processing it one byte at a time. You would be much better off trying to process it in good size chunks at a time. To set that up start modifying the global code we just wrote like this:

func PostInit(parm as integer) returns boolean
handles _MSG_POST_INIT
'This function will be called after all objects have processed their Startup function

'These are HINTS for optimization, do not rely on them.
SetSerialRecvSize(BasicSerial_1.Commport, 800) 'a HINT at how large the data messages will be
SetSerialTimeout(BasicSerial_1.Commport, 2) 'indicate that you want data quickly if there is any gap in the serial stream

return true
endfunc

Next you need to modify your DataReceived function to deal with arrays of bytes arriving instead of individual bytes. The easiest way to do that is take your current logic and wrap it in a FOR loop. This will give you a decent feel for whether you have bought anything with your optimization. There are many further optimizations you could perform. For instance, it is common to simply buffer data until at least one packet has arrived (see SetArrayData for the fastest way to do that), then parse the packet and copy it. Often this is easier to code than the state machine you have set up. And it is usually much faster as well.

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
PostPosted: Thu Apr 29, 2010 3:06 am 
Offline
User avatar

Joined: Tue Feb 23, 2010 9:36 am
Posts: 12
Location: Almagro, Ciudad Real (Spain)
Hi,

I want to change the size of the receive buffer during run of the comunication protocol. In the startup I need a single byte buffer but later I need to increase this receive buffer. I've done the following:
Code:
func datareceived(data[] as byte)

    If receive_status == IDLE Then ' When not started any kind of communication.
        If data[0] == ENQ Then
            receive_status = WAIT_EMITTER
            comm_status = RX   ' The general state of the communication is received.
            Timer1.Enabled = True
        EndIf

    ElseIf receive_status == WAIT_EMITTER Then
        emitter = data[0]
        receive_status = WAIT_RECEIVER

    ElseIf receive_status == WAIT_RECEIVER Then
        If data[0] == identity Then   ' If I am the receiver
            output_data[0] = ACK
            BasicSerial_1.senddata=output_data
            receive_status = WAIT_CMD
            cmd_index = 0
        EndIf

    ElseIf receive_status == WAIT_CMD Then
        If cmd_index < CMD_LENGTH Then
            cmd[cmd_index] = data[0]
            cmd_index = cmd_index + 1
        Else
            cmd[cmd_index] = data[0]
            receive_status = WAIT_NUMDATA
            comandoR = cmd[0]*256 + cmd[1]
        EndIf

    ElseIf receive_status == WAIT_NUMDATA Then
        numdatos = data[0]
        numbyte = numdatos * 4
        SetSerialRecvSize(BasicSerial_1.Comport, numbyte)
        redim(datos,numbyte)
        receive_status = WAIT_DATA
        crc_Int = 0
        byte_index = 0

    ElseIf receive_status == WAIT_DATA Then
        datos = data
        SetSerialRecvSize(BasicSerial_1.Comport, numbyte)
        receive_status = WAIT_CRC

ElseIf receive_status == WAIT_CRC Then
        crc = crc_Int Mod 256
        If data[0] == crc And receiver <> BROADCAST Then
            output_data[0] = ACK
            BasicSerial_1.senddata = output_data
            send_status = IDLE
            receive_status = IDLE
            comm_status = IDLE
            data[0] = 0
            Timer1.Enabled = False
            timer1_counter = 0
            Timer2.Enabled = False
            timer2_counter = 0

            Redim(param_array, numdatos)
            FromBytes(param_array,datos,false)
    endif
endfunc


In WAIT_DATA, I expected the datareceive event would be generated when the "numbyte" bytes are received and the data[] array size would be "numbyte". However, this datareceive event is still generated with each single byte received and the data[] array size remains 1.

¿How can I do to change the size of the receive buffer during run of the comunication protocol?


Top
 Profile  
 
PostPosted: Thu Apr 29, 2010 6:26 am 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
Do NOT think of SetSerialReceiveSize as the size of the buffer. I cannot stress this enough. SetSerialReceiveSize and SetSerialTimeout are hints and only hints. You must be prepared for data to come through in arbitrary sized chunks. For more details, read viewtopic.php?f=4&t=128&p=355&hilit=setserialtimeout#p355

If you want to process data a byte at a time, in your DataReceived function do something like
Code:
func DataReceived(data[] as byte)
    dim i as integer
    dim max as integer
    max = len(data)-1
    for i = 0 to max
        ProcessByte(data[i])
    next
endfunc

'Add this as a global function
func ProcessByte (data as byte)
    'Your byte processing here
endfunc

Keep single byte processing to a minimum if possible, as it tends to be slow.

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ] 

All times are UTC - 7 hours


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group