0

I am attempting to send SPI data through an optocoupler level shifter. (Basically takes the 3.3v signal to a 5v signal)

In this process, the level shifter flips all the bits (inverted logic) instead of using another inverter to realign the signal, I decided it would be easier to simply send inverted bits out of the SPI port.

To do this, I set the mode on the chip select to active high and changed the clock polarity. Now I attempting to bit flip the data I am sending out.

  unsigned short* tx;
  unsigned short* rx;
  while(!feof(data_file)){
    fread(buffer, 2, 80, data_file);
    tx = buffer;
    for(int i = 0; i < 80; i++){
      (*tx)= ~(*tx); //added this line
      spi.transfer((unsigned char*) tx,(unsigned char*) rx, 2);
      (*rx)= ~(*rx); //also added this line
      printf("0x%04x\n", *rx);
      pinControl.selectChip(i%8);
      usleep(5);
    }

before adding this there was nice clean output Data received:

0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x050b
0x050b
0x050b
0x050b

After inverting:

0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9

Note the data I am sending is monotonically incrementing, so the second output is incorrect.

My hypothesis is

(*tx) = ~(*tx)

is not actually inverting the bits.

So I attempted to test this theory.

  unsigned short* tx;
  unsigned short* rx;
  (*tx)=4;
  std::cout << *tx << endl;
  std::cout << ~(*tx) << endl;
  (*tx) = ~(*tx);
  std::cout << (*tx);

Yielded:

4
-5 <-- this is ~4 correct
65531 <-- this is unexpected
Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
Dustin K
  • 150
  • 2
  • 11
  • 2
    Where does `rx` point to? It is uninitialized. And what is `buffer`? And `while feof` is always wrong. – KamilCuk Jul 18 '19 at 21:00
  • 2
    Unrelated: Give [Why is “while (!feof(file))” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) a read when you get a chance – user4581301 Jul 18 '19 at 21:02
  • Suggestion: Print out `*tx` before and after flipping it. – user4581301 Jul 18 '19 at 21:03
  • 3
    Your problem is not with inverting the bits, that is something that can be claimed with high confidence. More can be said after [mcve] is posted. – Eugene Sh. Jul 18 '19 at 21:05
  • Changed the clock polarity? – stark Jul 18 '19 at 21:06
  • Note that your unwanted output is alternating between two 16-bit values that are related by flipping all bits. That suggests to me that you're getting more (or fewer) inversions than you intend. – John Bollinger Jul 18 '19 at 21:33
  • Clock polarity refers to whether SPI data is sent on the falling edge or rising edge of the clock. The slave device updates on falling edge so flipping the polarity fixes that. – Dustin K Jul 18 '19 at 21:35
  • 3
    Note also that 65531 is the *unsigned* 16-bit integer with the same bit pattern as the signed 16-bit two's complement integer with value -5. – John Bollinger Jul 18 '19 at 21:36
  • @JohnBollinger The OP have actually added *two* inversions to the code. So now to figure out how they are related. – Eugene Sh. Jul 18 '19 at 21:36
  • Hmmm very interesting. I will need to look more into this. You are right 65531 is the two's comp of 4. – Dustin K Jul 18 '19 at 21:41
  • 1
    The example code reads (up to) 80 `unsigned short`s, but then it outputs the first one 80 times (plus or minus inversion), ignoring the rest. – John Bollinger Jul 18 '19 at 21:42
  • @John Boillinger The transfer function automatically increments the pointer by the amount of bytes in the last parameter. So I guess whether this in increments at the beginning or after a successful transfer could explain why it is getting inverted twice. – Dustin K Jul 18 '19 at 21:50
  • @DustinK, I'm having trouble finding any documentation that seems relevant to your `particular spi.transfer()` function. Which SPI API are you using? – John Bollinger Jul 18 '19 at 21:56
  • I am using Derek Malloy's repo for BeagleBone Black. https://github.com/derekmolloy/exploringBB/tree/version2/library/bus – Dustin K Jul 19 '19 at 13:10

2 Answers2

1

The problem in both of your code examples is incorrect use of pointers. You declare the pointers correctly, but you do not assign valid memory addresses to them.

For example, when you do this:

unsigned short* tx;
(*tx)=4;

tx contains some random address because you did not assign it. So, when you de-reference it and try to put 4 there on the next line, there is no way to tell where this 4 actually goes.

There might be value 0 or close to it in tx and your program will crash (SEGFAULT). Or, some other code could be using this memory, and will overwrite the value, which would explain why you suddenly got 65531 though it has nothing to do with the previous value.

In the code example with the SPI call, there are two problems:

  1. You never advance the tx pointer in the loop, so you always inverting and sending the same two bytes.

  2. You do not assign any valid memory to rx pointer, so like with your test, the data goes to random place, and your program might crash at random.

And finally, the big question is: how did you declare buffer? If it is declared in the same way as rx and tx, then even reading from file may not work correctly.

Here is a version of your code that should work:

unsigned short tx;
unsigned short rx;

while(fread(&tx, 2, 1, data_file) > 0) {
   tx = ~tx;
   spi.transfer(&tx, &rx, 2);
   rx = ~rx; //also added this line
   printf("0x%04x\n", rx);
   pinControl.selectChip(i % 8);
   usleep(5);
}
Lev M.
  • 6,088
  • 1
  • 10
  • 23
  • kind of strange that the code worked in certain circumstances before but then stopped later... maybe it wasn't working correctly, but only seemed that way. It seems to be working correctly now after trying your solution. Thanks! – Dustin K Jul 19 '19 at 14:23
  • @DustinK The code worked some times because sometimes, the random values left in memory before your program loaded were "good enough" for what your program needed to do. This is how C and C++ work: they let you read and write any memory belonging to your program and use variables before assigning value to them, but the results are unpredictable. There are not enough characters in a comment to explain further, but google "buffer overflow" to learn more about this kind of shenanigans. – Lev M. Jul 19 '19 at 20:18
1

rx is an uninitialized pointer. It happens to be pointing to some accessible memory location which happens to be valid. You need to point that to a buffer, like you did with tx.

Your program (supposedly) reads 80 words of data from a file, so the intent is presumably to transfer these through spi.transfer. However, your loop, though it counts through 80 words, is not incrementing the tx and rx pointers.

Consequently, what you're doing is repeatedly inverting the bits of buffer[0] where tx always points. Likewise, rx keeps pointing to the same unknown memory location, which is toggled.

The repeating pairs of values you are seeing:

0xfd36
0x02c9

are in fact bitwise inverses of each other. If you know the correspondence between hexadecimal digits and binary nybbles, this is obvious.

You must also test the return value of fread. fread returns how many elements it was able to read, which may be as low as zero.

The while (!feof(...)) pattern is incorrect. feof(f) is always false for a freshly opened stream on which no input operation has been attempted yet.

The stdio paradigm is that the return values of I/O operations must be individually tested. If an input operation fails, that could be due to end of data, or due to some I/O error. After an input failure on stream f, one of the two flags feof(f) or ferror(f) is true to distinguish end-of-data from an I/O error.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • unsigned short* tx; unsigned short* rx; tx = (unsigned short*)calloc(1,2); rx = (unsigned short*)calloc(1,2); That is the original code, tried posting the cliff notes version, but that is how I instantiated the original pointers – Dustin K Jul 19 '19 at 13:12