joan2937 / pigpio

pigpio is a C library for the Raspberry which allows control of the General Purpose Input Outputs (GPIO).
The Unlicense
1.46k stars 410 forks source link

bscXfer hicups #295

Closed Yardie- closed 4 years ago

Yardie- commented 5 years ago

HI Joan Firstly haha yes writing documentation is quite boring but I find your code easy to follow and quite elegant IMHO. I especially like the way you do the 2 line if else statements. It makes it very easy to read.

I have been getting a few strange things happening. WARNING this could get a little long winded so I will just introduce the problem first. To simplify the issue I setup an Arduino with the standard Wire library as the I2C master with the PI as the slave. The master simply sends one char which the slave then returns.

The main problem I am finding with this is the master sends a byte then terminates the connection in the normal way. Then asks the slave for a byte to be sent back

The slave is receiving the byte as sent however the BSC hangs onto the old data and repeatedly sends the same thing even though it is clear ( by printing out the buffer ) that the txBuf has changed.

The data received by the master is the old data.

I have tried

Eventually it changes but it is difficult to say if it is because of a timeout or something else.

I thought that maybe I could sort of HUP the BSC or clear it but that seems to require a Term/Init sequence. I haven't yet tried adding another function in pigpio to set the bscsReg[BSC_CR]=0; / clear device /

I hope I have explained the problem clearly enough.

BTW if you set the txCnt to zero it sort of stops everything. I haven't revisited this to see what stops but early in my testing I made the mistake and fixed it in my code. When it was set to zero the slave doesn't respond to the master at all (or so it seemed).

Yardie- commented 5 years ago

even though the xfer.txCnt is set each time it seems to be ignored and the new data is stuck incrementally into the bsc's actual buffer until it is full then the buffer is flushed and the new data goes into the correct place. 18 seems to be the magic number. (sorry I still can't get the hang of this markup stuff here) Any ideas??

This is the cleanest way I have found so far to get a reliable result.

the master needs to send the data (which is received cleanly the first time on the slave end) as

` bsc status S S S S S R R R R R T T T T T RB TE RF TF RE TB 1 0 0 0 1 0 0 1 0 Status flags receive FIFO empty transmit FIFO full
rx(in) = 0x18 tx(out) = 0x3

bsc status S S S S S R R R R R T T T T T RB TE RF TF RE TB 1 0 1 0 0 0 0 1 0 Status flags receive FIFO empty
rx(in) = 0x18 tx(out) = 0x18 `

incrementing the number of bytes in the transmit FIFO each time until it is full and the buffer is reset and the correct new data is sent.

` bsc status S S S S S R R R R R T T T T T RB TE RF TF RE TB 0 0 16 0 0 0 1 1 0 Status flags receive FIFO empty transmit FIFO empty rx(in) = 0x18 tx(out) = 0x18

bsc status S S S S S R R R R R T T T T T RB TE RF TF RE TB 1 0 0 0 1 0 0 1 0 Status flags receive FIFO empty transmit FIFO full
rx(in) = 0x18 tx(out) = 0x18 ` the slave code is simply

while(1) { xfer.txBuf[0] = echo; xfer.txCnt = 1; int status_now = bscXfer(&xfer); if (status != status_now ) { status = status_now; show_status(); printf("rx(in) = 0x%x tx(out) = 0x%x \n", xfer.rxBuf[0], xfer.txBuf[0]); xfer.txBuf[0] = (uint8_t) xfer.rxBuf[0]; xfer.txCnt = 1; echo = (uint8_t) xfer.rxBuf[0]; } usleep(200); }

master output follows ----------------8<------------------------ ` Please enter a number 24 Sending 0x18 Asking for 18 bytes (expecting 0x18)

(1) 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 (2) 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 `

Yardie- commented 5 years ago

Ok I can get it to work BUT

the last character comes first and I have to burn the first 4 reads of a 4 byte value. and the last character comes first example results below. I can work around this but it is pretty strange.

the slave flags are

a a a a a a a - - IT HC TF IR RE TE BK EC ES PL PH I2 SP EN 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 1

Please enter a number 2814142956 Sending 0xEC 0x69 0xBC 0xA7 2814142956 Asking for 4 bytes (expecting 0x 0xEC 0x69 0xBC 0xA7)

(1) 0x00 0x09 0x00 0x00 (2) 0x00 0x09 0x00 0x00 (3) 0x00 0x09 0x00 0x00 (4) 0x00 0x09 0x00 0x00 (5) 0x00 0xEC 0x69 0xBC (6) 0xA7 0xEC 0x69 0xBC Please enter a number 7645637 Sending 0xC5 0xA9 0x74 0x00 7645637 Asking for 4 bytes (expecting 0x 0xC5 0xA9 0x74 0x00)

(1) 0xA7 0xEC 0x69 0xBC (2) 0xA7 0xEC 0x69 0xBC (3) 0xA7 0xEC 0x69 0xBC (4) 0xA7 0xEC 0x69 0xBC (5) 0xA7 0xC5 0xA9 0x74 (6) 0x00 0xC5 0xA9 0x74 Please enter a number

Yardie- commented 5 years ago

OK some traps for those following me.

I also used a command byte 0x0E to trigger the update as I was echoing back the data sent. As we are talking to a buffer it has the data last added so to echo you need to send the data. then a trigger to read the sent data. Sorry the code is rough but it is reliable.

in arduino it looks something like

typedef union { uint8_t bytes[4]; uint32_t number; } UINT32;

uint8_t slave_address = 8; UINT32 in; in.number = 123456;

uint8_t buffer[8]; Wire.beginTransmission(slave_address); // transmit to device #8 Wire.write(in.bytes,(uint8_t) 4); Wire.endTransmission(); // stop transmitting delay(100); Wire.requestFrom(slave_address,(uint8_t)16); // request 16 bytes from slave device to flush the buffer Wire.beginTransmission(address); // transmit to device #8 Wire.write(0x0E); //ask to send data Wire.endTransmission(); // stop transmitting delay(100); Wire.requestFrom(address,(uint8_t)5); // request 5 bytes from slave device #8 for (int x = 0; x < 5 ; x++) buffer[x] = Wire.read();

joan2937 commented 5 years ago

I'm not sure if you are saying there is a bug in the software or not. Any data you write to the BSC FIFO will be appended to any existing data in the FIFO. The FIFO is only read by the master (in this case Arduino).

Yardie- commented 5 years ago

No not at all. I was not trying to blame you of anything or say you had done anything incorrectly. I was just trying to understand what was actually going on.

I couldn't really find any information so I once again ended up here. I can't seem to find a reasonably forum to discuss this stuff in. Also people like yourself who understand this stuff properly and are not ' over it' are pretty thin on the ground. Sorry to disturb you. I am really enjoying working with pigpio it is very well written. Thank you again.

The BCM documentation is very scant. I don't know how you got as far as you did.

The process of appending to the FIFO as apposed to writting to it entirely was what tripped me up. I presumed that if I wrote to txBuf[0] that would also be writing to that position in the bufffer on the chip.

Is there a way to flush the tx buffer of the chip or is that only not possible. I tried setting the BRK bit to reset the buffer but that didn't seem to do anything. The only way I have found to cleanly and reliably get the same data repeatedly is for the master to read the whole 16byte buffer each time. I suppose I could add a marker between each piece of data but that is getting messy. It appears as both I2C buses on the BCM chip only handle a 16 byte buffer.

Looking through your code I thought that as you had just mapped directly to the address space of the chip but evidently that is not the case. I would like to watch the rxBuffer directly to trigger a full bscXfer when a command comes in (instead of copying the whole thing each loop), but maybe that presumption is wrong as well. bscXfer can't be that much more overhead.

You can close this if you like.(I sort of cut you off last time Sorry)

joan2937 commented 5 years ago

If you look at the current documentation and you can think of any changes to clarify what's happening with BSC xfer that might be useful. I'm only thinking of a couple of paragraphs at most as I then have to slide that into all the relevant places.

Don't worry if you don't want to, I'm sure you have had enough of this by now.

Yardie- commented 5 years ago

Ok I'll have a go. It might help clarify a few things for me as well. So it's a win win. I know it's hard to write documentation when you already have a deep understanding of the way the system works. I'll get back to you here.

Yardie- commented 5 years ago

Ok Joan here is first draft of the introduction. I will do more after I do some more testing. You will of course have to adjust it to suit your style and the SPI interface parameters.

This function provides a low-level interface to the SPI/I2C Hardware Slave interface on BCM chip. This peripheral allows the Pi to act as a hardware slave device on an I2C or SPI bus.

This is not a bit bang version and as such is OS timing independent. The bus timing is handled directly by the chip. The output process is simple. You simply append data to the FIFO buffer on the chip. This works like a queue, you add data to the queue and the master removes it.

This can create a bit of confusion at the start the master needs to know how much data to remove to arrive at the start again.

joan2937 commented 5 years ago

@Yardie- That's fine, I'll incorporate words to that effect into the text.

Yardie- commented 5 years ago

I am sorry Joan I'm done. This is between the BSC and the Arduino, The cleanest way I can get this to work with the Arduino Wire library is have the Arduino do a fake request for the whole 32bytes before each read. This works cleanly and reliably every time from an Arduino nano to a PI Zero over a 2m wire at 100kHz so it is what I was after. It's time for me to move on.

I wouldn't have a clue how to document it, however I have added working Arduino code below.

on the PI it's like

inside the main loop

If rx count > 0 process the command and set the tx buffer and tx count

on the Arduino it's

uint8_t slave_address = 119; uint8_t out[] = { 0x66, 0x66, 0x48, }; uint8_t how_many = 32;

// send command Wire.beginTransmission(slave_address); // start transmit to device Wire.write(out,(uint8_t) 3); Wire.endTransmission(); // stop transmitting

// do fake read of the whole 32 byte buffer Wire.requestFrom(slave_address,how_many); delay(100);

how_many = 5; Wire.requestFrom(slave_address,how_many); while (Wire.available()) { // slave may send less than requested uint8_t in = Wire.read(); // receive a byte as unsigned 8 bit int Serial.print("\n 0x"); Serial.print(in,HEX); }

Yardie- commented 5 years ago

OH MY GOD I HAVE FINALLY GOT IT IT'S SO SIMPLE I'M AN IDIOT

When the code returns after a call to bscXfer it is necessary to set txCnt to 0 so simply doing this fixes it

bscXfer(&xfer); xfer.txCnt = 0;

It was just adding more data at each call. Thanks Joan good luck. ps You still get the data from the last call but that is just a FIFO order issue inherent in the FIFO buffer system. Thanks