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

engineer.r152, facekim and 8 Guests are viewing this topic.

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1000 on: September 05, 2021, 11:56:25 am »
There may be gain when the SD card plus interface is fast enough. But I doubt that when the card is doing 24MB/s there will gain. In the original code they go up to 48MHz for the card clock if the card can handle it and to a 4 bit data bus. The transfer into the FIFO register is 32bits wide, so the overhead for the cpu to do the transfer won't be that high. The MMC-SD interface in the F1C100s is the limiting factor here.

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5802
  • Country: es
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1001 on: September 05, 2021, 12:32:58 pm »
This is complex and depends a lot on your application.
When reading from an external device that's much slower that your cpu, if you can do something else while fetching the data, it will boost the peformance noticeably.
Ex, if you could compute the FFT while fetching new data from the sd card.

If you're stuck until you get the data, it won't make any difference.
For copying ram to ram, it should be faster as the cpu doesn' have to access the bus to move the data.

Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1002 on: September 05, 2021, 12:40:52 pm »
If you're stuck until you get the data, it won't make any difference.

And that was my point in the first place.

As stated before the goal is to get a working version that emulates the functionality of the original code. Big improvements are for a later stage.

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5802
  • Country: es
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1003 on: September 05, 2021, 01:01:29 pm »
Also, if you read from the SD/spi/i2c device, usually you have to read one data at a time: start, wait for the flag, read the buffer, and repeat n times until filling the buffer.
I don't know if the f1C100s supports that, but DMA can often handle the peripheral, ex. you can set the SPI/I2C address and read 2KB in a single shot.
So in the end it'll be faster anyways.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1004 on: September 05, 2021, 06:40:09 pm »
It took a bit of fiddling due to mismatching code, but the first part of the SD card works.

Low level initialization of the card is implemented and working. Without a card inserted the red "SD ERROR" text is displayed, with a card inserted it continues to display the scope trace :)

Had a hard time finding source code to go by so needed to reverse the mid level card layer from the original code, while I used the sunxi-mmc.c code from u-boot for the low level layer. This resulted in the response words not being in the right spot |O

Code used in the sunxi-mmc.c file looks like it swaps the response registers for long responses (136 bit)
Code: [Select]
if (cmd->resp_type & MMC_RSP_136)
  {
cmd->response[0] = readl(&priv->reg->resp3);
cmd->response[1] = readl(&priv->reg->resp2);
cmd->response[2] = readl(&priv->reg->resp1);
cmd->response[3] = readl(&priv->reg->resp0);

debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n", cmd->response[3], cmd->response[2], cmd->response[1], cmd->response[0]);
}
  else
  {
cmd->response[0] = readl(&priv->reg->resp0);
debug("mmc resp 0x%08x\n", cmd->response[0]);
}

In the initialization code from the mid level layer a check is done to see if the card is ready. This was against the wrong bit, but did not gave problems until the publish rca command (cmd03) was send.
Code: [Select]
    //Send some initialization commands until the card is ready?????
    do
    {
      //Send application specific command follows command to the card
      command.cmdidx    = 55;
      command.cmdarg    = 0;
      command.resp_type = 5;
      sd_card_send_command(&command, 0);

      //Send host capacity support information command
      command.cmdidx    = 41;
      command.cmdarg    = 0x40FF8000;                      //Need to figure out these settings
      command.resp_type = 7;
      result = sd_card_send_command(&command, 0);
     
    //0 means still initializing 
    } while((command.response[3] & 0x80000000) == 0);

So I was a bit at a loss here and tried with displaying info on the screen to find the problem. Could not find it, and went through the Physical Layer Simplified Specification Version 8.00 to see what was going on. Was until I looked at the original send_command function of the scope that the mismatch occurred to me.
Code: [Select]
    cmd->response[0] = puVar6[8];
    cmd->response[1] = puVar6[9];

    if ((cmd->resp_type & 2) != 0)
    {
      cmd->response[2] = puVar6[10];
      cmd->response[3] = puVar6[11];
    }

After changing this in my version of the code it worked straight away :-DD

The SD card code that is around on the net did not provide a lot of help, because most of it is even less readable then what Ghidra made of it. :palm:
The STM32 version for arduino uses HAL and success in finding what that does. The linux code is similar, with all the driver layering. Did not look at the arduino spi version, but previous experience with arduino stuff did not make me happy either.

All this software is nice if you just want to make something with it on a dedicated target for it, but trying to learn from it jeeezzz.

So on to the next bit of the SD card code. Still lots more to do since it only does the card identification for now.

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1005 on: September 08, 2021, 09:54:58 am »
Filled in the remainder of the initialization code and tested with a couple of different cards. The card information is retrieved and decoded.

Now match it all up to FatFs latest version and try to read and write some stuff to a card.

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1006 on: September 11, 2021, 03:29:22 pm »
Quote
Hello, is it me you're looking for?   8)

not a lot of progression due to other chores, lack of energy and thunderstorms :palm:

But today a good day and managed to get the FatFs module hooked in. Found the remainder of the functions that hook the FatFs functions to the actual disk layer. Still a lot of cleanup and reformatting to do, to make it more readable, and it might need a tweak on the configuration. The f_mount function is working though. Testing is a bit tedious since it requires swapping the SD card, so I think I will flash my test scope with the boot loader that starts FEL. That way I can leave the original SD card in the system and develop and test the remainder of the file stuff.

This means the saving and loading of the pictures and wave-forms. Also quite a bit of code, but with most of the FatFs functions identified it should go smooth :)

The SD card stuff might also be needed for the USB functionality since that routes it to the PC. Not sure how that is done, so yet another quest ;)

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1007 on: September 13, 2021, 01:44:09 pm »
There is a bit more to it then I hoped |O

Found what the four .sys on the SD card are used for, but need to plow through a lot of code to get to the nitty gritty.

The big files hold the thumbnails seen when the picture or waveform view is opened. (piclist.sys and wavelist.sys which are loaded with the load_selected_list_file function)

The smaller files hold file numbers in the order they are written. (pic_system.sys and wave_system.sys which are loaded with the load_selected_system_file function)

On a save 998 shorts are shifted up to the next slot freeing up the first slot for the new file number. To find the first free file number they try to open the files starting from 1.bmp or 1.wav up to 999.bmp or 999.wav. So if there are many files saved it will take longer to find a free file.

If all slots are used it looks like it will write over the last one. Still have to find the function that does the actual save, so nut sure if the file name will be 1000.bmp (1000.wav) or 999.bmp or 0.bmp.

Another weird bit is what they do when switched to a view mode. They save the current scope state, including the trace data, but on exit of a view mode they switch to auto trigger mode and restart the scope with the saved settings, overwriting the restored trace data, so they could have skipped that.

The whole system feels very much like a quick thrown together bit of software to get the device out on the market. They could have done with an expert code review to improve on the quality, but that probably is to expensive :-DD

Ah well back to the grindstone :)

Offline tv84

  • Super Contributor
  • ***
  • Posts: 3211
  • Country: pt
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1008 on: September 13, 2021, 02:54:59 pm »
but that probably is to expensive :-DD

Not so. You do that for them for free and without source code!  :clap:
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1009 on: September 13, 2021, 03:12:04 pm »
You got me there :-DD

If they are reading this thread and use my findings they should consider making my life easier and fork over the sources 8)

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1010 on: September 14, 2021, 08:43:10 am »
Again a nice sample of the sloppy programming I run into.

In the function "handle_view_mode_touch()" they wait for touch to be released with the while loop, and then to make sure touch is released, they check it again in the "wait_for_touch_release()" function.
Code: [Select]
    if (uVar2 < 0x50)
    {
      cVar1 = *PTR_DAT_80022bb8;  //0x80192f02

      while (cVar1 != '\0')       //Wait for touch release
      {
        tp_i2c_read_status();
        cVar1 = *puVar3;
      }

      wait_for_touch_release();
      return 1;   //Return button touched
    }

Which also has to much code for the job. The first if is not needed since the do while does the same.
Code: [Select]
void wait_for_touch_release(void)
{
  undefined *puVar1;
 
  tp_i2c_read_status();

  puVar1 = PTR_DAT_8002b120;       //0x80192f02

  if (*PTR_DAT_8002b120 == '\0')   //0 means no touch
  {
    return;
  }

  do
  {
    tp_i2c_read_status();
  } while (*puVar1 != '\0');      //not 0 means touch

  return;
}

My version of the function that works perfectly in what I have written so far.
Code: [Select]
void tp_i2c_wait_for_touch_release(void)
{
  //Wait until touch is released
  while(havetouch)
  {
    //Read the touch panel status
    tp_i2c_read_status();
  }
}

To me this all means that they employ programmers who do not have the slightest idea of what they are doing :palm:

Offline 1audio

  • Frequent Contributor
  • **
  • Posts: 304
  • Country: us
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1011 on: September 16, 2021, 12:04:28 am »
If they opened the code and offered it as a platform similar to some of the phone projects there would be a lot of opportunities as display devices. I would really like a stand alone low frequency FFT analyzer. The current FFT in it is borderline useless. I suspect there are other special cases that do not need extended bandwidth.
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1012 on: September 16, 2021, 05:42:28 am »
If they opened the code and offered it as a platform similar to some of the phone projects there would be a lot of opportunities as display devices. I would really like a stand alone low frequency FFT analyzer. The current FFT in it is borderline useless. I suspect there are other special cases that do not need extended bandwidth.

I'm well on my way in opening it up. I have identified the FFT function in the Ghidra archive but dit not reverse it yet.

The semi reversed scope code will need a do over once all the parts have been done, because it is a bit messy due to the way it is coming together.

And I agree with you that it could make a nice audio frequency analyzer. The 8 bits is a bit low for good snr, but doing things like peak detection for the separate frequencies should be doable.

One problem with the current FPGA setup is to obtain a larger number of samples. So for higher resolution FFT the FPGA needs to be rewritten. Something I have on the agenda, but it will take time since that is a new playing field, even though I did FPGA stuff in the 1990's. The principle will be the same, but the capabilities are multiple.

Despite its limitations, all in all this scope is a nice play object :)

All my work can be found here: https://github.com/pecostm32/FNIRSI-1013D-Hack

Offline robca

  • Frequent Contributor
  • **
  • Posts: 257
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1013 on: September 18, 2021, 05:58:00 pm »
Not sure if you know, but just in case: ARM has a highly optimized DSP library, including FFT (it's in the CMSIS). It's optimized for cores with native FP functionality but also for M0 and M3 cores without native FP

When I looked at it, even if the code didn't look super-optimized, I discovered that even a minor change would decrease performance, since the developers must have relied on compiler optimization and optimal registry usage and loop unrolling

When you get to looking at the FFT, I'm pretty sure that you will find the CMSIS implementation better than whatever the original code does  ;D
 
The following users thanked this post: I wanted a rude username, pcprogrammer

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1014 on: September 19, 2021, 09:59:59 am »
Finally some progress again.

after fixing where I f'ed up in FatFs, the loading of the .sys files is happening and the thumbnails are being displayed.

Had two issues with FatFs. Default configuration is set for dos file names and "pic_system.sys" or "wave_system.sys" does not comply with that. Due to this f_open failed with "FR_INVALID_NAME". Since it did open "piclist.sys" it was quickly clear I needed LFN support enabled.

After fixing that it hung on f_read. This took a bit longer to find. It did work with a read of 511 bytes, but not with 512. Tested my low level sd_card_read with multiple blocks and that worked without problems. Then I realized I cleaned up the FatFs code and fixed the no no I mentioned in a previous post. So I went back to the original and started to compare what I changed.

Turns out that I missed a continue that was being used in a for loop. That behaves differently for a while loop, and that is what I changed the code to. So fixed by changing the continue into a goto.


The original FatFs f_read code, where there is no check on the *br pointer not being NULL
Code: [Select]
/*-----------------------------------------------------------------------*/
/* Read File                                                             */
/*-----------------------------------------------------------------------*/

FRESULT f_read (
FIL* fp, /* Open file to be read */
void* buff, /* Data buffer to store the read data */
UINT btr, /* Number of bytes to read */
UINT* br /* Number of bytes read */
)
{
FRESULT res;
FATFS *fs;
DWORD clst;
LBA_t sect;
FSIZE_t remain;
UINT rcnt, cc, csect;
BYTE *rbuff = (BYTE*)buff;


*br = 0; /* Clear read byte counter */
res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
remain = fp->obj.objsize - fp->fptr;
if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */

for ( ; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */
if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
if (csect == 0) { /* On the cluster boundary? */
if (fp->fptr == 0) { /* On the top of the file? */
clst = fp->obj.sclust; /* Follow cluster chain from the origin */
} else { /* Middle or end of the file */
#if FF_USE_FASTSEEK
if (fp->cltbl) {
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
} else
#endif
{
clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */
}
}
if (clst < 2) ABORT(fs, FR_INT_ERR);
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
fp->clust = clst; /* Update current cluster */
}
sect = clst2sect(fs, fp->clust); /* Get current sector */
if (sect == 0) ABORT(fs, FR_INT_ERR);
sect += csect;
cc = btr / SS(fs); /* When remaining bytes >= sector size, */
if (cc > 0) { /* Read maximum contiguous sectors directly */
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect;
}
if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
#if FF_FS_TINY
if (fs->wflag && fs->winsect - sect < cc) {
memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
}
#else
if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
}
#endif
#endif
rcnt = SS(fs) * cc; /* Number of bytes transferred */
continue;
}
#if !FF_FS_TINY
if (fp->sect != sect) { /* Load data sector if not in cache */
#if !FF_FS_READONLY
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= (BYTE)~FA_DIRTY;
}
#endif
if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
}
#endif
fp->sect = sect;
}
rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */
if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */
#if FF_FS_TINY
if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
#else
memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
#endif
}

LEAVE_FF(fs, FR_OK);
}


My version of the code
Code: [Select]
//----------------------------------------------------------------------------------------------------------------------------------
//Read File 
//
//Return:
//  A FRESULT value
//
//Input:
//  Pointer to the file structure
//  Buffer to store the read data
//  Number of bytes to read
//  Pointer to a varialble to return the number of bytes read in
//
//----------------------------------------------------------------------------------------------------------------------------------
FRESULT f_read(FIL* fp, void* buff, UINT btr, UINT* br)
{
  FRESULT res;
  FATFS *fs;
  DWORD clst;
  LBA_t sect;
  FSIZE_t remain;
  UINT rcnt, cc, csect;
  BYTE *rbuff = (BYTE*)buff;

  //Check if the input parameters are valid
  if(!fp || !buff)
    return(FR_INVALID_OBJECT);

  //Clear read byte counter when given
  if(br)
    *br = 0;

  //Check validity of the file object
  res = validate(&fp->obj, &fs);

  //Check validity
  if(res != FR_OK || (res = (FRESULT)fp->err) != FR_OK)
    LEAVE_FF(fs, res);

  //Check access mode
  if(!(fp->flag & FA_READ))
    LEAVE_FF(fs, FR_DENIED);

  //Calculate how many bytes are left in the file
  remain = fp->obj.objsize - fp->fptr;

  //Truncate btr by remaining bytes
  if(btr > remain)
    btr = (UINT)remain;

  //Repeat until btr bytes read
  while(btr > 0)
  {
    //On the sector boundary?
    if((fp->fptr % SS(fs)) == 0)
    {
      //Sector offset in the cluster
      csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));

      //On the cluster boundary?
      if(csect == 0)
      {
        //On the top of the file?
        if(fp->fptr == 0)
        {
          //Follow cluster chain from the origin
          clst = fp->obj.sclust;
        }
        //Middle or end of the file
        else
        {
#if FF_USE_FASTSEEK
          if(fp->cltbl)
          {
            //Get cluster# from the CLMT
            clst = clmt_clust(fp, fp->fptr);
          }
          else
#endif
          {
            //Follow cluster chain on the FAT
            clst = get_fat(&fp->obj, fp->clust);
          }
        }

        //cluster in wrong place??
        if(clst < 2)
          ABORT(fs, FR_INT_ERR);

        //Cluster out of range??
        if(clst == 0xFFFFFFFF)
          ABORT(fs, FR_DISK_ERR);

        //Update current cluster
        fp->clust = clst;
      }

      //Get current sector
      sect = clst2sect(fs, fp->clust);

      //Invalid sector??
      if(sect == 0)
        ABORT(fs, FR_INT_ERR);

      sect += csect;
      cc = btr / SS(fs);

      //When remaining bytes >= sector size,
      if(cc > 0)
      {
        //Read maximum contiguous sectors directly
        //Clip at cluster boundary
        if((csect + cc) > fs->csize)
        {
          cc = fs->csize - csect;
        }

        if(disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK)
          ABORT(fs, FR_DISK_ERR);

#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2
//Replace one of the read sectors with cached data if it contains a dirty sector
#if FF_FS_TINY
        if(fs->wflag && ((fs->winsect - sect) < cc))
        {
          memcpy((rbuff + ((fs->winsect - sect) * SS(fs))), fs->win, SS(fs));
        }
#else
        if((fp->flag & FA_DIRTY) && ((fp->sect - sect) < cc))
        {
          memcpy((rbuff + ((fp->sect - sect) * SS(fs))), fp->buf, SS(fs));
        }
#endif
#endif
        //Number of bytes transferred
        rcnt = SS(fs) * cc;
       
        //Continue after update of counters and pointers. Original code uses for loop and continue
        goto read_update;
      }

#if !FF_FS_TINY
      //Load data sector if not in cache
      if(fp->sect != sect)
      {
#if !FF_FS_READONLY
        if(fp->flag & FA_DIRTY)
        {
          //Write-back dirty sector cache
          if(disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK)
            ABORT(fs, FR_DISK_ERR);

          fp->flag &= (BYTE)~FA_DIRTY;
        }
#endif
        //Fill sector cache
        if(disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK)
          ABORT(fs, FR_DISK_ERR);
      }
#endif
      fp->sect = sect;
    }

    //Number of bytes remains in the sector
    rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);

    //Clip it by btr if needed
    if(rcnt > btr)
      rcnt = btr;

#if FF_FS_TINY
    //Move sector window
    if(move_window(fs, fp->sect) != FR_OK)
      ABORT(fs, FR_DISK_ERR);

    //Extract partial sector
    memcpy(rbuff, (fs->win + (fp->fptr % SS(fs))), rcnt);
#else
    //Extract partial sector
    memcpy(rbuff, (fp->buf + (fp->fptr % SS(fs))), rcnt);
#endif

    //Update counters and pointers
read_update:   
    btr -= rcnt;
    rbuff += rcnt;
    fp->fptr += rcnt;

    //Return bytes read, only when variable is given
    if(br)
     *br += rcnt;
  }

  LEAVE_FF(fs, FR_OK);
}

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


Attached are two pictures of what my version shows. This is test code the scope starts with directly. Still have to hook it into the main menu and write the code for handling all the file actions.
Made some changes.
  • The original code uses a two pixel wide line. My code uses a single pixel wide line for now.
  • Added a check to see if there is a thumbnail available. The original code suggest there can be a mismatch between the two files used for the list data.

My code differs quite a bit from the original since that is so inefficient with a lot of unnecessary copying of data. It took quite a bit of time to work through all of it to get a proper understanding of what was going on.

My binary version of the code, which is far from finished, but already has a fair bit of the functionality of the original is now 183KB in size. The original binary is 1.6MB. Sure part of this is from not using the bitmaps, but still.

Offline frenky

  • Supporter
  • ****
  • Posts: 1003
  • Country: si
    • Frenki.net
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1015 on: September 19, 2021, 02:02:36 pm »
Tnx for your effort I really appreciate it. Few days ago I was using my 1013D (for the second time since I got it) and I could not find how to change time scale.
I threw away user manual with packaging so I had to turn to youtube reviews to see how other do it... (tap on the left or right side of screen).   :palm:
My intuition told me to just pinch and zoom in horizontal or vertical direction to change desired scale, but no...
« Last Edit: September 19, 2021, 02:05:42 pm by frenky »
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1016 on: September 19, 2021, 03:23:15 pm »
Hi Frenky,

yes that part is not that intuitive. The touch panel is capable of multi touch, but implementing such a pinch feature takes some investigation. A simpler change would be to make a drop down menu for it.

But first step is to get a version of the code that mimics how the original works. That can then be improved upon, but for me the step after this first code reversal is to make my own FPGA implementation to improve on some other short comings.

For example, see if the pwm to control the screen brightness can be done on a higher frequency. This because the second scope I got has an even louder and very irritating beep when the brightness is turned down. |O

One change I have to make to the code I have now is add an item to the main menu, that allows for changing the orientation of the touch panel. I used the original binary via FEL a couple of days ago to setup some test pictures and waveforms, to discover, "Oh yes and shit, changed the touch panel on this scope because it was broken and needs the modified orientation"  :palm:

Otherwise it is not usable for anyone who has a standard version of the scope :-DD

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1017 on: September 20, 2021, 05:10:16 pm »
I was looking at the code to display the pictures on the screen and found that they tried to speed things up.

When you need to save a picture that can be viewed on a computer, but also on your own system, the easiest solution would be to create a bitmap with your screen data and when it is displayed on your own system load it back onto the screen. (creating a .png could be better, but is more of a software effort)

They did it differently, created a bitmap header, copied in the scope settings and trace data and after that the bitmap is copied from the screen. When displayed on the own system it just reads back the settings and trace data and uses that to recreate the original screen.

The read back is faster since only 15000 bytes are read, but with the rest of the code being very inefficient it probably won't make a huge difference.

Where they messed up is that in the header the file size is wrong. Set to 0x000C5FF8 (811000) while the file is only 0x00C3500 (800000) bytes. Not a big problem, but they also use the special parameter ic to translate some parameter that is used as the offset to the pixel data. But this offset is not used when they copy in the pixel data, so there can be a problem there.

Why the images come out wrong on my system is still a bit of a mystery.

Edit: Found why the image goes wrong on my system. It expects the pixel array directly after the header. The offset in the header is ignored. Not sure what windows does with it, but both my linux machines (Mint 20 and Ubuntu 16) do it wrong. :palm:

Edit: Tried it on virtualbox running windows xp and that does display the image correctly, so it is linux bitmap handling that sucks.
« Last Edit: September 20, 2021, 06:28:22 pm by pcprogrammer »
 

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1018 on: September 21, 2021, 03:39:57 pm »
Analyzed the setup and trace data in the .bmp and .wav files. Even though they save and load 15000 bytes only 10000 are used.

The first 1000 bytes are used for the settings and measurements. They use shorts for everything, even if a setting is just a byte.
In the below code segment is the code Ghidra made of the first part of the save_system_setup function with my comments on what is what.

Code: [Select]
  //Data is saved as shorts.

  *(ushort *)buffer = (ushort)pbVar2[0x3a];                        //run mode

  *(ushort *)((int)buffer + 2) = (ushort)*pbVar2;                  //channel 1 enable
  *(ushort *)((int)buffer + 4) = (ushort)pbVar2[3];                //volts/div
  *(ushort *)((int)buffer + 6) = (ushort)pbVar2[4];                //fft enable
  *(ushort *)((int)buffer + 8) = (ushort)pbVar2[1];                //coupling
  *(ushort *)((int)buffer + 10) = (ushort)pbVar2[2];               //magnification

  *(ushort *)((int)buffer + 0xc) = (ushort)pbVar2[0xc];            //channel 2 enable
  *(ushort *)((int)buffer + 0xe) = (ushort)pbVar2[0xf];            //volt/div
  *(ushort *)((int)buffer + 0x10) = (ushort)pbVar2[0x10];          //fft enable
  *(ushort *)((int)buffer + 0x12) = (ushort)pbVar2[0xd];           //coupling
  *(ushort *)((int)buffer + 0x14) = (ushort)pbVar2[0xe];           //magnification

  *(ushort *)((int)buffer + 0x16) = (ushort)pbVar2[10];            //time base setting

  *(ushort *)((int)buffer + 0x18) = (ushort)pbVar2[0x16];          //move speed

  *(ushort *)((int)buffer + 0x1a) = (ushort)pbVar2[0x21];          //trigger mode
  *(ushort *)((int)buffer + 0x1c) = (ushort)pbVar2[0x22];          //trigger edge
  *(ushort *)((int)buffer + 0x1e) = (ushort)pbVar2[0x23];          //trigger channel

  *(ushort *)((int)buffer + 0x20) = (ushort)pbVar2[0x38];          //battery charge level. Is used to display the state of when picture or waveform was taken

  *(ushort *)((int)buffer + 0x22) = (ushort)pbVar2[0x42];          //right menu mode

  *(ushort *)((int)buffer + 0x24) = (ushort)pbVar2[0x18];          //triggerflag2
  *(ushort *)((int)buffer + 0x26) = (ushort)pbVar2[0x17];          //triggerflag1

  *(undefined2 *)((int)buffer + 0x28) = *(undefined2 *)(pbVar2 + 0x1a);  //disp_x_start.

  puVar10 = PTR_DAT_80002024;    //0x80192ece
  iVar21 = DAT_8000201c;         //0x80361378

  if (pbVar2[10] < 9)        //time base setting. Long time base values 50S - 100mS
  {
    sVar4 = *(short *)PTR_DAT_80002014 + -1;    //0x80192eaa  (disp_xpos)
  }
  else                       //short time base settings. 50mS - 10nS
  {
    sVar4 = *(short *)(pbVar2 + 0x1c);           //disp_sample_count
  }

  *(short *)((int)buffer + 0x2a) = sVar4;   //time base dependent save of sample count info


  iVar25 = DAT_80002020;                     //0x801FA24C  base address of measurement settings

  puVar30 = (ushort *)((int)buffer + 0x12e); //index into the buffer

  *(undefined2 *)((int)buffer + 0x2c) = *(undefined2 *)PTR_DAT_80002018;  //0x80192ec4  State flag for getting start+end in sample buffers???

  pbVar15 = (byte *)(iVar25 + -1);          //0x801FA24B

  sVar4 = 0xc;

  *(undefined2 *)((int)buffer + 0x2e) = *(undefined2 *)puVar10;              //0x80192ece
  *(undefined2 *)((int)buffer + 0x30) = *(undefined2 *)PTR_DAT_80002028;     //0x80192ebc
  *(undefined2 *)((int)buffer + 0x32) = *(undefined2 *)PTR_DAT_8000202c;     //0x80192ebe

  *(ushort *)((int)buffer + 0x34) = (ushort)pbVar2[0xb];                     //copy of time base settings
  *(ushort *)((int)buffer + 0x36) = (ushort)pbVar2[0x43];                    //view mode

  *(undefined2 *)((int)buffer + 0x50) = *(undefined2 *)(pbVar2 + 0x24);      //trigger pos on screen
  *(undefined2 *)((int)buffer + 0x52) = *(undefined2 *)(pbVar2 + 0x26);      //trigger level screen offset

  *(undefined2 *)((int)buffer + 0x54) = *(undefined2 *)(pbVar2 + 6);         //channel 1 trace offset

  *(undefined2 *)((int)buffer + 0x56) = *(undefined2 *)(pbVar2 + 0x12);      //channel 2 trace offset

  *(ushort *)((int)buffer + 0x78) = (ushort)*(byte *)(iVar21 + 2);           //screen brightness
  *(ushort *)((int)buffer + 0x7a) = (ushort)*(byte *)(iVar21 + 3);           //grid brightness
  *(ushort *)((int)buffer + 0x7c) = (ushort)*(byte *)(iVar21 + 4);           //always trigger 50%
  *(ushort *)((int)buffer + 0x7e) = (ushort)*(byte *)(iVar21 + 7);           //x-y display mode

                                                                             //measurements enable channel 1
  *(ushort *)((int)buffer + 0xa0) = (ushort)*(byte *)(iVar25 + 0x100);       //vmax
  *(ushort *)((int)buffer + 0xa2) = (ushort)*(byte *)(iVar25 + 0x112);       //vmin
  *(ushort *)((int)buffer + 0xa4) = (ushort)*(byte *)(iVar25 + 0x122);
  *(ushort *)((int)buffer + 0xa6) = (ushort)*(byte *)(iVar25 + 0x132);
  *(ushort *)((int)buffer + 0xa8) = (ushort)*(byte *)(iVar25 + 0x142);
  *(ushort *)((int)buffer + 0xaa) = (ushort)*(byte *)(iVar25 + 0x152);
  *(ushort *)((int)buffer + 0xac) = (ushort)*(byte *)(iVar25 + 0x162);
  *(ushort *)((int)buffer + 0xae) = (ushort)*(byte *)(iVar25 + 0x172);
  *(ushort *)((int)buffer + 0xb0) = (ushort)*(byte *)(iVar25 + 0x182);
  *(ushort *)((int)buffer + 0xb2) = (ushort)*(byte *)(iVar25 + 0x192);
  *(ushort *)((int)buffer + 0xb4) = (ushort)*(byte *)(iVar25 + 0x1a2);
  *(ushort *)((int)buffer + 0xb6) = (ushort)*(byte *)(iVar25 + 0x1b2);       //duty-

                                                                             //measurements enable channel 2
  *(ushort *)((int)buffer + 0xb8) = (ushort)*(byte *)(iVar25 + 0x1c2);       //vmax
  *(ushort *)((int)buffer + 0xba) = (ushort)*(byte *)(iVar25 + 0x1d2);
  *(ushort *)((int)buffer + 0xbc) = (ushort)*(byte *)(iVar25 + 0x1e2);
  *(ushort *)((int)buffer + 0xbe) = (ushort)*(byte *)(iVar25 + 0x1f2);
  *(ushort *)((int)buffer + 0xc0) = (ushort)*(byte *)(iVar25 + 0x202);
  *(ushort *)((int)buffer + 0xc2) = (ushort)*(byte *)(iVar25 + 0x212);
  *(ushort *)((int)buffer + 0xc4) = (ushort)*(byte *)(iVar25 + 0x232);
  *(ushort *)((int)buffer + 0xc6) = (ushort)*(byte *)(iVar25 + 0x242);
  *(ushort *)((int)buffer + 200) = (ushort)*(byte *)(iVar25 + 0x252);
  *(ushort *)((int)buffer + 0xca) = (ushort)*(byte *)(iVar25 + 0x262);
  *(ushort *)((int)buffer + 0xcc) = (ushort)*(byte *)(iVar25 + 0x272);
  *(ushort *)((int)buffer + 0xce) = (ushort)*(byte *)(iVar25 + 0x282);        //duty-

//Channel 1 measured values????
  *(short *)((int)buffer + 0xd0) = (short)((uint)*(undefined4 *)(iVar25 + 0x104) >> 0x10);
  *(short *)((int)buffer + 0xd2) = (short)*(undefined4 *)(iVar25 + 0x104);
  *(short *)((int)buffer + 0xd4) = (short)((uint)*(undefined4 *)(iVar25 + 0x114) >> 0x10);
  *(short *)((int)buffer + 0xd6) = (short)*(undefined4 *)(iVar25 + 0x114);
  *(short *)((int)buffer + 0xd8) = (short)((uint)*(undefined4 *)(iVar25 + 0x124) >> 0x10);
  *(short *)((int)buffer + 0xda) = (short)*(undefined4 *)(iVar25 + 0x124);
  *(short *)((int)buffer + 0xdc) = (short)((uint)*(undefined4 *)(iVar25 + 0x134) >> 0x10);
  *(short *)((int)buffer + 0xde) = (short)*(undefined4 *)(iVar25 + 0x134);
  *(short *)((int)buffer + 0xe0) = (short)((uint)*(undefined4 *)(iVar25 + 0x144) >> 0x10);
  *(short *)((int)buffer + 0xe2) = (short)*(undefined4 *)(iVar25 + 0x144);
  *(short *)((int)buffer + 0xe4) = (short)((uint)*(undefined4 *)(iVar25 + 0x154) >> 0x10);
  *(short *)((int)buffer + 0xe6) = (short)*(undefined4 *)(iVar25 + 0x154);
  *(short *)((int)buffer + 0xe8) = (short)((uint)*(undefined4 *)(iVar25 + 0x164) >> 0x10);
  *(short *)((int)buffer + 0xea) = (short)*(undefined4 *)(iVar25 + 0x164);
  *(short *)((int)buffer + 0xec) = (short)((uint)*(undefined4 *)(iVar25 + 0x174) >> 0x10);
  *(short *)((int)buffer + 0xee) = (short)*(undefined4 *)(iVar25 + 0x174);
  *(short *)((int)buffer + 0xf0) = (short)((uint)*(undefined4 *)(iVar25 + 0x184) >> 0x10);
  *(short *)((int)buffer + 0xf2) = (short)*(undefined4 *)(iVar25 + 0x184);
  *(short *)((int)buffer + 0xf4) = (short)((uint)*(undefined4 *)(iVar25 + 0x194) >> 0x10);
  *(short *)((int)buffer + 0xf6) = (short)*(undefined4 *)(iVar25 + 0x194);
  *(short *)((int)buffer + 0xf8) = (short)((uint)*(undefined4 *)(iVar25 + 0x1a4) >> 0x10);
  *(short *)((int)buffer + 0xfa) = (short)*(undefined4 *)(iVar25 + 0x1a4);
  *(short *)((int)buffer + 0xfc) = (short)((uint)*(undefined4 *)(iVar25 + 0x1b4) >> 0x10);
  *(short *)((int)buffer + 0xfe) = (short)*(undefined4 *)(iVar25 + 0x1b4);


//Channel 2 measured values????
  *(short *)((int)buffer + 0x100) = (short)((uint)*(undefined4 *)(iVar25 + 0x1c4) >> 0x10);
  *(short *)((int)buffer + 0x102) = (short)*(undefined4 *)(iVar25 + 0x1c4);
  *(short *)((int)buffer + 0x104) = (short)((uint)*(undefined4 *)(iVar25 + 0x1d4) >> 0x10);
  *(short *)((int)buffer + 0x106) = (short)*(undefined4 *)(iVar25 + 0x1d4);
  *(short *)((int)buffer + 0x108) = (short)((uint)*(undefined4 *)(iVar25 + 0x1e4) >> 0x10);
  *(short *)((int)buffer + 0x10a) = (short)*(undefined4 *)(iVar25 + 0x1e4);
  *(short *)((int)buffer + 0x10c) = (short)((uint)*(undefined4 *)(iVar25 + 500) >> 0x10);
  *(short *)((int)buffer + 0x10e) = (short)*(undefined4 *)(iVar25 + 500);
  *(short *)((int)buffer + 0x110) = (short)((uint)*(undefined4 *)(iVar25 + 0x204) >> 0x10);
  *(short *)((int)buffer + 0x112) = (short)*(undefined4 *)(iVar25 + 0x204);
  *(short *)((int)buffer + 0x114) = (short)((uint)*(undefined4 *)(iVar25 + 0x214) >> 0x10);
  *(short *)((int)buffer + 0x116) = (short)*(undefined4 *)(iVar25 + 0x214);
  *(short *)((int)buffer + 0x118) = (short)((uint)*(undefined4 *)(iVar25 + 0x234) >> 0x10);
  *(short *)((int)buffer + 0x11a) = (short)*(undefined4 *)(iVar25 + 0x234);
  *(short *)((int)buffer + 0x11c) = (short)((uint)*(undefined4 *)(iVar25 + 0x244) >> 0x10);
  *(short *)((int)buffer + 0x11e) = (short)*(undefined4 *)(iVar25 + 0x244);
  *(short *)((int)buffer + 0x120) = (short)((uint)*(undefined4 *)(iVar25 + 0x254) >> 0x10);
  *(short *)((int)buffer + 0x122) = (short)*(undefined4 *)(iVar25 + 0x254);
  *(short *)((int)buffer + 0x124) = (short)((uint)*(undefined4 *)(iVar25 + 0x264) >> 0x10);
  *(short *)((int)buffer + 0x126) = (short)*(undefined4 *)(iVar25 + 0x264);
  *(short *)((int)buffer + 0x128) = (short)((uint)*(undefined4 *)(iVar25 + 0x274) >> 0x10);
  *(short *)((int)buffer + 0x12a) = (short)*(undefined4 *)(iVar25 + 0x274);
  *(short *)((int)buffer + 300) = (short)((uint)*(undefined4 *)(iVar25 + 0x284) >> 0x10);
  *(short *)((int)buffer + 0x12e) = (short)*(undefined4 *)(iVar25 + 0x284);

      //Copy 12 * 2 values. This is the list of enabled measurements to show them on the display
  do  //puVar30 = (ushort *)((int)buffer + 0x12e);
  {
    sVar4 = sVar4 + -1;
    puVar30[1] = (ushort)pbVar15[1];    //Initial pVar15 = //0x801FA24B
    pbVar15 = pbVar15 + 2;
    puVar30 = puVar30 + 2;
    *puVar30 = (ushort)*pbVar15;
  } while (sVar4 != 0);

  *(ushort *)((int)buffer + 400) = (ushort)pbVar2[0x39];                      //charging indicator
  *(ushort *)((int)buffer + 0x192) = (ushort)pbVar2[0x38];                    //battery charge level

  *(undefined2 *)((int)buffer + 0x194) = *(undefined2 *)PTR_DAT_80002030;     //0x80192ec6
  *(undefined2 *)((int)buffer + 0x196) = *(undefined2 *)PTR_DAT_80002034;     //0x80192ec8


After the settings is the trace data. This is processed data from the last sample buffers (channel1tracebuffer4 and channel2tracebuffer4 in my code) and display data from buffers where the scope saves the y coordinate of every sample displayed. (channel1ypoints and channel2ypoints in my code)

The buffers are little endian shorts (LSB, MSB)
  • Offset 1000 - 1500 samples of channel 1 trace data
  • Offset 4000 - 1500 samples of channel 2 trace data
  • Offset 7000 - 750 y coordinates of channel 1 display data
  • Offset 8500 - 750 y coordinates of channel 2 display data

From the settings two items are used to display the trace data.
  • buffer + 0x28 is the x start position. Can be > 0 when a picture is made of a zoomed waveform view
  • buffer + 0x2a is the number of samples or y coordinates that are available

There are still settings that have no clear meaning.

Already started with the loading part of the code and skip a lot of memory copy the original code does. Just read the data in separate chunks into the needed buffers instead of reading all the data into a big buffer and then copy it to where it needs to be.

But to fully implement the picture and waveform view I have to finish the code for trace displaying, because that is also used when viewing items.

So it is circle back to that part of the code before finishing the view bit.

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1019 on: September 23, 2021, 08:01:15 am »
Found another oversight / error. :-DD

For waveform view the state of the measurements are ignored. This means that when the waveform view is selected and an item is opened the measurements enabled will be the ones enabled before opening the waveform view, and not the ones that where enabled at the time of the waveform capture.

Not a big deal, but why :-//

In the code there is a specific "if" to check which view type is selected. For picture view the states are loaded, for waveform not. The measured values on the other hand are loaded for both types.

In my version I will load the states for both types.

Online Kean

  • Supporter
  • ****
  • Posts: 2042
  • Country: au
  • Embedded systems & IT consultant
    • Kean Electronics
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1020 on: September 23, 2021, 11:51:35 am »
Sounds like the programmer tried to include a useful feature, but made a mistake in implementation.
As there was probably no formal specification for the functionality, the tester (if there was one) didn't know what to test and thus didn't spot the implementation error.
 
The following users thanked this post: pcprogrammer

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1021 on: September 23, 2021, 01:22:41 pm »
Big chance there was no formal tester to check it all.

There are many faults in this code, that could easily be the result of missing formal specifications.

Another one I detected while testing the functionality of the view modes, is that when the power is turned off while the scope is in a view mode the settings in the flash are corrupted. Guess they forgot to add an "if" statement there. :-DD

The most obvious sign of this is that the traces are shifted to other positions on the screen. Mostly towards the bottom.

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1022 on: September 25, 2021, 01:38:53 pm »
Bummer, things grounded to a halt. |O

Was working on the delete functionality of the view items and discovered things in FatFs are not working, and I have no idea what is wrong.

At first I thought it might be the not yet initializing of the BSS data, but after implementing that it still fails.

Despite the FF_FS_READONLY define being set to 0 to make a read/write file system, it won't write to a file, create a file or even delete a file.

Selecting a file for reading and then reading from it works without problems. Reverted back to the original unmodified code, but no dice. Problem remains.

I have to see if I can create an emulation project on my linux machine to test the FatFs code and try to find the problem. :box:

Online pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3574
  • Country: nl
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1023 on: September 25, 2021, 03:19:04 pm »
Many many posts ago, in a galaxy far far away :) someone questioned me writing in this thread. I responded it helped me, and I'll be darned it did.

After writing the previous post it hit me I did not try doing a file delete at startup. Did try a f_open with a create. So in the startup code direct after the f_mount call I added a f_unlink and I noticed it took longer to start, but I did not get an error on the delete. The file was still on the SD card. Hmmm, so I looked at the SD card layer and noticed I again made a copy, paste, not modify error.  :palm:

I wrote the read function first and copied that for the write function, but forgot to change the data type from SD_DATA_READ to SD_DATA_WRITE. And that's all folks as the well known comics say.

It does what it needs to do now.

Wasted good part of the day on this, but that is the price you pay for making stupid mistakes |O

Offline pickle9000

  • Super Contributor
  • ***
  • Posts: 2438
  • Country: ca
Re: FNIRSI-1013D "100MHz" tablet oscilloscope
« Reply #1024 on: September 25, 2021, 03:38:47 pm »
Perseverance!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf