Forum Index
HomeZBasic Home   Forum RulesForum Rules   Forum FAQForum FAQ   MemberlistMemberlist   UsergroupsUsergroups   RSS FeedRSS Feed
Site SearchSite Search   LinksLinks   DownloadDownload   Digests and SubscriptionsDigests and Subscriptions
ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in   RegisterRegister
bi-directional serial communication on a single line

 
Post new topic   Reply to topic    Forum Index -> ZX-24
Author Message
Bruno Deletre



Joined: 30 Mar 2009
Posts: 4

Posted: 31 March 2009, 6:12 AM    Post subject: bi-directional serial communication on a single line Reply with quote

Hi folks,

As I recently switched from BS2 to ZX24p, I was wondering if there was a simple way to use a serial communication with "Tx" and "Rx" on the same I/O line. Actually the BS2 commands I want to find a replacement for are:

SEROUT SamePIN, baudrate, [0,Command, slaveaddr,reg2read]
SERIN SamePIN, baudrate, Timeout, timeoutfunc,[Var_in_LSB,Var_in_HSB,CRC]

In my application, those commands were dedicated to interrogate and receive data from parallax’s MLX90614 sensors sharing the same bidirectional data line.

Thank you for your help,

Bruno
Back to top
spamiam



Joined: 13 Nov 2005
Posts: 666

Posted: 31 March 2009, 15:38 PM    Post subject: Reply with quote

Well, Dallas 1-wire is an established protocol for single wire bidirectional communicaiton. I2C uses 2 wires, one for the clock, and 1 for bidirectional data.

You can always make up your own protocol as the situation demands.

-Tony
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR

Posted: 31 March 2009, 16:07 PM    Post subject: Re: bi-directional serial communication on a single line Reply with quote

Bruno Deletre wrote:
I was wondering if there was a simple way to use a serial communication with "Tx" and "Rx" on the same I/O line.
Although not simple, I believe that something like the code below (untested) might work. It uses the same pin for Rx and Tx by closing, reconfiguring and re-opening a channel. Note that the CRC value is ignored in this code. I haven't checked to see if the CRC8() function is compatible with the algorithm used by this device.
Code:
Function ReadMLX90614() as UnsignedInteger
  Const pin as Byte = 5
  Const chan as Byte = 3
  Const speed as Long = 9600
 
  ' prepare the queue
  Dim q(1 to 30) as Byte
  Call OpenQueue(q)
 
  ' prepare the channel for transmitting, send command
  Call DefineCom(chan, 0, pin, 8)
  Call OpenCom(chan, speed, 0, q)
  Call PutQueueStr(q, Chr(0) & "!TEMC" & Chr(&H5a) & Chr(&H07))
 
  ' wait until the data is transmitted
  Do While ((StatusCom(chan) And &H4) = 0)
  Loop

  ' reconfigure the channel for receiving
  Call CloseCom(chan, 0, 0)
  Call DefineCom(chan, pin, 0, 8)
  Call OpenCom(chan, speed, q, 0)
 
  ' wait for data to arrive
  Do While (GetQueueCount(q) < 2)
  Loop

  ' extract the returned value from the queue
  Dim val as UnsignedInteger
  Call GetQueue(q, val, SizeOf(val))
  ReadMLX90614 = val
 
  ' close the channel
  Call CloseCom(chan, 0, 0)
End Function

Although this issue is not specifically addressed, you might find the PBasic Conversion Guide useful.
Back to top
dlh



Joined: 15 Dec 2006
Posts: 266
Location: ~Cincinnati

Posted: 31 March 2009, 17:10 PM    Post subject: Reply with quote

This is something that I would like to see addressed - perhaps as an option when configuring a software UART. It would save a pin when doing half-duplex comms. With this and a 555 timer chip, you could do RS485 half-duplex master/slave using a single pin which would be helpful with the ZX-24 & ZX-328n. I also used a single pin with the BX-24 and NetMedia's LCD display that also included 8 ADC channels. I could query a channel, then receive the data using a single line/pin.
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR

Posted: 03 April 2009, 16:56 PM    Post subject: Re: bi-directional serial communication on a single line Reply with quote

dkinzer wrote:
I believe that something like the code below (untested) might work.
We have confirmed that the code below does work correctly. Note that this code is slightly different from that first posted above in that a different command to the device is used. It has also been tested at 2400 and 9600 baud and appears to work equally well at those speeds.

Code:
Const chan as Byte = 3
Const pin as Byte = 5
Const slaveID as Byte = &H30
Const speed as Long = 4800
Function ReadMLX90614() as UnsignedInteger
  ' prepare the queue
  Dim q(1 to 30) as Byte
  Call OpenQueue(q)
 
  ' prepare the channel for transmitting, send command
  Call DefineCom(chan, 0, pin, &H08)
  Call OpenCom(chan, speed, 0, q)
  Call PutQueueStr(q, Chr(0) & "!TEMR" & Chr(slaveId) & Chr(&H07))
 
  ' wait until the data is transmitted
  Do While CBool(StatusCom(chan) And &H04)
  Loop

  ' reconfigure the channel for receiving
  Call CloseCom(chan, 0, 0)
  Call DefineCom(chan, pin, 0, 8)
  Call OpenCom(chan, speed, q, 0)
 
  ' wait for data to arrive
  Do While (GetQueueCount(q) < 2)
  Loop

  ' extract the returned value from the queue
  Dim val as UnsignedInteger
  Call GetQueue(q, val, SizeOf(val))
  ReadMLX90614 = val
 
  ' close the channel
  Call CloseCom(chan, 0, 0)
End Function


While waiting for our test device to arrive, we also implemented some changes in the software UART code to realize half-duplex mode when the Rx and Tx pins are the same. For non-inverted operation, the line is driven in "open drain" mode during transmission, i.e. actively pulled low for a zero and allowed to float high for a one. Although perhaps less likely to be used, inverted operation is also supported in half-duplex mode.

With this new functionality (not yet generally available), the interface code can be changed to that shown below.
Code:
Function ReadMLX90614Ex() as UnsignedInteger
  ' prepare the queues
  Dim iq(1 to 20) as Byte
  Call OpenQueue(iq)
  Dim oq(1 to 20) as Byte
  Call OpenQueue(oq)
 
  ' prepare the channel for transmitting, send command
  Call DefineCom(chan, pin, pin, &H08)
  Call OpenCom(chan, speed, iq, oq)
  Call PutQueueStr(oq, Chr(0) & "!TEMR" & Chr(slaveId) & Chr(&H07))
 
  ' wait for data to arrive
  Do While (GetQueueCount(iq) < 2)
  Loop

  ' extract the returned value from the queue
  Dim val as UnsignedInteger
  Call GetQueue(iq, val, SizeOf(val))
  ReadMLX90614Ex = val
 
  ' close the channel
  Call CloseCom(chan, 0, 0)
End Function

The new functionality will be in the next release of the VM and ZX Library, anticipated by the end of the month. Note, however, that the new functionality is not supported on mega32-based devices such as the now-discontinued ZX-24.
Back to top
dlh



Joined: 15 Dec 2006
Posts: 266
Location: ~Cincinnati

Posted: 03 April 2009, 17:13 PM    Post subject: Re: bi-directional serial communication on a single line Reply with quote

dkinzer wrote:
While waiting for our test device to arrive, we also implemented some changes in the software UART code to realize half-duplex mode when the Rx and Tx pins are the same.
Thanks, Don.
Back to top
Bruno Deletre



Joined: 30 Mar 2009
Posts: 4

Posted: 04 April 2009, 7:10 AM    Post subject: Reply with quote

Thank you Don for this reply.

I tested the first version of your code (openC,closeC,openC,closeC) on a single sensor and it works fine. Your last version, when available, will be cleaner, thank you.

Consequently I tested your code using multiples MLX90614 in series. I fear their cummulated current draw is too important for a single pin (the spec says 15-20mA per sensor !!!). I am really very new in this domain, and my question may be silly: is there a way to design a self directing bi-directional amplifier ? I would actually prefer to keep a bus configuration for ease of on site wiring first and to keep pins available.

In any case, thank you a lot for your answers.

Bruno
Back to top
Bruno Deletre



Joined: 30 Mar 2009
Posts: 4

Posted: 04 April 2009, 9:56 AM    Post subject: Reply with quote

Don,

Forgive me for my last post. It was very early in the morning and I should have done my own reset first. It was actually a problem of reset and data collision. I finally managed to have up to 6 sensors in the same line, thank you.

I added the PEC calculation inspired from the forum and the MLX datasheet.
I post it in case somebody would search such a strange thing.

Code:
'####################################################################################################################################
'####################################################################################################################################
'
'                               FUNCTIONS TO READ AND WRITE MLX90614 PARALLAX SENSORS SHARING THE SAME DATA LINE
'
'####################################################################################################################################
'####################################################################################################################################
Const PECERROR as single=-1000.0
Const MULTIPURPOSE_COM as Byte=3
Const MLX_count as integer =4
Const MLX_DATA_PIN as Byte = 8 '
Const MLX_RST_PIN as Byte= 7 '
Const MLX_COM_SPEED as long =9600


