Author Topic: FNIRSI-1013D "100MHz" tablet oscilloscope  (Read 389549 times)

Anty_94, engineer.r152, Gennady111 and 7 Guests are viewing this topic.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1025 on: September 25, 2021, 03:55:49 pm »
Perseverance!

That's what you need for doing something like this :-+

Offline Kean

  • Supporter
  • ****
  • Posts: 2054
  • Country: au
  • Embedded systems & IT consultant
    • Kean Electronics
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1026 on: September 25, 2021, 08:28:29 pm »
Glad you worked it out.  I was going to suggest a corrupt SD card.
I've encountered embedded FAT code (elm-chan maybe) that could read files fine, but will fail to write when there was some corruption.
Obviously there is no embedded chkdsk/fsck type functionality, so you have to run that on a PC to fix things.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5835
  • Country: es
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1027 on: September 25, 2021, 08:38:13 pm »
That's what you need for doing something like this :-+
And a lot of time, or to be a very smart cat so you have 7 lives for fixing that crappy chinesium thing  :-DD
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1028 on: September 26, 2021, 05:42:33 am »
Glad you worked it out.  I was going to suggest a corrupt SD card.
I've encountered embedded FAT code (elm-chan maybe) that could read files fine, but will fail to write when there was some corruption.
Obviously there is no embedded chkdsk/fsck type functionality, so you have to run that on a PC to fix things.

I knew the card was fine, since with the original code it was able to create new pictures, and I also tested creating a file on the card with my PC. That slowed testing things down also, loading the software to the scope, run it, take the card out, insert in the PC, check if file was written, etc. Needed to copy the files to my main disk to be able to open with wxHexeditor, since that refuses to open a file on a disk mounted in the media folder. This is some Linux problem.

But it is fixed and a lot of the functionality is implemented now. Unfortunately I had to quit early yesterday due to a thunderstorm passing by. Today I hope to get the remainder of the view part implemented and see if I can get the save picture and waveform in there too.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1029 on: September 26, 2021, 10:09:19 am »
Perseverance!

And a lot of time

It takes a lot of skills, perseverance and time to do something like this.

What started as a relative simple fix of my new touch panel having reversed coordinates, became quite the undertaking of reversing the code.

Discovered a lot of crap in the original code, so decided to make my own interpretation of it, but still based on parts of the original code and functionality.

One has to have a good understanding of programming on a low level, and programming what so ever, to be able to do stuff like this. A lot of the time it is very frustrating, looking at output of Ghidra and trying to get an understanding of what some variable is for. Also seeing through what a compiler did to get to the assembly code is not easy.

I read somewhere in this forum that most of the time it is easier and faster to just write your own code then to reverse engineer it, and that is certainly true. Did that for the display part of the code, and also did that for other parts once I saw the logic behind things.

For others who are thinking of doing something like this be prepared to invest a lot of time.

Offline tv84

  • Super Contributor
  • ***
  • Posts: 3212
  • Country: pt
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1030 on: September 26, 2021, 10:18:32 am »
For others who are thinking of doing something like this be prepared to invest a lot of time.

Plenty of knowledge also of what a scope should be doing - You forgot this part! :clap:
 
The following users thanked this post: Kean, pcprogrammer

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1031 on: September 26, 2021, 02:25:19 pm »
Question to all:

What is preferred in case of a file error (create, open, read or write)
  • show a message for half a second (or longer) and just continue
  • show a message and wait for the user to acknowledge having seen it and need touch to continue

The original scope code does not do any of this. In the save picture function when a file create fails it tries it again and if that fails it tries one more time and does not check on that failing and continues with writing to the file :palm: It does not do harm but the user is not aware of anything going wrong.

Online ledtester

  • Super Contributor
  • ***
  • Posts: 3032
  • Country: us
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1032 on: September 26, 2021, 03:26:02 pm »
I would vote for #2... but you could also make it configurable.
 

Offline pickle9000

  • Super Contributor
  • ***
  • Posts: 2438
  • Country: ca
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1033 on: September 26, 2021, 03:27:49 pm »
I'm for temporary. Notification is a must.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1034 on: September 26, 2021, 03:29:04 pm »
I would vote for #2... but you could also make it configurable.

That is a good idea. I also lean towards #2, but with your idea it can be both ways :)

Offline pickle9000

  • Super Contributor
  • ***
  • Posts: 2438
  • Country: ca
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1035 on: September 26, 2021, 03:36:47 pm »
I assume that at least initially you will be using the card to log not only software functions but save waveforms I can see you getting multiple confirms in a row. A configuration file would be excellent.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1036 on: September 26, 2021, 03:43:06 pm »
Confirmation would only be in case of an error.

If it succeeds it will display this for only half a second, just like the scope does it now.

But I already made a note in the code to add an option of notification confirmation on or off :)
« Last Edit: September 26, 2021, 03:51:57 pm by pcprogrammer »
 

