2

I need to output 8-bit integers via parallel (i.e. using 8 pins on the same port) at 1024hz. From what I can see, I have two options:

1) Write to each pin individually using DigitalOut.

2) Avoid the mbed library altogether, and do something like:

void sendByte(char byte)
{ 
    DataPort|= (((byte >>0x00) & 0x01) << LCD_D0);
    DataPort|= (((byte >>0x01) & 0x01) << LCD_D1);
    DataPort|= (((byte >>0x02) & 0x01) << LCD_D2);
    DataPort|= (((byte >>0x03) & 0x01) << LCD_D3);
    DataPort|= (((byte >>0x04) & 0x01) << LCD_D4);
    DataPort|= (((byte >>0x05) & 0x01) << LCD_D5);
    DataPort|= (((byte >>0x06) & 0x01) << LCD_D6);
    DataPort|= (((byte >>0x07) & 0x01) << LCD_D7);
}

I've heard that DigitalOut is rather slow, although I'm not sure of what frequency it can handle. Is the second option likely to be faster? Can anyone compare the two methods?

SIDE QUESTION: I'll be using the output with a DAC to produce an analog signal. Will I need to use a buffer in between, or are the pins guaranteed to output simultaneously?

19172281
  • 685
  • 1
  • 9
  • 24
  • 3
    If you care about performance, why not arrange the connections so that the data lines are connected to pins mapped to adjaced bits in the register; e.g. port A pins 3..10? Then you can just write `DataPort |= (byte & 0xFF) << LCD_D0;` – jms May 29 '18 at 16:25
  • Ok, so writing to the port all at once? Would that actually guarantee that they would output simultaneously? – 19172281 May 29 '18 at 16:35
  • 1
    Yes. You can output them simultaneously even when the data lines aren't connected to sequential pins, as long as all data lines share the same I/O register. You can accomplish that simply by doing *one* write to the ouput register instead of eight. – jms May 29 '18 at 16:41
  • By I/O register you mean port? So I wouldn't need a buffer between the MCU and DAC? I've seen this being done when controlling a motor, but can't seem to figure out why it's necessary. – 19172281 May 29 '18 at 16:45
  • Consider using DMA (you are basically limited only by the peripheral bus speed). – filo May 29 '18 at 18:57

2 Answers2

1

SIDE QUESTION: I'll be using the output with a DAC to produce an analog signal. Will I need to use a buffer in between, or are the pins guaranteed to output simultaneously?

You don't need a buffer in between, if you update all bits with just a single write to the special memory location (also known as register) controlling that I/O port.

The code in your question would not write all bits at the same time, but this will:

void sendByte(char value)
{ 
    uint32_t temp = DataPort;
    //clear the pins connected to D0..D7
    temp &= ~((1 << LCD_D0)
            | (1 << LCD_D1)
            | (1 << LCD_D2)
            | (1 << LCD_D3)
            | (1 << LCD_D4)
            | (1 << LCD_D5)
            | (1 << LCD_D6)
            | (1 << LCD_D7));
    //Write the value to the pins
    temp |= (((value >> 0) & 1) << LCD_D0)
          | (((value >> 1) & 1) << LCD_D1)
          | (((value >> 2) & 1) << LCD_D2)
          | (((value >> 3) & 1) << LCD_D3)
          | (((value >> 4) & 1) << LCD_D4)
          | (((value >> 5) & 1) << LCD_D5)
          | (((value >> 6) & 1) << LCD_D6)
          | (((value >> 7) & 1) << LCD_D7);
    //apply the new value to the I/O register. All pins are updated at once.
    DataPort = temp;
}  

You could also make the code more efficient by connecting the data lines to I/O pins that are mapped to sequential bits in the I/O port registers (e.g. D0 to PORT_A_3, D1 to PORT_A_4 ... D7 to PORT_A_10)

Then you could simplify that function to just

#define LCD_D0 3
void sendByte(char value)
{ 
    DataPort = (DataPort & ~(0xFF << LCD_D0)) | (value & 0xFF) << LCD_D0);
}  

When it comes to the overhead MBED, I have no idea.

jms
  • 8,504
  • 3
  • 22
  • 45
0

The mbed code seems to be this:

void port_write(port_t *obj, int value) {
    *obj->reg_out = (*obj->reg_in & ~obj->mask) | (value & obj->mask);
}

So it's not in this case using any bit-banding, and includes the overhead of read/modify/write. That makes it non-optimal for cases where you know the last written value.

By calculating the byte value directly, you save 7 writes, 8 reads, and the arithmetic operations. You also get synchronous updates of the whole byte at once, rather than changing each bit in sequence. This is rather critical for your DAC application, and even then you need to consider the jitter between individual lines.

You can probably use the mbed framework to set up the GPIO, and provide the register addresses (making your code a little more portable).

Sean Houlihane
  • 3,733
  • 1
  • 16
  • 25
  • 1
    I agree, but keep in mind that bit banding is only a feature on the higher-end ARM cores (cortex-M3 and cortex-M4). – jms May 29 '18 at 18:47
  • In the CPU, maybe, but some vendors implement it in the system (or peripheral). – Sean Houlihane May 29 '18 at 18:50
  • "That makes it non-optimal for cases where you know the last written value." - What do you mean by the last written value? I would have though that it doesn't matter if you're rewriting the entire port. – 19172281 May 30 '18 at 09:37
  • Transforming a register value is 1 cycle/1 instruction. Reading a value from a peripheral and transforming it is 2 instructions, and more than 3 cycles. Reads (or volatile variables as they are seen in C) are expensive – Sean Houlihane May 30 '18 at 09:40
  • The mbed code is always read/modify/write - that is precisely why it is 'slow' – Sean Houlihane May 30 '18 at 09:41