Using Com3

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
victorf
Posts: 342
Joined: 01 January 2006, 4:08 AM
Location: Schenectady, New York

Using Com3

Post by victorf »

I wish to use com3 to communcate with a device. The device I want to talk to implements an RS232 interface using a MAX232A. I am a bit confuses as to whether I should use inverted or non-inverted logic to configure Com3. The communications will be 9600 baud, 8 data bits, No parity and 1 stop bit. I wish to transmit on pin PC6 and receive on PC5.

I must admit that I am confused by how to prepare Com3. What routines need be called to get this going? :?

Any enlightenment will be appreciated.

Vic
Vic Fraenckel
KC2GUI
windswaytoo ATSIGN gmail DOT com
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

First, a little background information. The RS232 standard calls for a logic 1 (also called "mark") to be transmitted as a level between -3 and -15 volts and a logic 0 (also called "space") as a level between +3 and +15 volts. A level converter like the MAX232(A) performs both inversion and level conversion. A +5 volt input on the TTL side yields a negative voltage on the RS232 side and a zero volt input on the TTL side generates a positive voltage on the RS232 side.

Since the device that you're connecting will be generating and expecting RS232 voltage levels, you need to add circuitry between the ZX and the device to perform the level conversion. A MAX232 is probably the easiest way to do this. Appendix C of the ZBasic Reference manual shows several alternatives to performing the level conversion.

Once you have the level converter in place, you'll want to select non-inverting mode for Com3.

Some devices have serial interfaces that don't conform to the RS232 standard but still work in most cases. The Com1 channel on the ZX-24 is an example of this. A mark is transmitted as zero volts and a space is transmitted as +5 volts. If you wanted to connect two ZX-24s using Com1 on one and Com3 on the other, you'd need to select inverting mode.

It is also possible to using inverting mode and a few resistors and diodes to connect to a device with a conforming RS232 interface. This should only be done if the connection is short (a few feet or so). To implement this interface, you need clipping diodes and a limiting resistor on the input to the ZX and a limiting resistor on the output from the ZX. Again, this is not recommended (because doing it incorrectly can damage the ZX) but it should work.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I neglected to answer the second part of your question.

To set up Com3 you have to prepare two queues, define the Com3 configuration and then open the port. Here is a code fragment that shows how it might be done.

Code: Select all

Const rxPin as Byte = 20
Const txPin as Byte = 19
Const qsize as Byte = 40
Dim oq3(1 to qsize) as Byte
Dim iq3(1 to qsize) as Byte

Call OpenQueue(iq3, SizeOf(iq3))
Call OpenQueue(oq3, SizeOf(oq3))
Call DefineCom(3, rxPin, txPin, &H08)
Call OpenCom(3, 9600, iq3, oq3)
You may need to adjust the queue size. It may make sense in your application to have one queue larger than the other. It may also make sense to completely eliminate one of them if you will only be sending data or only receiving data.

If you want to use Com4, Com5 or Com6 you must first invoke ComChannels() to specify the overall parameters. This is required because the same software is used internally for all 4 software UARTs and it needs to know, before any of the channels are opened, the maximum number of channels that will be used and the maximum baud that will be used. Com3 is handled as a special case (for compatibility reasons) so this special setup is not required if you only use Com3. Looked at another way, the firmware is pre-configured for 1 software UART with a maximum baud of 19200 and if you want a different configuration you need to use ComChannels() to specify what you want.
- Don Kinzer
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

Post by pjc30943 »

Just a clarification question:

The docs say ComChannels has no effect on coms are already open... but I'm not clear which ports this is referring to: all of them, even ones to be opened in the future, or just ones that are already open (is it a compile-level statement, or runtime-level, etc.)

If I go from 3 ports to 2, for example, by closing a port, it may be benificial to redefine ComChannels for higher maximum baudrates. For this, _all_ coms must first be closed, right?
Or can ComChannels statement affect only the next port that is opened, just not those already in place? This still fits with the documentation's description, I believe.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

The docs say ComChannels has no effect on coms are already open.
Just to clarify, it says that invoking ComChannels() has no effect if any of Com3 to Com6 is open. This means that if you want to change the number of available (software-based) channels or change the maximum speed, you can only do so when none if those channels is open.

The reason that this is so is because both the number of channels and the maximum speed affect the way that the timing is generated. The timing and sequencing cannot be changed when a channel is open because it would corrupt any character that was in the process of being transmitted or received.
- Don Kinzer
jorsam
Posts: 1
Joined: 10 June 2008, 5:12 AM

Post by jorsam »

Hi,

Great chat and very clear how to define, set and open the serial communication using rxPin and txPin as rx and tx, etc.

But I have a basic question, sorry I'm a beginner:
Which command is used to send and receive serial communication using this open port?

Example: how to code a simple program to send and ASCII character to txPin and how to read a string from the rxPin?

Any help will be very much appreciated.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

jorsam wrote:Which command is used to send and receive serial communication using this open port?
The serial channels in ZBasic are all buffered; the transmit and receive queues that you specify when a channel is opened are used for the transmit and receive buffers. Consequently, you don't send and receive characters directly to and from the channel's rx and tx pins. Rather, you put characters to be transmitted into the channel's transmit queue and you read the received characters from the channel's receive queue. Although this design may seem complicated, it has the significant advantage that data transmission and reception can be done in the background.
jorsam wrote:Example: how to code a simple program to send and ASCII character to txPin and how to read a string from the rxPin?
Here is a simple example program showing how to set up a serial channel and then send a character and a string of characters to a serial channel's output queue.

Code: Select all

Const txPin as Byte = 5
Const rxPin as Byte = 6
Const CR as Byte = &H0d
Const LF as Byte = &H0a

Dim iq(1 to 25) as Byte
Dim oq(1 to 40) as Byte

Sub Main()
  ' initialize the queues
  Call OpenQueue(iq)
  Call OpenQueue(oq)

  ' define the characteristics of the serial channel
  Call DefineCom(3, rxPin, txPin, &H08)

  ' open the serial channel
  Call OpenCom(3, 9600, iq, oq)
  
  ' send a carriage return and line feed as individual bytes
  Call PutQueueByte(oq, CR)
  Call PutQueueByte(oq, LF)

  ' send a string with a CRLF
  Call PutQueueStr(oq, "Hello, world!" & Chr(CR) & Chr(LF))
End Sub
Reading data received via the serial channel is slightly more complicated because you don't know when the data you want is going to start arriving and when it will have been completely received. Whether you're wanting to read single characters or sequences of characters from the receive queue, the StatusQueue() and GetQueueCount() functions will be useful to determine when enough data has arrived. The difference between these functions is that StatusQueue() will return True if the queue contains one or more characters. On the other hand, GetQueueCount() returns the number of characters available in the queue. The example below is the Main() subroutine from above with the addition of a loop that echos the received characters to the debug output.

Code: Select all

Sub Main()
  ' initialize the queues
  Call OpenQueue(iq)
  Call OpenQueue(oq)

  ' define the characteristics of the serial channel
  Call DefineCom(3, rxPin, txPin, &H08)

  ' open the serial channel
  Call OpenCom(3, 9600, iq, oq)
 
  ' send a carriage return and line feed as individual bytes
  Call PutQueueByte(oq, CR)
  Call PutQueueByte(oq, LF)

  ' send a string with a CRLF
  Call PutQueueStr(oq, "Hello, world!" & Chr(CR) & Chr(LF))

  ' echo all received characters to the debug console
  Do
    If (StatusQueue(iq)) Then
      Dim c as Byte
      Call GetQueue(iq, c, 1)
      If (c = CR) Then
        Debug.Print
      ElseIf &#40;c <> LF&#41; Then
        Debug.Print Chr&#40;c&#41;;
      End If
    End If
  Loop
End Sub
Here is an alternate implementation of the echo loop using GetQueueStr().

Code: Select all

  ' echo all received characters to the debug console
  Do
    If &#40;StatusQueue&#40;iq&#41;&#41; Then
      Dim str as String
      Dim strLen as Integer, idx as Integer
      str = GetQueueStr&#40;iq&#41;
      strLen = Len&#40;str&#41;
      For idx = 1 to strLen
        Dim c as Byte
        c = Asc&#40;str, idx&#41;
        If &#40;c = CR&#41; Then
          Debug.Print
        ElseIf &#40;c <> LF&#41; Then
          Debug.Print Chr&#40;c&#41;;
        End If
      Next idx
    End If
  Loop
One of the challenges related to reading from a queue is you can't tell ahead of time whether or not it contains a specific character or sequence of characters (e.g. a CR/LF). If your application requires that you wait for receipt of such a termination character, it may be best to read characters from the queue into an array or string until you get the termination character(s).
- Don Kinzer
Post Reply