Home  |  Contents 

Microcontroller and PC projects
  Forum Index : Microcontroller and PC projects         Section
Subject Topic: uM2(+): data logging - cheap fast & easy Post ReplyPost New Topic
Page of 9 Next >>
Author
Message << Prev Topic | Next Topic >>
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Offline
Posts: 2340
Posted: 27 March 2016 at 12:21pm | IP Logged Quote matherp

This one I'm pleased with

Since playing with the ESP8266 I've been interested in exploring how Serial Flash memory chips could be used for secure data logging on the Micromite as an alternative to SDcards or an external device like an Openlog.

These chips are ridiculously cheap so there is no reason not to include one on every PCB. Winbond branded chips work perfectly with the program and are recommended.





The example shown above is the one I used to develop the code and will hold 4Mbytes. Compatible chips are available from 512kbyte to 16Mbytes and the code should automatically cater for any of them.

Connections to the 8-pin chips are simple:

pin 1 (chip select) - connect to any Micromite output capable pin
pin 2 (Data out) - connect to the SPI-IN pin on the Micromite
pin 3 (write protect) - 10K resistor to 3.3V
pin 4 (GND) - connect to ground
pin 5 (Data in) - connect to the SPI-OUT pin on the Micromite
pin 6 (Clock) - connect to the SPI-CLK pin on the Micromite
pin 7 (Hold) - 10K resistor to 3.3V
pin 8 (VCC) - connect to 3.3V

The chips are a wide version of SOIC and can be easily soldered to a DIP adapter. This could then be socketed on the PCB allowing it to be swapped for reading separately in the same way as an SD card.

The intention of the coding was to hide all the workings of the chip from the user and make the user interface as simple as possible so there are just three user routines:


formatlog(chip_select_pin_number)

This routine completely erases the chip and tests it for correct erasure. In the event that the erase fails the routine will print a message and the program will end. The routine prints the ID of the chip and the size in Mbytes. It also writes to the chip an index of used pages that the logging routine uses to determine where a new record should be appended.

writelog(string-to-write, chip_select_pin_number, timestamp)

This routine appends the supplied string to the log. If the variable "timestamp" is non-zero it will automatically prepend the date and time comma-separated to the front of the string. If timestamp is 0 or not specified the string will be written as supplied. Logging strings can be constructed in the normal way using STR$ to format numbers and the "+" operator to concatenate multiple sub-strings.

readlog(chip_select_pin_number)

This routine reads the complete log and prints the contents to the console. To upload to a PC just set Teraterm (or equivalent) to record the terminal session and call readlog. The routine does not change the log data in any way so logging can be restarted after reading without any impact.

NB all of the routines are completely self-contained and can be called from the command line with no requirement for anything to have been run or set up previously - there are no global variables or constants - everything required to run is stored on the chip itself. They also obey the requirements for sharing the SPI bus with a TFT screen on the Micromite.

This means calls to the formatlog and readlog routines need never be included in the running program as they can just be called from the command line as and when required.

It really can't get any easier to include logging in any Micromite program.

The test program attached demonstrates just how easy it is to including logging commands in a program. I've tested the code using 1000, 10000, and 100000 log records. It takes 183 seconds to construct and log 10000 records.

The C-source is attached, there is nothing complicated and it was all written first in Basic and then just converted to C for improved performance.


option explicit 
option default none 
dim integer i 
const chipselectpin=1 'chip select pin 

'  Example program to demonstrate logging to a serial flash chip 

testdata() 'sets up the testdata generator 

formatlog(chipselectpin) 'format the chip 

timer=0 
for i=1 to 1000 'log 1000 strings with timestamps 
  writelog(int2Text(i),chipselectpin,1) 
next i 
print "1,000 records constructed and logged in ",timer\1000," seconds" 
pause 3000 

readlog(chipselectpin) ' read back the log 

end 

'**************************************** 


sub formatlog(cspin as integer) 'erase the chip and set up the page index 
  local integer i,bytecount,r(2) 
  local s$ length 10 
  spi open 20000000,3,8 
  bytecount = getpagecount(s$,cspin)*256 
  print "JEDEC ID = ",s$ 
  print "Memory size is ",bytecount\131072," Mbits" 
  if not (erasechip(bytecount,cspin)) then 
    print "erase failed" 
    end 
  endif 
  for i=0 to bytecount\524288 
    setpagewritten(i,cspin) 'mark the index pages and first real page as used 
  next i 
  spi close 
  print "Format complete" 
