MEL PICBASIC Forum - USB/SD LOGGING


  • USB/SD LOGGING

    Lately many of my projects have required data logging of at least ambient temperature. So I have put together a base board to build from. The board has the following components:
    DS1337C (RTC) with a backup battery
    LM34
    SERIAL(USART)
    USB
    SD card
    A few extra pins for external circuitry
    18F2550 with a 4 MHz resonator running at 48 MHz

    In practice data is saved to the SD card along with a time and date stamp. The USB port is used for real time system monitoring if needed. Normal operation feed back is from one LED.

    To set this up Darrel's Instant Interrupts will be needed. Thanks Darrel!!
    http://www.picbasic.co.uk/forum/showthread.php?t=3251

    SD card routines are from http://melabs.com/samples/PBP-mixed/sdfs3.htm for SD cards up to 2GB.

    The USB will need six files copied from the PBP\USB18 directory to the project directory. This is for PBP 2.60, if another version of PBP is used the files will be different and not covered here.
    cdc_desc.bas
    usb_scd.asm
    usb_cdc.inc
    usb_dev.asm
    usb_dev.inc
    usb_mem.asm


    click on schematic to view if it is to large here.


    The first block of code does not write to the SD card. It is for testing the USB and RTC. The LED is a "heart beat" on this one.

    The second code has the SD routines added in. Along with the SD routines some more feed back has been added.

    If a SD card is not present the USB will still output the time, date, temperature, and now "NO CARD".
    The LED will blink as a 1 Hz heart beat as the first code if a card is present and the card is not being written. If the card is being written to then the the LED blink will be faster and dimmer. Do not remove the SD card while it is being written to.

    If the SD card is not present then the LED will be off.
    Code:
        '<FL_PIC18F2550>'
        '<FL_PBPW>'
        DEFINE OSC 48
        @ __CONFIG   _CONFIG1L, _PLLDIV_1_1L & _CPUDIV_OSC1_PLL2_1L & _USBDIV_2_1L
        @ __CONFIG   _CONFIG1H, _FOSC_HSPLL_HS_1H
        @ __CONFIG   _CONFIG2H, _WDT_OFF_2H & _WDTPS_512_2H
        @ __CONFIG   _CONFIG2L, _PWRT_ON_2L & _VREGEN_ON_2L
        @ __CONFIG   _CONFIG3H, _PBADEN_OFF_3H & _MCLRE_OFF_3H
        @ __CONFIG   _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
        INCLUDE "cdc_desc.bas"
        INCLUDE "DT_INTS-18.bas"
        INCLUDE "ReEnterPBP-18.bas"
         ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   INT_INT,  _CNT_PLUS,   PBP,  yes
            INT_Handler   USB_INT,  _SERVICE_USB,  ASM,  yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    ENDASM
    '#########
    'START USB AND INTERRUPTS
        PAUSE 100           'TIME TO SETTLE
        USBINIT             'INITIALIZE USB
        USBSERVICE          'SERVICE USB
        UIE = $7F           'ENABLE USB INTERRUPTS
        UEIE = $9F          'ENABLE USB ERROR INTERRUPTS
        PAUSE 500           'MORE TIME
        USBINIT
        USBSERVICE
        @   INT_ENABLE  USB_INT
        USBSERVICE
        @ INT_ENABLE  INT_INT
    '#########
    'VARs AND DEFINEs
        ADCON1 = 001110
        LED      VAR PORTA.2
        TEXT_TIME VAR BYTE[6]
        TEXT_DATE VAR BYTE[6]
        TEXT_TEMP VAR BYTE[8]
        B0       VAR BYTE
        B1       VAR BYTE
        CNT VAR     WORD
        CRON VAR BYTE[11]
        CRON_D VAR BYTE[11]
        SPACE VAR BYTE[2]
        T_BUFFER   VAR BYTE[8]
        T_ONES   VAR    BYTE
        T_TENS   VAR    BYTE
        T_HUNS   VAR    BYTE
        OUT_TEMP VAR BYTE
        ADC_TEMP VAR WORD
        S_TEMP   VAR BYTE
        X_TEMP   VAR BYTE
    'RTC DEFINES
        DS_SCL      VAR     PORTB.1    'CLOCK
        DS_SDA      VAR     PORTB.2    'DATA
        RTC CON     010000
        SEC_REG CON $00
        CONT_REG CON $0E
        CNTRL CON 000000 
    'RTC VARS
        sec VAR BYTE: mins VAR BYTE: hr VAR BYTE: day VAR BYTE
        date VAR BYTE: mon VAR BYTE: yr VAR BYTE
    'RTC DEC VARS
         SEC_O VAR BYTE: SEC_T VAR BYTE: MIN_O VAR BYTE: MIN_T VAR BYTE
         HR_O VAR BYTE: HR_T VAR BYTE: MON_O VAR BYTE: MON_T VAR BYTE
         DATE_O VAR BYTE: DATE_T VAR BYTE: YR_O VAR BYTE: YR_T VAR BYTE
    '#########
    'SETS THE RTC TO PULSE OUT AT 1HZ
        I2CWRITE DS_SDA, DS_SCL, RTC, CONT_REG, [CNTRL]
    'BUILD SOME USB TEXT
        FOR B0 = 0 TO 5
        LOOKUP B0,[" TIME "],B1
        TEXT_TIME(B0) = B1
        NEXT B0
        FOR B0 = 0 TO 5
        LOOKUP B0,[" DATE "],B1
        TEXT_DATE(B0) = B1
        NEXT B0
        FOR B0 = 0 TO 7
        LOOKUP B0,[" TEMP F "],B1
        TEXT_TEMP(B0) = B1
        NEXT B0
        SPACE[0] = $d
        SPACE[1] = $a
    '#########
    'IF THE RTC NEEDS SET GOTO THE SET_RTC ROUTINE AND
    'ENTER THE TIME AND DATE. RUN THE CODE ONCE THEN RE-COMMENT.
              'GOSUB SET_RTC
    '#########
    'MAIN PROGRAM
        CHECK:
        IF CNT >= 60 THEN 'NUMBER OF SECONDS
        CNT = 0
        GOSUB GET_T
        GOSUB READ_RTC
        GOSUB USB_DISPLAY
        ENDIF
        GOTO CHECK
    '#########
    '#########
    'ISRs
        CNT_PLUS:
             TOGGLE LED
             CNT = CNT + 1
        @ INT_RETURN
        
        SERVICE_USB:
            USBSERVICE
        @   INT_RETURN
    '#########
        SET_RTC:
        yr = $10
        mon = $10
        date = $09
        sec = $00
        mins = $52
        hr = $02
        I2CWRITE DS_SDA, DS_SCL, RTC, SEC_REG, [sec,mins,hr,day,date,mon,yr]
        RETURN
    '#########
        READ_RTC:
        I2CREAD DS_SDA, DS_SCL, RTC, SEC_REG, [sec,mins,hr,day,date,mon,yr]
        
        SEC_T = sec & $70
        SEC_T = SEC_T>>4
        SEC_O = sec & $0F
        
        MIN_T = mins & $70
        MIN_T = MIN_T>>4
        MIN_O = MINs & $0F
        
        HR_T = hr & $70
        HR_T = HR_T>>4
        HR_O = hr & $0F
        
        MON_T = mon & $70
        MON_T = MON_T>>4
        MON_O = mon & $0F
        
        DATE_T = date & $70
        DATE_T = DATE_T>>4
        DATE_O = date & $0F
        
        YR_T = yr & $70
        YR_T = YR_T>>4
        YR_O = yr & $0F
    
        CRON[0] = " "
        CRON[1] = $30+HR_T
        CRON[2] = $30+HR_O
        CRON[3] = ":"
        CRON[4] = $30+MIN_T
        CRON[5] = $30+MIN_O
        CRON[6] = ":"
        CRON[7] = $30+SEC_T
        CRON[8] = $30+SEC_O
        CRON[9] = $d
        CRON[10] = $a
    
        CRON_D[0] = " "
        CRON_D[1] = $30+MON_T
        CRON_D[2] = $30+MON_O
        CRON_D[3] = "/"
        CRON_D[4] = $30+DATE_T
        CRON_D[5] = $30+DATE_O
        CRON_D[6] = "/"
        CRON_D[7] = $30+YR_T
        CRON_D[8] = $30+YR_O
        CRON_D[9] = $d
        CRON_D[10] = $a
        RETURN
    '#########
        GET_T:
        ADC_TEMP = 0
        FOR X_TEMP = 1 TO 20
        ADCON0=00000001
        GOSUB READ_AD
        S_TEMP = ADRESH
        ADC_TEMP = ADC_TEMP + S_TEMP
        PAUSE 250
        NEXT X_TEMP
        OUT_TEMP = ADC_TEMP / 20
        OUT_TEMP = OUT_TEMP * 13/10
        T_HUNS = OUT_TEMP/100
        T_TENS = (OUT_TEMP - T_HUNS * 100)/10
        T_ONES = OUT_TEMP-((T_HUNS*100)+(T_TENS*10))
        T_BUFFER[0] = $30+T_HUNS
        T_BUFFER[1] = $30+T_TENS
        T_BUFFER[2] = $30+T_ONES
        T_BUFFER[3] = " "
        T_BUFFER[4] = " "
        T_BUFFER[5] = " "
        T_BUFFER[6] = $d
        T_BUFFER[7] = $a
        RETURN
    
        READ_AD:
        PAUSE 50
        ADCON0.1=1
        WHILE ADCON0.2=1:WEND
        RETURN
    '#########
        USB_DISPLAY:
        PAUSE 1
        USBOUT 3, SPACE, 2, CHECK
        PAUSE 1
        USBOUT 3, TEXT_TIME, 6, CHECK
        PAUSE 1
        USBOUT 3, CRON, 11, CHECK
        PAUSE 1
        USBOUT 3, TEXT_DATE, 6, CHECK
        PAUSE 1
        USBOUT 3, CRON_D, 11, CHECK
        PAUSE 1
        USBOUT 3, TEXT_TEMP, 8, CHECK
        PAUSE 1
        USBOUT 3, T_BUFFER, 8, CHECK
        PAUSE 1
        RETURN
    Code:
        '<FL_PIC18F2550>'
        '<FL_PBPL>'
        DEFINE OSC 48
        @ __CONFIG   _CONFIG1L, _PLLDIV_1_1L & _CPUDIV_OSC1_PLL2_1L & _USBDIV_2_1L
        @ __CONFIG   _CONFIG1H, _FOSC_HSPLL_HS_1H
        @ __CONFIG   _CONFIG2H, _WDT_OFF_2H & _WDTPS_512_2H
        @ __CONFIG   _CONFIG2L, _PWRT_ON_2L & _VREGEN_ON_2L
        @ __CONFIG   _CONFIG3H, _PBADEN_OFF_3H & _MCLRE_OFF_3H
        @ __CONFIG   _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
    
            ' Alias PIC pins and registers for SD/MMC card
        SD_WE        VAR    PORTA.4    ' SD card write protect
        SD_WE_TRIS   VAR    TRISA.4    ' SD card write protect direction
        SDI          VAR    PORTA.5    ' SPI data in SD #7
        SDI_TRIS     VAR    TRISA.5    ' SPI data in direction
        SCL          VAR    PORTA.3    ' SPI clock  SD #5
        SCL_TRIS     VAR    TRISA.3    ' SPI clock direction
        SD_CS        VAR    PORTC.2    ' SD card chip select SD #1
        SD_CS_TRIS   VAR    TRISC.2    ' SD card chip select direction
        SD_CD        VAR    PORTC.0    ' SD card detect
        SD_CD_TRIS   VAR    TRISC.0    ' SD card detect direction
        SDO          VAR    PORTC.1    ' SPI data out   SD #2
        SDO_TRIS     VAR    TRISC.1    ' SPI data out direction
        INCLUDE "SDFS.BAS"
        SDC_UseHardSPI = FALSE    ' Use hardware SSP port for SPI.
        INCLUDE "cdc_desc.bas"
        INCLUDE "DT_INTS-18.bas"
        INCLUDE "ReEnterPBP-18.bas"
         ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   INT_INT,  _CNT_PLUS,   PBP,  yes
            INT_Handler   USB_INT,  _SERVICE_USB,  ASM,  yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    ENDASM
    '#########
    'START USB AND INTERRUPTS
        PAUSE 100           'TIME TO SETTLE
        USBINIT             'INITIALIZE USB
        USBSERVICE          'SERVICE USB
        UIE = $7F           'ENABLE USB INTERRUPTS
        UEIE = $9F          'ENABLE USB ERROR INTERRUPTS
        PAUSE 500           'MORE TIME
        USBINIT
        USBSERVICE
        @   INT_ENABLE  USB_INT
        USBSERVICE
        @ INT_ENABLE  INT_INT
    '#########
    'VARs AND DEFINEs
        ADCON1 = 001110
        LED      VAR PORTA.2
        TEXT_TIME VAR BYTE[6]
        TEXT_DATE VAR BYTE[6]
        TEXT_TEMP VAR BYTE[8]
        TEXT_NO_CARD VAR BYTE[9]
        MAC_FileName VAR BYTE[11]
        B0       VAR BYTE
        B1       VAR BYTE
        CNT VAR     WORD
        CIU VAR     BYTE 'CARD IN USE
        CRON VAR BYTE[11]
        CRON_D VAR BYTE[11]
        SPACE VAR BYTE[2]
        T_BUFFER   VAR BYTE[8]
        T_ONES   VAR    BYTE
        T_TENS   VAR    BYTE
        T_HUNS   VAR    BYTE
        OUT_TEMP VAR BYTE
        ADC_TEMP VAR WORD
        S_TEMP   VAR BYTE
        X_TEMP   VAR BYTE
    'RTC DEFINES
        DS_SCL      VAR     PORTB.1    'CLOCK
        DS_SDA      VAR     PORTB.2    'DATA
        RTC CON     010000
        SEC_REG CON $00
        CONT_REG CON $0E
        CNTRL CON 000000
    'RTC VARS
        sec VAR BYTE: mins VAR BYTE: hr VAR BYTE: day VAR BYTE
        date VAR BYTE: mon VAR BYTE: yr VAR BYTE
    'RTC DEC VARS
         SEC_O VAR BYTE: SEC_T VAR BYTE: MIN_O VAR BYTE: MIN_T VAR BYTE
         HR_O VAR BYTE: HR_T VAR BYTE: MON_O VAR BYTE: MON_T VAR BYTE
         DATE_O VAR BYTE: DATE_T VAR BYTE: YR_O VAR BYTE: YR_T VAR BYTE
    'SD CARD FILE
         FILE_seconds VAR BYTE: FILE_minutes VAR BYTE: FILE_hours VAR BYTE
         FILE_day VAR BYTE: FILE_month VAR BYTE: FILE_year VAR BYTE
    '#########
    'SETS THE RTC TO PULSE OUT AT 1HZ
        I2CWRITE DS_SDA, DS_SCL, RTC, CONT_REG, [CNTRL]
    'SETS FILE NAME TO THE TIME BOARD IS POWERED
        GOSUB READ_RTC
        MAC_FileName[0] = $30+HR_T
        MAC_FileName[1] = $30+HR_O
        MAC_FileName[2] = $30+MIN_T
        MAC_FileName[3] = $30+MIN_O
        MAC_FileName[4] = $30+SEC_T
        MAC_FileName[5] = $30+SEC_O
        MAC_FileName[6] = " "
        MAC_FileName[7] = " "
        MAC_FileName[8] = "T"
        MAC_FileName[9] = "X"
        MAC_FileName[10] = "T"
    'BUILD SOME USB TEXT
        FOR B0 = 0 TO 5
        LOOKUP B0,[" TIME "],B1
        TEXT_TIME(B0) = B1
        NEXT B0
        FOR B0 = 0 TO 5
        LOOKUP B0,[" DATE "],B1
        TEXT_DATE(B0) = B1
        NEXT B0
        FOR B0 = 0 TO 7
        LOOKUP B0,[" TEMP F "],B1
        TEXT_TEMP(B0) = B1
        NEXT B0
        SPACE[0] = $d
        SPACE[1] = $a
        FOR B0 = 0 TO 9
        LOOKUP B0,[" NO CARD",$d,$a],B1
        TEXT_NO_CARD(B0) = B1
        NEXT B0
    '#########
    'IF THE RTC NEEDS SET GOTO THE SET_RTC ROUTINE AND
    'ENTER THE TIME AND DATE. RUN THE CODE ONCE THEN RE-COMMENT.
              'GOSUB SET_RTC
    '#########
    'MAIN PROGRAM
        CHECK:
        IF CNT >= 60 THEN 'NUMBER OF SECONDS
        CNT = 0
        GOSUB GET_T
        GOSUB READ_RTC
        GOSUB USB_DISPLAY
    
        IF (SD_WE = 0) AND (SD_CD = 0)  THEN
        GOTO SD_WRITE
        ELSE
        PWM LED,25,250
        USBOUT 3, TEXT_NO_CARD, 10, CHECK
        PAUSE 500
        ENDIF
        ENDIF
        GOTO CHECK
    '#########
    '#########
    'ISRs
        CNT_PLUS:
             IF CIU = 1 THEN
             PWM LED,75,250
             ELSE
             IF (SD_WE = 0) AND (SD_CD = 0)  THEN
             TOGGLE LED
             ELSE
             LOW LED
             ENDIF
             ENDIF
             CNT = CNT + 1
        @ INT_RETURN
    
        SERVICE_USB:
            USBSERVICE
        @   INT_RETURN
    '#########
        SET_RTC:
        yr = $10
        mon = $10
        date = $09
        sec = $00
        mins = $52
        hr = $02
        I2CWRITE DS_SDA, DS_SCL, RTC, SEC_REG, [sec,mins,hr,day,date,mon,yr]
        RETURN
    '#########
        READ_RTC:
        I2CREAD DS_SDA, DS_SCL, RTC, SEC_REG, [sec,mins,hr,day,date,mon,yr]
    
        SEC_T = sec & $70
        SEC_T = SEC_T>>4
        SEC_O = sec & $0F
    
        MIN_T = mins & $70
        MIN_T = MIN_T>>4
        MIN_O = MINs & $0F
    
        HR_T = hr & $70
        HR_T = HR_T>>4
        HR_O = hr & $0F
    
        MON_T = mon & $70
        MON_T = MON_T>>4
        MON_O = mon & $0F
    
        DATE_T = date & $70
        DATE_T = DATE_T>>4
        DATE_O = date & $0F
    
        YR_T = yr & $70
        YR_T = YR_T>>4
        YR_O = yr & $0F
    
        CRON[0] = " "
        CRON[1] = $30+HR_T
        CRON[2] = $30+HR_O
        CRON[3] = ":"
        CRON[4] = $30+MIN_T
        CRON[5] = $30+MIN_O
        CRON[6] = ":"
        CRON[7] = $30+SEC_T
        CRON[8] = $30+SEC_O
        CRON[9] = $d
        CRON[10] = $a
    
        CRON_D[0] = " "
        CRON_D[1] = $30+MON_T
        CRON_D[2] = $30+MON_O
        CRON_D[3] = "/"
        CRON_D[4] = $30+DATE_T
        CRON_D[5] = $30+DATE_O
        CRON_D[6] = "/"
        CRON_D[7] = $30+YR_T
        CRON_D[8] = $30+YR_O
        CRON_D[9] = $d
        CRON_D[10] = $a
    
        FILE_seconds = (SEC_T*10)+SEC_O
        FILE_minutes = (MIN_T*10)+MIN_O
        FILE_hours = (HR_T*10)+HR_O
        FILE_day = (DATE_T*10)+DATE_O
        FILE_month = (MON_T*10)+MON_O
        FILE_year = (YR_T*10)+YR_O
        RETURN
    '#########
        GET_T:
        ADC_TEMP = 0
        FOR X_TEMP = 1 TO 20
        ADCON0=00000001
        GOSUB READ_AD
        S_TEMP = ADRESH
        ADC_TEMP = ADC_TEMP + S_TEMP
        PAUSE 250
        NEXT X_TEMP
        OUT_TEMP = ADC_TEMP / 20
        OUT_TEMP = OUT_TEMP * 13/10
        T_HUNS = OUT_TEMP/100
        T_TENS = (OUT_TEMP - T_HUNS * 100)/10
        T_ONES = OUT_TEMP-((T_HUNS*100)+(T_TENS*10))
        T_BUFFER[0] = $30+T_HUNS
        T_BUFFER[1] = $30+T_TENS
        T_BUFFER[2] = $30+T_ONES
        T_BUFFER[3] = " "
        T_BUFFER[4] = " "
        T_BUFFER[5] = " "
        T_BUFFER[6] = $d
        T_BUFFER[7] = $a
        RETURN
    
        READ_AD:
        PAUSE 50
        ADCON0.1=1
        WHILE ADCON0.2=1:WEND
        RETURN
    '#########
        USB_DISPLAY:
        PAUSE 1
        USBOUT 3, SPACE, 2, CHECK
        PAUSE 1
        USBOUT 3, TEXT_TIME, 6, CHECK
        PAUSE 1
        USBOUT 3, CRON, 11, CHECK
        PAUSE 1
        USBOUT 3, TEXT_DATE, 6, CHECK
        PAUSE 1
        USBOUT 3, CRON_D, 11, CHECK
        PAUSE 1
        USBOUT 3, TEXT_TEMP, 8, CHECK
        PAUSE 1
        USBOUT 3, T_BUFFER, 8, CHECK
        PAUSE 1
        RETURN
    '#########
    'SD CARD ROUTINES
        SD_WRITE:
        CIU = 1
        SDINIT:
        ' FSInit initializes the card and reads all the preliminary information from it
        GOSUB FSInit
        IF (FAT_error != 0) THEN STOP
    
        ' Display card directory
        GOSUB FINDfirst     ' Find first file on card
        WHILE (FAT_error = 0)
            GOSUB FINDnext  ' Find next file on card
        WEND
    
        SDFILENAME:
        ' This section defines a specific short (8.3) filename
        ' Note that spaces are use in empty elements and must be upper case for Windows
        FAT_FileName[0] = MAC_FileName[0]
        FAT_FileName[1] = MAC_FileName[1]
        FAT_FileName[2] = MAC_FileName[2]
        FAT_FileName[3] = MAC_FileName[3]
        FAT_FileName[4] = MAC_FileName[4]
        FAT_FileName[5] = MAC_FileName[5]
        FAT_FileName[6] = MAC_FileName[6]
        FAT_FileName[7] = MAC_FileName[7]
        FAT_FileName[8] = MAC_FileName[8]
        FAT_FileName[9] = MAC_FileName[9]
        FAT_FileName[10] = MAC_FileName[10]
    
        FAT_seconds = FILE_seconds
        FAT_minutes = FILE_minutes
        FAT_hours = FILE_hours
        FAT_day = FILE_day
        FAT_month = FILE_month
        FAT_year = FILE_year+20
    
        SDOPEN_W:
        ' Open a file for write
        FAT_mode = "A"      ' Write mode APPEND
        GOSUB FSfopen   ' Open file pointed to by Byte array FAT_FileName
        IF (FAT_error = 10) THEN STOP
    
        SD_WRITE_FILE:
        ' Write to file
        FAT_src[0] = "T"
        FAT_src[1] = "I"
        FAT_src[2] = "M"
        FAT_src[3] = "E"
        FAT_src[4] = $d
        FAT_src[5] = $a
        FAT_src[6] = $30+HR_T
        FAT_src[7] = $30+HR_O
        FAT_src[8] = ":"
        FAT_src[9] = $30+MIN_T
        FAT_src[10] = $30+MIN_O
        FAT_src[11] = ":"
        FAT_src[12] = $30+SEC_T
        FAT_src[13] = $30+SEC_O
        FAT_src[14] = $d
        FAT_src[15] = $a
        FAT_src[16] = "D"
        FAT_src[17] = "A"
        FAT_src[18] = "T"
        FAT_src[19] = "E"
        FAT_src[20] = $d
        FAT_src[21] = $a
        FAT_src[22] = $30+MON_T
        FAT_src[23] = $30+MON_O
        FAT_src[24] = ":"
        FAT_src[25] = $30+DATE_T
        FAT_src[26] = $30+DATE_O
        FAT_src[27] = ":"
        FAT_src[28] = $30+YR_T
        FAT_src[29] = $30+YR_O
        FAT_src[30] = $d
        FAT_src[31] = $a
        FAT_src[32] = " "
        FAT_src[33] = $30+T_HUNS
        FAT_src[34] = $30+T_TENS
        FAT_src[35] = $30+T_ONES
        FAT_src[36] = " "
        FAT_src[37] = "F"
        FAT_src[38] = $d
        FAT_src[39] = $a
        FAT_src[40] = $d
        FAT_src[41] = $a
        FAT_count = 42
        GOSUB FSfwrite
    
        IF (FAT_error = 10) THEN STOP
        IF (FAT_error != 0) THEN STOP
    
        SDCLOSE:
        ' Close file
        GOSUB FSfclose
        IF (FAT_error != 0) THEN STOP
        PAUSE 5000
        CIU = 0
        GOTO CHECK