|
|
View previous topic :: View next topic |
Author |
Message |
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
Simultaneous Access of FRAM Problem |
Posted: Mon Apr 02, 2007 12:47 pm |
|
|
3.236 18F6627
Hello All,
I'm reading and storing an ADC value in FRAM in an INT_AD ISR. The conversion is started using the special event feature of the ECCP2 as suggested by Ttelmah.
My problem is this, I need to be able to pull data from the FRAM for processing in a large chunk (1024bytes). I was having problems and finally figured out that the ISR was interrupting the transfer of the data... duhhh.
I guess I need to write a routine to deal with all the issues that this raises. Is there an example to help me or a term to search for that might get me started?
My first approach would be to abandon the sequential read from FRAM. That obviously won't work because of the time involved and the need for the ISR to store values every ~4mS. I could use a while loop and a single value FRAM read routine. I'd have to disable the INT_AD prior to the read and re-enable just after. I'm wondering how bad that would bugger up my timing in reading the signal from the ADC...
Any ideas or suggestions would be greatly appreciated,
John |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Mon Apr 02, 2007 1:27 pm |
|
|
I don't know if this approach will be sufficient for your needs....
If there is a flag/bit you can test for, then this becomes quite easy. What you need to know is if the A/D conversion has been initiated. I know that the A/D's GO/DONE bit in the ADCON register will indicate this, but if this bit is only set for a very short period of time it's likely that you'll miss the event entirely.
My thinking goes like this. The FRAM read routine will be of the form
Code: | while (!done || !busy) {
// do reads here
if (busy) { // this bit is set by the ECCP2 - it signifies that an A/D
// conversion is now in progress, so you should save what address
// you're now at, and terminate the read so that you can exit the
// FRAM read 'cleanly'
}
// you also have a test to see if you've read all 1024 bytes - if so
// done is now true to exit from the while loop
} |
When the A/D conversion is internally triggered, if a read is in progress, then this type of code structure should pick that up and kick itself out of the read, hopefully before the RA interrupt gets triggered. When the RA interrupt is finished executing, the while loop should be automatically re-entered because 'done' isn't true yet. Just ensure that the first time through the loop the code calls a read from 'address' - which was either set to 0 or was set to whereever the routine left off.
If the GO/DONE bit is set for a relatively long time - say it stays set until the processor actually completes an A/D conversion, then this approach will probably work. |
|
|
treitmey
Joined: 23 Jan 2004 Posts: 1094 Location: Appleton,WI USA
|
|
Posted: Mon Apr 02, 2007 1:36 pm |
|
|
..edited..
my post is not applicable as this is SPI and
not I2C..
see below
Last edited by treitmey on Tue Apr 03, 2007 2:40 pm; edited 3 times in total |
|
|
sjbaxter
Joined: 26 Jan 2006 Posts: 141 Location: Cheshire, UK
|
|
Posted: Mon Apr 02, 2007 2:03 pm |
|
|
I don't suppose you wired up the HOLD pin of the FRAM to an I/O pin on the pic.
If you did, you could put the FRAM in HOLD in the INT_AD and release it at the end of the interrupt routine. Theoretically, as the ISR kicks in, the FRAM read function gets halted (as the context is switched to the ISR), the FRAM current operation gets suspended and when the ISR is complete, the FRAM is released and the context switches back to the point in the read function where it was before. That's what the HOLD pin is designed for.
If you haven't wired the HOLD pin .... ignore all the above !! ;-)
It doesn't help for writing the value of the AD you have just read, but it does deal with how to handle the interruption a large read (or write) operation. _________________ Regards,
Simon. |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Mon Apr 02, 2007 3:14 pm |
|
|
Guys,
Thanks for the fantastic ideas. I'll digest them this evening when I have a chance. A couple of notes in the mean time...
Sorry I forgot to include it: FM25256 SPI chip.
Newguy... I think I'll end up with your approach.
Treitmey... Good idea, but not sure if I want to move control of AD reads back and forth between the interrupt and the fram_read function. I'll look at it more deeply.
Simon... I didn't implement it, BUT I have several pins left over and could possibly do it. I need to do some more reading and see if it will work.
Thanks again,
John |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Mon Apr 02, 2007 6:45 pm |
|
|
Here is my first go at it. I can't test it yet, but I thought I'd share it and see if anyone has any thoughts on my approach.
Code: | //continue until all the data is transferred to the holding array
while(holding_array_index < N)
{
//if not currently making an AD conversion
//and there's enough time left to read another value
if(!GO_DONE && (get_timer3() < 20000))
{
//read the value from FRAM
holding_array[holding_array_index] = read_int16_fram(starting_fram_addr);
//increment to the next holding array location
holding_array_index++;
//increment to the next FRAM location
starting_fram_addr += 2;
}
} |
I couldn't think of any other way to track whether another conversion was coming up. The TIMER_3 value is 39025, so I start with about 1/2 duty cycle for reading from FRAM. I was going to tweak it up until I had problems and then reduce it a little. Not very scientific, but I don't know how to calculate the overhead to make it as precise as possible.
Simon,
It's looking like your approach makes the most sense. I think I'm going to try the previous approach and get it working, then I'll try yours.
EDIT: Doesn't look like that will work after doing some reading.
Thanks again,
John |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 03, 2007 2:17 am |
|
|
I'd suggest sybchronising the 'read' operation to the interrupt.
Have a global 'bit' field. Call it something like 'clear_to_go'.
Then in the interrupt, have a line like:
Then in the routine to read the data block, have:
Code: |
clear_to_go=false;
while(!clear_to_go) ;
//When you get here, an int_ad, will have _just_ happened, and there
//should be time to read a block
|
I use this approach, to synchronise a 'main' operation, to a PWM, for similar reasons.
Best Wishes |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Apr 03, 2007 8:03 am |
|
|
RJ,
I think I see what you are doing, but I still have a couple of questions.
For your approach to work, I assume that the data block needs to be a fixed and known size... obviously small enough to be read between interrupts.? How would you determine how large of a block to read? I'm going to test it with a couple get_timer3()'s on each side and see how long a single read takes, and then do the math and add some padding....
Was the ! a typo in your example? Wouldn't it be:
Code: | int_ad
clear_to_go = TRUE;
data_transfer()
{
while(data_needs_to_be_transferred)
{
clear_to_go = FALSE;
while(clear_to_go)
{
read_a_fixed_size_block_here
break;
}
}
} |
Thanks,
John
EDIT:
I see that my 'while' won't work. I think I see how yours works, now.... duhh. |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 03, 2007 9:11 am |
|
|
FRAM reads, should be pretty fast. Normally limited by the clock speed of the interface, and how long it takes to transfer 8 bits. Now if (for instance), you are using a normal I2C unit, at 400Hz, then you only send the initialisation once, and the reads can be performed sequentially. I'd have expected to be able to move perhaps 20000+ bytes/second (including the overheads in accessing a target array etc.). So 1024 bytes, should only need perhaps 1/20th second. If this is too long, then instead transfer a smaller block, and do repeated transfers the same way.
Glad you worked out my 'logic'...
Best Wishes |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Apr 03, 2007 9:25 am |
|
|
Using part at 40MHz, SPI FRAM at clock_div4.
Running the timer tests now and see that I can get about 512 bytes transferred between the interrupts in sequential transfer. Interrupts are every 1/256 of a second.
Looks like it will work great after I finish swapping everything over to your method.
Thanks a bunch,
John |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|