end sub 

sub writelog(s$,cspin as integer,timestamp as integer) 'write a string to the next location on the chip with optional timestamp 
  local integer i,x,buff(63),f,pagecount 
  local d$ length 10 
  spi open 20000000,3,8 
  pagecount=getpagecount(d$,cspin) 
  x=getnextfreepage(pagecount,cspin) 
  if x<>pagecount-1 then 
    if x<>pagecount\2048 then x=x-1 'point to the previous page to see if it has space 
    readpage(x,buff(0),cspin) 'read in first page 
    readpage(x+1,buff(32),cspin) 'read in the second page 
    f=getfirstfreebyte(buff()) 
    if timestamp then 
      i=loadstring(date$+","+time$+","+s$,buff(),f) 
    else 
      i= loadstring(s$,buff(),f) 
    endif 
    if i>=256 then 'write the bit on the next page and set the page marker as used 
      writepage(x+1,buff(32),cspin) 
      setpagewritten(x+1,cspin) 
    endif 
    writepage(x,buff(0),cspin) 
  else 
    print "Error: Chip full" 
  endif 
  spi close 
end sub 

sub readlog(cspin as integer) 'read and print the log 
  local integer x,buff(63),f=0, n,i 
  local s$  
  spi open 20000000,3,8 
  x=getpagecount(s$,cspin)\2048 'number of indexpages 
  readpage(x,buff(0),cspin) 'read in first two pages 
  readpage(x+1,buff(32),cspin) 
  if buff(0) =-1 then
    spi close
    exit sub 'no data 
  endif
  n=getstring(s$, buff(0),f) 
  print s$ 
  do while n<>&HFF 'repeat until no more data 
     if f>=256 then 
        x=x+1 
        readpage(x,buff(0),cspin) 'read in next two pages 
        readpage(x+1,buff(32),cspin) 
        f=f-256 
     endif 
     n=getstring(s$, buff(0),f) 
     print s$ 
  loop 
  spi close 
end sub 

function getnextfreepage(pagecount as integer, cspin as integer) as integer 'gets the page number of the next completely unused page 
  local integer i,j,r(31),p=0,found=0,indexsize=pagecount\2048 
  for i=0 to indexsize-1 'potentially look at all pages 
    readpage(i,r(),cspin) 
    for j=0 to 31 
      if r(j) then  
        p=j 'there must be at least one spare page if not zero 
        found=1 
        exit for 
      endif 
    next j 
    if found then exit for 
  next i 
  for j=0 to 63 
    if (r(p)>>j) and 1 then exit for 
  next j 
  getnextfreepage=i*2048+p*64+j 
end function 

sub setpagewritten(pageno as integer, cspin as integer) 'set a page as partially or completely written 
  local integer buff(31),i 
  local integer mappage=pageno\2048 'we have 2048 bits per page 
  local integer wordno=(pageno-mappage*2048)\64 'locate the word in the page 
  local integer bitno= 1<<(pageno mod 64) 'locate the bit in the word in the page 
  readpage(mappage,buff(),cspin) 
  buff(wordno) =buff(wordno) XOR bitno 
  writepage(mappage,buff(),cspin) 
end sub 

CFunction getpagecount 'returns the chip size in 256-byte pages and the chip ID 
    00000000 
    27BDFFD0 AFB20020 AFBF002C AFB40028 AFB30024 AFB1001C AFB00018 8CB00000  
    3C029D00 8C420088 00101880 00621021 8C420000 24030008 10430007 00809021  
    3C029D00 8C420010 02002021 24050008 0040F809 00003021 3C029D00 8C42001C  
    02002021 0040F809 24050005 2403009F 3C02BF80 AC435820 3C03BF80 8C625810  
    30420080 1040FFFD 3C02BF80 8C435820 3C03BF80 AC405820 8C625810 30420080  
    1040FFFD 3C02BF80 8C535820 3C03BF80 AC405820 8C625810 30420080 1040FFFD  
    3C02BF80 8C545820 3C03BF80 AC405820 8C625810 30420080 1040FFFD 3C119D00  
    8E22001C 3C03BF80 02002021 24050006 8C705820 0040F809 00000000 00133A00  
    24020010 AFA20010 00F43821 8E220030 00073A00 00F03821 26440001 00E03021  
    0040F809 00073FC3 24040006 A2440000 8FBF002C 2610FFF8 24020001 02021004  
    00021FC3 8FB40028 8FB30024 8FB20020 8FB1001C 8FB00018 03E00008 27BD0030  
End CFunction 

Cfunction erasechip 'size of chip in bytes, chip select pin number 
    00000000 
    27BDFFD8 AFB40020 AFBF0024 AFB3001C AFB20018 AFB10014 AFB00010 8CB30000  
    3C029D00 8C42001C 0080A021 24050005 0040F809 02602021 24030006 3C02BF80  
    AC435820 3C03BF80 8C625810 30420080 1040FFFD 3C109D00 8E02001C 3C11BF80  
    02602021 24050006 8E235820 0040F809 00000000 8E02001C 02602021 0040F809  
    24050005 240200C7 AE225820 3C03BF80 8C625810 30420080 1040FFFD 3C029D00  
    8C42001C 02602021 24050006 3C03BF80 8C635820 0040F809 3C119D00 3C10BF80  
    8E220004 0040F809 3404C350 8E22001C 02602021 0040F809 24050005 24020005  
    AE025820 8E025810 30420080 1040FFFD 00000000 8E025820 AE125820 8E025810  
    30420080 1040FFFD 00000000 8E125820 8E22001C 02602021 7C129420 0040F809  
    24050006 32420001 5440FFE6 8E220004 3C029D00 8C42001C 02602021 0040F809  
    24050005 24030003 3C02BF80 AC435820 3C03BF80 8C625810 30420080 1040FFFD  
    3C02BF80 8C435820 3C03BF80 AC405820 8C625810 30420080 1040FFFD 3C02BF80  
    8C435820 3C03BF80 AC405820 8C625810 30420080 1040FFFD 3C02BF80 8C435820  
    3C03BF80 AC405820 8C625810 30420080 1040FFFD 00000000 8E820004 3C03BF80  
    8C635820 5C400006 3C02BF80 14400014 3C029D00 8E820000 10400010 3C02BF80  
    AC525820 3C03BF80 8C625810 30420080 1040FFFD 3C029D00 8C42001C 02602021  
    24050006 3C03BF80 8C635820 0040F809 00000000 10000007 00001021 3C029D00  
    8C42001C 02602021 0040F809 24050006 24020001 8FBF0024 8FB40020 8FB3001C  
    8FB20018 8FB10014 8FB00010 03E00008 27BD0028  

End Cfunction 

CSub writepage 'page number, 256 byte data buffer, chip select pin number 
    00000000 
    27BDFFD8 AFB3001C AFB00010 AFBF0024 AFB40020 AFB20018 AFB10014 8CD30000  
    3C029D00 8C42001C 8C940000 00A08021 02602021 0040F809 24050005 24030006  
    3C02BF80 AC435820 0014A200 3C03BF80 8C625810 30420080 1040FFFD 3C119D00  
    8E22001C 3C12BF80 02602021 24050006 8E435820 0040F809 00000000 8E22001C  
    02602021 0040F809 24050005 24020002 AE425820 3C03BF80 8C625810 30420080  
    1040FFFD 3C02BF80 7E843C00 8C435820 3C03BF80 AC445820 8C625810 30420080  
    1040FFFD 3C02BF80 7E943A00 8C435820 3C03BF80 AC545820 8C625810 30420080  
    1040FFFD 3C02BF80 8C435820 3C03BF80 AC405820 8C625810 30420080 1040FFFD  
    3C02BF80 8C425820 00002021 3C03BF80 240500FF 02041021 80420000 AC625820  
    8C625810 30420080 1040FFFD 00000000 8C625820 10850003 3C029D00 1000FFF5  
    24840001 8C42001C 02602021 0040F809 24050006 24120100 3C119D00 3C10BF80  
    8E220004 0040F809 240400FA 8E22001C 02602021 0040F809 24050005 24020005  
    AE025820 8E025810 30420080 1040FFFD 00000000 8E025820 AE125820 8E025810  
    30420080 1040FFFD 00000000 8E125820 8E22001C 02602021 0040F809 24050006  
    32420001 5440FFE7 8E220004 8FBF0024 8FB40020 8FB3001C 8FB20018 8FB10014  
    8FB00010 03E00008 27BD0028  
End CSub 

CSub readpage 'page number, 256 byte data buffer, chip select pin number 
    00000000 
    27BDFFD8 AFB2001C AFB10018 AFBF0024 AFB30020 AFB00014 8CD20000 3C029D00  
    8C42001C 8C930000 00A08821 02402021 0040F809 24050005 24030003 3C02BF80  
    AC435820 00139A00 3C03BF80 8C625810 30420080 1040FFFD 3C02BF80 7E643C00  
    8C435820 3C03BF80 AC445820 8C625810 30420080 1040FFFD 3C02BF80 7E733A00  
    8C435820 3C03BF80 AC535820 8C625810 30420080 1040FFFD 3C02BF80 8C435820  
    3C03BF80 AC405820 8C625810 30420080 1040FFFD 3C02BF80 8C425820 00002021  
    3C03BF80 24050100 AC705820 8C625810 30420080 1040FFFD 00000000 8C705820  
    02241021 24840001 7C108420 1485FFF6 A0500000 3C029D00 8C42001C 02402021  
    0040F809 24050006 8FBF0024 8FB30020 8FB2001C 8FB10018 8FB00014 03E00008  
    27BD0028  
End CSub 

CFunction getfirstfreebyte 
    00000000 
    90830000 240200FF 00003021 10620014 00003821 90830001 1062000A 24020001  
    24020002 240500FF 24080100 00821821 90630000 54650006 24420001 10000002  
    00403021 00403021 10000005 00023FC3 5448FFF7 00821821 24060100 00003821  
    00C01021 03E00008 00E01821  
End CFunction 

CFunction loadstring 'copies a string into the write buffer 
    00000000 
    8CC30000 90820000 00621021 0043302A 14C0000A 24660001 00A31821 90870000  
    24C60001 24C5FFFF 0045282A A0670000 24840001 10A0FFF9 24630001 03E00008  
    00021FC3  
End CFunction 

CFunction getstring ' gets a string ferom the read buffer 
    00000000 
    8CC20000 00A21821 90690000 01224821 0122182A 5460000C 25220001 24430001  
    00A21021 90480000 24630001 2467FFFF 0127382A A0880000 24420001 10E0FFF9  
    24840001 25220001 00021FC3 ACC30004 ACC20000 00A92821 90A20001 03E00008  
    00001821  
End CFunction 

'**************************** 
' test data generation 

'small 
sub testdata 
  DATA "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" 
  DATA "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" 
'tens 
  DATA "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" 
'big 
  DATA "thousand", "million", "billion" 
  dim small(19) AS STRING length 10, tens(7) AS STRING length 10, big(2) AS STRING length 10 
  FOR i = 1 TO 19 
    READ small(i) 
  NEXT 
  FOR i = 0 TO 7 
    READ tens(i) 
  NEXT 
  FOR i = 0 TO 2 
    READ big(i) 
  NEXT 

end sub 

FUNCTION int2Text(number AS integer) as string 
    local num AS integer, outP AS STRING length 60, unit AS INTEGER 
    local tmpLng1 AS integer 
  
    IF 0 = number THEN 
        int2Text = "zero" 
        EXIT FUNCTION 
    END IF 
  
    num = ABS(number) 
  
    DO 
        tmpLng1 = num MOD 100 
        SELECT CASE tmpLng1 
            CASE 1 TO 19 
                outP = small(tmpLng1) + " " + outP 
            CASE 20 TO 99 
                SELECT CASE tmpLng1 MOD 10 
                    CASE 0 
                        outP = tens((tmpLng1 \ 10) - 2) + " " + outP 
                    CASE ELSE 
                        outP = tens((tmpLng1 \ 10) - 2) + "-" + small(tmpLng1 MOD 10) + " " + outP 
                END SELECT 
        END SELECT 
  
        tmpLng1 = (num MOD 1000) \ 100 
        IF tmpLng1 THEN 
            outP = small(tmpLng1) + " hundred " + outP 
        END IF 
  
        num = num \ 1000 
        IF num < 1 THEN EXIT DO 
  
        tmpLng1 = num MOD 1000 
        IF tmpLng1 THEN outP = big(unit) + " " + outP 
  
        unit = unit + 1 
    LOOP 
  
    IF number < 0 THEN outP = "negative " + outP 
    Do WHILE ASC(RIGHT$(outp,1))<=32 
       outp = LEFT$(outp,len(outp)-1)  
    loop 
    int2Text = outP 
END FUNCTION 
'********************************* 


#define writeenable 0x06
#define pageprogram 0x02
#define readstatus1 0x05
#define readdata  0x03
#define eraseall  0xC7
#define JEDEC 0x9F
#define SPIsend(a) {int j;SPIBUF=a; while((SPISTAT & 0x80)==0); j=SPIBUF;}
#define SPIread(a) {SPIBUF=a; while((SPISTAT & 0x80)==0); a=SPIBUF;}
#define SPISTAT *(volatile unsigned int *)(0xbf805810)             
#define SPIBUF *(volatile unsigned int *)(0xbf805820)              

long long pagecount(char id[],long long *cspin){
    int pin=*cspin,i,j,k;
    if(ExtCurrentConfig[pin]!=EXT_DIG_OUT)ExtCfg(pin,EXT_DIG_OUT,0);
    PinSetBit(pin,LATCLR);
    SPIsend(JEDEC);
    SPIread(i);
    SPIread(j);
    SPIread(k);
    PinSetBit(pin,LATSET);
    IntToStr(&id[1],i*65536+j*256+k,16);
    id[0]=6;
    return (1<<(k-8));
}
int erasechip(long long *size, long long *cspin){
    int p,pin=*cspin;
    char m;
    PinSetBit(pin,LATCLR);
    SPIsend(writeenable);
    PinSetBit(pin,LATSET);
    PinSetBit(pin,LATCLR);
    SPIsend(eraseall); 
    PinSetBit(pin,LATSET);
    do {
        uSec(50000);
        PinSetBit(pin,LATCLR);
        SPIsend(readstatus1)
        SPIread(m);
        PinSetBit(pin,LATSET);
    } while(m & 1);
    PinSetBit(pin,LATCLR);
    SPIsend(readdata); 
    SPIsend(0);
    SPIsend(0);
    SPIsend(0); //start address is zero
    for(p=0;p<*size;p++){
        SPIread(m); 
        if(m!=0xFF){
            PinSetBit(pin,LATSET);
            return 0;
        }
    }
    PinSetBit(pin,LATSET);
    return 1;
}

void writepage(long long *address, char d[], long long *cspin){
    int k=0,add,m,pin=*cspin;
        add=*address<<8; //convert page number to byte number
        PinSetBit(pin,LATCLR);
        SPIsend(writeenable);
        PinSetBit(pin,LATSET);
        PinSetBit(pin,LATCLR);
        SPIsend(pageprogram); 
        SPIsend((add>>16) & 0xFF);
        SPIsend((add>>8) & 0xFF);
        SPIsend(add & 0xFF);
        for(m=0;m<256;m++) {
            SPIsend(d[k]); 
            k++;
        }
        PinSetBit(pin,LATSET);
        
        do {
            uSec(250);
            PinSetBit(pin,LATCLR);
            SPIsend(readstatus1)
            SPIread(m);
            PinSetBit(pin,LATSET);
        } while(m & 1);

}
void readpage(long long *address, char d[], long long *cspin){
    int k,add,pin=*cspin;
    char m;
    add=*address<<8; //convert page number to byte number
    PinSetBit(pin,LATCLR);
    SPIsend(readdata); 
    SPIsend((add>>16) & 0xFF);
    SPIsend((add>>8) & 0xFF);
    SPIsend(add & 0xFF);
    for(k=0;k<256;k++){
        SPIread(m); 
        d[k]=m;
    }
    PinSetBit(pin,LATSET);
}
long long getfirstfreebyte(unsigned char d[]){
    int j=0;
    
    if(d[0]==0xFF) return 0;
    for(j=1;j<256;j++){
        if(d[j]==0xFF) return j;
    }
    return 256;
}
long long loadstring(unsigned char s[],unsigned char b1[],long long *pos){
    int k,j=0,len=s[0],start=*pos;
    for(k=start;k<=start+len;k++){
        b1[k]=s[j];
        j++;
    }
    return start+len;
}
long long getstring(unsigned char s[],unsigned char b1[],long long *pos){
    int k,j=0,start=*pos,len=b1[start];
    for(k=start;k<=start+len;k++){
        s[j]=b1[k];
        j++;
    }
    *pos=start+len+1; //start of next string
    return b1[start+len+1];
}