Offline pickle9000

  • Super Contributor
  • ***
  • Posts: 2438
  • Country: ca
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1037 on: September 26, 2021, 03:52:38 pm »
So if you made a settings change (I assume it's saved to the sd card) would it generate the same messages?
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1038 on: September 26, 2021, 04:00:33 pm »
So if you made a settings change (I assume it's saved to the sd card) would it generate the same messages?

Good one, but the settings are saved in the flash on power down. Could also fail, but by then the power is gone and no way of showing a message any more.

The option is only for the confirmation of the notification. The notification itself will always be displayed, but with confirmation disabled it will only show for half a second. It will only be in case of an error, which under normal conditions won't happen.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1039 on: September 26, 2021, 04:34:07 pm »
Already implemented :-DD

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1040 on: September 26, 2021, 06:58:22 pm »
f___ |O

things where going so good, but I ran into a new strange problem :-//

This time I think it really is a FatFs thing. Instead of writing the picture file in a single blob I chopped it into chunks to avoid a lot of memory copies. Also using the same function to write the waveform file, which works without problems.

Here is the part of the code doing it all.
Code: [Select]
  //Open the new file. On failure signal this and quit
  result = f_open(&viewfp, viewfilename, FA_CREATE_NEW | FA_WRITE);

  //Check if file opened without problems
  if(result == FR_OK)
  {
    //For pictures the bitmap header needs to be written
    if(type == VIEW_TYPE_PICTURE)
    {
      //Write the bitmap header
      result = f_write(&viewfp, bmpheader, sizeof(bmpheader), 0);
    }

    //Check if still ok to proceed
    if(result == FR_OK)
    {
      //Save the settings for the trace portion of the data and write them to the file
      scope_prepare_setup_for_file();

      //Write the setup data to the file
      if((result = f_write(&viewfp, viewfilesetupdata, sizeof(viewfilesetupdata), 0)) == FR_OK)
      {
        //Write the trace data to the file
        //Save the channel 1 sample data
        if((result = f_write(&viewfp, (uint8 *)channel1tracebuffer4, 3000, 0)) == FR_OK)
        {
          //Save the channel 2 sample data
          if((result = f_write(&viewfp, (uint8 *)channel2tracebuffer4, 3000, 0)) == FR_OK)
          {
            //Save the channel 1 display data
            if((result = f_write(&viewfp, (uint8 *)channel1ypoints, 1500, 0)) == FR_OK)
            {
              //Save the channel 2 display data
              if((result = f_write(&viewfp, (uint8 *)channel2ypoints, 1500, 0)) == FR_OK)
              {
                //Finish with an empty blob of data to reach the needed file size
                //Clear part of the thumbnail data for this. Is reloaded anyway so does not matter
                memset((uint8 *)viewthumbnaildata, 0, 5000);
               
                //Save it to the file
                if((result = f_write(&viewfp, (uint8 *)viewthumbnaildata, 5000, 0)) == FR_OK)
                {
                  //For pictures extra code is needed to write the pixel data to the file
                  if(type == VIEW_TYPE_PICTURE)
                  {
                    //Write the pixel data
                    result = f_write(&viewfp, (uint8 *)maindisplaybuffer, PICTURE_DATA_SIZE, 0);
                  }
                }
              }
            }
          }
        }
      }
    }

    //Close the file
    f_close(&viewfp);

    //Check if all went well
    if(result == FR_OK)
    {
      //Show the saved successful message
      scope_display_file_status_message(MESSAGE_SAVE_SUCCESSFUL);
    }
    else
    {
      //Signal unable to write to the file
      scope_display_file_status_message(MESSAGE_FILE_WRITE_FAILED);
    }
  }
  else
  {
    //Signal unable to create the file
    scope_display_file_status_message(MESSAGE_FILE_OPEN_FAILED);
  }

For a waveform file the bitmap header and pixel array is not written, and the file looks good. It is 15000 bytes long and I can see the channel 1 trace data. The settings part looks good to. The other bits are zero, because the code to fill that is not implemented yet. (Only channel 1 is partially done)

For the picture it writes the bitmap header, and it seems to write part of the settings. The file is only 512 bytes long, but I can see that the bitmap header is correct and the first bit of the settings data is also there and correct.

When I skip the writing of the 70 byte header (commented out the first f_write) it works without problems, and the resulting file then contains the pixel data. (last f_write)

So it looks like writing less then a sector and then write more data fails somehow.

I will try a work around for now, where I combine the first two writes and hope that solves it.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1041 on: September 27, 2021, 06:15:11 am »
A light bulb appeared this early morning :)  (no emoticon for this :-//)

The SD card interface needs the data to be 32 bits aligned, since the FIFO works with 32 bits data. So the problem was caused by the bitmap header being 70 bytes, which is not a multiple of 32 bits. This lead to the second write becoming misaligned and thus failed.

The original code did have code for this but I dismissed it as being a bit of bullshit. They allocated a buffer 512 times the original buffer filled that with the data from the buffer, but for this they used memcpy with the size not being the original size but the size times 512. So reading outside the original buffer. The disk_read function is the same so writes outside the original buffer. :horse:

Code: [Select]
int disk_write(int pdrv,byte *buff,int sector,uint count)

{
  uint *buffer;
  int iVar1;
  uint unaff_r4;
 
  switch(pdrv)
  {
  case 0:
    if (((uint)buff & 3) == 0) {
      iVar1 = sd_card_write(0,sector,count,buff);
      if (iVar1 == 0) {
        return 0;
      }
    }
    else {
      buffer = malloc(count << 9);
      if (buffer != NULL) {
        memcpy(buffer,buff,count << 9);
        iVar1 = sd_card_write(0,sector,count,(byte *)buffer);
        free(buffer);
        return (uint)(iVar1 != 0);
      }
    }
    unaff_r4 = 1;
    break;

  case 1:
  case 2:
  case 3:
  case 4:
    break;

  default:
    unaff_r4 = 4;
  }
  return unaff_r4;
}

int disk_read(int pdrv,byte *buff,int sector,uint count)

{
  uint *buffer;
  int iVar1;
 
  if (pdrv == 0)
  {
    if (((uint)buff & 3) == 0)
    {
      iVar1 = sd_card_read(0,sector,count,buff);

      if (iVar1 == 0)
      {
        return 0;
      }
    }
    else
    {
      buffer = malloc(count << 9);

      if (buffer != NULL)
      {
        iVar1 = sd_card_read(0,sector,count,(byte *)buffer);

        memcpy(buff,buffer,count << 9);

        free(buffer);

        return (uint)(iVar1 != 0);
      }
    }

    count = 1;
  }

  return count;
}

I thought to have it covered with making sure the input buffers to FatFs were 32 bits aligned. :palm:

Rewrote the SD card code to be able to handle whatever aligned data. Most optimum is 32 bits aligned of course. For 16 bit it has to do 2 reads or writes and for 8 bits 4 reads or writes per 32 bits FIFO access.

So the problem is solved and the saving of the files works.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1042 on: September 27, 2021, 03:57:37 pm »
:blah: Also seeing through what a compiler did to get to the assembly code is not easy.

Here is an example of what I meant with the above about compiler tricks

Code: [Select]
  uVar4 = DAT_800253b8;    //0x38E38E39
  puVar7 = DAT_800253c8;   //0x801C3748  channel 1 trace on screen buffer  channel1ypoints[-1]

  lVar1 = (longlong)(int)uVar4 * (longlong)(int)((puVar7[1] - 0x2e) * 10);
  *(char *)(iVar2 + dataindex + 0x14 + (iVar6 >> 2)) = ((char)(int)(lVar1 >> 0x23) - (char)(lVar1 >> 0x3f)) + '\x05';

In this piece of code the y coordinate is reduced by a factor of 3.61666 and then gets 5 added to it.

Translated to decimal numbers: ((954437177 * ((y - 46) * 10)) / 34359738368) + 5
This results in 434 being reduced to 125 (Max y = 480 - 46 = 434)

The first line does the multiplications and the second line does the divide. (lVar1 >> 0x23) The subtract with the msb is probably for adjusting when result became negative.

So what line of C code results to this pseudo code?

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1043 on: September 28, 2021, 11:20:58 am »
I made a list of what is done and what still needs to be implemented, which is still quite a bit :palm:

Implemented so far
  • System setup
  • Timer interrupt
  • Touch panel interface
  • Flash reading and writing
  • SD card functionality based on FatFs
  • FPGA interface functions
  • Display library
  • User interface
    • The main screen with all the menus
    • The picture view screen with picture view almost done. (Still needs displaying of the trace data)
    • The waveform view screen with waveform view almost done. (Still needs displaying of the trace data)
    • Saving of pictures and waveforms
  • Trace data handling and displaying
    • Long time base settings 50S-100mS. (This is in roll mode and fully done)
    • Short time base settings 50mS-10nS. (This is partially implemented. Channel 1 data is fetched and displayed raw. Only part of the processing is implemented.)
  • Battery charge measurement
  • Power off interrupt for settings save. (Only the interrupt part and not yet tested. Needs the saving of the settings implemented first.)

Still to do
  • USB interface
  • Saving and loading of the settings to and from flash
  • Power off interrupt for settings save
  • Battery charge measurement
  • Finish the picture and waveform single item view. Needs the trace data processing functionality
  • Trace processing and displaying for the short time bases (50mS-10nS)
  • Calibration
  • Auto set
  • Cursor measurements display
  • Measurements calculations and displaying
  • FFT calculation and displaying
  • Firmware update (This means both code within my version as creating an actual firmware update that can be loaded on a scope)

So still a lot of work ahead of me.

First edit to flag battery charge monitoring done on 28th of September 2021 at 18:56:41
Second edit to flag power off interrupt done on 28th of September 2021 at 21:00:00

Edit: These two where reasonable small parts to implement. Battery code was already investigated, so not a lot of work and the power off interrupt part was only investigating four fairly small and simple functions.
« Last Edit: September 28, 2021, 07:06:54 pm by pcprogrammer »
 
The following users thanked this post: frenky, Kean, robca, ledtester, wolfy007, zildan, serverror

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1044 on: October 01, 2021, 10:33:33 am »
Unfortunately not able to work on the project all the time :(

But today again time for it. Analyzed the settings in the flash memory and found out I'm still not done with finding the variables :o

They restore data to a point in memory I have not found the code for that makes use of the data. I think it is some calibration data. The rest of the settings are known :)

This is the function that copies the data loaded from flash address 0x001FD000 to the actual settings
Code: [Select]
void restore_config_data(uint *address)  //0x8035344E

{
  undefined2 uVar1;
  undefined *puVar2;
  int iVar3;
  int iVar4;
  undefined2 *puVar5;
  uint *puVar6;
  int iVar7;
  undefined *puVar8;
 
  puVar5 = DAT_80027a1c;    //0x802F18B0
  iVar4 = DAT_80027a18;     //0x801FA24C    base of measurement settings
  iVar3 = DAT_80027a14;     //0x80361378    base of settings like screen brightness
  puVar2 = DAT_80027a10;    //0x8019D5A0    base of scope settings

  puVar6 = address + 0x53;
  puVar8 = (undefined *)(iVar4 + -1);
  iVar7 = 0xc;

  //                                                                               bytes
  puVar2[0] = *(undefined *)address;                    //channel 1 enable         0x8035344E   0
  puVar2[3] = *(undefined *)((int)address + 2);         //          volts/div      0x80353450   1
  puVar2[4] = *(undefined *)(address + 1);              //          fft enable     0x80353452   2
  puVar2[1] = *(undefined *)((int)address + 6);         //          coupling       0x80353454   3
  puVar2[2] = *(undefined *)(address + 2);              //          magnification  0x80353456   4

  //                                                                               bytes
  puVar2[0xc] = *(undefined *)((int)address + 10);      //channel 2 enable         0x80353458   5
  puVar2[0xf] = *(undefined *)(address + 3);            //          volts/div      0x8035345A   6
  puVar2[0x10] = *(undefined *)((int)address + 0xe);    //          fft enable     0x8035345C   7
  puVar2[0xd] = *(undefined *)(address + 4);            //          coupling       0x8035345E   8
  puVar2[0xe] = *(undefined *)((int)address + 0x12);    //          magnification  0x80353460   9

  //                                                                               byte
  puVar2[10] = *(undefined *)(address + 5);             //time base                0x80353462   10

  //                                                                               byte
  puVar2[0x16] = *(undefined *)((int)address + 0x16);   //move speed               0x80353464   11

  //                                                                               bytes
  puVar2[0x21] = *(undefined *)(address + 6);           //trigger mode             0x80353466   12
  puVar2[0x22] = *(undefined *)((int)address + 0x1a);   //trigger edge             0x80353468   13
  puVar2[0x23] = *(undefined *)(address + 7);           //trigger channel          0x8035346A   14

  //                                                                               byte
  puVar2[0x42] = *(undefined *)((int)address + 0x1e);   //right menu state         0x8035346C   15


  //                                                                                    shorts
  *(undefined2 *)(puVar2 + 0x24) = *(undefined2 *)(address + 0x14);                   //0x8035349E   trigger position on screen    40
  *(undefined2 *)(puVar2 + 0x26) = *(undefined2 *)((int)address + 0x52);              //0x803534A0   trigger level screen offset   41

  //                                                                                    shorts
  *(undefined2 *)(puVar2 + 6) = *(undefined2 *)(address + 0x15);                      //0x803534A2   channel 1 screen offset       42
  *(undefined2 *)(puVar2 + 0x12) = *(undefined2 *)((int)address + 0x56);              //0x803534A4   channel 2 screen offset       43

  //                                                                                    bytes
  *(undefined *)(iVar3 + 2) = *(undefined *)(address + 0x1e);                         //0x803534C6   screen brightness             60
  *(undefined *)(iVar3 + 3) = *(undefined *)((int)address + 0x7a);                    //0x803534C8   grid brightness               61
  *(undefined *)(iVar3 + 4) = *(undefined *)(address + 0x1f);                         //0x803534CA   always 50% trigger            62
  *(undefined *)(iVar3 + 7) = *(undefined *)((int)address + 0x7e);                    //0x803534CC   xy display mode               63

  //Measurement settings
  //Channel 1 enable 12 items                                                           bytes
  *(undefined *)(iVar4 + 0x100) = *(undefined *)(address + 0x28);                     //0x803534EE   Vmax          80
  *(undefined *)(iVar4 + 0x112) = *(undefined *)((int)address + 0xa2);                //0x803534F0   Vmin          81
  *(undefined *)(iVar4 + 0x122) = *(undefined *)(address + 0x29);                     //0x803534F2   Vavg          82
  *(undefined *)(iVar4 + 0x132) = *(undefined *)((int)address + 0xa6);                //0x803534F4   Vrms          83
  *(undefined *)(iVar4 + 0x142) = *(undefined *)(address + 0x2a);                     //0x803534F6   Vpp           84
  *(undefined *)(iVar4 + 0x152) = *(undefined *)((int)address + 0xaa);                //0x803534F8   Vp            85
  *(undefined *)(iVar4 + 0x162) = *(undefined *)(address + 0x2b);                     //0x803534FA   Freq          86
  *(undefined *)(iVar4 + 0x172) = *(undefined *)((int)address + 0xae);                //0x803534FC   Cycle         87
  *(undefined *)(iVar4 + 0x182) = *(undefined *)(address + 0x2c);                     //0x803534FE   Tim+          88
  *(undefined *)(iVar4 + 0x192) = *(undefined *)((int)address + 0xb2);                //0x80353500   Tim-          89
  *(undefined *)(iVar4 + 0x1a2) = *(undefined *)(address + 0x2d);                     //0x80353502   Duty+         90
  *(undefined *)(iVar4 + 0x1b2) = *(undefined *)((int)address + 0xb6);                //0x80353504   Duty-         91

  //Channel 2 enable 12 items                                                           bytes
  *(undefined *)(iVar4 + 0x1c2) = *(undefined *)(address + 0x2e);                     //0x80353506   Vmax          92
  *(undefined *)(iVar4 + 0x1d2) = *(undefined *)((int)address + 0xba);                //0x80353508   Vmin          93
  *(undefined *)(iVar4 + 0x1e2) = *(undefined *)(address + 0x2f);                     //0x8035350A   Vavg          94
  *(undefined *)(iVar4 + 0x1f2) = *(undefined *)((int)address + 0xbe);                //0x8035350C   Vrms          95
  *(undefined *)(iVar4 + 0x202) = *(undefined *)(address + 0x30);                     //0x8035350E   Vpp           96
  *(undefined *)(iVar4 + 0x212) = *(undefined *)((int)address + 0xc2);                //0x80353510   Vp            97
  *(undefined *)(iVar4 + 0x232) = *(undefined *)(address + 0x31);                     //0x80353512   Freq          98
  *(undefined *)(iVar4 + 0x242) = *(undefined *)((int)address + 0xc6);                //0x80353514   Cycle         99
  *(undefined *)(iVar4 + 0x252) = *(undefined *)(address + 0x32);                     //0x80353516   Tim+         100
  *(undefined *)(iVar4 + 0x262) = *(undefined *)((int)address + 0xca);                //0x80353518   Tim-         101
  *(undefined *)(iVar4 + 0x272) = *(undefined *)(address + 0x33);                     //0x8035351A   Duty+        102
  *(undefined *)(iVar4 + 0x282) = *(undefined *)((int)address + 0xce);                //0x8035351C   Duty-        103

  //Channel 1 calibration data                                                          shorts
  puVar5[0] = *(undefined2 *)(address + 0x3c);                                        //0x802F18B0 = 0x8035353E  channel1_calibration_factor    120
  puVar5[1] = *(undefined2 *)((int)address + 0xf2);                                   //0x802F18B2 = 0x80353540  channel1_calibration_data[0]
  puVar5[2] = *(undefined2 *)(address + 0x3d);                                        //0x802F18B4 = 0x80353542
  puVar5[3] = *(undefined2 *)((int)address + 0xf6);                                   //0x802F18B6 = 0x80353544
  puVar5[4] = *(undefined2 *)(address + 0x3e);                                        //0x802F18B8 = 0x80353546
  puVar5[5] = *(undefined2 *)((int)address + 0xfa);                                   //0x802F18BA = 0x80353548
  puVar5[6] = *(undefined2 *)(address + 0x3f);                                        //0x802F18BC = 0x8035354A  channel1_calibration_data[5] (needs to be copied into [6]!!)

  //Channel 2 calibration data                                                          shorts
  puVar5[7] = *(undefined2 *)((int)address + 0xfe);                                   //0x802F18BE = 0x8035354C  channel2_calibration_factor    127
  puVar5[8] = *(undefined2 *)(address + 0x40);                                        //0x802F18C0 = 0x8035354E  channel2_calibration_data[0]
  puVar5[9] = *(undefined2 *)((int)address + 0x102);                                  //0x802F18C2 = 0x80353550
  puVar5[10] = *(undefined2 *)(address + 0x41);                                       //0x802F18C4 = 0x80353552
  puVar5[0xb] = *(undefined2 *)((int)address + 0x106);                                //0x802F18C6 = 0x80353554
  puVar5[0xc] = *(undefined2 *)(address + 0x42);                                      //0x802F18C8 = 0x80353556
  puVar5[0xd] = *(undefined2 *)((int)address + 0x10a);                                //0x802F18CA = 0x80353558  channel2_calibration_data[5]   133


  //Some other calibration data that still needs to be investigated
  //                                                                                    shorts
  puVar5[0xe] = *(undefined2 *)(address + 0x43);                                      //0x802F18CC = 0x8035355A   134
  puVar5[0xf] = *(undefined2 *)((int)address + 0x10e);                                //0x802F18CE = 0x8035355C
  puVar5[0x10] = *(undefined2 *)(address + 0x44);                                     //0x802F18D0 = 0x8035355E
  puVar5[0x11] = *(undefined2 *)((int)address + 0x112);                               //0x802F18D2 = 0x80353560


  //                                                                                    bytes
  *(undefined *)(puVar5 + 0x12) = *(undefined *)(address + 0x45);                     //0x802F18D4 = 0x80353562
  *(undefined *)((int)puVar5 + 0x25) = *(undefined *)((int)address + 0x116);          //0x802F18D5 = 0x80353564
  *(undefined *)(puVar5 + 0x13) = *(undefined *)(address + 0x46);                     //0x802F18D6 = 0x80353566
  *(undefined *)((int)puVar5 + 0x27) = *(undefined *)((int)address + 0x11a);          //0x802F18D7 = 0x80353568
  *(undefined *)(puVar5 + 0x14) = *(undefined *)(address + 0x47);                     //0x802F18D8 = 0x8035356A
  *(undefined *)((int)puVar5 + 0x29) = *(undefined *)((int)address + 0x11e);          //0x802F18D9 = 0x8035356C

  //                                                                                    shorts
  puVar5[0x15] = *(undefined2 *)(address + 0x48);                                     //0x802F18DA = 0x8035356E
  puVar5[0x16] = *(undefined2 *)((int)address + 0x122);                               //0x802F18DC = 0x80353570
  puVar5[0x17] = *(undefined2 *)(address + 0x49);                                     //0x802F18DE = 0x80353572
  puVar5[0x18] = *(undefined2 *)((int)address + 0x126);                               //0x802F18E0 = 0x80353574
  puVar5[0x19] = *(undefined2 *)(address + 0x4a);                                     //0x802F18E2 = 0x80353576
  puVar5[0x1a] = *(undefined2 *)((int)address + 0x12a);                               //0x802F18E4 = 0x80353578

  //                                                                                    bytes
  *(undefined *)(puVar5 + 0x1b) = *(undefined *)(address + 0x4b);                     //0x802F18E6 = 0x8035357A
  *(undefined *)((int)puVar5 + 0x37) = *(undefined *)((int)address + 0x12e);          //0x802F18E7 = 0x8035357C
  *(undefined *)(puVar5 + 0x1c) = *(undefined *)(address + 0x4c);                     //0x802F18E8 = 0x8035357E
  *(undefined *)((int)puVar5 + 0x39) = *(undefined *)((int)address + 0x132);          //0x802F18E9 = 0x80353580
  *(undefined *)(puVar5 + 0x1d) = *(undefined *)(address + 0x4d);                     //0x802F18EA = 0x80353582
  *(undefined *)((int)puVar5 + 0x3b) = *(undefined *)((int)address + 0x136);          //0x802F18EB = 0x80353584

  //                                                                                    shorts
  puVar5[0x1e] = *(undefined2 *)(address + 0x4e);                                     //0x802F18EC = 0x80353586
  puVar5[0x1f] = *(undefined2 *)((int)address + 0x13a);                               //0x802F18EE = 0x80353588
  puVar5[0x20] = *(undefined2 *)(address + 0x4f);                                     //0x802F18F0 = 0x8035358A
  puVar5[0x21] = *(undefined2 *)((int)address + 0x13e);                               //0x802F18F2 = 0x8035358C
  puVar5[0x22] = *(undefined2 *)(address + 0x50);                                     //0x802F18F4 = 0x8035358E

  uVar1 = *(undefined2 *)((int)address + 0x142);                                      //             0x80353590

  //                                                                                    short
  puVar5[0x23] = uVar1;                                                               //0x802F18F6 = 0x80353590  time cursor enable??? for what



  //                                                                                    byte
  *(char *)(iVar4 + 0x292) = (char)uVar1;                                             //0x801FA4DE = 0x80353590  time cursor enable       161

  //                                                                                    shorts
  *(undefined2 *)(iVar4 + 0x294) = *(undefined2 *)(address + 0x51);                   //0x801FA4E0 = 0x80353592  time cursor 1 position   162
  *(undefined2 *)(iVar4 + 0x296) = *(undefined2 *)((int)address + 0x146);             //0x801FA4E2 = 0x80353594  time cursor 2 position   163

  //                                                                                    byte
  *(undefined *)(iVar4 + 0x29a) = *(undefined *)(address + 0x52);                     //0x801FA4E6 = 0x80353596  volt cursor enable       164

  //                                                                                    shorts
  *(undefined2 *)(iVar4 + 0x29c) = *(undefined2 *)((int)address + 0x14a);             //0x801FA4E8 = 0x80353598  volt cursor 1 position   165
  *(undefined2 *)(iVar4 + 0x29e) = *(undefined2 *)(address + 0x53);                   //0x801FA4EA = 0x8035359A  volt cursor 2 position   166


  //12 items times 2
  //Load and store bytes
  //Source start      0x8035359C
  //Destination start 0x801FA24C
  //This is the list of enabled measurement items in the order of displaying
  do {
    iVar7 = iVar7 + -1;
    puVar8[1] = *(undefined *)((int)puVar6 + 2);
    puVar6 = puVar6 + 1;
    puVar8 = puVar8 + 2;
    *puVar8 = *(undefined *)puVar6;
  } while (iVar7 != 0);


  //System OK flag (0x1432)                                                             short
  *(undefined2 *)(puVar2 + 0x44) = *(undefined2 *)(address + 100);                    //0x8019D5E4 = 803535DE
  return;
}


This is my version of it
Code: [Select]
void scope_restore_config_data(void)
{
  uint32  channel;
  uint32  index;
  uint16 *ptr;
 
  //Restore the channel 1 settings
  scopesettings.channel1.enable        = settingsworkbuffer[0];
  scopesettings.channel1.coupling      = settingsworkbuffer[3];
  scopesettings.channel1.magnification = settingsworkbuffer[4];
  scopesettings.channel1.voltperdiv    = settingsworkbuffer[1];
  scopesettings.channel1.fftenable     = settingsworkbuffer[2];
  scopesettings.channel1.traceoffset   = settingsworkbuffer[42];

  //Restore the channel 2 settings
  scopesettings.channel2.enable        = settingsworkbuffer[5];
  scopesettings.channel2.coupling      = settingsworkbuffer[8];
  scopesettings.channel2.magnification = settingsworkbuffer[9];
  scopesettings.channel2.voltperdiv    = settingsworkbuffer[6];
  scopesettings.channel2.fftenable     = settingsworkbuffer[7];
  scopesettings.channel2.traceoffset   = settingsworkbuffer[43];
 
  //Restore trigger settings
  scopesettings.triggermode     = settingsworkbuffer[12];
  scopesettings.triggeredge     = settingsworkbuffer[13];
  scopesettings.triggerchannel  = settingsworkbuffer[14];
  scopesettings.triggerposition = settingsworkbuffer[40];
  scopesettings.triggeroffset   = settingsworkbuffer[41];
 
  //Restore the time base
  scopesettings.timeperdiv = settingsworkbuffer[10];

  //Restore the move speed
  scopesettings.movespeed = settingsworkbuffer[11];
 
  //Restore the right menu state
  scopesettings.rightmenustate = settingsworkbuffer[15];
 
  //Restore screen brightness, grid brightness, always 50% trigger and xy display mode
  scopesettings.screenbrightness = settingsworkbuffer[60];
  scopesettings.gridbrightness   = settingsworkbuffer[61];
  scopesettings.alwaystrigger50  = settingsworkbuffer[62];
  scopesettings.xymodedisplay    = settingsworkbuffer[63];
 
  //Restore the time cursor settings
  scopesettings.timecursorsenable   = settingsworkbuffer[161];
  scopesettings.timecursor1position = settingsworkbuffer[162];
  scopesettings.timecursor2position = settingsworkbuffer[163];
 
  //Restore the volt cursor settings
  scopesettings.voltcursorsenable   = settingsworkbuffer[164];
  scopesettings.voltcursor1position = settingsworkbuffer[165];
  scopesettings.voltcursor2position = settingsworkbuffer[166];

 
  //Point to the first measurement enable setting
  ptr = &settingsworkbuffer[80];
 
  //Restore the measurements enable states
  for(channel=0;channel<2;channel++)
  {
    //12 measurements per channel
    for(index=0;index<12;index++)
    {
      //Copy the current measurement state and point to the next one
      scopesettings.measuresstate[channel][index] = *ptr++;
    }
  }
 
  //Restore the calibration data
  channel1_calibration_factor = settingsworkbuffer[120];
  channel2_calibration_factor = settingsworkbuffer[127];

  //Point to the first channel calibration value
  ptr = &settingsworkbuffer[121];
 
  //Copy the saved values to the working set
  for(index=0;index<6;index++,ptr++)
  {
    //Copy the data for both channels
    channel1_calibration_data[index] = ptr[0];
    channel2_calibration_data[index] = ptr[7];
  }

  //The last entry is a copy of the fore last value
  //Different from the original code where they use a switch statement instead of an array index for getting the data
  channel1_calibration_data[6] = settingsworkbuffer[126];
  channel2_calibration_data[6] = settingsworkbuffer[133];

  //Need the system ok flag  (settingsworkbuffer[200])
 
 
}


Need to implement the counter part of it for writing to the flash and need to hook it in to the main code for testing.

When that is done it is back to analyzing and reversing code again, since all the remaining items on the to do list are still like mars. Only touched on the surface :-DD

Edit: Stupid me keeps making the copy, paste not modify errors. Forgot to change the scopesettings.channel1. to  scopesettings.channel2. :palm:
« Last Edit: October 01, 2021, 02:22:50 pm by pcprogrammer »
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1045 on: October 01, 2021, 01:57:40 pm »
Settings loading and saving to flash is implemented and working. Added the new notification confirmation setting to it.

Here are the updated lists

Implemented so far
  • System setup
  • Timer interrupt
  • Touch panel interface
  • Flash reading and writing
  • SD card functionality based on FatFs
  • FPGA interface functions
  • Display library
  • Battery charge measurement
  • Power off interrupt for settings save
  • Saving and loading of the settings to and from flash
  • User interface
    • The main screen with all the menus
    • The picture view screen with picture view almost done. (Still needs displaying of the trace data)
    • The waveform view screen with waveform view almost done. (Still needs displaying of the trace data)
    • Saving of pictures and waveforms
  • Trace data handling and displaying
    • Long time base settings 50S-100mS. (This is in roll mode and fully done)
    • Short time base settings 50mS-10nS. (This is partially implemented. Channel 1 data is fetched and displayed raw. Only part of the processing is implemented.)

Still to do
  • USB interface
  • Finish the picture and waveform single item view. Needs the trace data processing functionality
  • Trace processing and displaying for the short time bases (50mS-10nS)
  • Calibration
  • Auto set
  • Cursor measurements display
  • Measurements calculations and displaying
  • FFT calculation and displaying
  • Firmware update (This means both code within my version as creating an actual firmware update that can be loaded on a scope)

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1046 on: October 01, 2021, 04:27:42 pm »
How funny  :-DD the code for displaying the cursor measurements, which is an absolute horror :palm: can display up to GHz.

Edit: Playing with the cursor on my still original scope shows a max of 5.00GHz when the two cursors touch each other and the time per div is 10nS. Which is of course correct, but still the code they wrote for it is shit.

Code: [Select]
        if (uVar9 == 1000)
        {
LAB_80010258:
          local_80 = '1';
          local_7f = 'G';
          local_7e = 'H';
          local_7d = 'z';
          local_7c = 0;
          goto LAB_80010394;
        }

I'm not going to reverse that code |O

For the curious people out there, the output of Ghidra for this function is here: https://github.com/pecostm32/FNIRSI-1013D-Hack/blob/main/Software%20reverse%20engineering/C%20analysis/Main%20loop/trace%20data%20processing/scope_display_cursor_measurements.c

Will write my own and save time and code size. :)

Edit: there is also an error in it when both channels are disabled and the volt cursor is on. See the lower left corner in the picture. "V1 = 201V" should not be there
« Last Edit: October 01, 2021, 05:27:42 pm by pcprogrammer »
 
The following users thanked this post: serverror

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1047 on: October 02, 2021, 11:40:21 am »
Cursor measurements done.

Way simpler code then the original code. Just a case of use your brain and some functions 8)

