SD_CARD Reader (How to read the line that I want) FatFs SAM4E-Ek

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone,

 

My problem is that I have a SAM4E-EK chip from ATMEL and my goal is to great a text file and upload it to a sd card and then read from the sd card. 

I have manage to open the file read the contents of the file and then print it on the LCD, but what I really want is to read every line separately and print it on different locataion on my LCD. 

Or perhaps more challenging will be to read specific sting for and example:

 

Hello, what to you thing about the elections in America?

Who do you think will be the next president of USA?

 

Imagining the above written text is in my sd text file and the string that I want to get and display are the underline ones.

Currently I'm using a FatFs library. My code:

 

/*Register the system object in logical drive*/
            memset(&fs, 0, sizeof(FATFS));
            f_mount(LUN_ID_SD_MMC_0_MEM, &fs);
            /*Open or Create a file on the drive*/
            res = f_open(&file_object, "ReadThis.txt",FA_OPEN_ALWAYS | FA_READ);
            /*Sometime, open file operation was failed, so try again once*/
            if (res) res = f_open(&file_object, "ReadThis.txt",FA_OPEN_ALWAYS | FA_READ);
            /*Write header to the log file*/
            
        //    TempChar[0] = f_gets(Buff, file_object, 300);
        //     f_read(&file_object, Buff, 1000, &bw);
            f_gets((char*)CharBuffer,80,&file_object);
            f_gets(Buff2,80, &file_object);
    
            ili93xx_set_foreground_color(COLOR_RED);
            ili93xx_draw_string(110, 80, (uint8_t *) Buff1);
            ili93xx_draw_string(110, 110, (uint8_t *) Buff1);

 

So please !! I'll be very great full  if you give some hints or perhaps some examples. THANKS!!!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But how do you plan to find those underlined words? Is it just that you want to print the 8th word on the first line and the 11th word from the second line? Or do you specifically want to search for "elections" or "USA"? Or what? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the fast reply!

 

Basically the text file will be some kind of log file. so 

 

Name of the machine ________

Type                          ________

voltage                      ________

Max Temp                  ________

Min Temp                  ________ 

 

What I want here is to take the content of the underline, but I was wondering also if there will be any problem is I store integers as well. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For logging I would recommend you use Comma Separated Values (CSV): https://en.wikipedia.org/wiki/Co...

 

That's exactly what the name suggests. So in your case something like:

Name of the machine, Type, voltage, Max Temp, Min Temp

Alpha, AVR, 5.3, 28, 17
Bravo, ARM, 3.1, 27, 14
Charlie, MIPS, 1.8, 31, 15
...

If you now read a line then you can easily pick out the items by searching for the commas. For example if you read the line:

Bravo, ARM, 3.1, 27, 14

and know the Max Temp you just search for the 3rd comma and read the value that follows this.

 

CSV is great because it's both human and computer readable and what's more Excel will read it as an input format so if you want to analyse this data in a spreadsheet (perhaps pick out the lowest of all the Min Temp's say) then you can easily load it and process it there too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the hint!!

 

Is it possible to be done with FatFs? because I have manage to open the file and read the content and display is but , again I read all the string does not matter that they separated with comma. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok course it's possible with FatFs. It's a full blown filing system and can achieve pretty much everything you can do with fopen()/fread()/fwrite()/etc. from <stdio.h> on a PC. Talking of which - it is 1,000,000 times easier to develop all this stuff using a PC and <stdio.h> amd reading/writing files on your PC hard drive. When you understand the data processing you need to do (things like strchr(), strtok(), atoi(), atof() and so on) then you can take all you learn in the easy to use PC environment and then implement it on your AVR. Sure the FatFs function interfaces are a little different to <stdio.h> but the concepts (fopen, fread, fgets, fseek, etc) are all the same things so it's just a question of semantics.

 

In what you want to do I probably would fgets() to get an entire line at a time. That will get you something like:

Bravo, ARM, 3.1, 27, 14

To pick the "27" out of that you might use strtok(). That is a standard C function that will take a single string and cut it into chunks based on some separator character (',' in this case). Or you could do the more manual method:

char line[200];

fgets(line, 200, filein);
char * p;
p = strchr(line, ','); // find the first ',' (after "Bravo,")
p++; // skip that ','
p = strchr(p, ','); // find the second ',' (after "ARM,")
p++; // skip that ','
p = strchr(p, ','); // find the third ',' (after "3.1,")
p++; // and skip that 3rd one then..
int n = atoi(p); // now n = 27

To test whether this (admittedly cumbersome code!) works you can test this on a PC:

$ cat data.csv
Alpha, AVR, 5.3, 28, 17
Bravo, ARM, 3.1, 27, 14
Charlie, MIPS, 1.8, 31, 15
...

$ cat csvtest.c
#include <stdio.h>

FILE * fin;
char line[200];

int main(void) {
	fin = fopen("data.csv", "r");
	fgets(line, 200, fin); // read and ignore the first line
	fgets(line, 200, fin); // read the 2nd line which should be the one we want
	puts(line); // print that out just to be sure (the joy of using a PC!!)
// and here comes the data processing code we want to test...
	char * p;
	p = strchr(line, ','); // find the first ',' (after "Bravo,")
	puts(p); // print that just to be sure
	p++; // skip that ','
	p = strchr(p, ','); // find the second ',' (after "ARM,")
	puts(p); // print that just to be sure
	p++; // skip that ','
	p = strchr(p, ','); // find the third ',' (after "3.1,")
	p++; // and skip that 3rd one then..
	puts(p); // print that just to be sure
	int n = atoi(p); // now n = 27
	printf("The value for Max Temp is %d\n", n);
	fclose(fin);

	return 0;
}
$ gcc -Os csvtest.c -o csvtest
csvtest.c: In function �main’:
csvtest.c:13:6: warning: incompatible implicit declaration of built-in function �strchr’ [enabled by default]
csvtest.c:8:7: warning: ignoring return value of �fgets’, declared with attribute warn_unused_result [-Wunused-result]
csvtest.c:9:7: warning: ignoring return value of �fgets’, declared with attribute warn_unused_result [-Wunused-result]
$ ./csvtest
Bravo, ARM, 3.1, 27, 14

, ARM, 3.1, 27, 14

, 3.1, 27, 14

 27, 14

The value for Max Temp is 27

 

Yeah! It worked. Took me about 3 minutes to test that on a PC. Now I know the technique works for splitting out that 27 value I can just take this code and make the AVR/ARm version replacing the fopen()/fgets() calls with the FatFs equivalent. (oh and it's not so easy to use puts()/printf() in those environments either so I'll remove all my test printing.

 

I really, really, really recommend you work as I just have - don't learn data processing and file handling on some inaccessible micro - do it on a PC where you can easily read/write files and use things like puts() and printf() to see your results. When you are happy with your data processing algorithm move it all to the micro and shoehorn your display into your limited LCD and so on.

 

For completeness here's my strtok() version:

$ cat csvtest.c
#include <stdio.h>

FILE * fin;
char line[200];

int main(void) {
	fin = fopen("data.csv", "r");
	fgets(line, 200, fin); // read and ignore the first line
	fgets(line, 200, fin); // read the 2nd line which should be the one we want
	puts(line); // print that out just to be sure (the joy of using a PC!!)
// and here comes the data processing code we want to test...
	char * p;
	p = strtok(line, ",");
	int i;
	for (i = 0; i < 5; i++) {
		puts(p);
		if (i == 3) {
			int n = atoi(p); // now n = 27
			printf("The value for Max Temp is %d\n", n);
		}
		p = strtok(NULL, ",");
	}
	fclose(fin);

	return 0;
}
$ ./csvtest 
Bravo, ARM, 3.1, 27, 14

Bravo
 ARM
 3.1
 27
The value for Max Temp is 27
 14

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Metio wrote:
again I read all the string does not matter that they separated with comma

It does, since you can then use e.g. strtok() to wasily split the line into its parts.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As Johan says, part of the reason I suggested CSV is so it is very easy to split the data into constituent parts. If we'd gone with your inital:

 

Name of the machine ________

Type                          ________

voltage                      ________

Max Temp                  ________

Min Temp                  ________ 

 

that's actually more difficult to "find" the data values in. With CSV you just read the line then count along the commas until you get to the data item you are interested in. You could use some other delimiter like:

Alpha*AVR*5.3*28*17
Bravo*ARM*3.1*27*14
Charlie*MIPS*1.8*31*15

then scan for '*' characters. But us humans find it more natural to read a list of items separated by commas (computers don't actually care).

 

Another way to do it might be:

Name=Alpha Type=AVR Volt=5.3 Max=28 Min=17
Name=Bravo Type=ARM Volts=3.1 Max=27 Min=14
Name=Charlie Type=MIPS Volts=1.8 Max=31 Min=15

then you could:

char * p;

p = strstr(line, "Max=");
if (p) p += 4; // that is strlen("Max=")
n = atoi(p);

but CSV still has the "Excel advantage" ;-)

 

I suppose you might even use:

"reading" : {
  "Name" : "Alpha",
  "Type" : "AVR",
  "Volt" : "5.3",
  "Max" : "28",
  "Min" ; "17"
},
...

In which case you just reinvented JSON but while this is easy for humans to read the fact is that it's not so easy to generate it in the first place.

 

XML would be another way to write the data - especially if you were then going to process the data with an XML aware language like Python (use "ElementTree" for simple XML access).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your help!! I'm really sorry but unfortunately this is something new for me. :)

 

So I have wrote the code and compiled it, but unfortunately I'm reading some weird signs on the LCD. Here is the code:

 

    
 

int load_file(void){
    
    /*Register the system object in logical drive*/
    memset(&fs, 0, sizeof(FATFS));
    f_mount(LUN_ID_SD_MMC_0_MEM, &fs);
    /*Open or Create a file on the drive*/
    res = f_open(&file_object, "ReadThis_v1.csv",FA_OPEN_EXISTING | FA_READ);
    /*Sometime, open file operation was failed, so try again once*/
    if (res) res = f_open(&file_object, "ReadThis_v1.csv",FA_OPEN_EXISTING | FA_READ);
    /*Write header to the log file*/
    
    
    if (res != FR_OK)
        {
            ili93xx_set_foreground_color(COLOR_RED);
            ili93xx_draw_string(60, 280, (uint8_t *)"File not Open");
            
        }
    else
        {
            ili93xx_set_foreground_color(COLOR_BLUE);
            ili93xx_draw_string(60, 280, (uint8_t *)"File Open");
            
            
            f_gets(&file_object,200, &res);
            f_gets(&file_object, 200, &res);
            
            p = strtok(&file_object, ",");
            int i;
            for (i = 0; 1 < 5; i++)
            {
                //puts(p);
                if (i == 1)
                {
                    int n = atoi(p);
                    ili93xx_draw_string(110, 80, (uint8_t *)n);
                    
                }
                p = strtok(NULL, ",");
            }
            
            f_close(&file_object);
        }
    return 1;	
    
}

 What I did I opened the text file wrote the data separated by , and then safe it like CSV file.

I'm confused because I can open the file but by some reason I cannot read it properly.

Actually the data that I'll be storing in this file then it will be updating a webpage :D I use the LCD screen just to make sure that I load the string ot integer that I want..

 

Thankk youu again :)

  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like he has what we used to call "fields." You can just read each line and the title is columns 0 through something and the value is columns something to the end of the line.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
            f_gets(&file_object,200, &res);

Makes  no sense at all! How can you use file_object as your line buffer as well?!?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

LOL, you are totally right, Thank youu!!

 

Here is my progress: I have manage to to get what ever string I want to, but there is another issue that I'm confused about. 

            f_gets(line1, 200, &file_object);
			f_gets(line2, 200, &file_object);
			f_gets(line3, 200, &file_object);
			
			
			p1 = strtok(line1,  ",");
			p2 = strtok(line2,  ",");
			p3 = strtok(line3,	",");
		
		
	
			int i;
			for (i = 0; i < 5; i++)
			{
				//puts(p);
				if (i == 1)
				{
					// n = atoi(p1);
					ili93xx_draw_string(110, 80, (uint8_t *) p1);
					ili93xx_draw_string(110, 110, (uint8_t *) p2);
					ili93xx_draw_string(110, 140, (uint8_t *) p3);
				}
				p1 = strtok(NULL, ",");
				p2 = strtok(NULL, ",");
				p3 = strtok(NULL, ",");
			}
			
			f_close(&file_object);
		}