Edited by matherp on 27 March 2016 at 12:46pm



Back to Top View matherp's Profile Search for other posts by matherp
 
robert.rozee
Guru
Guru


Joined: 31 December 2012
Location: New Zealand
Online Status: Offline
Posts: 1311
Posted: 27 March 2016 at 12:42pm | IP Logged Quote robert.rozee

an excellent concept that looks extremely useful. i would love to see something similar included within the next micromite 2 firmware release.

might i suggest one more function:
a$ = readlogline(CS, i)

this would be used thus:
i = 0
do
a$ = readlogline(CS, i)
if (i>0) print a$
loop until (i=0)


calling with i=0 would initialize to the start of the flash chip. the variable i would return containing addressing information for the next line to read (could be page number and offset into page), or 0 if there is no next line to read.

with this addition, a flash chip could be used to hold data that an mmbasic program could itself make use of.


cheers,
rob :-)

Edited by robert.rozee on 27 March 2016 at 12:44pm
Back to Top View robert.rozee's Profile Search for other posts by robert.rozee
 
Grogster
Guru
Guru
Avatar

Joined: 31 December 2012
Location: New Zealand
Online Status: Offline
Posts: 6128
Posted: 27 March 2016 at 1:08pm | IP Logged Quote Grogster

Now, this is interesting.

I am going to have to have a play around with this.....

Do Serial Flash Memories not have page boundaries to worry about like EEPROM?
I note you are setting up 256 byte pages, but in theory, could you setup any page size you like, or are these kinds of memory chips set for a 256-byte page at factory?

__________________
Smoke makes things work. When the smoke gets out, it stops!
Back to Top View Grogster's Profile Search for other posts by Grogster Visit Grogster's Homepage
 
Chris Roper
Senior Member
Senior Member


Joined: 19 May 2015
Location: South Africa
Online Status: Offline
Posts: 280
Posted: 27 March 2016 at 1:15pm | IP Logged Quote Chris Roper

Grogster wrote:
Now, this is interesting.

I am going to have to have a play around with this.....

Do Serial Flash Memories not have page boundaries to worry about like EEPROM?
I note you are setting up 256 byte pages, but in theory, could you setup any page size you like, or are these kinds of memory chips set for a 256-byte page at factory?


It has been a while since I last worked with Winbond Flash but I seem to recall it is in pages of 4K.

Cheers
Chris


__________________
http://caroper.blogspot.com/
Back to Top View Chris Roper's Profile Search for other posts by Chris Roper Visit Chris Roper's Homepage
 
Grogster
Guru
Guru
Avatar

Joined: 31 December 2012
Location: New Zealand
Online Status: Offline
Posts: 6128
Posted: 27 March 2016 at 1:18pm | IP Logged Quote Grogster

Okey dokey, thanks.

I am in the process of downloading the datasheet so I can get a better idea of this memory.

__________________
Smoke makes things work. When the smoke gets out, it stops!
Back to Top View Grogster's Profile Search for other posts by Grogster Visit Grogster's Homepage
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Offline
Posts: 2340
Posted: 27 March 2016 at 1:20pm | IP Logged Quote matherp

Quote:
with this addition, a flash chip could be used to hold data that an mmbasic program could itself make use of.


Rob you already have everything needed to do something like this in the current code:

the function "writepage" writes a block of data to a specific page on the chip

writepage(page_number, data, chip_select_pin)

because of the way variable passing to CFunctions works "data" can be anything that adds up to 256 bytes

dim string mystring$ length 255 '1 byte length + 255 bytes of data
dim float myfloats!(63) ' 64 x 4-bytes
DIM integer myintegers%(31) '32 x 8-bytes

Likewise "readpage" can do the same thing

readpage(page_number, mystring$, chip_select_pin)
readpage(page_number, myfloat!(), chip_select_pin)
readpage(page_number, myintegers%(), chip_select_pin)

I specifically wanted to make the logging commands context free i.e. the Basic program shouldn't have to remember anything in order to use them. However, using the primitives as above you can use the flash chip for anything. If you want to use logging in addition then it is probably best to do direct access to high pages in the flash memory and then logging will continue to work from the beginning of flash. It would then be up to the Basic program to know what was in each page used in this way. Effectively readpage and writepage allow direct access to any 256-byte record. The log data can't be treated in this way as it is effectively a sequential stream so to access the 100th string you have to read the first 99 first.

readpage and writepage would just need wrapping with SPI OPEN and SPI CLOSE commands and if used by themselves the user would need to ensure chip_select_pin was set as an ouput (this is done at the moment in C by the "pagecount" comand)



Back to Top View matherp's Profile Search for other posts by matherp
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Offline
Posts: 2340
Posted: 27 March 2016 at 1:25pm | IP Logged Quote matherp

Do Serial Flash Memories not have page boundaries to worry about like EEPROM?


Yes pages for writing are on 256-byte boundaries. My code takes care of all this for you without wasting any space.

The 4K "page" is the minimum that can be erased but I only use full chip erase.

Flash memory is erased to all "1s". Writes convert the relevant 1s to 0s. So I can write the same data to a page with no effect or I can write additional data to a page that is part written by reading what was there, updating with the new data and then re-writing.
Back to Top View matherp's Profile Search for other posts by matherp
 
Grogster
Guru
Guru
Avatar

Joined: 31 December 2012
Location: New Zealand
Online Status: Offline
Posts: 6128
Posted: 27 March 2016 at 1:46pm | IP Logged Quote Grogster

Lovely, thanks for that.

Datasheet for W25Q80B says on page 5 that the device is arranged as 256-byte pages.

As all the hard work has been done for us with your Cfunctions for this, I am now seriously looking at using one of these instead of the SD card to store my database for the security system - I don't need much space for that, but was using the SD card for easy editing, really.

Food for thought.



__________________
Smoke makes things work. When the smoke gets out, it stops!
Back to Top View Grogster's Profile Search for other posts by Grogster Visit Grogster's Homepage
 
Grogster
Guru
Guru
Avatar

Joined: 31 December 2012
Location: New Zealand
Online Status: Offline
Posts: 6128
Posted: 27 March 2016 at 2:19pm | IP Logged Quote Grogster

I see that you can buy the 8-pin DIL for 21 million dollars.....

21 mill DIL IC

__________________
Smoke makes things work. When the smoke gets out, it stops!
Back to Top View Grogster's Profile Search for other posts by Grogster Visit Grogster's Homepage
 
twofingers
Guru
Guru


Joined: 02 June 2014
Location: Germany
Online Status: Online
Posts: 595
Posted: 27 March 2016 at 3:05pm | IP Logged Quote twofingers

@Peter

a very good idea! Thanks for the find and your code!
I ordered 5Pcs W25Q64FVSSIG (8MB chip) for a little more than 2 Euros.

Michael

EDIT
Quote:
I see that you can buy the 8-pin DIL for 21 million dollars.....

That must be a bargain!

Edited by twofingers on 27 March 2016 at 3:08pm
Back to Top View twofingers's Profile Search for other posts by twofingers
 
plover
Senior Member
Senior Member


Joined: 18 April 2013
Location: Australia
Online Status: Offline
Posts: 229
Posted: 27 March 2016 at 4:05pm | IP Logged Quote plover


Back to Top View plover's Profile Search for other posts by plover
 
matherp
Guru
Guru


Joined: 11 December 2012
Location: United Kingdom
Online Status: Offline
Posts: 2340
Posted: 27 March 2016 at 4:36pm | IP Logged Quote matherp

UPDATE

I should have mentioned in the original post that the spare pad on the back of many TFT displays will take one of the 25Q series chips and, at least on the displays I've checked, is correctly wired. The only additional connection is to the F_CS chip select pin on the display.

To improve logging speed by a factor of 2 please substitute this CFunction

CFunction getnextfreepage
    00000000
    27BDFEC8 AFB7012C AFBF0134 AFBE0130 AFB60128 AFB50124 AFB40120 AFB3011C 
    AFB20118 AFB10114 AFB00110 8C820004 8C830000 000227C3 308407FF 00831821 
    0064B82B 02E2B821 0017BD40 00031AC2 02E3B825 1AE00049 8CB60000 0000A021 
    3C159D00 3C10BF80 241E0003 27B20110 24130020 8EA2001C 02C02021 0040F809 
    24050005 00141A00 AE1E5820 8E025810 30420080 1040FFFD 7C623C00 8E045820 
    AE025820 8E025810 30420080 1040FFFD 7C623A00 8E045820 AE025820 8E025810 
    30420080 1040FFFD 00000000 306300FF 8E025820 AE035820 8E025810 30420080 
    1040FFFD 00000000 8E025820 27A30010 AE115820 8E025810 30420080 1040FFFD 
    00000000 8E115820 7C118C20 A0710000 24630001 1472FFF6 02C02021 8EA2001C 
    0040F809 24050006 8FA20010 8FA30014 00431025 14400013 00002021 27A20018 
    24040001 8C430000 8C450004 00651825 1460000C 24420008 24840001 5493FFFA 
    8C430000 26940001 0297102A 5440FFC2 8EA2001C 10000003 24040100 0000A021 
    00002021 27A30010 000410C0 00621021 8C460000 8C450004 30C30001 14600011 
    00001021 24020001 00055840 240A0040 00024827 00461806 012B4804 30480020 
    00453807 01231825 00E8180B 30630001 54600005 0014A140 24420001 144AFFF5 
    00024827 0014A140 02842021 8FBF0134 00042180 00441021 00021FC3 8FBE0130 
    8FB7012C 8FB60128 8FB50124 8FB40120 8FB3011C 8FB20118 8FB10114 8FB00110 
    03E00008 27BD0138 
End CFunction


for this Basic code

function getnextfreepage(pagecount as integer, cspin as integer) as integer 'gets the page number of the next completely unused page 
  local integer i,j,r(31),p=0,found=0,indexsize=pagecount\2048  
  for i=0 to indexsize-1 'potentially look at all pages  
    readpage(i,r(),cspin)  
    for j=0 to 31  
      if r(j) then   
        p=j 'there must be at least one spare page if not zero  
        found=1  
        exit for  
      endif  
    next j  
    if found then exit for  
  next i  
  for j=0 to 63  
    if (r(p)>>j) and 1 then exit for  
  next j  
  getnextfreepage=i*2048+p*64+j  
end function  


C source


long long getnextfreepage(long long *pagecount, long long *cspin){
  long i,j,k=0,found=0,indexsize=*pagecount/2048,add,pin=*cspin ; 
  union ftype{
       long long a[32];
       char b[256];
  }r;
  for (i=0; i< indexsize;i++){ //potentially look at all pages  
    char m;
    add=i<<8; //convert page number to byte number
    PinSetBit(pin,LATCLR);
    SPIsend(readdata); 
    SPIsend((add>>16) & 0xFF);
    SPIsend((add>>8) & 0xFF);
    SPIsend(add & 0xFF);
    for(k=0;k<256;k++){
        SPIread(m); 
        r.b[k]=m;
    }
    PinSetBit(pin,LATSET);
    for(j=0 ;j<32;j++){
      if (r.a[j]) {  
        k=j; //there must be at least one spare page if not zero  
        found=1  ;
        break;  
      }
    }  
    if (found) break;  
  } 
  for (j=0 ;j<64;j++)  if ((r.a[k]>>j) & 1) break;
  return i*2048+k*64+j  ;
}



For those learning C note how the union ftype allows me to treat a 256-byte area of memory as either an array of bytes (8-bit) or an array of long longs (64-bit)

Edited by matherp on 27 March 2016 at 5:32pm
Back to Top View matherp's Profile Search for other posts by matherp
 


Page of 9 Next >>
In the news...
 
Post ReplyPost New Topic
Printable version Printable version
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot delete your posts in this forum
You cannot edit your posts in this forum
You cannot create polls in this forum
You cannot vote in polls in this forum

Powered by Web Wiz Forums version 7.8
Copyright ©2001-2004 Web Wiz Guide

This page was generated in 0.1563 seconds.
Privacy Policy     Process times : 0, 0, 0, 0.16