Using Arduino DUE Direct Port Access to emulate a transceiver

Hello all,

I am using DUE Direct Port Access to emulate an octal bus transceiver (SN74ALS245A). Here you can see the datasheet: https://www.ti.com/lit/ds/symlink/sn74ahc245-q1.pdf?ts=1638396285835&ref_url=https%3A%2F%2Fwww.google.com%2F

The Arduino due will simulate the behavior of this octal bus transceiver, and for that, I need to transfer 8 bits from the due inputs to the outputs in around 10 ns. This transceiver has 8 inputs and 8 outputs. I was able to emulate 1 pin A1 to B1 by setting PORTB pin 13 (LED) as output (Output enable register = OER) and ORTB pin 2 as input (PDSR = Pin Data Status Register), but I{m kind of stock now that I´m trying to emulate 8 input bits and 8 outputs..... I read on the SAM3X / SAM3A Series datasheet that you can use OWSR and OWER as well.

Here´s the code I did for 1 pin (A1 to B1)

define variables
uint32_t pin13 = (1u << 27); // mask for pin 13; equals BIT 27 in port B. 
                             //uint32_t is a numeric type that guarantees 32 bits, the value is unsigned.
uint32_t pin2 = (1u << 25); // mask for pin 2; equals BIT 25 in port B.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  //pinMode(pin13, OUTPUT);
  REG_PIOB_OER = pin13; // set PORTB pin 13 (LED) as output (Output enable register = OER)

  //set pin2 as input
  //pinMode(pin2, INPUT);
  //REG_PIOB_PDSR = pin2; // set PORTB pin 2 as input (PDSR = Pin Data Status Register)
   pin2 = PIOB->PIO_PDSR & PIO_PDSR_P25; // set PORTB pin 2 as input (PDSR = Pin Data Status Register)
 }

void loop() {
  // put your main code here, to run repeatedly:
  // This routine checks the Pin Data Register's status
  //and sets the state of the LED accordingly.
  if ((PIOC->PIO_PDSR & PIO_PB25) == PIO_PB25)
    PIOB -> PIO_SODR = PIO_SODR_P27;  //Set Ouput Data Register
  else
    PIOB -> PIO_CODR = PIO_CODR_P27;  //Clear Output Data Register
  delay(10);
}

This code is an example I found that use OWSR and OWER

void setup(){
  // put your setup code here, to run once:
  pinMode(13, OUTPUT);        // declaring pin 13 as output using pinMode
  REG_PIOC_OWER = 0x00000002; // This sets bit 1 of the OWSR(Output Write Status Register) high
  REG_PIOC_OWDR = 0xfffffffd; // This clears all bits except bit 1 of the OWSR
}
void loop(){
  // put your main code here, to run repeatedly:
  REG_PIOC_ODSR = 0x00000002; // sets bit 1 of the ODSR high
  delay(2000);             
  REG_PIOC_ODSR = 0x00000000; // sets bit 0 of the ODSR low  
  delay(2000);   
}

I would like to ask the help of the community to try to find an example or maybe you can help to assemble a code that emulates an octal bus transceiver. or you can give your advice on what to do next.

Thank you for your help

How will you implement the tri-state bus? What clocking will you use to coordinate the transfer with the other devices?
Paul

It's not going to happen that quick. At 84MHz, the Due has a cycle time of about 12ns, and it takes more than one cycle to write to an output port (not even counting "get something to output" part.)

Your current one-bit loop has about 9 instructions, each of which probably takes more than one cycle to complete (by the time you take into account flash wait states and bus latencies to the IO ports.) Is ~100ns going to be fast enough?

  if ((PIOC->PIO_PDSR & PIO_PB25) == PIO_PB25)
   80148:       4b06            ldr     r3, [pc, #24]   ; (80164 <loop+0x1c>)
   8014a:       f04f 6200       mov.w   r2, #134217728  ; 0x8000000
   8014e:       6bdb            ldr     r3, [r3, #60]   ; 0x3c
    PIOB -> PIO_SODR = PIO_SODR_P27;  //Set Ouput Data Register
  else
    PIOB -> PIO_CODR = PIO_CODR_P27;  //Clear Output Data Register
  delay(10);
   80150:       200a            movs    r0, #10
  if ((PIOC->PIO_PDSR & PIO_PB25) == PIO_PB25)
   80152:       f013 7f00       tst.w   r3, #33554432   ; 0x2000000
   80156:       4b04            ldr     r3, [pc, #16]   ; (80168 <loop+0x20>)
    PIOB -> PIO_SODR = PIO_SODR_P27;  //Set Ouput Data Register
   80158:       bf14            ite     ne
   8015a:       631a            strne   r2, [r3, #48]   ; 0x30
    PIOB -> PIO_CODR = PIO_CODR_P27;  //Clear Output Data Register
   8015c:       635a            streq   r2, [r3, #52]   ; 0x34
  delay(10);
   8015e:       f000 bbdc       b.w     8091a <delay>

Hi Paul_KD7HB!!

Thank you for your reply, at this moment I'm still trying to emulate the transceiver behavior, in this first part, I'm still trying to move bits from "port A" to "port B" as fast as possible. (the datasheet mentioned between 19ns and 10ns). So any help is more than welcome :grinning:

Thanks,
Ale1284

Hi @westfw,

Thank you for your reply and your help. I think 100ns works, as I was mentioning to @Paul_KD7HB now I'm trying to move 8 bits from "port A" to "port B" as fast as possible (see picture below).

I will try to implement the code you posted (thank you very much for sharing it with me). Once again any advice or recommendation is more than welcome.

Thanks,
Ale1284

Hi @ale1284

On the Arduino Due it's possible to read a port (A, B, C or D) using the PIO Controller's PSDR (Pin Data Status Register) and write using its ODSR (Output Data Status Register).

Additionally, the PIO Controller's OWER (Output Write Enable Register) determines which port pins can be set written to in the ODSR register and the OER (Output Enable Register) sets these pins to become outputs.

For example to control the first 4 port pins on PORT D (PD0, PD1, PD2 and PD3):

void setup() {
  PIOD->PIO_OWER = 0xFFFFFFFF;     // Enable writes to whole of PortD's 32-bit, Output Data Status (ODSR) register: (B11111111111111111111111111111111)
  PIOD->PIO_OER =  0x0000000F;     // Set the lowest 4-bits of PortD to outputs: (B00000000000000000000000000001111)
}

void loop() {   
  PIOD->PIO_ODSR = PIO_ODSR_P0;   // Set the output on P0 (digital pin 25) high, other outputs low: (B00000000000000000000000000000001)
  PIOD->PIO_ODSR = PIO_ODSR_P1;   // Set the output on P1 (digital pin 26) high, other outputs low: (B00000000000000000000000000000010)
  PIOD->PIO_ODSR = PIO_ODSR_P2;   // Set the output on P2 (digital pin 27) high, other outputs low: (B00000000000000000000000000000100)
  PIOD->PIO_ODSR = PIO_ODSR_P3;   // Set the output on P3 (digital pin 28) high, other outputs low: (B00000000000000000000000000001000)
}

There are two suitable batches of pins on PORT C, from PC1 TO PC9 (D33 to D41) and PC12 to PC18 (D51 to D44). If you interleave inputs and outputs, (A1 and B1 = PC1 and PC2, A2 and B2 = PC2 and PC3, and so on...), then you should only need to read using the PDSR register, shift once to the left with the << operator then output with the ODSR.

Port C registers can be accessed by in the same way as the above example, but replacing PIOD with PIOC.

Why? I'm just curious.

Hi flashko, we are planning to use it for teaching purposes. So any advice on how to program the due to achieve this and recommendations are more than welcome. Thanks