Author Topic: STM32F4 SPI read problem  (Read 1625 times)

0 Members and 1 Guest are viewing this topic.

Offline UlfberthTopic starter

  • Newbie
  • Posts: 8
  • Country: 00
STM32F4 SPI read problem
« on: February 02, 2023, 12:27:31 pm »
Hi  :)

I got problem with receiving data from nrf24l01+
First I thought that this is hardware issue, and slave device doesn't send right data, so I plugged logic analyzer which showed me that hardware is ok.
After that I launched debugger and found out that this is software issue, but I still didn't managed to find the mistake  :scared:

So what I'm trying to do is:
1) Write register of slave.
2) Read that register.

so steps to write address are:

1) Select NRF 2) Send register address + write cmd 3) Send register value 4) Release device

And for read:

1) Select NRF 2) Send register address + read cmd 3) Send 5 dummy bytes 4) save data to array 5) Release device

The problem is, 1st byte in array always wrong, the rest is right, but they are shifted by 1. And after 1 cycle 1st data in array is actually the one which should be last  :-//
It's on screenshot below, instead of rxBuf[0]=0x51 I get 1C then after cycle its 0x55...

I assume mistake is in read function, but I can't figure it out  :-[

Code and screenshots is below

SPI settings
Code: [Select]
void spi1_init(void)
{
    // Enable clock for SPI2
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

    // Configure SPI2
    SPI1->CR1 = 0; // Reset CR1 register
    SPI1->CR1 |= SPI_CR1_BR_0;  // Baud rate prescaler = 4 => 16/4=4mhz
    SPI1->CR1 |= SPI_CR1_MSTR; // Master mode
    SPI1->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI; // Software slave management
    SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI2
}

void spi1_gpio_init(void) // PA4 - SPI1_NSS | PA5-SPI1_SCK | PA6-SPI1_MISO| PA7-SPI1_MOSI
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Enable GPIOA Clock

GPIOA->MODER |= GPIO_MODER_MODER4_0;  //PA4 GPIO OUT mode
GPIOA->MODER |= GPIO_MODER_MODER5_1;  //Enable Alternate function mode for GPIOA PA5
GPIOA->MODER |= GPIO_MODER_MODER6_1;  //Enable Alternate function mode for GPIOA PA6
GPIOA->MODER |= GPIO_MODER_MODER7_1;  //Enable Alternate function mode for GPIOA PA7

GPIOA->AFR[0] |= 0x5UL<<GPIO_AFRL_AFSEL5_Pos;  //GPIO alternate function set 0101 AF5 SPI PA5
GPIOA->AFR[0] |= 0x5UL<<GPIO_AFRL_AFSEL6_Pos;  //GPIO alternate function set 0101 AF5 SPI PA6
GPIOA->AFR[0] |= 0x5UL<<GPIO_AFRL_AFSEL7_Pos;  //GPIO alternate function set 0101 AF5 SPI PA7
}

code for read and write functions

Code: [Select]
void test_read(void)
{

uint8_t rxBuf[5]={0};



GPIOA->BSRR = GPIO_BSRR_BR4;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = NRF24_REG_TX_ADDR | NRF24_CMD_R_REGISTER;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0xFF;
    while (!(SPI1->SR & SPI_SR_RXNE));
    rxBuf[0]=SPI1->DR;
    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0xFF;
    while (!(SPI1->SR & SPI_SR_RXNE));
    rxBuf[1]=SPI1->DR;
    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0xFF;
    while (!(SPI1->SR & SPI_SR_RXNE));
    rxBuf[2]=SPI1->DR;
    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0xFF;
    while (!(SPI1->SR & SPI_SR_RXNE));
    rxBuf[3]=SPI1->DR;
    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0xFF;
    while (!(SPI1->SR & SPI_SR_RXNE));
    rxBuf[4]=SPI1->DR;

    while (!(SPI1->SR & SPI_SR_TXE));
    while (SPI1->SR & SPI_SR_BSY);


    GPIOA->BSRR = GPIO_BSRR_BS4;

}

void test_write(void)
{
GPIOA->BSRR = GPIO_BSRR_BR4;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = NRF24_REG_TX_ADDR | NRF24_CMD_W_REGISTER;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0x51;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0x52;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0x53;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0x54;

    while (!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = 0x55;

    while (!(SPI1->SR & SPI_SR_TXE));
    while (SPI1->SR & SPI_SR_BSY);


    GPIOA->BSRR = GPIO_BSRR_BS4;
}
 

Offline wek

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: sk
Re: STM32F4 SPI read problem
« Reply #1 on: February 02, 2023, 01:09:41 pm »
Basically, after *each* Tx you should wait for RXNE and read out the received byte.

JW
 
The following users thanked this post: Ulfberth

Offline UlfberthTopic starter

  • Newbie
  • Posts: 8
  • Country: 00
Re: STM32F4 SPI read problem
« Reply #2 on: February 02, 2023, 01:28:35 pm »
Is my sequence wrong?

My steps when I read from spi:
1) I wait for TXE ( transmit buffer empty)
2) I put data into tx DR
3) I wait for RXE (Receive buffer not empty)
4) I take data from rx DR and put it into array

 

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: dk
Re: STM32F4 SPI read problem
« Reply #3 on: February 02, 2023, 01:48:22 pm »
Is my sequence wrong?

My steps when I read from spi:
1) I wait for TXE ( transmit buffer empty)
2) I put data into tx DR
3) I wait for RXE (Receive buffer not empty)
4) I take data from rx DR and put it into array

remember that every transmit is also a receive

try waiting for RXE and doing a dummy read of DR after sending the read command


 
The following users thanked this post: Ulfberth

Offline Sherlock Holmes

  • Frequent Contributor
  • **
  • !
  • Posts: 570
  • Country: us
Re: STM32F4 SPI read problem
« Reply #4 on: February 02, 2023, 01:53:54 pm »
Is my sequence wrong?

My steps when I read from spi:
1) I wait for TXE ( transmit buffer empty)
2) I put data into tx DR
3) I wait for RXE (Receive buffer not empty)
4) I take data from rx DR and put it into array

I wrote this last year, the SPI to NRF code works correctly.

https://github.com/Steadsoft/embedded/tree/main/NucleoF446RE/NRF24

These are just apps, using the API I created, this is for testing RX functionality, all STM32

https://github.com/Steadsoft/embedded/blob/main/NucleoF446RE/NRF24/Receiver/nrf_hal_rx_project.c

So focus on the API libraries I created, that's where the real effort went.
« Last Edit: February 02, 2023, 01:58:57 pm by Sherlock Holmes »
“When you have eliminated all which is impossible, then whatever remains, however improbable, must be the truth.” ~ Arthur Conan Doyle, The Case-Book of Sherlock Holmes
 
The following users thanked this post: Ulfberth

Offline UlfberthTopic starter

  • Newbie
  • Posts: 8
  • Country: 00
Re: STM32F4 SPI read problem
« Reply #5 on: February 02, 2023, 02:07:07 pm »
OMG thanks guys, I was very inattentive and totally forgot that when I send read command of course I have to do dummy read, that's where this weird byte came from.

Although it's still unclear for me.Why it is 0x1C I get? because if you look at logic analyzer, it shows that there is 0x0E on MISO like when 0x10(read command) sent, so where this 1C comes from?  :-//

Also thanks for API's I will take a look at them, after I will manage to get NRF24 working with low level functions  :)

« Last Edit: February 02, 2023, 02:09:04 pm by Ulfberth »
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3671
  • Country: gb
  • Doing electronics since the 1960s...
Re: STM32F4 SPI read problem
« Reply #6 on: February 02, 2023, 03:29:42 pm »
Also remember that the receiver should be cleared out before you load the first byte into the transmitter.

This is in case you get e.g. an interrupt or an RTOS switch after loading TX, and then when you clear out RX you end up dumping good data.

I have moved most of my SPI stuff to DMA, because software servicing is such a hassle while DMA works really well, and with a negligible CPU overhead. See e.g. this thread
https://www.eevblog.com/forum/microcontrollers/32f417-spi-running-at-one-third-the-speed-it-should/msg3940687/#msg3940687

The 32F4 peripherals are quite slow because they are running at a fraction of the CPU clock speed, and apparently the intention was to use DMA for everything, so ST provided very little buffering on SPI and UARTs.

Also carefully check the /CS timing (before and after). IMHO 53.4% of the code found on github doesn't work in the 21st century because it was done on 16MHz chips and nobody checked the timing, so it worked by luck, and when you get to today's 168MHz+ chips, the timing breaks. By 53.4% I mean 53.4% of the portion of github code that ever worked, which is at best 50% :) :)
« Last Edit: February 02, 2023, 03:33:19 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 
The following users thanked this post: boB, Ulfberth

Offline UlfberthTopic starter

  • Newbie
  • Posts: 8
  • Country: 00
Re: STM32F4 SPI read problem
« Reply #7 on: February 03, 2023, 07:09:29 am »
Yes, I will go for DMA of course, but since I'm not well experience in stm32 and in C, my current goal is to make simple version without DMA or interupts and after it's done, I will go for next step.

Eventually I want to create something similar to FPV system for a toy car, based on stm32f407, ov7670 or ov2640  and nrf24l01, I chose those parts simply because I have them laying around  :)
I know that's not an easy task especially for beginner and such project will need DMA and even some kind of image compression because nrf24 would not be able to send image at 25 fps
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3671
  • Country: gb
  • Doing electronics since the 1960s...
Re: STM32F4 SPI read problem
« Reply #8 on: February 03, 2023, 11:45:21 am »
Here is an example source for 32F417 SPI and DMA.

Code: [Select]

/*
*
* DMA-only version of HAL_SPI2_TransmitReceive() but fixed for SPI3 and xx-only options added.
*
* For use where fast transfers are needed, on the limit of SPI3 speed so with zero gaps. This is impossible
* to do by polling at 10.5mbps or 21mbps and is probably marginal at 5.25mbps. The 16 bit SPI mode just
* manages gap-free with polling but works only with even block sizes, and has the "first byte problem" which
* DMA gets around.
*
* ** DMA ONLY SO NO CCM ACCESS SO THE TWO BUFFERS HAVE TO BE "STATIC" **
*
* This function is blocking so the caller can set CS=1 right away (check device data sheet!). A non-blocking
* version would make sense only if transmitting only (txonly=true) but the caller would have to tidy up
* the DMA and SPI3->CR2.
*
* _spi3_set_mode() can still be used to set up the clock speed, phase, etc; this function does nothing
* to SPI3 other than to enable/disable DMA. It does however make sense only if SPI3 is a Master.
*
* Two modes, obviously mutually exclusive, for tx-only and rx-only:
* If txonly, dumps rx data so you don't need to allocate a buffer for it
* If rxonly, transmits all-0x00 so you don't need to feed SPI with some "known garbage"
*
* The rx-only mode is superfluous in most cases but it does avoid shifting non-0x00 data to the device
* while we are reading data out of it. With some devices this can matter. The ADS1118 ADC is one such.
*
* Because this function needs to work with _spi3_set_mode(), care is taken to not modify the SPI config.
*
* The yield parameter just releases control to RTOS, so equal-priority tasks don't have to wait until DMA
* finished. This might be useful for long/slow DMA transfers. Usually not useful.
* 31/8/22 if true, worsens corruption of incoming USB VCP data, a lot more than it is corrupted
* anyway!
*
* Returns (false) if memory is in CCM, size=0, null pointers...
*
* NULL pointers are allowed if using the txonly or rxonly modes; then the unused one can be NULL.
*
*
*/

bool SPI3_DMA_TransmitReceive(uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, bool txonly, bool rxonly, bool yield)
{

  // Check for invalid inputs

if ( Size==0 ) return (false);
  if ( (pTxData==NULL) && !rxonly ) return (false);
  if ( (pRxData==NULL) && !txonly ) return (false);

uint32_t txadd = (uint32_t) pTxData;
uint32_t rxadd = (uint32_t) pRxData;
static uint8_t txonly_target=0; // must not be in CCM
static uint8_t rxonly_source=0; // must not be in CCM

// Test for CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K (linkerscript.ld)
if ( ((txadd>=0x10000000) && (txadd<0x10010000)) || ((rxadd>=0x10000000) && (rxadd<0x10010000)) )
{
return(false);
}

// DMA ch 1 clock enable already done in b_main.c
//RCC->AHB1ENR |= (1u << 21); // DMA1EN=1 - DMA1 clock enable
//hang_around_us(1); // give it a chance to wake up

SPI3->CR1 &= ~SPI_CR1_SPE; // disable SPI

// DMA1 Ch 0 Stream 0 is SPI3 RX

DMA1_Stream0->CR = 0; // disable DMA so all regs can be written
// Wait until DMA clears the EN bit by itself.
// Should be pointless since the previous transfer waited for end of transfer (hardware bug)
//while ( (DMA1_Stream0->CR & DMA_SxCR_EN) != 0) {};

DMA1->LIFCR = (0x03d << 0); // clear int flags & transfer complete - 111101 stream 0

DMA1_Stream0->NDTR = Size;

if (txonly)
{
DMA1_Stream0->M0AR = (uint32_t) &txonly_target; // memory address to dump rx data to
}
else
{
DMA1_Stream0->M0AR = rxadd; // memory address in normal mode
}

DMA1_Stream0->PAR = (uint32_t) &(SPI3->DR); // peripheral address
DMA1_Stream0->FCR = 0; // direct mode

if (txonly)
{
DMA1_Stream0->CR = 0 << 25 // CHSEL: ch 0
|  0 << 23   // MBURST: memory burst - single transfer
|  0 << 21 // PBURST: peripheral burst - single transfer
|  3 << 16 // PL: highest priority
|  0 << 15 // PINCOS: no peripheral address increment offset
|  0 << 13 // MSIZE: memory data size: byte
|  0 << 11 // PSIZE: peripheral data size: byte
|  0 << 10 // MINC: memory address increment: 0
|  0 << 9 // PINC: peripheral address increment: 0
|  0 << 8 // CIRC: no circular mode
|  0 << 6 // DIR: peripheral to memory
|  0 << 5 // PFCTRL: DMA is flow controller
|  1 << 0; // EN: enable stream
}
else
{
DMA1_Stream0->CR = 0 << 25 // CHSEL: ch 0
|  0 << 23   // MBURST: memory burst - single transfer
|  0 << 21 // PBURST: peripheral burst - single transfer
|  3 << 16 // PL: highest priority
|  0 << 15 // PINCOS: no peripheral address increment offset
|  0 << 13 // MSIZE: memory data size: byte
|  0 << 11 // PSIZE: peripheral data size: byte
|  1 << 10 // MINC: memory address increment: 1
|  0 << 9 // PINC: peripheral address increment: 0
|  0 << 8 // CIRC: no circular mode
|  0 << 6 // DIR: peripheral to memory
|  0 << 5 // PFCTRL: DMA is flow controller
|  1 << 0; // EN: enable stream
}

// DMA1 Ch 0 Stream 7 is SPI3 TX

DMA1_Stream7->CR = 0; // disable DMA so all regs can be written
// Wait until DMA clears the EN bit by itself.
// Should be pointless since the previous transfer waited for end of transfer (hardware bug)
//while ( (DMA1_Stream7->CR & DMA_SxCR_EN) != 0) {};

DMA1->HIFCR = (0x03d << 22); // clear int flags & transfer complete - 111101 stream 7

DMA1_Stream7->NDTR = Size;

if (rxonly)
{
DMA1_Stream7->M0AR = (uint32_t) &rxonly_source; // memory address to fetch dummy tx data from
}
else
{
DMA1_Stream7->M0AR = txadd; // memory address in normal mode
}

DMA1_Stream7->PAR = (uint32_t) &(SPI3->DR); // peripheral address
DMA1_Stream7->FCR = 0; // direct mode

if (rxonly)
{
DMA1_Stream7->CR = 0 << 25 // CHSEL: ch 0
|  0 << 23   // MBURST: memory burst - single transfer
|  0 << 21 // PBURST: peripheral burst - single transfer
|  0 << 16 // PL: priority low
|  0 << 15 // PINCOS: no peripheral address increment offset
|  0 << 13 // MSIZE: memory data size: byte
|  0 << 11 // PSIZE: peripheral data size: byte
|  0 << 10 // MINC: memory address increment: 0
|  0 << 9 // PINC: peripheral address increment: 0
|  0 << 8 // CIRC: no circular mode
|  1 << 6 // DIR: memory to peripheral
|  0 << 5 // PFCTRL: DMA is flow controller
|  1 << 0; // EN: enable stream
}
else
{
DMA1_Stream7->CR = 0 << 25 // CHSEL: ch 0
|  0 << 23   // MBURST: memory burst - single transfer
|  0 << 21 // PBURST: peripheral burst - single transfer
|  0 << 16 // PL: priority low
|  0 << 15 // PINCOS: no peripheral address increment offset
|  0 << 13 // MSIZE: memory data size: byte
|  0 << 11 // PSIZE: peripheral data size: byte
|  1 << 10 // MINC: memory address increment: 1
|  0 << 9 // PINC: peripheral address increment: 0
|  0 << 8 // CIRC: no circular mode
|  1 << 6 // DIR: memory to peripheral
|  0 << 5 // PFCTRL: DMA is flow controller
|  1 << 0; // EN: enable stream
}

// Config SPI3 to let DMA handle the data. These need to be cleared when transfer complete!
// This starts the transfer

SPI3->CR2 |= 3; // TXDMAEN, RXDMAEN: 11 - both set in one go
SPI3->CR1 |= (1<<6); // SPE=1 enable SPI

// Wait for DMA to finish. Blocking is necessary to prevent device CS=1 too early.
// There could be a timeout here but a failure is impossible short of duff silicon, because
// we are a Master and generating the SPI clock.
// 20/11/22: A primitive timeout has been implemented anyway.

volatile uint32_t limit = 0;

while(true)
{

limit--;
if (limit==0) break;

// Either end-transfer detection method below works but the NDTR may be less reliable
// http://efton.sk/STM32/gotcha/g20.html
// [url]https://www.eevblog.com/forum/microcontrollers/32f4-3-ways-to-detect-end-of-dma-transfer/[/url]
#if 0
uint16_t temp1;
temp1 = DMA1_Stream0->NDTR;
if ( temp1 == 0 ) break; // transfer count = 0
#else
uint32_t temp2;
temp2 = DMA1->LISR;
if ( (temp2 & (1<<5)) !=0 ) break; // TCIF0
#endif
if (yield) osDelay(1); // release to RTOS (see notes in comments)

// Third method of detecting end of transfer: wait for DMA to set EN to 0.
// All 3 methods produce the same time from last SPI clock to CS=1 (1.8us).

//if ( (DMA1_Stream0->CR & DMA_SxCR_EN) == 0) break;

}

SPI3->CR2 &= ~3; // TXDMAEN, RXDMAEN: 00 - both cleared in one go

// Clear int pending flags. They get cleared at the top of this function anyway, but...

DMA1->LIFCR = (0x03d << 0); // clear int flags & transfer complete - 111101 stream 0
DMA1->HIFCR = (0x03d << 22); // clear int flags & transfer complete - 111101 stream 7

    // Clear any rx data and the overrun flag in case not all received data was read
// A small delay is needed because the very last transfer may not propagate to DR.
// Not needed because RX DMA has read out everything
hang_around_us(1);
    SPI3->DR;
SPI3->DR;
    SPI3->SR;

SPI3->CR1 &= ~(1<<6); // SPE=0 disable SPI

    return (true);

}

Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 
The following users thanked this post: Ulfberth

Offline Sherlock Holmes

  • Frequent Contributor
  • **
  • !
  • Posts: 570
  • Country: us
Re: STM32F4 SPI read problem
« Reply #9 on: February 03, 2023, 05:00:08 pm »
OMG thanks guys, I was very inattentive and totally forgot that when I send read command of course I have to do dummy read, that's where this weird byte came from.

Although it's still unclear for me.Why it is 0x1C I get? because if you look at logic analyzer, it shows that there is 0x0E on MISO like when 0x10(read command) sent, so where this 1C comes from?  :-//

Also thanks for API's I will take a look at them, after I will manage to get NRF24 working with low level functions  :)

I spent a lot of time refining the two libraries that are in that project's repo. I too was monitoring everything with a scope and logic analyzer.

The small API presented in that library contains most of the details that I addressed and were put together painstakingly and with input from numerous blogs and so on.

The two test projects 'Transmitter' and 'Receiver' are very draft, they were really just heavily exercising the NRF device, hammering it with data, the 'Receiver; us just a slightly tweaked copy of the transmitter code so isn't even receiving yet.

The 'Transmitter' project is the one I used as I tested, very heavily running for hours on end, the interrupt handling was the last stuff I added. Of course that code is tied to the board I used and that code is confined to these functions:

https://github.com/Steadsoft/embedded/blob/main/libraries/nrf24_hal_support/nrf24_hal_support.c#L58

https://github.com/Steadsoft/embedded/blob/main/libraries/nrf24_hal_support/nrf24_hal_support.c#L73

https://github.com/Steadsoft/embedded/blob/main/libraries/nrf24_hal_support/nrf24_hal_support.c#L121

It was during this work that I began to fatigue over how unhelpful the C language can be, in order to create clean, tidy, readable code I had to spend a great deal of time doing donkey work, so I've postponed that NRF work as I start looking at designing a better programming language.

This was one of the more helpful blogs, worth a read.


“When you have eliminated all which is impossible, then whatever remains, however improbable, must be the truth.” ~ Arthur Conan Doyle, The Case-Book of Sherlock Holmes
 
The following users thanked this post: Ulfberth

Offline Sherlock Holmes

  • Frequent Contributor
  • **
  • !
  • Posts: 570
  • Country: us
Re: STM32F4 SPI read problem
« Reply #10 on: February 03, 2023, 05:21:35 pm »
OMG thanks guys, I was very inattentive and totally forgot that when I send read command of course I have to do dummy read, that's where this weird byte came from.

Although it's still unclear for me.Why it is 0x1C I get? because if you look at logic analyzer, it shows that there is 0x0E on MISO like when 0x10(read command) sent, so where this 1C comes from?  :-//

Also thanks for API's I will take a look at them, after I will manage to get NRF24 working with low level functions  :)

That 1C rings a bell, its been like 10 weeks now since I even looked at the NRF stuff so I've gone rusty, if you'd posted your question back then I'd have been right in there with you!

Note that whenever you send a command opcode, the NRF concurrently sends back the status register, so it could be that that you're seeing. SPI is basically just shift registers and there are two so as it shifts the bits in that you are sending, it has to shift something out, so it might as well have some use, that's why they shift the status out, just to avoid wasting that opportunity.

That characteristic is embedded in the API, all NRF command calls rely on _ReadSingleByteRegister or _WriteSingleByteRegister and these always returns the status, since the very act of issuing a command always causes the status to come back anyway, so as a convenience I always return it.

Each of the numerous API calls include that last status arg and I suspect that's what you're seeing - although the bit pattern isn't quite what I'd expect...

This is the NRF spec I used by the way.

I spent many hours on all this and was often puzzled, but I got it to make total sense in the end and the stuff I was seeing on the scope was always understood, always made sense to me but only after weeks n weeks of experimentation.





« Last Edit: February 03, 2023, 05:28:02 pm by Sherlock Holmes »
“When you have eliminated all which is impossible, then whatever remains, however improbable, must be the truth.” ~ Arthur Conan Doyle, The Case-Book of Sherlock Holmes
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf