3

I am wondering if it is possible to send 9 data bits framed with a start/stop bit at 1.25MBaud.

I am currently using PySerial to configure the UART pins on the pi. I've stumbled across a few problems.

Pyserial requires you to send in ascii to the port, which is 7 bits, however can be converted into a byte with a leading serial using bytes(data.encode("ascii")

Pyserial does not seem to like 1.25MBaud

Any advice to maximize speed and configurability of the UART pins would be appreciated!

Steve Robillard
  • 34,687
  • 17
  • 103
  • 109
SerialMadMan
  • 31
  • 1
  • 4
  • 1
    This is the type of question where a little info of the project and what you are trying to accomplish would help us help you. – Steve Robillard Dec 03 '14 at 11:54
  • 1
    I am trying to emulate a master in a master slave protocol, the physical layer is RS-485. Once I figure out how to output 9 data bits at a specified baud rate I can use the appropriate transceivers or create my own. The parity bit would have to be switched from mark to space within 1 bit time (800ns) – SerialMadMan Dec 08 '14 at 09:44

2 Answers2

3

The serial driver (see stty, man stty) only supports 5-8 data bits.

The nearest standard serial speed is 230400.

You can get 1.25 Mbps by using the init_uart_clock setting in /boot/config.txt (set it to 16 times the maximum value you want to use, i.e. init_uart_clock=20000000). stty will then let you select 1250000 bps.

I'm not aware of a practical solution for the 9 data bits. I've read of people trying to use the parity bit as an extra data bit.

joan
  • 71,024
  • 5
  • 73
  • 106
  • Thank you so much! I will try this. I'd need to change the parity within 1 bit time – SerialMadMan Dec 08 '14 at 09:54
  • I don't think that will be feasible. I see no way of achieving your stated aims with the Pi under Linux and frankly I can't see it being achieved in bare metal either. The 1.25MBs is the killer for me. Is there no way the speed requirement could be relaxed? – joan Dec 08 '14 at 10:00
  • No! It's too test hardware which is at this speed! – SerialMadMan Dec 08 '14 at 10:13
  • Is this two way communications are do you just need to send? How long are the messages (total number of bits) to be sent and can there be gaps between each message? – joan Dec 08 '14 at 10:21
  • 600+ characters to be sent, 200+ characters to be received, characters are 9 data bits framed with start/stop bits. the response gap is very small but thats fine as I plan to emulate the master in a master/slave protocol. I've seen the raspberry pi buffer messages this size and output at a slower baud rate than i want! – SerialMadMan Dec 08 '14 at 10:26
  • I think you could almost bit-bang the output side. I don't see any way of reading the input. – joan Dec 08 '14 at 10:40
  • Can you advice further how to bit bang @ 1.25Mbaud? I have a RS-485 USB that can read the messages (Identifying parity errors) to then read the 9 data bits, theres no way i can toggle parity transmitting using this USB. I can just connect them both and use the USB as a log! – SerialMadMan Dec 08 '14 at 11:06
  • I'll need to try an experiment. It might take several hours. I'll let you know either way but please be patient. – joan Dec 08 '14 at 11:18
  • I've tried and the fastest I've been able to reliably bit-bang (and check the output with a logic analyser) is 250 kbps with 9-bit data (1 start, 9 data, 1 stop). I'm afraid this is a no go as far as I can see. – joan Dec 08 '14 at 14:36
  • Correction, 500 kbps if I switch off logic analyser auto-baud option and set 500 kbps. – joan Dec 08 '14 at 15:44
  • I'll go learn FPGA now – SerialMadMan Dec 08 '14 at 16:01
  • I have an update I'll add as an answer. – joan Dec 08 '14 at 16:51
2

I have experimented and have a sort of solution to sending 9-bit data at 1.25 Mbps.

It involves modifications to my pigpio library.

I've added a gpioWaveAddSerialX() function which extends the gpioWaveAddSerial() function to allow for setting the number of data bits. This function constructs a waveform representing the serial data to be transmitted.

This also required an update to the internal waveBitDelay() function to allow for a variable number of data bits.

The other needed change was to change the base clock from 1 MHz to 1.25 MHz by changing clkDivI = 50 * micros to clkDivI = 40 * micros in the internal initClock() function.

The manifest constant PI_WAVE_MAX_BAUD in pigpio.h was changed to 1000000.

9-bit data 1.25 Mbps overview 9-bit data 1.25 Mbps detail

waveBitDelay

static void waveBitDelay(unsigned baud, unsigned bits, unsigned *bitDelay)
{
   unsigned fullBit, halfBit, s, e, d, m, i, err, t;

   /* scaled 100X */

   fullBit = 100000000 / baud;
   halfBit =  50000000 / baud;

   d = (fullBit/200)*200;

   s = 0;

   e = d;

   t = d/100;
   bitDelay[0] = t ? t : 1;

   err = d / 3;

   for (i=0; i<bits; i++)
   {
      s = e;

      m = halfBit + (i+1)*fullBit;

      e = s + d;

      if ((e-m) < err) e+=200;

      t = (e-s)/100;
      bitDelay[i+1] = t ? t : 1;
   }

   s = e;

   e = ((100*(bits+2)*1000000 / baud)+100)/200*200;

   t = (e-s)/100;
   bitDelay[bits+1] = t ? t : 1;
}

gpioWaveAddSerialX

int gpioWaveAddSerialX
   (unsigned gpio,
    unsigned bbBaud,
    unsigned bbBits,
    unsigned offset,
    unsigned numChar,
    char     *bstr)
{
   int i, b, p, lev, c, v;

   uint16_t *wstr = bstr;

   unsigned bitDelay[17];

   DBG(DBG_USER,
      "gpio=%d baud=%d bits=%d offset=%d numChar=%d str=[%s]",
      gpio, bbBaud, bbBits, offset, numChar, myBuf2Str(numChar,
      (char *)bstr));

   CHECK_INITED;

   if (gpio > PI_MAX_USER_GPIO)
      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);

   if ((bbBaud < PI_WAVE_MIN_BAUD) || (bbBaud > PI_WAVE_MAX_BAUD))
      SOFT_ERROR(PI_BAD_WAVE_BAUD,
         "gpio %d, bad baud rate (%d)", gpio, bbBaud);

   if (numChar > PI_WAVE_MAX_CHARS)
      SOFT_ERROR(PI_TOO_MANY_CHARS, "too many chars (%d)", numChar);

   if (offset > PI_WAVE_MAX_MICROS)
      SOFT_ERROR(PI_BAD_SER_OFFSET, "offset too large (%d)", offset);

   if (bbBits > 8) numChar /= 2;

   if (!numChar) return 0;

   waveBitDelay(bbBaud, bbBits, bitDelay);

   for (i=0; i<=bbBits+1; i++) DBG(0, "bit%d delay=%d", i, bitDelay[0]);

   p = 0;

   wf[2][p].gpioOn  = (1<<gpio);
   wf[2][p].gpioOff = 0;
   wf[2][p].flags   = 0;

   if (offset > bitDelay[0]) wf[2][p].usDelay = offset;
   else                      wf[2][p].usDelay = bitDelay[0];

   for (i=0; i<numChar; i++)
   {
      p++;

      /* start bit */

      wf[2][p].gpioOn = 0;
      wf[2][p].gpioOff = (1<<gpio);
      wf[2][p].usDelay = bitDelay[0];
      wf[2][p].flags   = 0;

      lev = 0;

      if (bbBits < 9)
         c = bstr[i];
      else
         c = wstr[i];

      DBG(0, "i=%d c=%x", i , c);

      for (b=0; b<bbBits; b++)
      {
         if (c & (1<<b)) v=1; else v=0;

         if (v == lev) wf[2][p].usDelay += bitDelay[b+1];
         else
         {
            p++;

            lev = v;

            if (lev)
            {
               wf[2][p].gpioOn  = (1<<gpio);
               wf[2][p].gpioOff = 0;
               wf[2][p].flags   = 0;
            }
            else
            {
               wf[2][p].gpioOn  = 0;
               wf[2][p].gpioOff = (1<<gpio);
               wf[2][p].flags   = 0;
            }

            wf[2][p].usDelay = bitDelay[b+1];
         }
      }

      /* stop bit */

      if (lev) wf[2][p].usDelay += bitDelay[9];
      else
      {
         p++;

         wf[2][p].gpioOn  = (1<<gpio);
         wf[2][p].gpioOff = 0;
         wf[2][p].usDelay = bitDelay[9];
         wf[2][p].flags   = 0;
      }
   }

   p++;

   wf[2][p].gpioOn  = (1<<gpio);
   wf[2][p].gpioOff = 0;
   wf[2][p].usDelay = bitDelay[0];
   wf[2][p].flags   = 0;

   return rawWaveAddGeneric(p, wf[2]);
}

