2

I need to work on a serial protocol that uses the 9th bit as a 'wakeup' bit. All bytes are transmitted with the 9th bit in zero except for the first one, indication the message start.

On the Raspi' usart PL011, I tried to use the method I'm using over the years on a x86, accessing the hardware port itself:

I look at the port addrees on /proc/tty/driver/ttyAMA to get the address:

0: uart:PL011 rev3 mmio:0x3F201000 irq:83

Then I try to open and use the port, like getting the permission:

ioperm (0x3F201000, 7, 1)

I wasn't really expecting this to work since the address is 'mmio:' and not 'port:'. And it doesn't.

So, I tried to use the serial port the basic way, opening a file descriptor and setting the parameters.

2 issues so far:

1 - Mark/Space parity doesn't seem to work. Besides that, even setting the IGNPAR flag on the termios structure, the driver 'drops' received bytes that comes with the wrong parity. I'm logging the transmission on the 'server' machine and bytes are all been sent correctly (code works fine on x86 clients). I read on post here there is a patch to add MARK/SPACE parity and will try it ASAP. Even though, I guess there is nothing to do with the IGNPAR bit, as I was supposed to read all bytes anyway. But this is all irrelevant if not solved issue 2 below:

2 - How can I detect partity error on RAW communication? Today, using the port itself, I can set the port to SPACE and when whe first byte arrives with a MARK, I get the byte and the parity bit error, like this:

int uart_test_receive_char ()
{
    return (inb (portcom + UART_LSR) & 0x05);
}

where portcom is the port address. The return value & 1 says there is a byte on the buffer and value & 4 indicates a parity error.

I didn't find a way to check for this using a file descriptor and 'read' or 'select' commands.

Any hint? Thanks!

Luiz Cressoni
  • 81
  • 1
  • 7

3 Answers3

1

I can't really help you with the file descriptor as this stuff just did whatever it felt like in my experience. Although I dived pretty deep into hardware access.

I have written kind of my own library, which is pretty ugly and currently on hold but it should help you a fair bit. Take a look.

The basics were based on this - good read.

It was meant to mimic the classic AVR-style register access, resulting in this ugly structure.

Use it like this:

// used by setup_HAL
struct bcm2835_peripheral gpio = { GPIO_BASE };
struct bcm2835_peripheral uart = { UART_BASE };
struct bcm2835_peripheral i2c  = { BSC0_BASE };

setup_HAL();  // be sure to always call this first

    UART_CR = 0;                                        // disable all control bits
    UART_LCRH &= ~FEN;                                  // flush tx fifo
    UART_ICR = 0x7FF;                                   // clear all interrupts

    UART_IBRD = 9;
    UART_FBRD = 14;
    UART_LCRH &= ~PEN;
    UART_LCRH = FEN | CS8;                              // enable 16Byte FIFOs, 8Data bits
    UART_CR = UARTEN | TXE | RXE;

uart_putc(0x55);

Bonus: Force parity

if(check_parity(c) == 1) UART_LCRH &= ~EPS;
else UART_LCRH |= EPS;
mystery
  • 518
  • 2
  • 11
  • Thanks a lot! I was wondering that I would need to use another USART chip connected to the I2C or SPI bus, but will read all that carefully. One less chip on the board and a free bus is definitely a god idea :) – Luiz Cressoni Jun 30 '16 at 11:05
0

I can't help if you want to pursue the serial driver route.

My pigpio library supports arbitrary byte sizes on arbitrary GPIO using bit banging.

In my tests it seems fairly stable at bit rates of 19.2 kbps and less.

I show an example using the command line utility pigs but the underlying calls may be made from C or Python.

# Open GPIO 4 to bit bang serial link at 19200 bps and 9 data bits.
#
$ pigs slro 4 19200 9 

# Create serial data for GPIO 4 at 19200 bps and 9 data bits.
# Use 1 stop bit (2 half bits) and offset 0 from start of wave.
# Each 9 bit quantity is defined by 2 bytes (least significant first).
$ pigs wvas 4 19200 9 2 0 0x41 1 0x42 0 0x43 1 0x44 0 0x45 1 0x46 0

# Create wave from added data,
$ pigs wvcre

# Set GPIO 4 as an output (otherwise no data will be transmitted).
$ pigs m 4 w

# Transmit the serial data.
$ pigs wvtx 0

# Read up to 1000 bytes of data from GPIO 4.
$ pigs slr 4 1000

# 12 is the returned byte count, two bytes per 9 bit character.
12 65 1 66 0 67 1 68 0 69 1 70 0
joan
  • 71,024
  • 5
  • 73
  • 106
  • Joan: I'll probably need some code examples (C, C++) on how to use this feature of your library. Does it have support for a RS485 bus direction signal? – Luiz Cressoni Mar 30 '16 at 17:52
  • The library only supports bit banging the TX and RX signals. – joan Mar 30 '16 at 18:11
0

Thanks to Mystery I managed to write a driver with the necessary features. Using register access, after setting up everything one need to read the DR register and look for the error flags. For multidrop mode, look for the PE (parity error) flag.

For setting up space parity, set the 'stick' and 'even' parity:

UART_LCRH |= SPS; //stick
UART_LCRH |= EPS; //EVEN
UART_LCRH |= PEN; //Enable partity check

To read the received bytes, I use a 16bit buffer using the 8th bit as a 'wakeup' bit and set it when the received byte has a parity error (on this case, when it comes with a 'mark' parity):

//pb gives the current receive buffer position
while((UART_FR & RXFE) == 0 && pb < MY_BUFFER_SIZE)
{
    int ret = UART_DR;
    __u16 u16byte = ret & 0xff;    //filter the received data
    if(ret & PE)
        u16byte |= 0x100;          //set wakeup bit if got parity error
    if((ret & (FE | BE | OE)) == 0) //ignore if any other error
    {
        mu16RxBuffer[pb++] = u16byte;
    }
}

Again, thanks a lot!

Luiz Cressoni
  • 81
  • 1
  • 7