Shop OBEX P1 Docs P2 Docs Learn Events
Bit banging code for serial communication — Parallax Forums

Bit banging code for serial communication

aregalaaregala Posts: 10
edited 2012-01-10 23:17 in BASIC Stamp
Hi,
I have a device that I need to control with a BS2p40 via RS-232. The device requires 8 data bits and even parity, which are not supported by SERIN/SEROUT from the BS. Does anyone have bit banging code for serial communication I could use (in whole or at least as a starting point)?

The device settings are:

Baud 19200
1 start bit
8 data bits
1 even parity
1 stop bit

Any help would be greatly appreciated!!!
Thanks,
Alan

Comments

  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-06 11:41
    The BASIC Stamp is not fast enough to bit-bang serial. The interpreter on the BS2p40 takes on the order of 100µs to execute even the simplest instruction, and at 19200 baud each bit is 52 µs. You will have to find a work-around. One possibility would be an external UART such as the MAX31xx that supports all combinations of bit length and parity. The Stamp communicates with the UART using the SHIFT commands.
  • aregalaaregala Posts: 10
    edited 2012-01-06 15:19
    Thanks Tracy! I just placed an order for the MAX3100's; now I just need to get the code together (by Monday!). I don't suppose you have any examples available doing something like this? I've never done this before.

    Thanks,
    Alan
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-06 20:23
    Sure, You can take a look at an article at this URL I wrote on using the MAX3100 to make an IrDA transceiver for the Stamp. Also follow an active link there to a nice tutorial that Al Williams wrote about using the MAX3110. Both articles have code snippets. You can also probably find code nuggets if you search the forum archives.

    I hope you also ordered the necessary baud rate crystal!

    There is a configuration word that sets various options. Once that is done, you can transmit data words, and the 9 lsbs contain your data. Note that the MAX3100 does not calculate parity. It expects your code to do that and to stuff it into the 9th bit.
    evenparity = x.bit7 ^ x.bit6 ^ x.bit5 ^ x.bit4 ^ x.bit3 ^ x.bit2 ^ x.bit1 ^ x.bit0
    There may be a trick fast way to do that, but I have forgotten. Ask if you have specific questions. Monday, huh?!
  • aregalaaregala Posts: 10
    edited 2012-01-07 08:45
    Your article is helpful, as is the link to that other example. I didn't get the crystals, and looks like I may need capacitors too. Can you please clarify if I need to invert the signals out of the MAX3110 to communicate properly with my device via standard RS232 (ie with 74HC00)?

    Thanks!
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-07 10:46
    The MAX3110 does not require an inverter. Both the inversion of the signal and level shifting to +/- 5 V are included within the chip. The chip incorporates the charge pump and even the capacitors to generate the +/-5 V supply from the single 5V supply. (No wonder it costs $12ea).

    The MAX3100 on the other hand includes neither the charge pump nor the inverting driver/receiver, so you would have to add those in the form of a MAX232 or some such.

    The crystal required is either 1.8432MHz or 3.6864MHz and it takes two 18pF or 20pF capacitors (either one will do). Or a resonator such as this one, which incorporates the capacitors.
  • aregalaaregala Posts: 10
    edited 2012-01-08 23:34
    I decided to go with the MAX3110 to make life easier. I spent a great deal of time reviewing your code (and Al Williams'), and I have a first crack at a version of my own. I'm fairly green at this, so I'm REALLY hoping you can take a look at this and let me know what you think. Unfortunately, I can't test it out yet, as I'm waiting for the new components to arrive, but I'd love to get it flushed out as much as possible before they arrive. My goal is now to have the code ready to go tomorrow (Mon) and have it completed on Tues!

    One more question- if I know that the max data received will be 9 bytes, would I need to worry about the 8 byte buffer for received data not being enough? I wouldn't think so, but thought I'd ask anyways...
    ' MAX3110E Driver for Basic Stamp
    ' RS 232 Port settings 19.2k baudrate, 1 start, 1 stop, 8 data bits, even parity
    din CON 11
    dout CON 10
    clk CON 9
    cs CON 8
    
    
    datasend VAR Byte(13) ' 13 bytes of data to send
    datarec VAR  Byte(8)  ' 8 bytes of data to receive
    char VAR Word   ' will combine 8-bit command/status and 8-bit data
    rts VAR char.BYTE1.BIT1     ' for data send
    cts VAR char.BYTE1.BIT1     ' for data read
    Pt  VAR Bit     ' even parity bit
    tst VAR Bit
    i  VAR Byte
    
    
    AUXIO   ' uses X i/o pins
    
    
    ' write to configuration register, 19.2k baudrate, 8 bits, parity enabled (1100 0000 0010 0000)
    writemode:
    LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[$C020\16]
    HIGH cs
    
    
    ' transmit datasend
    senddata:
    FOR i = 0 TO 12           ' send all 13 bytes
    
    
    senddata1:
    char.BYTE0 = datasend(i)
    Pt = char.BYTE0.BIT7 ^ char.BYTE0.BIT6 ^ char.BYTE0.BIT5 ^ char.BYTE0.BIT4 ^ char.BYTE0.BIT3 ^ char.BYTE0.BIT2 ^ char.BYTE0.BIT1 ^ char.BYTE0.BIT0  ' calculate parity bit
    char.BYTE1 = $80 + Pt     ' make rts high, adds parity bit
    
    
    senddata2:
      LOW cs
      PULSOUT sclk,4  ' get one bit
      tst = dout      ' test that transmit buffer is empty
      HIGH cs
      IF tst = 1 THEN senddata2: ' loop back until ready
      LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[char\16]
      HIGH cs
      i = i + 1
    NEXT
    
    
    senddataend:
      LOW cs  'turn off rts pin
      SHIFTOUT din, sclk, MSBFIRST,[$8600\16]    ' Is this right? Is this necessary?
      HIGH cs
      GOTO recdata
    
    
    '*********************
    ' now enter a loop to receive data, 8 bytes stored in datarec
    recdata:
    FOR i = 0 TO 7
      LOW din
        LOW cs   ' sequence to receive data, both 8-bit status and 8-bit data
          SHIFTIN dout, sclk, MSBPRE,[char\16]
        HIGH cs
        datarec(i) = char.BYTE0
    NEXT
    MAINIO
    RETURN
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-01-09 00:39
    attachment.php?attachmentid=78421&d=1297987572

    -Phil
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-09 09:49
    The program structure looks fine. There are some specific things I noticed about the command words, as follows...

    -- For 19.2kbaud using a 3.6864MHz crystal, I think you want "A" for the baud rate choice:
    SHIFTOUT din, sclk, MSBFIRST,[$C02A\16]

    To make RTS high=stop I think the mask should be $20
    char.BYTE1 = $20 + Pt ' make rts high=stop, adds parity bit

    The code to turn RTS low= start, without transmitting data, should be $8400.
    SHIFTOUT din, sclk, MSBFIRST,[$8400\16]

    As to the question of whether RTS needs to be used to hold off a response from the peripheral until the transmission from the Stamp is finished, that depends on the peripheral. It is a non-issue if the peripheral has to receive the entire 13-byte command from the Stamp before it sends back its response. But if the peripheral starts sending back immediately, you will have to make allowances for that. You will notice that Al William's code bit-bangs the shiftin/shiftout, in order to allow transmission and reception at the same time, and Al explained his rationale for doing it that way. It depends on how the peripheral works, and there are workarounds too that still use the Stamp's SHIFTxxx commands.
  • aregalaaregala Posts: 10
    edited 2012-01-09 10:31
    Thanks for your feedback. See below:

    - I forgot to specify that I'm using a 1.8432MHz crystal; is $C020 good?

    - Regarding RTS (getting ready to send data), I was following your "fox" example, but I didn't copy over your entire comment "rts is bit 1, pin is made high when bit is low". This is preparing to send data. You also have "char.byte1=$80". Is that right?

    sendfox:
    char.byte1=$80 ' make rts high, red led on
    ' rts is bit 1, pin is made high when bit is low.


    - I think I need to do the opposite for ending the transmission; I used your same values for turning off RTS (below). Is that right?

    sendfoxend:
    low cs ' now turn off RTS pin
    ' the TE\ bit=1, so no data is transmitted.
    shiftout din,sclk,msbfirst,[$8600\16]
    high cs

    - I did notice how Al Williams allowed to receive while transmitting; my peripheral "shouldn't" respond until my last (13th) byte is transmitted. Then, it will respond with 8 bytes of it's own in one case, and in another case it will respond with 9 bytes (I will know when). Since the buffer for the MAX3110 is only 8 bytes, will that be a problem if the response is 9 bytes?
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-09 11:59
    From table 6 in the '3110 data sheet, the baud parameter is $9 in order to produce 19.2kbaud with the 1.8432MHz xtal. $C029

    I forgot that the RTS bit is inverted at the RTS\ pin on the chip and will be inverted again when you jumper the RTS\ pin over to the T2in pin that then produces the RS232 output levels. See "Figure 13. RS-232 Typical Operating Circuit". So in that case, the RTS bit controls flow in the standard manner of logical high=start and low=stop.

    The response of 9 bytes should not be a problem 1) if you can read out at least one byte before the peripheral sends the 9th, or 2) RTS can control the flow from the peripheral.
  • aregalaaregala Posts: 10
    edited 2012-01-10 03:58
    Just to give you an update- it's 3:50am and I'm going to call it quits for the night. I think the code for RS232 is "feeling good" but not quite working. I tried testing on a MAX3100 with MAX232, but I don't know if I have the hardware is setup correctly- do I need to have a Vs+ and Vs- like they show in the data sheet (8.5+/8.5-), or can that be just 5/0? It doesn't really matter, as the MAX3110 is arriving tomorrow (er, today), but it would be nice to know. I'm going to set that 3110 up and try it again. I spent several hours modifying my other code around it, so once I can make the RS232 communication work, I'm home free!
  • aregalaaregala Posts: 10
    edited 2012-01-10 11:26
    Okay, I got the new MAX3110 hooked up, but I'm still not able to receive a response from it. The hardware setup is pretty straight forward (I'm doing exactly what's listed in Al Williams' example, http://www.wd5gnr.com/suart.htm, not using the RTS or CTS pins, just TX and RX). When I send, I don't get any response from the peripheral (all $00). I can confirm that the peripheral is working properly, as I can connect it directly to my computer, send a command manually and get a response (using Modbus Poll). Based on this, I would think that something in my code is not quite right. Since I'm not using the RTS pins, does that even matter in my code? Any thoughts?
    writemode:
    LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[$C029\16]   ' write to config register, 19.2k baudrate, 8 bits, parity enabled (1100 0000 0010 0000)
    HIGH cs
    HIGH 14    'turn on LED at X14
    ' transmit datasend
    senddata:
    '  MAINIO  ' LCD uses Mainio
    '  SEROUT  PIN_LCDTX,BAUDMODE_LCD,[CLEAR,POSITION,68,"Sending",POSITION,81,"message"]
    '  PAUSE   DISPLAY_DELAY
    '  AUXIO   ' Com uses X i/o pins
    
    
    FOR i = 0 TO (sendbytes - 1)           ' send # bytes according to type of command
    senddata1:
    char.BYTE0 = dataio(i)
    Pt = char.BYTE0.BIT7 ^ char.BYTE0.BIT6 ^ char.BYTE0.BIT5 ^ char.BYTE0.BIT4 ^ char.BYTE0.BIT3 ^ char.BYTE0.BIT2 ^ char.BYTE0.BIT1 ^ char.BYTE0.BIT0  ' calculate parity bit
    char.BYTE1 = $82 + Pt     ' make rts high, adds parity bit
      DEBUG "Send byte ", DEC i, " "
      DEBUG HEX2 ? dataio(i)
    senddata2:
      LOW cs
      PULSOUT sclk,4  ' get one bit
      tst = dout      ' test that transmit buffer is empty
      HIGH cs
      IF tst = 1 THEN senddata2: ' loop back until ready
      LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[char\16]
      HIGH cs
    NEXT
    senddataend:
      LOW cs  'turn off rts pin
      SHIFTOUT din, sclk, MSBFIRST,[$8400\16]    ' Set RTS low for inactive
      HIGH cs
      GOTO recdata
    '*********************
    ' now enter a loop to receive data, X bytes stored in datarec
    recdata:
      MAINIO  ' LCD uses Mainio
      SEROUT  PIN_LCDTX,BAUDMODE_LCD,[CLEAR,POSITION,68,"Receive",POSITION,81,"message"]
      PAUSE   DISPLAY_DELAY
      AUXIO   ' Com uses X i/o pins
    FOR i = 0 TO (sendbytes - 1)    ' clear all send dataio bytes
      dataio(i) = 0
    NEXT
    FOR i = 0 TO (recbytes - 1)
      LOW din
        LOW cs   ' sequence to receive data, both 8-bit status and 8-bit data
          SHIFTIN dout, sclk, MSBPRE,[char\16]
        HIGH cs
        dataio(i) = char.BYTE0
        DEBUG "Receive byte ", DEC i, " "
        DEBUG HEX2 ? dataio(i)
    NEXT
    LOW 14     'LED test light
    MAINIO
    RETURN
    
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-10 13:00
    Is the MAX3110 transmitting the packet, and correctly? You can hook that side up to your computer to see. You should also be able to do a loopback test. rx to tx. Bytes transmitted should also be received. An oscilloscope or a sniffer can be very useful to resolve these problems, to visualize the baud rate, polarity, bit order, parity, pinout, flow control etc. etc.

    My code using SHIFT should also work, judging from what you have said about the command/response protocol of the device.
  • aregalaaregala Posts: 10
    edited 2012-01-10 13:51
    I don't think it's transmitting correctly. Do you have a simple Windows program you recommend for interfacing with serial? Loopback test did not work. I wish I had an O-scope to resolve this!

    I am using SHIFT, as you recommended (I'm only using the hardware setup with MAX3110 from Al Williams' example, not the code).
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-10 14:39
    I use mostly Mac, so I am not that up on PC terminal programs. If you have an older version of Windows (XP for example), you will have Hyperterminal in the accessories/communications folder. No longer on recent Windows. You can try PST (Parallax serial terminal) available on the Parallax downloads site. I don't think it has an option for 8bit+parity, but at least you could use it to test without the parity bit. On the Mac my favorite terminal program is CoolTerm, and there is a windows version for that, but I don't know the level of support. It does allow 8bit with parity and also display of hex values, in case your data is not all readable ascii.

    With a multimeter, you should find that the output voltage from RS232 tx pin on the chip is -5V or thereabouts. And if you have the Stamp stream out ascii nulls as fast as possible, you should see that voltage flickering upward. The Stamp should be able to control the RTS output. These are just things to see if the chip is hooked up correctly. One common mistake with SHIFT commands is to have the Dout and Din lines reversed.

    A device like the Saleae logic analyzer that Parallax sells can be almost more even more valuable than a cheap 'scope for this kind of situation, because it can decode the data that you can also see displayed on the computer screen.
  • aregalaaregala Posts: 10
    edited 2012-01-10 15:08
    I do see the -5V on the TX pin. I double checked the Dout and Din to make sure they're good. I connected an LED to RTS, and it stays on nominally and then turns off then back on once I run 1 round of send and receive. I just connected the TX and RX again, and now I seem to be getting something received, but not what I expect. The first received byte is the last one I sent, and then all the other bytes equal the very first byte I sent. I thought I would've been further along after 6 hours straight!
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-10 20:05
    It should be possible to turn that led attached to RTS on and off at will from a Stamp program. Also, it should be possible to read the status of the CTS pin. Did you locate a terminal program for the PC?

    Check pin 9 (X2) with a voltmeter to be sure the cystal is oscillating, it should be about 2.5V DC average. About the layout, this from the data sheet...
    Note: It is very important to keep crystal, resonator, and
    load-capacitor leads and traces as short and direct as
    possible. Make the X1 and X2 trace lengths and ground
    tracks short, with no intervening traces.
  • aregalaaregala Posts: 10
    edited 2012-01-10 20:11
    I sucked it up and got an oscilloscope to see what was going on. It took me a hour or so to figure out "how to figure it out", found that the MAX3110 was indeed sending good signals out to my peripheral, but the time between bytes sent was 35ms, way too long. It turns out that the DEBUG code I left in there was killing my transmission. I knew it would be something stupid like that. I commented it out, and voila- it FINALLY WORKS!!!!!!! Thanks SO MUCH for your help, Tracy. You're fantastic! Here's the final code for reference:

    BTW, any idea of how I change this thread to "solved"?
    ' RS-232 COMMUNICATION
    dataio    VAR  Byte(13) ' NEEDS TO BE 13!!! used for sending and receiving
    char      VAR  Word   ' will combine 8-bit command/status and 8-bit data
    rts       VAR  char.BYTE1.BIT1     ' for data send
    cts       VAR  char.BYTE1.BIT1     ' for data read
    Pt        VAR  Bit     ' even parity bit
    tst       VAR  Bit
    comtype   VAR  Nib
    sendbytes VAR  Nib
    recbytes  VAR  Nib
    i          VAR  Nib
    
    
    Initialize:
      OUTPUT  11111001000000     'Set direction status for outputs and unused pins
                                    '7,8,9,10,11,12,13,15
      AUXIO                         'Set direction for auxilliary pins as outputs except 10, dout
      OUTPUT  11101111111111
      MAINIO                        'Switch back to main I/O
    
    
    '---------------------------------------------------
    'Read Command Position Counter
    '---------------------------------------------------
    ' Values for Command Position Read (always the same)
    ReadPositionSub:
    dataio(0) = $01      ' unit number
    dataio(1) = $03      ' Read command function code
    dataio(2) = $00      ' index address
    dataio(3) = $10      ' index address
    dataio(4) = $00      ' # regusters
    dataio(5) = $02      ' # registers (2)
    dataio(6) = $C5      ' CRC low
    dataio(7) = $CE      ' CRC high
    
    
    comtype = poscom     ' set communication type as read position
    
    
    GOSUB Communicate
    
    
    '---------------------------------------------------
    'RS-232 Communication with P70360
    '---------------------------------------------------
    Communicate:
    AUXIO   ' uses X i/o pins
    
    
    IF comtype = poscom THEN
        GOTO ReadPosition
      ELSEIF comtype = movecom THEN
        GOTO MoveProfile
      ELSE
        GOTO StoreMove
    ENDIF
    
    
    ReadPosition:
    sendbytes = possendbytes
    recbytes = posrecbytes
    GOTO writemode
    
    
    MoveProfile:
    sendbytes = movesendbytes
    recbytes = moverecbytes
    GOTO writemode
    
    
    StoreMove:
    sendbytes = storesendbytes
    recbytes = storerecbytes
    
    
    writemode:
    LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[$C029\16]   ' write to config register, 19.2k baudrate, 8 bits, parity enabled (1100 0000 0010 1001)
    HIGH cs
    ' transmit datasend
    
    senddata:
    FOR i = 0 TO (sendbytes - 1)           ' send # bytes according to type of command
    senddata1:
    char.BYTE0 = dataio(i)
    Pt = char.BYTE0.BIT7 ^ char.BYTE0.BIT6 ^ char.BYTE0.BIT5 ^ char.BYTE0.BIT4 ^ char.BYTE0.BIT3 ^ char.BYTE0.BIT2 ^ char.BYTE0.BIT1 ^ char.BYTE0.BIT0  ' calculate parity bit
    char.BYTE1 = $82 + Pt     ' make rts high, adds parity bit
    '  DEBUG "Send byte ", DEC i, " "
    '  DEBUG HEX2 ? dataio(i)
    senddata2:
      LOW cs
      PULSOUT sclk,4  ' get one bit
      tst = dout      ' test that transmit buffer is empty
      HIGH cs
      IF tst = 1 THEN senddata2: ' loop back until ready
      LOW cs
      SHIFTOUT din, sclk, MSBFIRST,[char\16]
      HIGH cs
      HIGH 0     ' test only- trigger after SHIFT complete
    NEXT
    senddataend:
      LOW cs  'turn off rts pin
      SHIFTOUT din, sclk, MSBFIRST,[$8400\16]    ' Set RTS low for inactive
      HIGH cs
      GOTO recdata
    '*********************
    ' now enter a loop to receive data, X bytes stored in datarec
    recdata:
    FOR i = 0 TO (sendbytes - 1)    ' clear all send dataio bytes
      dataio(i) = 0
    NEXT
    FOR i = 0 TO (recbytes - 1)  'CHANGE TO recbytes!!!
      LOW din
        LOW cs   ' sequence to receive data, both 8-bit status and 8-bit data
          SHIFTIN dout, sclk, MSBPRE,[char\16]
        HIGH cs
        dataio(i) = char.BYTE0
    '    DEBUG "Receive byte ", DEC i, " "
    '    DEBUG HEX2 ? dataio(i)
    NEXT
    MAINIO
    RETURN
    
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2012-01-10 23:17
    Good deal, and it's still Tuesday! Hopefully in the morning it continues chugging along.

    There should be a little dropdown box at the top of your message composition screen that lets you change the un/solved status.
Sign In or Register to comment.