UART flow control buffering data between UART and socket


#1

I have an application that opens a TCP/IP socket to a server and then forwards all data coming in on UART1 to the socket. If the socket is busy sending data (i.e. I haven’t received the callback to say that I can send more data to the socket yet) then I buffer the data and send it when the callback occurs.
Is there some way that I can control the CTS line on the UART so that I can stop the device connected to the modem sending data to UART1 too fast? Can I use the GPIO service (e.g. adl_ioWrite()) to do this?

regards,
Charles


#2

I think you need not manipulate the CTS pin from within your Open-AT application as CTS pin is internally managed by the Wavecom Operating System. Changing the value on this pin by connecting it to a GPIO and writing to GPIO might interfere with the functioning of the module.

However, in your case, I don’t think you need to manipulate CTS pin. This is because, when you are sending the data on the opened socket, your Open-AT task is active. This will in turn activate the GSM/GPRS and send the data. Now, in the meantime, if your external device sends some data, the V24 layer will internally buffer it. This is because, the V24 layer is not able to present the data to the Open-AT application task as the GSM/GPRS task is active. In case, the external device is sending the data at a very high rate, V24 layer will invoke the flow control to stop the external device from sending the data. This will be done when the buffers allocated for V24 layer are about to be used up. Hence, flow control will automatically come into picture (CTS/RTS pins). When the Open-AT task gets its turn, the data will be sent to it by the V24 layer and the CTS pin will be set to logic level high. This will indicate to the external device that it can now send more data.

Hope, this information helps you.
Open AT Fan.


#3

Thanks OpenAt Fan, I think you are right. I’m not sure were I got the idea that I could monitor or control the RS232 control signals using GPIO.

I still have a problem, in that I have subscribed to the V24 flow and it is sending me data faster than I can send it on the socket (especially if the UART is at 115200 and of course the GPRS and socket is much slower).
So is there some way that I can tell the V24 flow that I can’t handle any more and to back off until I can?
I’ve seen mention of “credits”, but nothing really explains what a credit is - are they buffers that you can hold onto and then let OpenAT know when you have finished with them?

regards,
Charles


#4

Hi Charles,
Yes, you can use the concept of “Credits” to tell the V24 to enable the hardware flow control and prevent swamping your application by excess of data.

Actually, “Credits” represent a way by which you tell to the module the amount of data blocks which are processed by you application. In simple terminology, you can view “Credits” as data blocks presented to your FCM data handler by the module and “Release of Credits” can be visualized as the release of the buffer used to store the data.

The module can allocate 6 such buffers for storing the received data.
Now, suppose, the external application sends data to your embedded application using V24 serial link. The received data will be allocated a buffer (“Credit”) and will be presented to your application by a call to the data handler.
The release of this buffer depends upon the value that you return from your data handler. Suppose, you return TRUE from the data handler. In this case, the buffer allocated for the received data is released and it acts as an indication that your application has processed the received data.
Now, suppose you retun FALSE from data handler. In this case, the buffer allocated internally to store data will not be released. As a total of 6 buffers can be allocated at a time, hence if your Open-AT application returns FALSE from the data handler for 6 consecutive times, all the buffers which can be internally allocated will be used up.
From now, the hardware flow control come into picture. The module will assert the CTS to logical low and will prevent the external application from sending more data over V24 serial link. When you explicitly release the buffer (using adl_fcmReleaseCredits ()) will the external application be allowed to send more data.

Hence, to solve the problem as in your case, you can configure the credits from within your application in such a way that you buffer the data until all the 6 credits are used up (Now the module will not receive any more data from V24, so you get enough time to send data to TCP socket).
When you are finished sending the data, release one credit. So the external applicaiton will be allowed to send some more data (by asserting CTS). After receiving data, again return FALSE from the data handler (to use up the credit again to enable h/w flow control). Then send the data again. This should solve your problem.

In case, you need more information on credits you can consult the ADL user guide. In case, you feel that ADL user guide is not explicit enough, you should consult the Wavecom Technical Support. :smiley:

Hope the above explanation help you solve your problems.

Best Regards,
Open AT Fan.


#5

Hi Open AT Fan,
I should have read the manual more carefully. The description for adl_fcmSubscribe() does explain the buffer/credit business (although not as well as you described it). Thanks.

I have tried changing my code to work the way I think it should, but now if I try to send more than just a few characters (i.e. paste a whole lot of text into Hyperterminal, rather than just typing in characters) it sends a few and then stops sending altogether. Any ideas of what I’ve got wrong?