Dim MLX_Addresses(1 to 4) as Byte
Public MLX_Temp(1 to 4) as single



#define VERBOSE_DEBUG
#define INCLUDE_MLX_TEST_MAIN

'######################################################################################################################################
#ifdef INCLUDE_MLX_TEST_MAIN
Sub Main()
   dim count as integer
   dim cycles as long
   dim temp as single
   MLX_Addresses(1)=&H05
   MLX_Addresses(2)=&H15
   MLX_Addresses(3)=&H25
   MLX_Addresses(4)=&H35

   Call MLX_InitAtHighPower()
   
   for cycles =1 to 1000
      for count =1 to MLX_count
         temp=MLX_Read(MLX_Addresses(count))
         while temp=PECERROR
            delay(0.01) 'Putting this delay at 0.05 enables to avoid almost any data collision with 6 sensors.
                     'In case it is too low, the PEC Check catches the error and resend the request
            temp=MLX_Read(MLX_Addresses(count))
         wend
            MLX_Temp(count)=temp
         Console.WriteLine( Cstr(cycles) & " -->MLX# " & Cstr(MLX_Addresses(count)) & " --> " & Cstr(MLX_Temp(count)) & "°C")
         delay(1.0)
      Next count
   next cycles
End Sub
#endif
'######################################################################################################################################

Public Function MLX_Read(byval sensor_address as byte) as Single
  ' As the MLX shares RX and TX lines, the solution proposed by Donald KINZER is to permanently
  ' define and destroy software COM ports and set them alternatively in single TX or single RX

  ' Prepare the queue
  Dim MLX_q(1 to 40) as Byte
  Call OpenQueue(MLX_q)

  Call Putpin(MLX_RST_PIN,zxOutputLow)
  Call Putpin(MLX_RST_PIN,zxOutputHigh)
  Call Putpin(MLX_RST_PIN,zxInputPullUp)
  Delay(0.05)
 
  ' Prepare the channel for transmitting, send command
  Call DefineCom(MULTIPURPOSE_COM, 0, MLX_DATA_PIN, &H08)
  Call OpenCom(MULTIPURPOSE_COM, MLX_COM_SPEED, 0, MLX_q)
  Call PutQueueStr(MLX_q, Chr(0) & "!TEMR" & Chr(sensor_address) & Chr(&H07))
 
  ' wait until the data is transmitted
  Do While CBool(StatusCom(MULTIPURPOSE_COM) And &H04)
  Loop

  ' Reconfigure the channel for receiving
  Call CloseCom(MULTIPURPOSE_COM, 0, 0)
  Call DefineCom(MULTIPURPOSE_COM, MLX_DATA_PIN, 0, &H08)
  Call OpenCom(MULTIPURPOSE_COM, MLX_COM_SPEED, MLX_q, 0)

  ' Wait for data to arrive
  Do While (GetQueueCount(MLX_q) < 3) ' Waiting for tempLow,tempHigh and PEC Values   Loop     ' Extract the returned value from the queue    Dim Temp as UnsignedInteger   dim PEC as byte   dim OK as boolean     Call GetQueue(MLX_q, Temp, SizeOf(Temp))   Call GetQueue(MLX_q, PEC, SizeOf(PEC))       'PEC Check   OK = Check_RW_PEC(sensor_address, &H07,Temp, PEC)      if OK=True then    MLX_Read = (Csng(Temp) *0.02)-273.0   else    MLX_Read = PECERROR   end if   ' Close the channel    Call CloseCom(MULTIPURPOSE_COM, 0, 0)      #ifdef VERBOSE_DEBUG   Debug.print "MLX_Read("; Cstr(sensor_address) ; ")"   #endif End Function  '######################################################################################################################################  Public sub MLX_InitAtHighPower()    dim count as integer        #ifdef VERBOSE_DEBUG    Debug.print "MLX_InitAtHighPower()"    #endif        'Initialise the bus    Call Putpin(MLX_RST_PIN,zxOutputLow)    Call Putpin(MLX_RST_PIN,zxOutputHigh)    Call Putpin(MLX_RST_PIN,zxInputPullUp)    Delay(0.05)        'set all sensors at low power continuous reading without output    for count =1 to MLX_count        call MLXSendCmd("!TEMC",MLX_Addresses(count),&H07)      Next count  end sub '######################################################################################################################################  Public sub MLX_InitAtLowPower()    dim count as integer        #ifdef VERBOSE_DEBUG    Debug.print "MLX_InitAtLowPower()"    #endif        'Initialise the bus    Call Putpin(MLX_RST_PIN,zxOutputLow)    Call Putpin(MLX_RST_PIN,zxOutputHigh)    Call Putpin(MLX_RST_PIN,zxInputPullUp)    Delay(0.05)        'set all sensors at low power continuous reading without output    for count =1 to MLX_count        call MLXSendCmd("!TEMZ",MLX_Addresses(count),&H07)      Next count  end sub '######################################################################################################################################  Public Sub MLXSendCmd(byval CMD as string,byval sensor_address as byte, byval Lowbyte as byte=0, byval Highbyte as byte=0) 'Set Sensor reading intervals                                 [0,"!TEMI",slaveaddr,intervalnonsleep,intervalsleep] 'Set Sensor alarm level                                       [0,"!TEMA",slaveaddr,alarmlowbyte,alarmhighbyte] 'Set sensor for continuous data reading at low power             [0,"!TEMZ",slaveaddr,$07] 'Set sensor for continuous data reading at low power & output       [0,"!TEMz",slaveaddr,$07]  Needs to be followed by data reception 'Set sensor for continuous data reading                      [0,"!TEMC",slaveaddr,$07] 'Set sensor for continuous data reading & output                [0,"!TEMc",slaveaddr,$07] Needs to be followed by data reception 'Stop continuous reading mode                                       [0,"!TEMS",slaveaddr]    ' Prepare the queue     Dim MLX_q(1 to 40) as Byte     Call OpenQueue(MLX_q)         Call Putpin(MLX_RST_PIN,zxOutputLow)    Call Putpin(MLX_RST_PIN,zxOutputHigh)    Call Putpin(MLX_RST_PIN,zxInputPullUp)    Delay(0.05)       ' Prepare the channel for transmitting, send command     Call DefineCom(MULTIPURPOSE_COM, 0, MLX_DATA_PIN, 8)     Call OpenCom(MULTIPURPOSE_COM, MLX_COM_SPEED, 0, MLX_q)        'Debug.print Cstr(Highbyte);"-";Cstr(Lowbyte)    if Highbyte=0 and Lowbyte=0 then       Call PutQueueStr(MLX_q, Chr(0) & CMD & Chr(sensor_address))    elseif Highbyte=0 and Lowbyte>-->0 then
      Call PutQueueStr(MLX_q, Chr(0) & CMD & Chr(sensor_address) & Chr(Lowbyte))
   else
      Call PutQueueStr(MLX_q, Chr(0) & CMD & Chr(sensor_address) & Chr(Lowbyte) & Chr(Highbyte))
   end if
 
   ' wait until the data is transmitted
   Do While CBool(StatusCom(MULTIPURPOSE_COM) And &H04)
   Loop

   ' Close the channel
   Call CloseCom(MULTIPURPOSE_COM, 0, 0)
 
    #ifdef VERBOSE_DEBUG
    Debug.print "MLXSendCmd(" ; Cstr(CMD); "," ;Cstr(sensor_address); "," ; Cstr(Lowbyte); "," ;Cstr(Highbyte); ")"
    #endif
End Sub
'#####################################################################################################################################
'#####################################################################################################################################

Public function Check_RW_PEC(byval slaveadrr as byte, byval CMD as byte,byval Temp as unsignedinteger, byval PEC as byte) as boolean
     ' PEC verification according to MELIXIS spec for MLX90614 Read word commands (page 15-16).
     ' The Write word command is slightly different and is not implemented.
     dim Current_CRC as byte
     dim Data as byte
     Check_RW_PEC=True
     Current_CRC=0
     Data  = shl(slaveadrr,1)+0
     Current_CRC=Calc_CRC8(Current_CRC,Data)
     Data  = CMD
     Current_CRC=Calc_CRC8(Current_CRC,Data)
     Data  = shl(slaveadrr,1)+1
     Current_CRC=Calc_CRC8(Current_CRC,Data)
     Data  = Lobyte(Temp)
     Current_CRC=Calc_CRC8(Current_CRC,Data)
     Data  = Hibyte(Temp)
     Current_CRC=Calc_CRC8(Current_CRC,Data)
     IF Current_CRCPEC then
      #ifdef VERBOSE_DEBUG
      Debug.print "--------------------------- PEC failed"
      #endif
      Check_RW_PEC=False
     end if
end function

'-------------------------------------------------------
'  Calc_CRC8_T  adapted from "Tony"'s code on the Zbasic Forum
'  Uses a look-up table to determine the new CRC Value
'-------------------------------------------------------
Private Function Calc_CRC8(ByVal Current_CRC as byte, ByVal Data as byte) as byte
   Calc_CRC8 = crcTable((Current_CRC xor DATA) + 1) 'add 1 to use ZX 1-based indexing
End Function

' polynominal  lookup table for CRC calculation
Private crcTable as ByteVectorData ({
   &H00,&H07,&H0E,&H09,&H1C,&H1B,&H12,&H15,&H38,&H3F,&H36,&H31,&H24,&H23,&H2A,&H2D,
   &H70,&H77,&H7E,&H79,&H6C,&H6B,&H62,&H65,&H48,&H4F,&H46,&H41,&H54,&H53,&H5A,&H5D,
   &HE0,&HE7,&HEE,&HE9,&HFC,&HFB,&HF2,&HF5,&HD8,&HDF,&HD6,&HD1,&HC4,&HC3,&HCA,&HCD,
   &H90,&H97,&H9E,&H99,&H8C,&H8B,&H82,&H85,&HA8,&HAF,&HA6,&HA1,&HB4,&HB3,&HBA,&HBD,
   &HC7,&HC0,&HC9,&HCE,&HDB,&HDC,&HD5,&HD2,&HFF,&HF8,&HF1,&HF6,&HE3,&HE4,&HED,&HEA,
   &HB7,&HB0,&HB9,&HBE,&HAB,&HAC,&HA5,&HA2,&H8F,&H88,&H81,&H86,&H93,&H94,&H9D,&H9A,
   &H27,&H20,&H29,&H2E,&H3B,&H3C,&H35,&H32,&H1F,&H18,&H11,&H16,&H03,&H04,&H0D,&H0A,
   &H57,&H50,&H59,&H5E,&H4B,&H4C,&H45,&H42,&H6F,&H68,&H61,&H66,&H73,&H74,&H7D,&H7A,
   &H89,&H8E,&H87,&H80,&H95,&H92,&H9B,&H9C,&HB1,&HB6,&HBF,&HB8,&HAD,&HAA,&HA3,&HA4,
   &HF9,&HFE,&HF7,&HF0,&HE5,&HE2,&HEB,&HEC,&HC1,&HC6,&HCF,&HC8,&HDD,&HDA,&HD3,&HD4,
   &H69,&H6E,&H67,&H60,&H75,&H72,&H7B,&H7C,&H51,&H56,&H5F,&H58,&H4D,&H4A,&H43,&H44,
   &H19,&H1E,&H17,&H10,&H05,&H02,&H0B,&H0C,&H21,&H26,&H2F,&H28,&H3D,&H3A,&H33,&H34,
   &H4E,&H49,&H40,&H47,&H52,&H55,&H5C,&H5B,&H76,&H71,&H78,&H7F,&H6A,&H6D,&H64,&H63,
   &H3E,&H39,&H30,&H37,&H22,&H25,&H2C,&H2B,&H06,&H01,&H08,&H0F,&H1A,&H1D,&H14,&H13,
   &HAE,&HA9,&HA0,&HA7,&HB2,&HB5,&HBC,&HBB,&H96,&H91,&H98,&H9F,&H8A,&H8D,&H84,&H83,
   &HDE,&HD9,&HD0,&HD7,&HC2,&HC5,&HCC,&HCB,&HE6,&HE1,&HE8,&HEF,&HFA,&HFD,&HF4,&HF3})


Once again thank you for your help ,

Bruno Very Happy
Back to top
Bruno Deletre



Joined: 30 Mar 2009
Posts: 4

Posted: 04 April 2009, 10:00 AM    Post subject: Reply with quote

This will be better with the real file

Bruno



MLX_test_pec.bas
 Description:
Functions to read and write MLX90614 sensors (parallax version) sharing the same dataline

Download
 Filename:  MLX_test_pec.bas
 Filesize:  9.89 KB
 Downloaded:  2460 Time(s)

Back to top
dlh



Joined: 15 Dec 2006
Posts: 266
Location: ~Cincinnati

Posted: 04 April 2009, 10:28 AM    Post subject: Reply with quote

Bruno Deletre wrote:
I fear their cummulated current draw is too important for a single pin (the spec says 15-20mA per sensor !!!).
The currents are the same whether using two pins or one.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    Forum Index -> ZX-24 Time synchro. with the server - Timezone/DST with your computer
Page 1 of 1

 


All content Copyright © 2005-2012 Elba Corp. All Rights Reserved.
Opinions expressed in posts are those of the author and not necessarily those of Elba Corp.
Powered by phpBB © 2001, 2005 phpBB Group