Test program.

#include <stdio.h>
#include <stdint.h>

#include <pigpio.h>

#define GPIO 14

int main(int argc, char *argv[])
{
   uint16_t buf[1024];
   int i, wid;

   if (gpioInitialise() < 0) return 1;

   gpioSetMode(GPIO, PI_OUTPUT);

   for (i=0; i<600; i++) buf[i] = i; 

   gpioWaveAddSerialX(GPIO, 1000000, 9, 2, 0, 1200, (char*)buf);

   wid = gpioWaveCreate();

   if (wid >= 0)
   {
      printf("ready recorder, then return\n");

      getchar();

      if (wid >= 0) gpioWaveTxSend(wid, 0);

      printf("stop recorder, then return\n");

      getchar();
   }
   else printf("error %d\n", wid);

   gpioTerminate();
}
joan
  • 71,024
  • 5
  • 73
  • 106
  • This looks awesome! Where can I find a tutorial to set up the pi to use C? – SerialMadMan Dec 08 '14 at 18:24
  • 1
    A C programming environment (gcc) will be included in most GNU/Linux distributions for the Pi. Certainly Raspbian does out of the box. What I've done may or may not work reliably for your hardware. There's only one way to find out. – joan Dec 08 '14 at 18:32
  • For sure, giving this a try. I have a B+ Model. – SerialMadMan Dec 09 '14 at 14:38
  • How did you call the functions in your library. For example how have you chosen which gpio to use? How did you set the speed? and how did you set the serial data to be sent? I'm so confused! – SerialMadMan Dec 12 '14 at 12:46
  • I'm also not sure how to change the clock speeds :( – SerialMadMan Dec 12 '14 at 12:50
  • I've added the test program I used. You can use any gpio you want. All the information you need should now be in the answer. – joan Dec 12 '14 at 12:52
  • I'm having problems adding gpioAddSerialX, is it included on your latest library? Thanks again! – SerialMadMan Dec 15 '14 at 13:33
  • @SerialMadMan No. You'll need to add it by cutting & pasting the post and copy it to pigpio.c (perhaps just after the existing gpioWaveAddSerial). You'll need to replace the existing waveBitDelay with the new one in the post. However I will be updating my library with this addition in a few days (perhaps this Wednesday). You might be better off waiting until I have done that. – joan Dec 15 '14 at 13:41
  • I did what you said, getting "undefined reference to" error, will be something silly i bet grr – SerialMadMan Dec 15 '14 at 13:43
  • I also added gpioWaveAddSerialX to header file :) – SerialMadMan Dec 15 '14 at 13:44
  • @SerialMadMan Ok. When you have finished making the changes you need to pigpio.c/pigpio.h you need to rebuild the library. So add the changes to the PIGPIO directory and make/make install again for the new functions to be available in the library. – joan Dec 15 '14 at 13:58
  • Thank you! I'm still getting errors. Let me know when pigpio is updated and ill apt-get again :) – SerialMadMan Dec 15 '14 at 14:14
  • @SerialMadMan Version 24 of pigpio is available at http://abyz.co.uk/rpi/pigpio/pigpio.zip – joan Dec 17 '14 at 22:19
  • I just downloaded the new version and gpioWaveSerialX is not there :( – SerialMadMan Jan 06 '15 at 21:59
  • Because the functionality has been added to the library. See http://abyz.co.uk/rpi/pigpio/cif.html#gpioWaveAddSerial – joan Jan 06 '15 at 22:07
  • ah yes! thanks. Your test program is now compiling using the PIGPIO. I'm struggling with how to use it now. Would it be possible to develop a test program which inputs a string of binary as data bits? For example data= "01000101010110100...". This would be very useful. I have an oscilloscope ready to observe results. – SerialMadMan Jan 07 '15 at 10:08
  • I'm a bit busy at the moment. By the time I got around to looking at this you could have done it yourself. – joan Jan 07 '15 at 12:03
  • There is some very interesting stuff here; I thought I would share my findings as I followed the post using the test program above but was not getting any output. For me 'wid' was being assigned a value of 0 which meant the 'gpioWaveTxSend(wid, 0)' command was never being executed. I removed the 'if (wid)' and was able to see the output which looked to be 1.25Mbaud. Great work joan and thanks for all the help. –  Feb 11 '15 at 15:21
  • @SerialMadMan Indeed, my mistake. I'll update the test program in the post. A legal wid is >= 0. – joan Feb 11 '15 at 19:48
  • Thanks a lot Joan. I've seen the pi outputting at 1.25 MBaud measured with a lecroy waverunner. Just to work on the application layer for a bit. Thanks again! – SerialMadMan Feb 23 '15 at 16:06