Ques About Writing Blocks of Data

Hello,

Im using a SAMD21 Metro M0 board(sinze Zero isnt sold anymore) setup to sample A1, @ 100kHz samplerate. And collect 0.5 seconds worth of data and store in extra SRAM chip.

But to keep it simple say ,im trying take 25k samples(each sample is 1 byte) from my micro-controllers ADC channel A1, and write this data to an extra SRAM chip, I hooked up. But when writing this data to the extra SRAM chip, I miss out on data since it takes time to "write data to SRAM".....Silly I know.

So my question:
Is there a way to collect data in background, while its writing data to extra SRAM chip,so I dont lose ADC values? Or is there a better way about collecting this data.

sounds like you want to read the ADC in an Interrupt service routine triggered by a timer.

assume someone can explain how to trigger an ISR on a timer.

Good idea! Been years since I called a ISR...ill play with this.

arduino interrupt

Hrmmm confused. Say I know my SRAM.writeblock() function takes 10mS. And I want to record 10mS while its using the SRAM.writeblock() function.....the problem im having is WHEN to use/call interrupt to collect ADC during this process....

Say I call ISR and collects 10mS of ADC
Then call SRAM.writeblock() function within same loop

Does this mean itll collect the 10mS of data WHILE my SRAM.writeblock() function is next in line? Am I understanding this correctly? Been awhile so....

there are 2 buffers commonly called ping/pong buffers.

The ISR does nothing but collect samples and puts them into one of the ping/pong buffers. when the ping buffer is filled it starts filling pong. when pong is filled it starts filling ping again

the code in loop() is waiting for the buffer currently being filling by the ISR to be filled. when it is, it writes it while the ISR is filling the pong register.

hope you understand the ISR is filling one buffer while loop() code writes the other buffer.

How do you address the extra RAM ? (What chip?)

Gotcha, what you just explained will solve exactly what I need to do. Ill try coding and filling 1 buffer in loop() and once full, write to SRAM, and sametime once buffer 1 is full , start filling other buffer 2. Make sense I hope? In your comment you said, "the code in loop() is waiting for the buffer currently being filling by the ISR to be filled. when it is, it writes it".

gcjr:
there are 2 buffers commonly called ping/pong buffers.

The ISR does nothing but collect samples and puts them into one of the ping/pong buffers. when the ping buffer is filled it starts filling pong. when pong is filled it starts filling ping again

the code in loop() is waiting for the buffer currently being filling by the ISR to be filled. when it is, it writes it while the ISR is filling the pong register.

hope you understand the ISR is filling one buffer while loop() code writes the other buffer.

J-M-L:
How do you address the extra RAM ? (What chip?)

I address my SRAM 4 times using a 25k bytes of data in 1 buffer. So address1 = 0,address2 = 25000,address3 = 50000,address4 = 75000. I use the writeBlock() function.

And I use 23LCV1024 SRAM chip with this library: GitHub - mattairtech/SRAM_23LC: Driver for Microchip Technology Inc. 23LC (23LCV) SPI SRAM chips for AVR, SAM3X (Due), and SAM M0+ (SAMD, SAML, SAMC) microcontrollers on the Arduino platform.

OK, buffering and block write definitely the way to go to benefit from extra performance.

the SAMD21 Metro M0 board has a 2Mb SPI flash memory chip built into the board that you can access using the Adafruit SPI Flash library and there is also Marzogh's SPIMemory library

may be worth giving it a try to see if that works for you? (and no need for extra hardware)

You are correct! But I already designed a PCB with my SRAM chips -_- And thanks, nice to know I have options at least! Ill let you all know,just started writing the code.

Hello, after going down the rabbit hole about timer interrupts and reading datasheet and other forum posts, have some questions?

So issue:
Im missing data when im writing a block of data to my SRAM chip.And after testing, time to write block of data to SRAM ,it takes 65mS.Used a simple millis() function for this.And my ADC read time from registers takes 10uS(100kHz).

Where im at so far:
Ill use TC4(Timer Counter) on SAMD21 and Nested Vector Interrupt Controller(NVIC) as timer interrupt to trigger a compare/match interrupt every X seconds to fill buffer1. Once buffer1 is full begin filling buffer2 inside ISR. While filling buffer2,main loop writes buffer1 to SRAM.