Well I don't know if this is the right way to do it but it kind of work . Imagining the text that I'm reading       


ATMEL, AVR, 5.4, 28, 17

BRAVO, ARM, 3.1, 27,14

Charlie, Mips, 1.8, 31,15


 I'm receiving weird outputs on my LCD for example when I put the 'i' in the if statement to be 0 I read on my LCD  

line1 - ATMEL

live 2 - BRAVO

line 3 - Charlie

 

which is perfect, but when i is 1 which by my understandings it has to cucly once and move to the second commo and display:

 

Line 1 - AVR

Line 2 - ARm

Line 3 - MIPs 

 

I'm getting 

 

Line 1 - MIPS

Line 2 - 1.8

Line 3 - 31 

 

Which means that all my buffers are reading only the 3th line. And one more weird thing that happens is that when I use the atoi() I'm getting some weird sings on my screen. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you are going to find that strtok() "remembers stuff".

 

As you know you call it once with the name of the string to process like:

p = strtok("hello,cruel,world", ",");

at which point it has effectively held "hello,cruel,world" internally but what it has done is replaced the "," with 0x00 end of string markers so it has split one string into 3. So it really holds "hello","cruel", "world". When you call it a second time:

p = strtok(NULL, ",");

you aren't even telling it what string data to process - because it already knows.

 

So the bottom line is you cannot be splitting 3 strings at once. You use it to break up a single string and use the parts of that. Then you move on to the next string and so on.

 

This may make you want to rethink how you are storing the data. Perhaps you really want to store it "across" rather than "down"? That is:

 

ATMEL, BRAVO, Charlie

AVR, ARM, Mips

5.4, 3.1, 1.8

28, 27, 31

17, 14, 15

 

But the problem here is that now to extend the data you have to "insert" stuff into the middle of the file rather than just adding onto the end:

 

ATMEL, BRAVO, Charlie, Delta

AVR, ARM, Mips, x86

5.4, 3.1, 1.8, 12.0

28, 27, 31, 56

17, 14, 15, 23

 

Another option is to "keep going back". You can go back in a file (so fread() will read the same thing again) using fseek().

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aside: @Cliff: I question that strtok actually splits the string into tokens at the first call. Since you can specify a different token delimiter for each call, my bet is that it EITHER has a cache for the string passed at the first call and "walks one token forward" for each call OR it might even be destructive (gotta look this up) only having a static pointer to the string passed at the first call, effectively destroying the callers string as it goes.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was kind of talking conceptually. I think it just maintains pointers in fact. But the point is that it holds state.

 

I guess the inquisitive can do this:

 

http://svn.savannah.nongnu.org/v...

 

And the "gotcha" there is:

#ifndef __DOXYGEN__
static char *p;
#endif

strtok_r() by the way is here:

 

http://svn.savannah.nongnu.org/v...

 

while it's implemented in Asm for efficiency there is a large comment at the top giving the algorithm it implements in C.

 

PS of course OP is using ARM not AVR so his libc is not the above - but the essence of it will be the same. In fact I guess one could go on a hunt for the source of NEWLIB and find out exactly what the ARM version implements.

EDIT: here in fact:

 

https://sourceware.org/git/gitwe...

 

with strtok() here:

 

https://sourceware.org/git/gitwe...

 

and strtok_r() here:

 

https://sourceware.org/git/gitwe...

 

Last Edited: Wed. Feb 24, 2016 - 03:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you guys for the help!!! I have manage to locate every char that I want with a case structure :D

 

BUt however I have very studied question frown. All morning I'm trying to convert the received chars into a integers or floats, but no progress. 

 


p1 = strtok(line1,  ",");

n = atoi(p1);

n = n++;

ili93xx_draw_string(110, 110, (uint8_t *) n); // trying to display it.


I'm trying with atoi function when I read the char without the ++ I don't display anything on my LCD without incrementation I get some crazy stuff. 

Any advises will be highly appreciated.

 

Thank you!! 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Silly question but why is it not simply:

p1 = strtok(line1,  ",");

ili93xx_draw_string(110, 110, p1); // trying to display it.

The fact is that say the input was:

Bravo, ARM, 3.1, 27, 14

and after a bit of strtok()ing p1 holds the address of "27" then ili93xx_draw_string() wants a pointer to characters and p1 *IS* a pointer to characters so why not just pass the "27" it is pointing to straight to the draw_string() routine?

 

If you do an atoi() then you are converting a string to an integer. You cannot pass an integer to a draw_string() routine that is looking for a pointer to characters. In fact if you lie to it and say "n really is a pointer to characters" then the 27 value in n is going to be used as if it were the address of characters. Now location 27 onwards in memory could contain pretty much anything and that is what the draw_string() is going to show!

 

So if you did use atoi() to convert "27" to 27. Then what you'd actually need to then do is convert 27 back to "27" using a function such as itoa() - if your compiler has such a function.

 

The only reason to do the atoi() and get the "27" in the form of an integer value 27 is if you plan to do some numerical processing of the data (like comparing this "max" to previous ones you have seen to see what the over max was?). Otherwise forget atoi() here, it is not needed if you just want to print a string of characters.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yep yep you are totally right :) I'm just stupid and didn't take that into account.. 

 

I did it exactly the way you have just said  and it's working beautifully.. :)

 

Thanks again for all the support!!! yes

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now that this is settled, let me thread jack slightly..

We often argue that developing and testing such stuff is easiest and best done on a PC. Problem is that file I/O is not the same on a PC doing Posix as in FatFS. But just the other week someone presented a project for doing "standard I/O" on an AVR (on top of FATFS? ). That would possibly be usable in such circumstances. OTOH you want your AVR code to be as lean as FATFS is, so an alternative would be to write FatFS-like wrappers to use on the PC?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The only thing is that I would claim that the person who implemented that "shim layer" did it the wrong way round!

 

What you really want is just to be able to sketch out and build a FatFs test program on a PC knowing it will work identically on the AVR (or whatever). What that other person was offering was a layer of interface code to be built into the AVR itself alongside FatFs and allow it to then use POSIX. That just seems like a waste of AVR (or whatever target - given that this thread is actually about ARM ;-) resources.

 

Now I know that ffsample.zip has a "win32" directory but that's an implementation of FatFs to read directly from a FAT32 harddrive attached to a PC. What I would like to see is a "shim layer" that just lays f_open(), f_read() on top of the POSIX open(), read() and so on (or more likely on top of <stdio> fopen(), fread() etc) so you can write FatFs syntax but have it access a file on any PC filing system.