Here’s what my program now does -

  • Once the socket is open I call adl_fcmSubscribe()
  • Then when I get a ADL_FCM_EVENT_FLOW_OPENNED event I call adl_fcmSwitchV24State() to switch the V24 to data state
  • In the V24 data handler callback, when I receive data if the socket is not busy then I send it to the socket using ed_SendDataExt() and then set a flag to say that the socket is busy and return TRUE (buffer/credit used). If I receive data and the flag is set to say that the socket is busy then I write the data to my circular buffer and return FALSE (buffer/credit still held).
  • When I get the callback (that I registered with ed_SocketTCPStart()) to say that the socket is available to send data again I look in the circular buffer to see if I have more to send, send it using ed_SendDataExt() and then call adl_fcmReleaseCredits(RS232Handle, 1) (if I just sent something form the circular buffer).

For data going the other way (i.e. received on the socket) I just call adl_fcmSendData() to send the data to the V24 fow each time the socket data handler callback is called and the callback always returns the number of bytes received.

Another question - presumably if I get this working the way it should then I don’t need to copy the data into my circular buffer and can just keep a copy of the pointer to the data block received in the V24 data handler callback until I have sent the data to the socket and called fcmReleaseCredits()?

Regards,
Charles


#6

Hi,
I think I’ve found the problem and I now have it working a lot better after a lot of re-writing (although it still occasionally doesn’t work perfectly).

I think that the key thing is that with all the “data handler callbacks” you are passed a pointer to a data buffer and that buffer belongs to whatever called your callback, so you must make sure that you only use that buffer while you are allowed to. This means that you sometimes need to take a copy of the data, or you need to only let OpenAT know that you have finished with the data buffer when you really have.

For UART data coming in and being sent to socket I now have-

  • Receive data handler callback for V24 flow.
  • Add data pointer and length to list of data packets.
  • If socket not busy, send data to socket
  • Return FALSE to the callback, indicating that we still are using the buffer
  • Wait for the “socket available to send data callback” and then remove then remove data packet from list of data packets and tell OpenAt that you have released it by calling adl_fcmReleaseCredits().

For socket data coming in and being sent to the UART I now have-

  • Receive data handler callback for socket
  • Allocate memory buffer (adl_memGet) and copy data into memory buffer
  • Send data to UART with adl_fcmSendData()
  • Add buffer to list of buffers waiting to be freed
  • Wait for ADL_FCM_EVENT_MEM_RELEASE event in V24 control handler and then release buffer (adl_memRelease)

Does this seem right?
My next task is to check that the RS232 modem control lines are doing what I expect them to.

regards,
Charles


#7

Hi Charles,
Sorry for the delayed response (I was out of station).
Yes, you are right in your notions regarding the buffer and data management for the socket and V24 serial link.
It is always advisable to copy the received data in a local buffer.

In case, you are stuck anywhere, you can always post in Open AT Forum. You will never be short of ideas. :smiley:

Best Regards,
Open AT Fan.


#8

Hi Charles,
I am also trying to do a similar thing and for this purpose, I am trying to enable UART2. That way I could use one UART for console/AT and the other for receiving data. But I am finding difficulties in enabling UART2 and using it for AT. Did you follow a similar approach? Os is there a way I need not use both UARTs and do it with only one UART? I would appreciate some tips from you if you got the UART2 working.

funtime.


#9

Hi Funtime,
Please use the AT+WMFM command to enable the UART2 of your module. This command takes different parameters for different version of AT firmware. So depending upon the version of AT firmware (AT Core Software ), you should give AT+WMFM command and enable it. You can do it either manually or through your Open-AT application using adl_atCmdCreate () API.

Once, the UART2 is enabled, you can use it to send AT commands/receive data depending upon your requirements.

Best Regards,
Open AT Fan.


#10

Hello

I do the same. I put the uart1 in data mode and I open a socket. I’d like to send the data what comes from uart1 to the socket. I put the ed_SendDataext in the fcmdata handler. When data arrives I send it, but nothng happens. The control message sent, but what I am typeing in the hyperterminal doesn’t arrived at the other side. How can I send the data from uart1 to the socket?


#11

Hmm I try it uart1. I put uart1 in data mode, and in the datahandler I send the data to a socket. The fixed text data: is arrived but after that I wrote the Data which come from the terminal but it isn’t arrived? Could anybody give me some help?