The question I have is how do I make sure I am always getting a value from my ADC when ISR is called? If I try to ask ISR to get current ADC value and fill buffer,and ISR only takes 2uS to execute,wont my ADC value not be able to occur? Or do I need to write my code in such a way that after leaving ISR it always get a ADC value and checks if its time to write to SRAM.

New to this ping/pong buffer and Ive found some help from this thread:
https://forum.arduino.cc/index.php?topic=332275.15
A thread about using timer interrupts on SAMD21

not familiar with your ADC and what is possible

but one approach is to read the ADC when the ISR occurs and trigger the next conversion before leaving the ISR

gcjr:
not familiar with your ADC and what is possible

but one approach is to read the ADC when the ISR occurs and trigger the next conversion before leaving the ISR

When you say" read the ADC when the ISR occurs", you mean inside of the main loop before it enters ISR??,and then trigger next ADC value before ISR is finished?

Or do you mean get ADC value in ISR,and before I leave begin next ADC value.

And thanks for replying and using your time on poor little me.

presumably the ISR occurs at the sampling rate you want. i'm suggesting reading the previously converted ADC sample and triggering the next conversion both in the ISR. hope this makes sense.

however, i don't know if there are Arduino routines to do this. based on S23.2 of the Atmel328P datasheet, it looks like it's possible

f I try to ask ISR to get current ADC value and fill buffer,and ISR only takes 2uS to execute,wont my ADC value not be able to occur?

i do wonder if you have enough MIPS to sample at 10 usec. how long does the ISR take (time to enter, process and exit)?

out of curiosity, what kind of signal are you sampling? what is it's frequency (i.e. Nyquist)?

gcjr:
presumably the ISR occurs at the sampling rate you want. i'm suggesting reading the previously converted ADC sample and triggering the next conversion both in the ISR. hope this makes sense.

however, i don't know if there are Arduino routines to do this. based on S23.2 of the Atmel328P datasheet, it looks like it's possible

i do wonder if you have enough MIPS to sample at 10 usec. how long does the ISR take (time to enter, process and exit)?

out of curiosity, what kind of signal are you sampling? what is it's frequency (i.e. Nyquist)?

Ah ok,have both inside ISR,gotcha.And setup ISR to execute at certain frequency.I was just trying to wrap my head around what frequency it should be and the time the ISR occurs. I plan on playing with the code today since I have some time.Im sure im just overthinking this. But my signals can range from 1Hz->24kHz. I can dumb down my sampling frequency to 50kHz instead of sampling at 100kHz,since Nyquist is 48kHz for my signals.And I havent yet tested how long it takes my ISR to execute, still have to setup the actual process of timer interrupt,plan on doing today.

Heres my simple ping/pong code setup for TC4 in 8bit mode,with ADC setup for 100kHz,2 channels.Without my SRAM/SD code. I was wondering what anyone thought,or if you see an error.Just want to make sure I have this ping/pong done.

Because when I try to incorporate my SRAM and SD code my results are useless. I feed a 1khz sinewave from my function generator into my inputs and goal is to see a 1kHz sinewave at output recorded onto SD card.

Questions:

Next step to figure out, would be to find out how long my main loop takes while writing to SRAM,and how long my ISR takes. And since you cant use delay inside ISR, would just toggling a pin HIGH/LOW using PORT manipulation be sufficient(I use this method to find ADC samplerate) hooked up to my oscilloscope? Or is this a bad idea?

And I was thinking,would I even need my extra SRAM chips if the ping/pong buffer makes sure I dont lose data? And then just write block of data to SD, X amount of times?

I attached code,since it says I reached max length for forum.But ISR stores current ADC sample in backBuffer,if backBuffer isnt full,start next ADC value.Once backBuffer is full,signal to main loop to swap backBuffer to activeBuffer,and wait till backBuffer is full again. Ill use my activeBuffer to write to SRAM or SD or whatever.In main loop.

ISR_PING_PONG.ino (10.2 KB)

not clear why your your SD card data is useless without seeing the data. does the data within a block look correct and the block skip data or is the data with an SD block from an ping/pong buffer bad.

i had mentioned that there may not be enough MIPs for sampling at 100 kHz (10 usec). at 16 Mhz, this is just 160 instructions, and that may be too few for just the ISR.

evaluating system performance is not uncommon. one approach is to count the number of iterations of loop() with some period of time (1.000 sec). from this you can determine how long loop() takes.

Then introduce an ISR which at leasts counts the number of ISR iterations with the same period of time. With the ISR active, the loop() count should drop. from previous number and the number with the ISR enabled, you should be able estimate how long the ISR takes.

i'm confused about the purpose of the SRAM. are you using it to buffer data between the ping/pong buffers and SD card?

i'll look over your code

it looks like the code is excessive and not necessarily doing things in the right order.

based on the comments, waiting for a conversion to complete instead of using that time to write to SD ram is not efficient.

              ADC->INTFLAG.bit.RESRDY = 1;
                // Data ready flag cleared
                while  (ADC->STATUS.bit.SYNCBUSY == 1)
                    ;

                ADC->SWTRIG.bit.START = 1;
                // Start ADC conversion
                while  ( ADC->INTFLAG.bit.RESRDY == 0 )
                    ;
                // Wait till conversion done
                while  (ADC->STATUS.bit.SYNCBUSY == 1)
                    ;

                ADCvalue = ADC->RESULT.reg;

I assume you're triggering the interrupt on a timer that should provide more than enough time for a conversion. Unless you think there is a problem the ISR should assume the conversion is complete, immediately start the next conversion and write the data to the ping/pong buffer. (are those wait loops really necessary)?

if the system is designed correctly, there no need to check if the conversion is ready and there's no need to break the adc value into bytes.

          tempByte =  (ADCvalue >> 8) & 0xFF;

if the adc value is 16 bits the ping/pong buffers should be uint16_t and write the adc value directly from the register to the buffer and handle wrap. after reading/storing the adv value, start the next conversion -- i think that's all the ISR should do.

    pingPong [ind++] =  ADC->RESULT.reg;
    ind = BUF_SIZE == ind ? 0 : ind;

the ping/pong buffers should be the size in bytes of the block to be written to SD. if the SD block size is 512 bytes, each ping/pong buffer should be 256 uint16_t. but you should really just have a single buffer (see below)

#define PINGPONG_SIZE 256
#define BUF_SIZE   2*PINGPONG_SIZE
      uint16_t   pingPong [BUF_SIZE];

there's no need for the ISR to indicate when a ping/pong is full and there's no need to check if there's space. (if there isn't, something is wrong)

          //signal to main loop,buffer is full
            if  (ind >= 512) {
                state = !state;

                captureActive = 0;
                ind = 1;
            }

            //If buffer isnt full start next ADC

            if  (ind < 512) {

if the system is designed properly, there's enough time to write an SD block before a ping/pong buffer is filled. loop(), not the ISR should do any admin work. loop() can monitor the ind to determine when a ping/pong buffer is full. of course loop() will take longer when writing to SD

   if (ping && PINGPONG_SIZE <= ind)  {
        ping = !ping;
        writeSdBuf (& pingPong [0], SD_BLK_SIZE);
    }
    else if (! ping && PINGPING_SIZE > ind)  {
        ping = !ping;
        writeSdBuf (& pingPong [PINGPONG_SIZE], SD_BLK_SIZE);
    }

of course none of the above is tested. i hope it makes sense. and it needs to be that simple to get done with as few MIPS as possible

can't tell you how many times i felt what I needed to do was basically as succinct as above, only to get lost and add tons of code to figure things out and then finally sort out my misunderstandings and (basically) confirm what I originally thought but ended up with a lot of debug code.

Thank you for replying, and when I mean my output data on SD card was useless,it just meant it didnt make any sense,looked nothing like a sinewave.

if the system is designed correctly, there no need to check if the conversion is ready and there's no need to break the adc value into bytes.

You are correct, if I only write to SD. But if I write to SRAM chip, they need to be 8bit....But once I get this figured out I shouldnt even need SRAM,shame since I designed my PCB to incorporate them -_-,I come from the hardware side of things,and using matlab too much, so apologies for my dumb code.

I agree with you if I set it up correctly I shouldnt have to wait for ADC conversion,but this line:

ind = BUF_SIZE == ind ? 0 : ind;

Am I reading this correctly?
If BUF_SIZ == ind,then ind = 0,if BUF_SIZ doesnt equal ind,then it equals ind.

Ill try to use your code when I have a chance tomorrow, I know it hasent been tested, but it seems all I need to do it setup a "volatile ind" to increment buffer,and a "bool ping" to toggle between ping/pong inside main loop. Id like to setup the writeSdbuf() function later. But thanks,been very helpful!