Code: [Select]
typedef struct tagTimeCalcData          TIMECALCDATA,         *PTIMECALCDATA;
typedef struct tagVoltCalcData          VOLTCALCDATA,         *PVOLTCALCDATA;

struct tagTimeCalcData
{
  uint32 mul_factor;
  uint8  time_scale;
  uint8  freq_scale;
};

struct tagVoltCalcData
{
  uint32 mul_factor;
  uint8  volt_scale;
};

const TIMECALCDATA time_calc_data[21] =
{
  {    100, 3, 3 },         // 50mS/div
  {  40000, 2, 4 },         // 20mS/div
  {  20000, 2, 4 },         // 10mS/div
  {  10000, 2, 4 },         //  5mS/div
  {   4000, 2, 4 },         //  2mS/div
  {   2000, 2, 4 },         //  1mS/div
  {   1000, 2, 4 },         //500uS/div
  {    400, 2, 4 },         //200uS/div
  {    200, 2, 4 },         //100uS/div
  {    100, 2, 4 },         // 50uS/div
  {  40000, 1, 5 },         // 20uS/div
  {  20000, 1, 5 },         // 10uS/div
  {  10000, 1, 5 },         //  5uS/div
  {   4000, 1, 5 },         //  2uS/div
  {   2000, 1, 5 },         //  1uS/div
  {   1000, 1, 5 },         //500nS/div
  {    500, 1, 5 },         //250nS/div
  {    200, 1, 5 },         //100nS/div
  {    100, 1, 5 },         // 50nS/div
  {  50000, 0, 6 },         // 25nS/div
  {  20000, 0, 6 }          // 10nS/div
};

const VOLTCALCDATA volt_calc_data[3][7] =
{
  { {  10000, 3 },  {  5000, 3 }, {  2000, 3 }, {  1000, 3 }, {  400, 3 }, {  200, 3 }, {  100, 3 } },
  { { 100000, 3 },  { 50000, 3 }, { 20000, 3 }, { 10000, 3 }, { 4000, 3 }, { 2000, 3 }, { 1000, 3 } },
  { {   1000, 4 },  {   500, 4 }, {   200, 4 }, {   100, 4 }, {   40, 4 }, {   20, 4 }, {   10, 4 } }
};

const char *magnitude_scaler[8] = { "p", "n", "u", "m", "", "K", "M", "G"};

void scope_display_cursor_measurements(void)
{
  uint32 height = 5;
  uint32 ch1ypos = 52;
  uint32 ch2ypos = 52;
  uint32 delta;
  char   displaytext[10];
 
  //Check if need to do anything here
  if(scopesettings.timecursorsenable || (scopesettings.voltcursorsenable && (scopesettings.channel1.enable || scopesettings.channel2.enable)))
  {
    //Check if time cursor is enabled
    if(scopesettings.timecursorsenable)
    {
      //Add height for two text lines
      height += 32;
     
      //Shift the voltage text positions down
      ch1ypos += 32;
      ch2ypos += 32;
    }
   
    //Check if volt cursor is enabled
    if(scopesettings.voltcursorsenable)
    {
      //Check if channel 1 is enabled
      if(scopesettings.channel1.enable)
      {
        //Add height for one text line
        height += 16;
       
        //Shift the channel 2 voltage text down
        ch2ypos += 16;
      }
     
      //Check if channel 2 is enabled
      if(scopesettings.channel2.enable)
      {
        //Add height for one text line
        height += 16;
      }
    }
 
    //Set gray background for the cursor measurements
    display_set_fg_color(0x00404040);

    //Draw rounded rectangle depending on what is enabled.
    display_fill_rounded_rect(5, 49, 102, height, 2);

    //Use white text and font_0
    display_set_fg_color(0x00FFFFFF);
    display_set_font(&font_0);
   
    //Check if time cursor is enabled
    if(scopesettings.timecursorsenable)
    {
      //Time texts are always on the top two lines

      //Get the time delta based on the cursor positions
      delta = scopesettings.timecursor2position - scopesettings.timecursor1position;
     
      //Get the time calculation data for this time base setting. Only for the short time bases so take of the first 9
      PTIMECALCDATA tcd = (PTIMECALCDATA)&time_calc_data[scopesettings.timeperdiv - 9];
     
      //For the time multiply with the scaling factor and display based on the time scale
      delta *= tcd->mul_factor;
     
      //Format the time for displaying
      scope_print_value(displaytext, delta, tcd->time_scale, "T ", "S");
      display_text(10, 52, displaytext);
     
      //Calculate the frequency for this time. Need to adjust for stay within 32 bits
      delta /= 10;
      delta = 1000000000/ delta;
     
      //Format the frequency for displaying
      scope_print_value(displaytext, delta, tcd->freq_scale, "F ", "Hz");
      display_text(10, 68, displaytext);
    }
   
    //Check if volt cursor is enabled
    if(scopesettings.voltcursorsenable)
    {
      PVOLTCALCDATA vcd;
      uint32        volts;
     
      //Get the volts delta based on the cursor positions
      delta = scopesettings.voltcursor2position - scopesettings.voltcursor1position;
     
      //Check if channel 1 is enabled
      if(scopesettings.channel1.enable)
      {
        //Calculate the voltage based on the channel 1 settings
        vcd = (PVOLTCALCDATA)&volt_calc_data[scopesettings.channel1.magnification][scopesettings.channel1.voltperdiv];
       
        //Multiply with the scaling factor for the channel 1 settings
        volts = delta * vcd->mul_factor;
       
        //Channel 1 text has a variable position
        //Format the voltage for displaying
        scope_print_value(displaytext, volts, vcd->volt_scale, "V1 ", "V");
        display_text(10, ch1ypos, displaytext);
      }
     
      //Check if channel 2 is enabled
      if(scopesettings.channel2.enable)
      {
        //Calculate the voltage based on the channel 2 settings
        vcd = (PVOLTCALCDATA)&volt_calc_data[scopesettings.channel2.magnification][scopesettings.channel2.voltperdiv];
       
        //Multiply with the scaling factor for the channel 2 settings
        volts = delta * vcd->mul_factor;
       
        //Channel 2 text has a variable position
        //Format the voltage for displaying
        scope_print_value(displaytext, volts, vcd->volt_scale, "V2 ", "V");
        display_text(10, ch2ypos, displaytext);
      }
    }
  }
}

//----------------------------------------------------------------------------------------------------------------------------------
//Simple non optimized function for string copy that returns the position of the terminator
//----------------------------------------------------------------------------------------------------------------------------------
char *strcpy(char *dst, const char *src)
{
  while(*src)
  {
    *dst++ = *src++;
  }
 
  //Terminate the copy
  *dst = 0;
 
  return(dst);
}

//----------------------------------------------------------------------------------------------------------------------------------

void scope_print_value(char *buffer, uint32 value, uint32 scale, char *header, char *sign)
{
  //Copy the header into the string buffer
  buffer = strcpy(buffer, header);

  //Need to find the magnitude scale for the input
  //The calculations are based on fixed point
  while(value >= 100000)
  {
    //Skip to the next magnitude
    scale++;
   
    //Bring the value in range
    value /= 1000;
  }

  //Format the remainder for displaying. Only 3 digits are allowed to be displayed
  if(value < 1000)
  {
    //Less then 1000 means x.yy
    buffer = scope_print_decimal(buffer, value, 2);
  }
  else if(value < 10000)
  {
    //More then 1000 but less then 10000 means xx.y
    value /= 10;
    buffer = scope_print_decimal(buffer, value, 1);
  }
  else
  {
    //More then 10000 and less then 100000 means xxx
    value /= 100;
    buffer = scope_print_decimal(buffer, value, 0);
  }

  //Make sure scale is not out of range
  if(scale > 7)
  {
    scale = 7;
  }
 
  //Add the magnitude scaler
  buffer = strcpy(buffer, magnitude_scaler[scale]);
 
  //Add the type of measurement sign
  strcpy(buffer, sign);
}

//----------------------------------------------------------------------------------------------------------------------------------

char *scope_print_decimal(char *buffer, uint32 value, uint32 decimals)
{
  char    b[12];
  uint32  i = 12;   //Start beyond the array since the index is pre decremented
  uint32  s;

  //For value 0 no need to do the work
  if(value == 0)
  {
    //Value is zero so just set 0 character
    b[--i] = '0';
  }
  else
  {
    //Process the digits
    while(value)
    {
      //Set current digit to decreased index
      b[--i] = (value % 10) + '0';

      //Check if decimal point needs to be placed
      if(i == 12 - decimals)
      {
        //If so put it in
        b[--i] = '.';
      }
     
      //Take of the current digit
      value /= 10;
    }
  }

  //Determine the size of the string
  s = 12 - i;
 
  //Copy to the buffer
  memcpy(buffer, &b[i], s);
 
  //terminate the string
  buffer[s] = 0;
 
  //Return the position of the terminator to allow appending
  return(&buffer[s]);
}


Tried using assembly versions of strcpy and strlen, but for some reason it did not work on the target. No problem under linux with the standard library functions, so I wrote my own strcpy, that returns a pointer to the end of the destination string instead of the beginning.

Checked it with different settings and cursor positions and it looks good.

So on to the next bit.

Implemented so far
  • System setup
  • Timer interrupt
  • Touch panel interface
  • Flash reading and writing
  • SD card functionality based on FatFs
  • FPGA interface functions
  • Display library
  • Battery charge measurement
  • Power off interrupt for settings save
  • Saving and loading of the settings to and from flash
  • Cursor measurements display
  • User interface
    • The main screen with all the menus
    • The picture view screen with picture view almost done. (Still needs displaying of the trace data)
    • The waveform view screen with waveform view almost done. (Still needs displaying of the trace data)
    • Saving of pictures and waveforms
  • Trace data handling and displaying
    • Long time base settings 50S-100mS. (This is in roll mode and fully done)
    • Short time base settings 50mS-10nS. (This is partially implemented. Channel 1 data is fetched and displayed raw. Only part of the processing is implemented.)

Still to do
  • USB interface
  • Finish the picture and waveform single item view. Needs the trace data processing functionality
  • Trace processing and displaying for the short time bases (50mS-10nS)
  • Calibration
  • Auto set
  • Measurements calculations and displaying
  • FFT calculation and displaying
  • Firmware update (This means both code within my version as creating an actual firmware update that can be loaded on a scope)

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1048 on: October 03, 2021, 10:39:01 am »
Returned to the trace data processing, even though I wrote earlier I would skip it and write my own FPGA implementation.

To get a first working version of this open scope firmware, and have people test it, it is easier to stick with the original FPGA for now.

So I dove into the 25 and 10nS/div code. There are two functions per channel doing the work. Had to revert to my emulator to get to the bottom of the first function because the Ghidra output did not make it clear on what is being done.

This is the untouched C output
Code: [Select]
void scope_pre_process_ch1_25ns_data(void)
{
  undefined2 uVar1;
  int iVar2;
  int iVar3;
  uint *puVar4;
  uint *puVar5;
  uint *puVar6;
  uint *puVar7;
  uint **ppuVar8;
  uint **ppuVar9;
  byte *pbVar10;
  undefined2 *puVar11;
  uint uVar12;
  uint uVar13;
  byte bVar14;
  uint uVar15;
  int iVar16;
  short *psVar17;
  byte bVar18;
  undefined2 uVar19;
  uint *puVar20;
  uint *puVar21;
  uint uVar22;
  uint uVar23;
  uint uVar24;
  bool bVar25;
  bool bVar26;
 
  puVar7 = DAT_8000470c;
  puVar21 = DAT_80004708;
  iVar16 = 0x2e8;
  *(undefined2 *)((int)DAT_8000470c + 2) = *(undefined2 *)DAT_80004708;
  uVar19 = *(undefined2 *)((int)puVar21 + 2);
  puVar11 = (undefined2 *)((int)puVar7 + 2);
  do {
    uVar1 = *(undefined2 *)(puVar21 + 1);
    puVar11[2] = uVar19;
    uVar19 = *(undefined2 *)((int)puVar21 + 6);
    iVar16 = iVar16 + -1;
    puVar11 = puVar11 + 4;
    *puVar11 = uVar1;
    iVar3 = DAT_8000471c;
    iVar2 = DAT_80004718;
    puVar21 = puVar21 + 1;
  } while (iVar16 != 0);
  *(undefined2 *)(DAT_80004714 + 2) = *DAT_80004710;
  uVar15 = DAT_80004720;
  uVar12 = 0;
  if (*(short *)(iVar2 + 0x1c) == 0) {
    do {
      uVar13 = (uint)*(ushort *)(iVar3 + uVar12 * 2);
      if (*(ushort *)(iVar2 + 0x1e) < uVar13) {
        puVar21 = (uint *)(uVar13 - *(ushort *)(iVar2 + 0x1e));
        *(short *)(puVar7 + uVar12) = (short)puVar21;
      }
      else {
        puVar21 = puVar7 + uVar12;
        *(undefined2 *)puVar21 = 0;
      }
      uVar12 = uVar12 + 1 & 0xfffeffff;
    } while (uVar12 < uVar15);
  }
  else {
    do {
      psVar17 = (short *)(iVar3 + uVar12 * 2);
      puVar21 = puVar7 + uVar12;
      uVar12 = uVar12 + 2 & 0xfffeffff;
      *(short *)puVar21 = *psVar17 + *(short *)(iVar2 + 0x1e);
      *(short *)(puVar21 + 1) = psVar17[1] + *(short *)(iVar2 + 0x1e);
    } while (uVar12 < uVar15);
  }
  uVar12 = DAT_80004724;
  puVar5 = DAT_80004708;
  puVar7 = DAT_8000470c;
  if (3 < DAT_80004724) {
    puVar20 = (uint *)((uint)DAT_80004708 & 3);
    uVar13 = DAT_80004724;
    puVar21 = puVar20;
    if (puVar20 != NULL) {
      bVar14 = *(byte *)DAT_8000470c;
      puVar6 = (uint *)((int)DAT_8000470c + 1);
      if (puVar20 < (uint *)0x3) {
        puVar6 = (uint *)((int)DAT_8000470c + 2);
        puVar21 = (uint *)(uint)*(byte *)(uint *)((int)DAT_8000470c + 1);
      }
      puVar4 = (uint *)((int)DAT_80004708 + 1);
      *(byte *)DAT_80004708 = bVar14;
      puVar7 = puVar6;
      if (puVar20 < (uint *)0x2) {
        puVar7 = (uint *)((int)puVar6 + 1);
        bVar14 = *(byte *)puVar6;
      }
      puVar6 = puVar4;
      if (puVar20 < (uint *)0x3) {
        puVar6 = (uint *)((int)puVar5 + 2);
        *(byte *)puVar4 = (byte)puVar21;
      }
      uVar13 = (int)puVar20 + (uVar12 - 4);
      puVar5 = puVar6;
      if (puVar20 < (uint *)0x2) {
        puVar5 = (uint *)((int)puVar6 + 1);
        *(byte *)puVar6 = bVar14;
      }
    }
    uVar15 = (uint)puVar7 & 3;
    if (uVar15 == 0) {
      while (uVar12 = uVar13 - 0x20, 0x1f < uVar13) {
        uVar15 = puVar7[1];
        uVar13 = puVar7[2];
        uVar22 = puVar7[3];
        *puVar5 = *puVar7;
        puVar5[1] = uVar15;
        puVar5[2] = uVar13;
        puVar5[3] = uVar22;
        uVar15 = puVar7[4];
        uVar13 = puVar7[5];
        uVar22 = puVar7[6];
        uVar23 = puVar7[7];
        puVar7 = puVar7 + 8;
        puVar5[4] = uVar15;
        puVar5[5] = uVar13;
        puVar5[6] = uVar22;
        puVar5[7] = uVar23;
        puVar5 = puVar5 + 8;
        uVar13 = uVar12;
      }
      if ((bool)((byte)(uVar12 >> 4) & 1)) {
        uVar15 = *puVar7;
        uVar22 = puVar7[1];
        uVar23 = puVar7[2];
        uVar24 = puVar7[3];
        puVar7 = puVar7 + 4;
        *puVar5 = uVar15;
        puVar5[1] = uVar22;
        puVar5[2] = uVar23;
        puVar5[3] = uVar24;
        puVar5 = puVar5 + 4;
      }
      if ((int)(uVar13 << 0x1c) < 0) {
        uVar15 = *puVar7;
        uVar22 = puVar7[1];
        puVar7 = puVar7 + 2;
        *puVar5 = uVar15;
        puVar5[1] = uVar22;
        puVar5 = puVar5 + 2;
      }
      puVar20 = puVar5;
      puVar21 = puVar7;
      if ((bool)((byte)(uVar12 >> 2) & 1)) {
        puVar21 = puVar7 + 1;
        uVar15 = *puVar7;
        puVar20 = puVar5 + 1;
        *puVar5 = uVar15;
      }
      uVar19 = (undefined2)uVar15;
      if ((uVar12 & 3) != 0) {
        bVar26 = (bool)((byte)(uVar12 >> 1) & 1);
        uVar13 = uVar13 << 0x1f;
        bVar25 = (int)uVar13 < 0;
        puVar7 = puVar21;
        if (bVar26) {
          puVar7 = (uint *)((int)puVar21 + 2);
          uVar19 = *(undefined2 *)puVar21;
        }
        if (bVar25) {
          uVar13 = (uint)*(byte *)puVar7;
        }
        puVar21 = puVar20;
        if (bVar26) {
          puVar21 = (uint *)((int)puVar20 + 2);
          *(undefined2 *)puVar20 = uVar19;
        }
        if (bVar25) {
          *(byte *)puVar21 = (byte)uVar13;
        }
        return;
      }
      return;
    }
    uVar12 = uVar13 - 4;
    if (3 < uVar13) {
      ppuVar8 = (uint **)((int)puVar7 - uVar15);
      puVar21 = *ppuVar8;
      puVar7 = puVar5;
      if (uVar15 == 2) {
        do {
          ppuVar9 = ppuVar8;
          uVar15 = (uint)puVar21 >> 0x10;
          ppuVar8 = ppuVar9 + 1;
          puVar21 = *ppuVar8;
          bVar25 = 3 < uVar12;
          uVar12 = uVar12 - 4;
          uVar15 = uVar15 | (int)puVar21 << 0x10;
          puVar5 = puVar7 + 1;
          *puVar7 = uVar15;
          puVar7 = puVar5;
        } while (bVar25);
        puVar7 = (uint *)((int)ppuVar9 + 6);
      }
      else {
        if (uVar15 < 3) {
          do {
            ppuVar9 = ppuVar8;
            uVar15 = (uint)puVar21 >> 8;
            ppuVar8 = ppuVar9 + 1;
            puVar21 = *ppuVar8;
            bVar25 = 3 < uVar12;
            uVar12 = uVar12 - 4;
            uVar15 = uVar15 | (int)puVar21 << 0x18;
            puVar5 = puVar7 + 1;
            *puVar7 = uVar15;
            puVar7 = puVar5;
          } while (bVar25);
          puVar7 = (uint *)((int)ppuVar9 + 5);
        }
        else {
          do {
            ppuVar9 = ppuVar8;
            uVar15 = (uint)puVar21 >> 0x18;
            ppuVar8 = ppuVar9 + 1;
            puVar21 = *ppuVar8;
            bVar25 = 3 < uVar12;
            uVar12 = uVar12 - 4;
            uVar15 = uVar15 | (int)puVar21 << 8;
            puVar5 = puVar7 + 1;
            *puVar7 = uVar15;
            puVar7 = puVar5;
          } while (bVar25);
          puVar7 = (uint *)((int)ppuVar9 + 7);
        }
      }
    }
  }
  bVar18 = (byte)puVar21;
  bVar14 = (byte)uVar15;
  bVar26 = (bool)((byte)(uVar12 >> 1) & 1);
  uVar12 = uVar12 << 0x1f;
  bVar25 = (int)uVar12 < 0;
  if (bVar26) {
    pbVar10 = (byte *)((int)puVar7 + 1);
    bVar14 = *(byte *)puVar7;
    puVar7 = (uint *)((int)puVar7 + 2);
    bVar18 = *pbVar10;
  }
  if (bVar25) {
    uVar12 = (uint)*(byte *)puVar7;
  }
  if (bVar26) {
    pbVar10 = (byte *)((int)puVar5 + 1);
    *(byte *)puVar5 = bVar14;
    puVar5 = (uint *)((int)puVar5 + 2);
    *pbVar10 = bVar18;
  }
  if (bVar25) {
    *(byte *)puVar5 = (byte)uVar12;
  }
  return;
}


And this is the assembly, which tells a bit more, but still tough to figure out.
Code: [Select]
                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             void __stdcall scope_pre_process_ch1_25ns_data(void)
             void              <VOID>         <RETURN>
             undefined4        Stack[-0x18]:4 local_18                                XREF[2]:     80004614(W),
                                                                                                   800046b0(*) 
                             scope_pre_process_ch1_25ns_data                 XREF[1]:     scope_get_short_timebase_data:80
        8000460c e0 03 2d e9     stmdb      sp!,{r5 r6 r7 r8 r9}
        80004610 f0 20 9f e5     ldr        r2,[DAT_80004708]                                = 8019D5EAh
        80004614 04 40 2d e5     str        r4,[sp,#local_18]!
        80004618 b0 c0 d2 e1     ldrh       r12,[r2,#0x0]=>DAT_8019d5ea
        8000461c e8 00 9f e5     ldr        r0,[DAT_8000470c]                                = 801AEF2Ah
        80004620 ba 3f a0 e3     mov        r3,#0x2e8
        80004624 b2 c0 c0 e1     strh       r12,[r0,#0x2]=>DAT_801aef2c
        80004628 b2 c0 d2 e1     ldrh       r12,[r2,#0x2]=>DAT_8019d5ec
        8000462c 02 10 80 e2     add        r1,r0,#0x2
                             LAB_80004630                                    XREF[1]:     80004644(j) 
        80004630 b4 40 f2 e1     ldrh       r4,[r2,#0x4]!=>DAT_8019d5ee
        80004634 b4 c0 c1 e1     strh       r12,[r1,#0x4]=>DAT_801aef30
        80004638 b2 c0 d2 e1     ldrh       r12,[r2,#0x2]=>DAT_8019d5f0
        8000463c 01 30 53 e2     subs       r3,r3,#0x1
        80004640 b8 40 e1 e1     strh       r4,[r1,#0x8]!=>DAT_801aef34
        80004644 f9 ff ff 1a     bne        LAB_80004630
        80004648 c0 10 9f e5     ldr        r1,[DAT_80004710]                                = 8019E18Ch
        8000464c c0 c0 9f e5     ldr        r12,[DAT_80004714]                               = 801B066Eh
        80004650 b0 10 d1 e1     ldrh       r1,[r1,#0x0]=>DAT_8019e18c
        80004654 bc 20 9f e5     ldr        r2,[DAT_80004718]                                = 802F18B0h
        80004658 bc 50 9f e5     ldr        r5,[DAT_8000471c]                                = 8019ED5Ah
        8000465c b2 10 cc e1     strh       r1,[r12,#0x2]=>DAT_801b0670
        80004660 bc 11 d2 e1     ldrh       r1,[r2,#0x1c]=>DAT_802f18cc
        80004664 b4 30 9f e5     ldr        r3,[DAT_80004720]                                = 000005D2h
        80004668 00 00 51 e3     cmp        r1,#0x0
        8000466c 00 10 a0 e3     mov        r1,#0x0
        80004670 00 40 a0 03     moveq      r4,#0x0
        80004674 13 00 00 0a     beq        LAB_800046c8
                             LAB_80004678                                    XREF[1]:     800046ac(j) 
        80004678 be 71 d2 e1     ldrh       r7,[r2,#0x1e]=>DAT_802f18ce
        8000467c 02 60 81 e2     add        r6,r1,#0x2
        80004680 81 40 85 e0     add        r4,r5,r1, lsl #0x1
        80004684 b0 80 d4 e1     ldrh       r8,[r4,#0x0]=>DAT_8019ed5a
        80004688 01 c1 80 e0     add        r12,r0,r1, lsl #0x2
        8000468c 01 18 c6 e3     bic        r1,r6,#0x10000
        80004690 07 60 88 e0     add        r6,r8,r7
        80004694 b0 60 cc e1     strh       r6,[r12,#0x0]=>DAT_801aef2a
        80004698 b2 40 d4 e1     ldrh       r4,[r4,#0x2]=>DAT_8019ed5c
        8000469c be 61 d2 e1     ldrh       r6,[r2,#0x1e]=>DAT_802f18ce
        800046a0 03 00 51 e1     cmp        r1,r3
        800046a4 06 40 84 e0     add        r4,r4,r6
        800046a8 b4 40 cc e1     strh       r4,[r12,#0x4]=>DAT_801aef2e
        800046ac f1 ff ff 3a     bcc        LAB_80004678
                             LAB_800046b0                                    XREF[1]:     80004704(j) 
        800046b0 f0 01 bd e8     ldmia      sp!,{r4 r5 r6 r7 r8}=>local_18
        800046b4 68 20 9f e5     ldr        r2,[DAT_80004724]                                = 000016A8h
        800046b8 4c 10 9f e5     ldr        r1,[DAT_8000470c]                                = 801AEF2Ah
        800046bc 44 00 9f e5     ldr        r0,[DAT_80004708]                                = 8019D5EAh
        800046c0 04 90 9d e4     ldr        r9,[sp],#0x4
        800046c4 e4 ef ff ea     b          memcpy
                             LAB_800046c8                                    XREF[2]:     80004674(j), 80004700(j) 
        800046c8 81 c0 85 e0     add        r12,r5,r1, lsl #0x1
        800046cc b0 c0 dc e1     ldrh       r12,[r12,#0x0]=>DAT_8019ed5a
        800046d0 be 61 d2 e1     ldrh       r6,[r2,#0x1e]=>DAT_802f18ce
        800046d4 0c 00 56 e1     cmp        r6,r12
        800046d8 01 c1 80 20     addcs      r12,r0,r1, lsl #0x2
        800046dc b0 40 cc 21     strhcs     r4,[r12,#0x0]
        800046e0 03 00 00 2a     bcs        LAB_800046f4
        800046e4 be 61 d2 e1     ldrh       r6,[r2,#0x1e]=>DAT_802f18ce
        800046e8 01 71 80 e0     add        r7,r0,r1, lsl #0x2
        800046ec 06 c0 4c e0     sub        r12,r12,r6
        800046f0 b0 c0 c7 e1     strh       r12,[r7,#0x0]=>DAT_801aef2a
                             LAB_800046f4                                    XREF[1]:     800046e0(j) 
        800046f4 01 10 81 e2     add        r1,r1,#0x1
        800046f8 01 18 c1 e3     bic        r1,r1,#0x10000
        800046fc 03 00 51 e1     cmp        r1,r3
        80004700 f0 ff ff 3a     bcc        LAB_800046c8
        80004704 e9 ff ff ea     b          LAB_800046b0


This is my commented take on it.
Code: [Select]
void scope_pre_process_ch1_25ns_data(void)
{
  undefined2 uVar1;
  int iVar2;
  int iVar3;
  uint *puVar4;
  uint *puVar5;
  uint *puVar6;
  uint *puVar7;
  uint **ppuVar8;
  uint **ppuVar9;
  byte *pbVar10;
  undefined2 *puVar11;
  uint uVar12;
  uint uVar13;
  byte bVar14;
  uint uVar15;
  int iVar16;
  short *psVar17;
  byte bVar18;
  undefined2 uVar19;
  uint *puVar20;
  uint *puVar21;
  uint uVar22;
  uint uVar23;
  uint uVar24;
  bool bVar25;
  bool bVar26;
 
  puVar7 = DAT_8000470c;    //0x801AEF2A    = temptracebuffer1 + 4
  puVar21 = DAT_80004708;   //0x8019D5EA    = channel1tracebuffer1  (1500 samples from most likely adc1)
  iVar16 = 0x2e8;           //744

  //Copy a short from buffer1 to buffer2
  *(undefined2 *)((int)DAT_8000470c + 2) = *(undefined2 *)DAT_80004708;       //0x801aef2c = 0x8019d5ea

  uVar19 = *(undefined2 *)((int)puVar21 + 2);  //Get data from 0x8019d5ec
  puVar11 = (undefined2 *)((int)puVar7 + 2);   //point to next item in the temp buffer

  //Copy data from sample buffer to temp buffer while skipping a sample in the temp buffer. (Make room for another sample???)
  do
  {
    uVar1 = *(undefined2 *)(puVar21 + 1);          //0x8019d5ee
    puVar11[2] = uVar19;                           //0x801aef30  = 0x8019d5ec skip one item
    uVar19 = *(undefined2 *)((int)puVar21 + 6);    //0x8019d5f0
    iVar16 = iVar16 + -1;
    puVar11 = puVar11 + 4;
    *puVar11 = uVar1;                              //0x801aef34  = 0x8019d5ee
    puVar21 = puVar21 + 1;
  } while (iVar16 != 0);

  //Last copies done
  //0x0x801B0668 = 0x8019E18A
  //0x0x801B066C = 0x8019E18C

  iVar3 = DAT_8000471c;    //0x8019ED5A   channel1tracebuffer2  (1500 samples from most likely adc2)
  iVar2 = DAT_80004718;    //0x802F18B0   Calibration data


  *(undefined2 *)(DAT_80004714 + 2) = *DAT_80004710;         //0x801B0670 = 0x8019E18C
  uVar15 = DAT_80004720;                                     //0x000005D2 = 1490
  uVar12 = 0;

  if (*(short *)(iVar2 + 0x1c) == 0)   //0x802f18cc  Either 1 or 0 based on some calibration action
  {
    //Trace data showed this to be zero on my scope, so this loop is done
    do
    {
      uVar13 = (uint)*(ushort *)(iVar3 + uVar12 * 2);    //First sample from 0x8019ed5a

      if (*(ushort *)(iVar2 + 0x1e) < uVar13)      //0x802f18ce also some calibration data  (3 on my scope)
      {
        puVar21 = (uint *)(uVar13 - *(ushort *)(iVar2 + 0x1e));
        *(short *)(puVar7 + uVar12) = (short)puVar21;                  //puVar7 = temp trace buffer
      }
      else
      {
        puVar21 = puVar7 + uVar12;
        *(undefined2 *)puVar21 = 0;
      }

      uVar12 = uVar12 + 1 & 0xfffeffff;
    } while (uVar12 < uVar15);

    //Trace shows this
    //First samples
    //0x801AEF2A = 0x8019ED5A
    //0x801AEF2E = 0x8019ED5C
    //0x801AEF32 = 0x8019ED5E

    //Last samples
    //0x801B066A = 0x8019F8FA
    //0x801B066E = 0x8019F8FC
  }
  else
  {
    do
    {  //two samples at a time, but I miss the interleaving???? Maybe buffers or pointers defined as 32 bit items
      psVar17 = (short *)(iVar3 + uVar12 * 2);
      puVar21 = puVar7 + uVar12;
      uVar12 = uVar12 + 2 & 0xfffeffff;
      *(short *)puVar21 = *psVar17 + *(short *)(iVar2 + 0x1e);
      *(short *)(puVar21 + 1) = psVar17[1] + *(short *)(iVar2 + 0x1e);
    } while (uVar12 < uVar15);

    //Trace shows this
    //First samples
    //0x801AEF2A = 0x8019ED5A
    //0x801AEF2E = 0x8019ED5C
    //0x801AEF32 = 0x8019ED5E
    //0x801AEF36 = 0x8019ED60

    //Last samples
    //0x801B0662 = 0x8019F8F6
    //0x801B0666 = 0x8019F8F8
    //0x801B066A = 0x8019F8FA
    //0x801B066E = 0x8019F8FC

  }

  //DAT_80004724;   //0x000016A8   5800 bytes
  //DAT_80004708;   //0x8019D5EA   first buffer  destination
  //DAT_8000470c;   //0x801AEF2A   temp buffer   source
  //Copy the temp buffer back to the first trace buffer
  memcpy(DAT_80004708, DAT_8000470c, DAT_80004724);
}


It does confirm my thoughts on why they read a second buffer of 1500 bytes from the FPGA when in 25 or 10nS/Div setting. For the other time base settings they only sample using a single ADC. For the lowest two settings they also use the second ADC, but instead of having the FPGA do the interleaving of the two samples, they do it in the CPU.

That is all this first function does. Interleave the samples of the two reads. Only on the second set of data they do some calibration based action. In the code there is a check on a flag, if zero they use a subtract of a calibration value, if one they add the value. They probably measure the difference between the two ADC's and the flag is set based on which one is lower then the other.

This flag and value are determined in the calibration procedure and stored in the settings. This concerns the first four of the unknown calibration values found earlier. (2 per channel. Flag and value)

For this to make sense the two ADC's in one package have to have a possible mismatch. Need to look at the specifications.

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3623
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1049 on: October 04, 2021, 09:28:39 am »
I'm back on the part of the code I wrote about on page 39 post 950.

It is becoming a little bit clearer and managed to write a bit cleaner code that handles the data from the top reducing the number of data copies being made, but still fail to see the logic behind it.

The below code only does the first part of what the original code does. It interpolates the samples without copying the data to another buffer. (Tested this on my Linux machine)

Code: [Select]
void scope_up_sample_x_10(uint16 * buffer, uint32 count)
{
  register uint32  cnt, idx;
  register uint16 *sptr;
  register uint16 *dptr;
  register int32   sample1, sample2;
  register int32   delta;
 
  //Only do one tenth of the samples
  cnt = count / 10;
 
  //For the source point to the last sample to use
  sptr = &buffer[cnt];
 
  //For the destination point to the last result sample
  dptr = &buffer[count];
 
  //Get the first sample to use
  sample1 = *sptr--;
 
  //Process all the needed samples
  while(cnt)
  {
    //Store the first sample
    *dptr-- = sample1;
   
    //Get the second sample
    sample2 = *sptr--;
   
    //Fill in the in between samples
    //The original code uses a different approach
    //Get the samples shifted up for fractional calculations 10.22 bits
    sample1 <<= 22;
   
    //Calculate a delta step between the samples
    delta = (sample1 - (sample2 << 22)) / 10;
   
    for(idx=0;idx<9;idx++)
    {
      //Calculate the next sample with fixed point calculation
      //Since the direction is from last sample to first sample the step needs to be taken off
      sample1 -= delta;
     
      //Store the decimal part of it
      *dptr-- = sample1 >> 22;
    }
   
    //Save the second sample as the first sample
    sample1 = sample2;
   
    //One set of samples done
    cnt--;
  }
}

The same code with different offsets and another destination buffer can do the second interpolation, and it might even be possible to do the averaging in the same loop and again keep it all in one buffer, but to get an understanding I think I will have to keep it separate for now and have the output displayed as a trace on a screen.

So yet write some test software first to get to the bottom of it all.

Need to figure it out since this function is also used in the 25 and 10nS/div data processing.

So break out the popcorn and sit back, again :popcorn:


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf