CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

18F45K22 SPI SDCARD Read Speed Problem
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

18F45K22 SPI SDCARD Read Speed Problem
PostPosted: Wed May 25, 2022 12:58 am     Reply with quote

Hello,

I draw Bitmaps using PIC18F45K22, ST7735 1.8″ TFT and SD card.
I have a problem. I define TFT and SD CARD connections manually. I am not using hardware definition. SPI1 outputs for SDCCARD, SPI2 outputs for TFT. Everything works fine. I am using 2GB SD card. Processor frequency is 16MHZ and PLL is active. The screen works fast but it takes a long time to read the SD card.
BMP file is 60Kb in size and has 128x160 resolution with 256 bit color. It reads in 2 minutes and 3 seconds. Why do you think this might be?

Note: I tried to reduce the SD card memory with DISKPART and it didn't make any difference.

Source Code:
Code:


#device PIC18F45K22
#include <18F45K22.h>

#define TFT_CS  PIN_B2
#define TFT_DC  PIN_B3
#define TFT_DATA PIN_D4
#define TFT_CLK PIN_D0

#define   SDCARD_PIN_SELECT  PIN_C2
#define   SDCARD_PIN_SCL     PIN_C3
#define   SDCARD_PIN_SDO     PIN_C5
#define   SDCARD_PIN_SDI     PIN_C4


#define   DRAW_BMP_FROM_MMCSD_CARD               // Enable BMP draw from SD card
#define   pixel_buffer  350                      // Set pixel buffer to 500

      #FUSES NOWDT                    //No Watch Dog Timer
      #FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
      #FUSES NOPROTECT                //Code not protected from reading
      #FUSES BROWNOUT                 //Reset when brownout detected
      #FUSES NOPUT                    //No Power Up Timer
      #FUSES NOCPD                    //No EE protection
      #FUSES STVREN                   //Stack full/underflow will cause reset
      #FUSES NODEBUG                  //No Debug mode for ICD
      //#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
      #FUSES NOLVP
      #FUSES NOWRT                    //Program memory not write protected
      #FUSES NOWRTD                   //Data EEPROM not write protected
      #FUSES NOIESO                   //Internal External Switch Over mode disabled
      #FUSES FCMEN                    //Fail-safe clock monitor enabled
      #FUSES NOWRTC                   //configuration not registers write protected
      #FUSES NOWRTB                   //Boot block not write protected
      #FUSES NOEBTR                   //Memory not protected from table reads
      #FUSES NOEBTRB                  //Boot block not protected from table reads
      #FUSES NOCPB                    //No Boot Block code protection
      #FUSES NOMCLR                     //Master Clear pin enabled
      #FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled
      #fuses PLLEN
      #use delay(clock = 16000000)
      #use fast_io(b)
      #use fast_io(c)
      #use fast_io(d)
#include <sdcard.c>                              // SD card diver source code
#include <fat16.c>                               // FAT16 library source code
#include <ST7735_TFT.c>                          // ST7735 TFT driver source code
char *txt = "erroSr";
char *txt_sd = "DONE";
char *txt_sd1 = "DRAW";
char *txt_sd2 = "DRAW DONE";
char bmp[6] = "A.bmp";

void main()
{
setup_oscillator(OSC_16MHZ|OSC_PLL_ON);
set_tris_d(0b00010000);
set_tris_c(0b00010000);
set_tris_b(0);
delay_ms(500);
sdcard_init();
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  if(fat16_init() == 0){
    while(TRUE){
      drawtext(0, 10, txt_sd1, ST7735_YELLOW, ST7735_WHITE, 2);
      delay_ms(2000);
      bmpDraw(0, 0, bmp);
       delay_ms(2000);
      drawtext(0, 10, txt_sd2, ST7735_YELLOW, ST7735_WHITE, 2);     
    }
 }
  else
    drawtext(0, 10, txt, ST7735_YELLOW, ST7735_BLACK, 2);
}


SdCard Library:

Code:

#include <string.h>

int16 timeout;
enum _card_type{MMC, SDSC, SDHC} g_card_type;
enum sdcard_err{
  sdcard_goodec = 0, sdcard_idle = 0x01, sdcard_timeout = 0x80,
  sdcard_illegal_cmd = 0x04 };

#define GO_IDLE_STATE 0
#define SEND_IF_COND 8
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define WRITE_BLOCK 24
#define SEND_APP_OP_COND 41
#define APP_CMD 55
#define READ_OCR 58

void send_sdcard_command(int8 command, int32 sd_data, int8 sd_crc);
sdcard_err sdcard_init();
sdcard_err sdcard_read_byte(int32 addr, int8* data);
sdcard_err sdcard_read_sector(int32 sector_number, int8* data);
sdcard_err sdcard_read_data(int32 addr, int16 size, int8* data);
sdcard_err sdcard_write_sector(int32 sector_number, int8* data);
sdcard_err sdcard_write_byte(int32 addr, int8 &data);
sdcard_err sdcard_write_data(int32 addr, int16 size, int8 *data);
sdcard_err sdcard_get_r1();
sdcard_err sdcard_get_r7();
sdcard_err sdcard_go_idle_state();
sdcard_err sdcard_send_if_cond();
sdcard_err sdcard_send_app_cmd();
sdcard_err sdcard_sd_send_op_cond();
sdcard_err sdcard_read_ocr(int8* _ocr_byte_3);
sdcard_err sdcard_set_blocklen();
void sdcard_select();
void sdcard_deselect();

sdcard_err sdcard_init(){
  int8 i, resp, ocr_byte_3;
#if defined(SDCARD_SPI_HW)
  SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_64 | SPI_XMIT_L_TO_H);
  #define sdcard_xfer(x)    spi_read(x)
#else
  #if defined(SDCARD_PIN_SCL)
   output_drive(SDCARD_PIN_SCL);
  #endif
  #if defined(SDCARD_PIN_SDO)
   output_drive(SDCARD_PIN_SDO);
  #endif
  #if defined(SDCARD_PIN_SDI)
   output_float(SDCARD_PIN_SDI);
  #endif
    #use spi(MASTER, DI=SDCARD_PIN_SDI, DO=SDCARD_PIN_SDO, CLK=SDCARD_PIN_SCL, BITS=8, MSB_FIRST, MODE=3, baud=1000000)
    #define sdcard_xfer(x)    spi_xfer(x)
#endif
  output_high(SDCARD_PIN_SELECT);
  output_drive(SDCARD_PIN_SELECT);
  delay_ms(250);
  for(i = 0; i < 10; i++)                        // Send 80 cycles
    sdcard_xfer(0xFF);
  sdcard_select();
  resp = sdcard_go_idle_state();                 // Send CMD0
  sdcard_deselect();
  if(resp != sdcard_idle)
    return resp;
  sdcard_select();
  resp = sdcard_send_if_cond();                  // Send CMD8
  sdcard_deselect();
  if(resp != sdcard_idle)
    return resp;
  i = 0;
  do{
    sdcard_select();
    resp = sdcard_send_app_cmd();                // Send CMD58
    if((resp != sdcard_idle) && (resp != sdcard_illegal_cmd) && (resp != 0)){
      sdcard_deselect(); return resp;}
    resp = sdcard_sd_send_op_cond();             // Send ACMD41
    sdcard_deselect();
    delay_ms(100);
    i++;
  } while((resp == 0x01) && (i < 254));
  sdcard_deselect();
  if((resp != 0 || i >= 254) && (resp != sdcard_illegal_cmd))
    return sdcard_timeout;
  if(resp == 0x04){
  g_card_type =  MMC;
  }
  else {
  g_card_type = SDSC;
         // SDSC or SDHC type
  }
  if(g_card_type == SDSC){
    sdcard_select();
    resp = sdcard_read_ocr(&ocr_byte_3);
    sdcard_deselect();
    if(resp != sdcard_idle && resp != sdcard_illegal_cmd)
      return resp;
    if(resp != sdcard_illegal_cmd){
      if(bit_test(ocr_byte_3, 6)){// If bit 30 of the OCR register is 1 (CCS is 1) ==> SDHC type
        g_card_type =  SDHC;   
      }
      }
  }
 
 
  sdcard_select();
  resp = sdcard_set_blocklen();
  sdcard_deselect();
  if(resp != 0)
    return sdcard_timeout;
#if defined(SDCARD_SPI_HW)
  // Speed up the SPI bus
  SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H);
#else
   #use spi(MASTER, DI=SDCARD_PIN_SDI, DO=SDCARD_PIN_SDO, CLK=SDCARD_PIN_SCL, BITS=8, MSB_FIRST, MODE=3)
#endif
  return sdcard_goodec;
}
sdcard_err sdcard_read_byte(int32 addr, int8* data){
  int8 response;
  int16 i, byte_addr;
  int32 sector_number;
  timeout = 0xFFFF;
  sector_number = addr/512;
  if(g_card_type != SDHC)
    sector_number = sector_number << 9;
  byte_addr = addr % 512;
  sdcard_select();
  send_sdcard_command(17, sector_number, 0xFF);
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response == 0xFE){
      for(i = 0; i < byte_addr; i++)
        sdcard_xfer(0xFF);
      *data = sdcard_xfer(0xFF);
      byte_addr++;
      for(i = byte_addr; i < 512; i++)
        sdcard_xfer(0xFF);
      for(i = 0; i < 2; i++)
        sdcard_xfer(0xFF);
      sdcard_deselect();
      return sdcard_goodec;
    }
    timeout--;
  }
  sdcard_deselect();
  return sdcard_timeout;
}
sdcard_err sdcard_read_sector(int32 sector_number, int8* data){
  int8 response;
  int16 i;
  timeout = 0xFFFF;
  sdcard_select();
  for(i = 0; i < 10; i++)  sdcard_xfer(0xFF);
  sdcard_deselect();
  if(g_card_type != SDHC)
    sector_number = sector_number << 9;
  sdcard_select();
  send_sdcard_command(17, sector_number, 0xFF);
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response == 0xFE){
      for(i = 0; i < 512; i++)
        data[i] = sdcard_xfer(0xFF);
      for(i = 0; i < 2; i++)
        sdcard_xfer(0xFF);
      sdcard_deselect();
      return sdcard_goodec;
    }
    timeout--;
  }
  sdcard_deselect();
  return sdcard_timeout;
}
sdcard_err sdcard_read_data(int32 addr, int16 size, int8* data){
  int8 response = 0;
  int16 i, byte_addr, byte_number = 0;
  int32 sector_number;
  timeout = 0xFFFF;
  sector_number = addr/512;
  byte_addr = addr % 512;
  next_sector:
  if(g_card_type != SDHC)
    sector_number = sector_number << 9;
  sdcard_select();
  send_sdcard_command(17, sector_number, 0xFF);
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response == 0xFE){
      for(i = 0; i < byte_addr; i++)
        sdcard_xfer(0xFF);
      if((byte_addr + size) < 512){
        size += byte_addr;
        for(i = byte_addr; i < size; i++, byte_number++)
          data[byte_number] = sdcard_xfer(0xFF);
        for(i = size; i < 512; i++)
          sdcard_xfer(0xFF);
      }
      else{
        for(i = byte_addr; i < 512; i++, byte_number++, size--)
          data[byte_number] = sdcard_xfer(0xFF);
        for(i = 0; i < 2; i++)
          sdcard_xfer(0xFF);
        sdcard_deselect();
        if(g_card_type != SDHC)
          sector_number = sector_number >> 9;
        sector_number++;
        byte_addr = 0;
        goto next_sector;
      }
      for(i = 0; i < 2; i++)
        sdcard_xfer(0xFF);
      sdcard_deselect();
      return sdcard_goodec;
    }
    timeout--;
  }
  sdcard_deselect();
  return sdcard_timeout;
}
#if defined(SDCARD_WRITE)
sdcard_err sdcard_write_sector(int32 sector_number, int8 *data){
  int8 response;
  int16 i;
  timeout = 0xFFFF;
  if(g_card_type != SDHC)
    sector_number = sector_number << 9;
  sdcard_select();
  send_sdcard_command(24, sector_number, 0xFF);
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response != 0xFF){
      sdcard_xfer(0xFE);
      for(i = 0; i < 512; i++)                   // Send 512 data bytes
        sdcard_xfer(data[i]);
      for(i = 0; i < 2; i++)                     // Send 2 CRC bytes
        sdcard_xfer(0xFF);
      // get the error code back from the card; "data accepted" is 0bXXX00101
      response = sdcard_get_r1();
      if(response & 0x0A){
        sdcard_deselect();
        return response;
      }
      // wait for the line to go back high, this indicates that the write is complete
      while(sdcard_xfer(0xFF) == 0);
      sdcard_deselect();
      return sdcard_goodec;
    }
    timeout--;
  }
  sdcard_deselect();
  return sdcard_timeout;
}
sdcard_err sdcard_write_byte(int32 addr, int8 &data){
  int8  sector_data[512];
  int16 byte_addr;
  int32 sector_number;
  sector_number = addr/512;
  byte_addr = addr % 512;
  if(sdcard_read_sector(sector_number, sector_data) != 0)
    return 1;
  sector_data[byte_addr] = data;
  if(sdcard_write_sector(sector_number, sector_data) != 0)
    return 1;
  return sdcard_goodec;
}
sdcard_err sdcard_write_data(int32 addr, int16 size, int8 *data){
  int8  sector_data[512];
  int16 i, byte_addr;
  int32 sector_number;
  sector_number = addr/512;
  byte_addr = addr % 512;
  next_sector:
  if(sdcard_read_sector(sector_number, sector_data) != 0)
    return 1;
  if(byte_addr + size < 513){
    size += byte_addr;
    for(i = byte_addr; i < size; i++)  sector_data[i] = data[i - byte_addr];
    if(sdcard_write_sector(sector_number, sector_data) != 0)
      return 1;
  }
  else{
    for(i = byte_addr; i < 512; i++, size--)  sector_data[i] = data[i - byte_addr];
    if(sdcard_write_sector(sector_number, sector_data) != 0)
      return 1;
    byte_addr = 0;
    sector_number++;
    goto next_sector;
  }
  return sdcard_goodec;
}
#endif
void send_sdcard_command(int8 command, int32 sd_data, int8 sd_crc){
  int8 i;
  sdcard_xfer(0x40 | command);
  for(i = 0; i < 4; i++)
    sdcard_xfer(sd_data >> (3 - i) * 8);
  sdcard_xfer(sd_crc);
}
sdcard_err sdcard_go_idle_state(){
  send_sdcard_command(GO_IDLE_STATE, 0, 0x95);
  return sdcard_get_r1();
}
sdcard_err sdcard_send_if_cond(){
  send_sdcard_command(SEND_IF_COND, 0x1AA, 0x87);
  return sdcard_get_r7();
}
sdcard_err sdcard_send_app_cmd(){
  send_sdcard_command(APP_CMD, 0, 0xFF);
  return sdcard_get_r1();
}
sdcard_err sdcard_sd_send_op_cond(){
  send_sdcard_command(SEND_APP_OP_COND, 0x40000000, 0xFF);
  return sdcard_get_r1();
}
sdcard_err sdcard_read_ocr(int8* _ocr_byte_3){
  unsigned int8 i, response;
  timeout = 0xFFFF;
  send_sdcard_command(READ_OCR, 0, 0xFF);
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response != 0xFF){
      if(response == 0x04) return response;
      *_ocr_byte_3 = sdcard_xfer(0xFF);
      for(i = 0; i < 3; i++)
        sdcard_xfer(0xFF);
      return sdcard_idle;
    timeout--;
    }
  }
  return sdcard_timeout;
}
sdcard_err sdcard_set_blocklen(){
  send_sdcard_command(SET_BLOCKLEN, 512, 0xFF);
  return sdcard_get_r1();
}
sdcard_err sdcard_get_r1(){
  int8 response = 0;
  timeout = 0xFFFF;
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response != 0xFF){
      return response;
    }
    timeout--;
  }
  return sdcard_timeout;
}
sdcard_err sdcard_get_r7(){
  int8 i, response = 0;
  timeout = 0xFFFF;
  while(timeout){
    response = sdcard_xfer(0xFF);
    if(response != 0xFF){
      for(i = 0; i < 4; i++)
        sdcard_xfer(0xFF);
      return sdcard_idle;
    }
    timeout--;
  }
  return sdcard_timeout;
}
void sdcard_select(){
  output_low(SDCARD_PIN_SELECT);
}
void sdcard_deselect(){
  output_high(SDCARD_PIN_SELECT);
  sdcard_xfer(0xFF);
}


Fat16 Library:
Code:

#include <string.h>
#ignore_warnings    240

int16 Bytes_Per_Cluster, fat_Start;
int32 fat_Length, Root_Dir, Data_Start, address_pointer, file_pointer,
      file_size = 0, mbr_offset = 0;

int1 fat16_init();
int1 fat16_open_file(int8* fname);
int1 fat16_read_byte(int8* data);
int1 fat16_read_data(int32 size, int8* data);
int1 get_file_name(int32 file_entry_addr, int8 sname[]);

int1 sdcard_mbr_offset(int16 address){
  int8 _byte;
  if(sdcard_read_byte(address, &_byte) != 0)             // Read active byte of the partition
    return 1;                                            // Error occured while reading byte
  if(_byte != 0x80)
    return 0;
  sdcard_read_byte(address + 11, &mbr_offset);
  mbr_offset <<= 8;
  sdcard_read_byte(address + 10, &mbr_offset);
  mbr_offset <<= 8;
  sdcard_read_byte(address + 9, &mbr_offset);
  mbr_offset <<= 8;
  sdcard_read_byte(address +  8, &mbr_offset);
  mbr_offset *= 512;
  return 0;
}
int1 fat16_init(){
  const int8 *fat_sign = {"FAT"};
  int1 ec = 0;
  int8 FATs, Sectors_Per_Cluster, *file_system[3];
  int16  Bytes_Per_Sector, Reserved_Sectors, Root_Entries, Small_Sectors, Sectors_Per_FAT;
  int32  Hidden_Sectors, Total_Sectors;
  if(sdcard_init() != 0)
    return 1;                                      // Error initializing the SD card
  // Check the first sector of the SD card (MBR, FAT16 or FAT32)
  sdcard_read_data(0x52, 3, file_system);
  if(strcmp(file_system, fat_sign) == 0)
    return 1;                                      // SD Card with FAT32 system and without MBR (FAT32 not supported)
  sdcard_read_data(0x36, 3, file_system);
  if(strcmp(file_system, fat_sign) != 0){
  // First sector contains MBR (not FAT16 volume boot sector) ==> search for the first active partition offset
    if(sdcard_mbr_offset(0x1EE) != 0)  return 1;
    if(sdcard_mbr_offset(0x1DE) != 0)  return 1;
    if(sdcard_mbr_offset(0x1CE) != 0)  return 1;
    if(sdcard_mbr_offset(0x1BE) != 0)  return 1;
  }
  ec |= sdcard_read_byte(mbr_offset + 0x0C, &Bytes_Per_Sector);
  Bytes_Per_Sector <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x0B, &Bytes_Per_Sector);
  ec |= sdcard_read_byte(mbr_offset + 0x0D, &Sectors_Per_Cluster);
  ec |= sdcard_read_byte(mbr_offset + 0x0F, &Reserved_Sectors);
  Reserved_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x0E, &Reserved_Sectors);
  ec |= sdcard_read_byte(mbr_offset + 0x10, &FATs);
  ec |= sdcard_read_byte(mbr_offset + 0x12, &Root_Entries);
  Root_Entries <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x11, &Root_Entries);
  ec |= sdcard_read_byte(mbr_offset + 0x14, &Small_Sectors);
  Small_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x13, &Small_Sectors);
  ec |= sdcard_read_byte(mbr_offset + 0x17, &Sectors_Per_FAT);
  Sectors_Per_FAT <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x16, &Sectors_Per_FAT);
  ec |= sdcard_read_byte(mbr_offset + 0x1F, &Hidden_Sectors);
  Hidden_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x1E, &Hidden_Sectors);
  Hidden_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x1D, &Hidden_Sectors);
  Hidden_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x1C, &Hidden_Sectors);
  ec |= sdcard_read_byte(mbr_offset + 0x23, &Total_Sectors);
  Total_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x22, &Total_Sectors);
  Total_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x21, &Total_Sectors);
  Total_Sectors <<= 8;
  ec |= sdcard_read_byte(mbr_offset + 0x20, &Total_Sectors);
  if(ec != 0)
    return 1;
  Bytes_Per_Cluster = Sectors_Per_Cluster * Bytes_Per_Sector;
  fat_Length = Sectors_Per_FAT * (int32)Bytes_Per_Sector;
  fat_Start = mbr_offset + Reserved_Sectors * Bytes_Per_Sector;
  Root_Dir = fat_Start + (FATs * fat_Length);
  Data_Start = mbr_offset + (Root_Entries * 0x20) + (Bytes_Per_Sector - 1);
  Data_Start /= Bytes_Per_Sector;
  Data_Start += Reserved_Sectors + (FATs * Sectors_Per_FAT);
  Data_Start *= Bytes_Per_Sector;
  return 0;
}
int1 fat16_open_file(int8* fname){
  int1 ok = 0, ec = 0;
  int8 target_file[13], s_name[12];
  int8 fname_parse_pos = 0, name_size;
  int16 start_cluster;
  int32 addr;
  while(fname[fname_parse_pos] != '\0' && fname_parse_pos < 13){
    target_file[fname_parse_pos] = tolower(fname[fname_parse_pos]);
    fname_parse_pos++;
   }
   target_file[fname_parse_pos] = '\0';
   name_size = strlen(target_file);
   if(name_size > 12)
     return 1;                                   // Return error (file name not supported)
   addr = Root_Dir;
   while(addr < Data_Start && ok == 0){
     if(get_file_name(addr, s_name) != 0)
       return 1;                                 // Error reading file name from the SD card
     if(strcmp(s_name, target_file) == 0)
       ok = 1;
     if(ok == 0)
       addr += 32;
   }
   if(ok == 0)
     return 1;                                   // File not found ==> Error
  ec |= sdcard_read_byte(addr + 0x1F, &file_size);
  file_size <<= 8;
  ec |= sdcard_read_byte(addr + 0x1E, &file_size);
  file_size <<= 8;
  ec |= sdcard_read_byte(addr + 0x1D, &file_size);
  file_size <<= 8;
  ec |= sdcard_read_byte(addr + 0x1C, &file_size);
  ec |= sdcard_read_byte(addr + 0x1B, &start_cluster);
  start_cluster <<= 8;
  ec |= sdcard_read_byte(addr + 0x1A, &start_cluster);
  address_pointer = Data_Start + (int32)(start_cluster - 2) * (int32)Bytes_Per_Cluster;
  file_pointer = 0;
  return ec;
}
int1 fat16_read_byte(int8* data){
  if(file_pointer == file_size)
    return 1;
  if(sdcard_read_byte(address_pointer, &*data) != 0)
    return 1;                                    // Error reading data
  address_pointer++;
  file_pointer++;
  return 0;
}
int1 fat16_read_data(int32 fsize, int8* fdata){
  if(file_pointer == file_size)
    return 1;
  if(file_pointer + fsize > file_size){
    fsize = file_size - file_pointer;
    fdata[fsize] = '\0';
  }
  if(sdcard_read_data(address_pointer, fsize, fdata) != 0)
    return 1;                                    // Error reading data
  address_pointer += fsize;
  file_pointer += fsize;
  return 0;
}
int1 get_file_name(int32 file_entry_addr, int8 sname[]){
  int8 buf, i, j = 0;
  for(i = 0; i < 11; i++){
    if(sdcard_read_byte(i + file_entry_addr, &buf) != 0)
      return 1;
    if(buf != ' '){         
      sname[j] = tolower(buf);
      j += 1;
    }
  }
  sname[j] = '\0';
  if(strlen(sname) > 3){
    j = strlen(sname);
    for(i=j; i > j-3; --i)
      sname[i] = sname[i-1];
    sname[i] = '.';
  }
  sname[j+1] = '\0';
  return 0;
}


ST7735 Library:
Code:

#include <string.h>
int1 wrap = TRUE;
unsigned int8 colstart = 0, rowstart = 0, _tft_type;
#define _swap(a, b) { signed int16 t; t = a; a = b; b = t;}
#define _width         128
#define _height        160
#define ST7735_NOP     0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID   0x04
#define ST7735_RDDST   0x09
#define ST7735_SLPIN   0x10
#define ST7735_SLPOUT  0x11
#define ST7735_PTLON   0x12
#define ST7735_NORON   0x13
#define ST7735_INVOFF  0x20
#define ST7735_INVON   0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON  0x29
#define ST7735_CASET   0x2A
#define ST7735_RASET   0x2B
#define ST7735_RAMWR   0x2C
#define ST7735_RAMRD   0x2E
#define ST7735_PTLAR   0x30
#define ST7735_VSCRDEF 0x33
#define ST7735_COLMOD  0x3A
#define ST7735_MADCTL  0x36
#define ST7735_VSCRSADD 0x37
#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR  0xB4
#define ST7735_DISSET5 0xB6
#define ST7735_PWCTR1  0xC0
#define ST7735_PWCTR2  0xC1
#define ST7735_PWCTR3  0xC2
#define ST7735_PWCTR4  0xC3
#define ST7735_PWCTR5  0xC4
#define ST7735_VMCTR1  0xC5
#define ST7735_RDID1   0xDA
#define ST7735_RDID2   0xDB
#define ST7735_RDID3   0xDC
#define ST7735_RDID4   0xDD
#define ST7735_PWCTR6  0xFC
#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1
// Color definitions
#define   ST7735_BLACK   0x0000
#define   ST7735_BLUE    0x001F
#define   ST7735_RED     0xF800
#define   ST7735_GREEN   0x07E0
#define   ST7735_CYAN    0x07FF
#define   ST7735_MAGENTA 0xF81F
#define   ST7735_YELLOW  0xFFE0
#define   ST7735_WHITE   0xFFFF
const char Font[] = {
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46
};
const char Font2[] = {
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02
};
void spiwrite(unsigned int8 spidata){
  #ifndef TFT_SPI_HARDWARE
    int8 ss;
    for(ss = 0x80; ss; ss >>= 1) {
    if (spidata & ss)  output_high(TFT_DATA);
    else               output_low(TFT_DATA);
    output_high(TFT_CLK);
    output_low(TFT_CLK);}
  #else
    spi_write(spidata);
  #endif
}
void write_command(unsigned int8 cmd_){
  output_low(TFT_DC);
  output_low(TFT_CS);
  spiwrite(cmd_);
  output_high(TFT_CS);
}
void write_data(unsigned int8 data_){
  output_high(TFT_DC);
  output_low(TFT_CS);
  spiwrite(data_);
  output_high(TFT_CS);
}
void Bcmd(){
  write_command(ST7735_SWRESET);
  delay_ms(50);
  write_command(ST7735_SLPOUT);
  delay_ms(500);
  write_command(ST7735_COLMOD);
  write_data(0x05);
  delay_ms(10);
  write_command(ST7735_FRMCTR1);
  write_data(0x00);
  write_data(0x06);
  write_data(0x03);
  delay_ms(10);
  write_command(ST7735_MADCTL);
  write_data(0x08);
  write_command(ST7735_DISSET5);
  write_data(0x15);
  write_data(0x02);
  write_command(ST7735_INVCTR);
  write_data(0x00);
  write_command(ST7735_PWCTR1);
  write_data(0x02);
  write_data(0x70);
  delay_ms(10);
  write_command(ST7735_PWCTR2);
  write_data(0x05);
  write_command(ST7735_PWCTR3);
  write_data(0x01);
  write_data(0x02);
  write_command(ST7735_VMCTR1);
  write_data(0x3C);
  write_data(0x38);
  delay_ms(10);
  write_command(ST7735_PWCTR6);
  write_data(0x11);
  write_data(0x15);
  write_command(ST7735_GMCTRP1);
  write_data(0x09); write_data(0x16); write_data(0x09); write_data(0x20);
  write_data(0x21); write_data(0x1B); write_data(0x13); write_data(0x19);
  write_data(0x17); write_data(0x15); write_data(0x1E); write_data(0x2B);
  write_data(0x04); write_data(0x05); write_data(0x02); write_data(0x0E);
  write_command(ST7735_GMCTRN1);
  write_data(0x0B); write_data(0x14); write_data(0x08); write_data(0x1E);
  write_data(0x22); write_data(0x1D); write_data(0x18); write_data(0x1E);
  write_data(0x1B); write_data(0x1A); write_data(0x24); write_data(0x2B);
  write_data(0x06); write_data(0x06); write_data(0x02); write_data(0x0F);
  delay_ms(10);
  write_command(ST7735_CASET);
  write_data(0x00); write_data(0x02); write_data(0x08); write_data(0x81);
  write_command(ST7735_RASET);
  write_data(0x00); write_data(0x01); write_data(0x08); write_data(0xA0);
  write_command(ST7735_NORON);
  delay_ms(10);
  write_command(ST7735_DISPON);
  delay_ms(500);
}
void Rcmd1(){
  write_command(ST7735_SWRESET);
  delay_ms(150);
  write_command(ST7735_SLPOUT);
  delay_ms(500);
  write_command(ST7735_FRMCTR1);
  write_data(0x01);
  write_data(0x2C);
  write_data(0x2D);
  write_command(ST7735_FRMCTR2);
  write_data(0x01);
  write_data(0x2C);
  write_data(0x2D);
  write_command(ST7735_FRMCTR3);
  write_data(0x01); write_data(0x2C); write_data(0x2D);
  write_data(0x01); write_data(0x2C); write_data(0x2D);
  write_command(ST7735_INVCTR);
  write_data(0x07);
  write_command(ST7735_PWCTR1);
  write_data(0xA2);
  write_data(0x02);
  write_data(0x84);
  write_command(ST7735_PWCTR2);
  write_data(0xC5);
  write_command(ST7735_PWCTR3);
  write_data(0x0A);
  write_data(0x00);
  write_command(ST7735_PWCTR4);
  write_data(0x8A);
  write_data(0x2A);
  write_command(ST7735_PWCTR5);
  write_data(0x8A);
  write_data(0xEE);
  write_command(ST7735_VMCTR1);
  write_data(0x0E);
  write_command(ST7735_INVOFF);
  write_command(ST7735_MADCTL);
  write_data(0xC8);
  write_command(ST7735_COLMOD);
  write_data(0x05);
}
void Rcmd2green(){
  write_command(ST7735_CASET);
  write_data(0x00); write_data(0x02);
  write_data(0x00); write_data(0x7F + 0x02);
  write_command(ST7735_RASET);
  write_data(0x00); write_data(0x01);
  write_data(0x00); write_data(0x9F + 0x01);
}
void Rcmd2red(){
  write_command(ST7735_CASET);
  write_data(0x00); write_data(0x00);
  write_data(0x00); write_data(0x7F);
  write_command(ST7735_RASET);
  write_data(0x00); write_data(0x00);
  write_data(0x00); write_data(0x9F);
}
void Rcmd3(){
  write_command(ST7735_GMCTRP1);
  write_data(0x02); write_data(0x1C); write_data(0x07); write_data(0x12);
  write_data(0x37); write_data(0x32); write_data(0x29); write_data(0x2D);
  write_data(0x29); write_data(0x25); write_data(0x2B); write_data(0x39);
  write_data(0x00); write_data(0x01); write_data(0x03); write_data(0x10);
  write_command(ST7735_GMCTRN1);
  write_data(0x03); write_data(0x1D); write_data(0x07); write_data(0x06);
  write_data(0x2E); write_data(0x2C); write_data(0x29); write_data(0x2D);
  write_data(0x2E); write_data(0x2E); write_data(0x37); write_data(0x3F);
  write_data(0x00); write_data(0x00); write_data(0x02); write_data(0x10);
  write_command(ST7735_NORON);
  delay_ms(10);
  write_command(ST7735_DISPON);
  delay_ms(100);
}
void setAddrWindow(unsigned int8 x0, unsigned int8 y0, unsigned int8 x1, unsigned int8 y1){
  write_command(ST7735_CASET);
  write_data(0);
  write_data(x0 + colstart);
  write_data(0);
  write_data(x1 + colstart);
  write_command(ST7735_RASET);
  write_data(0);
  write_data(y0 + rowstart);
  write_data(0);
  write_data(y1 + rowstart);
  write_command(ST7735_RAMWR); // Write to RAM
}
void drawPixel(unsigned int8 x, unsigned int8 y, unsigned int16 color){
  if((x >= _width) || (y >= _height))
    return;
  setAddrWindow(x,y,x+1,y+1);
  write_data(color >> 8);
  write_data(color & 0xFF);
}
void fillRectangle(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color){
  unsigned int8 hi, lo;
  if((x >= _width) || (y >= _height))
    return;
  if((x + w - 1) >= _width) 
    w = _width  - x;
  if((y + h - 1) >= _height)
    h = _height - y;
  setAddrWindow(x, y, x+w-1, y+h-1);
  hi = color >> 8; lo = color;
  output_high(tft_dc);
  output_low(tft_cs);
  for(y=h; y>0; y--) {
    for(x = w; x > 0; x--) {
      spiwrite(hi);
      spiwrite(lo);
    }
  }
  output_high(tft_cs);
}
void fillScreen(unsigned int16 color) {
  fillRectangle(0, 0, _width, _height, color);
}
void drawFastVLine(unsigned int8 x, unsigned int8 y, unsigned int8 h, unsigned int16 color){
  unsigned int8 hi, lo;
  if((x >= _width) || (y >= _height))
    return;
  if((y + h - 1) >= _height)
    h = _height - y;
  hi = color >> 8; lo = color;
  setAddrWindow(x, y, x, y + h - 1);
  output_high(tft_dc);
  output_low(tft_cs);
  while (h--) {
    spiwrite(hi);
    spiwrite(lo);
  }
  output_high(tft_cs);
}
void drawFastHLine(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int16 color){
  unsigned int8 hi, lo;
  if((x >= _width) || (y >= _height))
    return;
  if((x + w - 1) >= _width)
    w = _width - x;
  hi = color >> 8; lo = color;
  setAddrWindow(x, y, x + w - 1, y);
  output_high(tft_dc);
  output_low(tft_cs);
  while (w--) {
    spiwrite(hi);
    spiwrite(lo);
  }
  output_high(tft_cs);
}
void drawCircle(signed int16 x0, signed int16 y0, signed int16 r, unsigned int16 color) {
  signed int16 f, ddF_x, ddF_y, x, y;
  f = 1 - r, ddF_x = 1, ddF_y = -2 * r, x = 0, y = r;
  drawPixel(x0  , y0 + r, color);
  drawPixel(x0  , y0 - r, color);
  drawPixel(x0+r, y0    , color);
  drawPixel(x0-r, y0    , color);
  while (x < y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
    drawPixel(x0 + x, y0 + y, color);
    drawPixel(x0 - x, y0 + y, color);
    drawPixel(x0 + x, y0 - y, color);
    drawPixel(x0 - x, y0 - y, color);
    drawPixel(x0 + y, y0 + x, color);
    drawPixel(x0 - y, y0 + x, color);
    drawPixel(x0 + y, y0 - x, color);
    drawPixel(x0 - y, y0 - x, color);
  }
}
void drawCircleHelper(signed int16 x0, signed int16 y0, signed int16 r, unsigned int8 cornername, unsigned int16 color) {
  signed int16 f, ddF_x, ddF_y, x, y;
  f = 1 - r, ddF_x = 1, ddF_y = -2 * r, x = 0, y = r;
  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;
    if (cornername & 0x4) {
      drawPixel(x0 + x, y0 + y, color);
      drawPixel(x0 + y, y0 + x, color);
    }
    if (cornername & 0x2) {
      drawPixel(x0 + x, y0 - y, color);
      drawPixel(x0 + y, y0 - x, color);
    }
    if (cornername & 0x8) {
      drawPixel(x0 - y, y0 + x, color);
      drawPixel(x0 - x, y0 + y, color);
    }
    if (cornername & 0x1) {
      drawPixel(x0 - y, y0 - x, color);
      drawPixel(x0 - x, y0 - y, color);
    }
  }
}
void fillCircleHelper(signed int16 x0, signed int16 y0, signed int16 r, unsigned int8 cornername, signed int16 delta, unsigned int16 color) {
  signed int16 f, ddF_x, ddF_y, x, y;
  f = 1 - r, ddF_x = 1, ddF_y = -2 * r, x = 0, y = r;
  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;

    if (cornername & 0x1) {
      drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
      drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
    }
    if (cornername & 0x2) {
      drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
      drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
    }
  }
}
void fillCircle(signed int16 x0, signed int16 y0, signed int16 r, unsigned int16 color) {
  drawFastVLine(x0, y0 - r, 2 * r + 1, color);
  fillCircleHelper(x0, y0, r, 3, 0, color);
}
void drawRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color){
  drawFastHLine(x, y, w, color);
  drawFastHLine(x, y + h - 1, w, color);
  drawFastVLine(x, y, h, color);
  drawFastVLine(x + w - 1, y, h, color);
}
void drawLine(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, unsigned int16 color){
  signed int16 steep, dx, dy, err, ystep;
  steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    _swap(x0,y0);
    _swap(x1,y1);
  }
  if (x0 > x1) {
    _swap(x0,x1);
    _swap(y0,y1);
  }
  dx = x1 - x0;
  dy = abs(y1-y0);

  err = dx / 2;
  if (y0 < y1) {
    ystep = 1;
  } else {
    ystep = -1;
  }

  for (; x0<=x1; x0++) {
    if (steep) {
      drawPixel(y0, x0, color);
    } else {
      drawPixel(x0, y0, color);
    }
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
void fillRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color) {
  signed int16 i;
  // Update in subclasses if desired!
  for (i = x; i < x + w; i++) {
    drawFastVLine(i, y, h, color);
  }
}
void drawRoundRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int8 r, unsigned int16 color) {
  drawFastHLine(x+r  , y    , w-2*r, color);
  drawFastHLine(x+r  , y+h-1, w-2*r, color);
  drawFastVLine(x    , y+r  , h-2*r, color);
  drawFastVLine(x+w-1, y+r  , h-2*r, color);
  drawCircleHelper(x+r    , y+r    , r, 1, color);
  drawCircleHelper(x+w-r-1, y+r    , r, 2, color);
  drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
  drawCircleHelper(x+r    , y+h-r-1, r, 8, color);
}
void fillRoundRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int8 r, unsigned int16 color) {
  fillRect(x+r, y, w-2*r, h, color);
  fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
  fillCircleHelper(x+r    , y+r, r, 2, h-2*r-1, color);
}
void drawTriangle(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, signed int16 x2, signed int16 y2, unsigned int16 color) {
  drawLine(x0, y0, x1, y1, color);
  drawLine(x1, y1, x2, y2, color);
  drawLine(x2, y2, x0, y0, color);
}
void fillTriangle(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, signed int16 x2, signed int16 y2, unsigned int16 color) {
  signed int16 a, b, y, last, dx01, dy01, dx02, dy02, dx12, dy12, sa, sb;
  // Sort coordinates by Y order (y2 >= y1 >= y0)
  if (y0 > y1) {
    _swap(y0, y1); _swap(x0, x1);
  }
  if (y1 > y2) {
    _swap(y2, y1); _swap(x2, x1);
  }
  if (y0 > y1) {
    _swap(y0, y1); _swap(x0, x1);
  }
  if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
    a = b = x0;
    if(x1 < a)      a = x1;
    else if(x1 > b) b = x1;
    if(x2 < a)      a = x2;
    else if(x2 > b) b = x2;
    drawFastHLine(a, y0, b-a+1, color);
    return;
  }
    dx01 = x1 - x0;
    dy01 = y1 - y0;
    dx02 = x2 - x0;
    dy02 = y2 - y0;
    dx12 = x2 - x1;
    dy12 = y2 - y1;
    sa   = 0;
    sb   = 0;
  if(y1 == y2) last = y1;   // Include y1 scanline
  else         last = y1-1; // Skip it
  for(y=y0; y<=last; y++) {
    a   = x0 + sa / dy01;
    b   = x0 + sb / dy02;
    sa += dx01;
    sb += dx02;
    /* longhand:
    a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
    b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    */
    if(a > b) _swap(a,b);
    drawFastHLine(a, y, b - a + 1, color);
  }
  // For lower part of triangle, find scanline crossings for segments
  // 0-2 and 1-2.  This loop is skipped if y1=y2.
  sa = dx12 * (y - y1);
  sb = dx02 * (y - y0);
  for(; y<=y2; y++) {
    a   = x1 + sa / dy12;
    b   = x0 + sb / dy02;
    sa += dx12;
    sb += dx02;
    /* longhand:
    a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
    b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    */
    if(a > b) _swap(a,b);
    drawFastHLine(a, y, b-a+1, color);
  }
}
void drawChar(unsigned int8 x, unsigned int8 y, unsigned int8 c, unsigned int16 color, unsigned int16 bg,  unsigned int8 size){
  int8 i, j;
  if((x >= _width) || (y >= _height))
    return;
  if(size < 1) size = 1;
  if((c < ' ') || (c > '~'))
    c = '?';
  for(i=0; i<5; i++ ) {
    unsigned int8 line;
    if(c < 'S')
      line = font[(c - 32)*5 + i];
    else
      line = font2[(c - 'S')*5 + i];
    for(j=0; j<7; j++, line >>= 1) {
      if(line & 0x01) {
        if(size == 1) drawPixel(x+i, y+j, color);
        else          fillRect(x+(i*size), y+(j*size), size, size, color);
      }
      else if(bg != color) {
           if(size == 1) drawPixel(x+i, y+j, bg);
           else          fillRect(x+i*size, y+j*size, size, size, bg);
        }
    }
  }
}
void setTextWrap(int1 w){
  wrap = w;
}
void drawtext(unsigned int8 x, unsigned int8 y, char *_text, unsigned int16 color, unsigned int16 bg, unsigned int8 size){
  unsigned int8 cursor_x, cursor_y;
  unsigned int16 textsize, i;
  cursor_x = x, cursor_y = y;
  textsize = strlen(_text);
  for(i = 0; i < textsize; i++){
    if(wrap && ((cursor_x + size * 5) > _width)){
      cursor_x = 0;
      cursor_y = cursor_y + size * 7 + 3 ;
      if(cursor_y > _height) cursor_y = _height;
      if(_text[i] == 0x20) goto _skip; }
    drawChar(cursor_x, cursor_y, _text[i], color, bg, size);
    cursor_x = cursor_x + size * 6;
    if(cursor_x > _width) cursor_x = _width;
    _skip:;}
}
void invertDisplay(int1 i) {
  if(i)
    write_command(ST7735_INVON);
  else
    write_command(ST7735_INVOFF);
}
void setScrollDefinition(unsigned int8 top_fix_height, unsigned int8 bottom_fix_height, int1 _scroll_direction){
  unsigned int8 scroll_height;
  scroll_height = _height - top_fix_height - bottom_fix_height;
  write_command(ST7735_VSCRDEF);
  write_data(0x00);
  write_data(top_fix_height);
  write_data(0x00);
  write_data(scroll_height);
  write_data(0x00);
  write_data(bottom_fix_height);
  write_command(ST7735_MADCTL);
  if(_scroll_direction){
    if(_tft_type == 0){
      write_data(0xD8);
    }
    if(_tft_type == 1){
      write_data(0xD0);
    }
    if(_tft_type == 2){
      write_data(0x18);
    }
  }
  else{
    if(_tft_type == 0){
      write_data(0xC8);
    }
    if(_tft_type == 1){
      write_data(0xC0);
    }
    if(_tft_type == 2){
      write_data(0x08);
    }
  }
}
void VerticalScroll(unsigned int8 _vsp) {
  write_command(ST7735_VSCRSADD);
  write_data(0x00);
  write_data(_vsp);
}
void NormalDisplay(){
  write_command(ST7735_NORON);
}
int16 Color565(int16 r, int16 g, int16 b){           // Convert 24-bit color to 16-bit color
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
void pushColor(unsigned int16 color){
  unsigned int8 hi, lo;
  hi = color >> 8; lo = color;
  output_high(tft_dc);
  output_low(tft_cs);
  spiwrite(hi);
  spiwrite(lo);
  output_high(tft_cs);
}

void TFT_GreenTab_Initialize(){
  output_high(TFT_CS);
  output_low(TFT_DC);
  output_drive(TFT_CS);
  output_drive(TFT_DC);
  #ifndef TFT_SPI_HARDWARE
    output_low(TFT_CLK);
    output_low(TFT_DATA);
    output_drive(TFT_CLK);
    output_drive(TFT_DATA);
  #else
    SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H);
  #endif
  Rcmd1();
  Rcmd2green();
  Rcmd3();
  colstart = 2;
  rowstart = 1;
  _tft_type = 0;
}
void TFT_RedTab_Initialize(){
  output_high(TFT_CS);
  output_low(TFT_DC);
  output_drive(TFT_CS);
  output_drive(TFT_DC);
  #ifndef TFT_SPI_HARDWARE
    output_low(TFT_CLK);
    output_low(TFT_DATA);
    output_drive(TFT_CLK);
    output_drive(TFT_DATA);
  #else
    SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H);
  #endif
  Rcmd1();
  Rcmd2red();
  Rcmd3();
  _tft_type = 0;
}
void TFT_BlackTab_Initialize(){
  output_high(TFT_CS);
  output_low(TFT_DC);
  output_drive(TFT_CS);
  output_drive(TFT_DC);
  #ifndef TFT_SPI_HARDWARE
    output_low(TFT_CLK);
    output_low(TFT_DATA);
    output_drive(TFT_CLK);
    output_drive(TFT_DATA);
  #else
    SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H);
  #endif
  Rcmd1();
  Rcmd2red();
  Rcmd3();
  write_command(ST7735_MADCTL);
  write_data(0xC0);
  _tft_type = 1;
}
void TFT_ST7735B_Initialize(){
  output_high(TFT_CS);
  output_low(TFT_DC);
  output_drive(TFT_CS);
  output_drive(TFT_DC);
  #ifndef TFT_SPI_HARDWARE
    output_low(TFT_CLK);
    output_low(TFT_DATA);
    output_drive(TFT_CLK);
    output_drive(TFT_DATA);
  #else
    SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H);
  #endif
  Bcmd();
  _tft_type = 2;
}

#if defined (DRAW_BMP_FROM_MMCSD_CARD)                       // Additional code for drawing BMP image files from MMC/SD card
#ifndef pixel_buffer
#define pixel_buffer  10
#endif
int1 bmpDraw(int8 x, int8 y, int8 *bmpname){
  int1 ec = 0, padding = 0;
  int8 bmpdata[pixel_buffer * 3],
       planes, depth, r, g, b, col, row;
  int16 i, buffer = pixel_buffer * 3, format, bmpHeight, bmpWidth, color;
  int32 offset, compression, bmp_size, row_size, padding_factor;
  if((x >= _width) || (y >= _height))
    return 1;
  if(fat16_open_file(bmpname) != 0)
    return 1;
  ec |= sdcard_read_byte(address_pointer + 1, &format);
  format <<= 8;
  ec |= sdcard_read_byte(address_pointer, &format);
  if(format != 0x4D42)                                             // BMP file format signature
    return 1;                                                      // Return error
  ec |= sdcard_read_byte(address_pointer + 0x0D, &offset);
  offset <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x0C, &offset);
  offset <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x0B, &offset);
  offset <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x0A, &offset);
  ec |= sdcard_read_byte(address_pointer + 0x13, &bmpWidth);
  bmpWidth <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x12, &bmpWidth);
  ec |= sdcard_read_byte(address_pointer + 0x17, &bmpHeight);
  bmpHeight <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x16, &bmpHeight);
  ec |= sdcard_read_byte(address_pointer + 0x1A, &planes);
  ec |= sdcard_read_byte(address_pointer + 0x1C, &depth);
  ec |= sdcard_read_byte(address_pointer + 0x21, &compression);
  compression <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x20, &compression);
  compression <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x1f, &compression);
  compression <<= 8;
  ec |= sdcard_read_byte(address_pointer + 0x1e, &compression);
  if(ec != 0 || compression != 0 || depth != 24 || planes != 1)
    return 1;
  bmp_size = file_size - offset;                                // bmp_size: BMP image raw size
  row_size = bmp_size / bmpHeight;                                   // row_size: number of bytes per row
  if((x + bmpWidth  - 1) >=  _width){                              // _width is the TFT screen width
    bmpWidth = _width  - x;
    padding = 1;                                                //Padding = 1 ==> only upper left part will be displayed
    padding_factor = bmpWidth / pixel_buffer;
    if(bmpWidth % pixel_buffer)
      padding_factor++;
    padding_factor *= buffer;
  }
  if((y + bmpHeight - 1) >= _height){                              // _height is the TFT screen height
    offset += row_size * (bmpHeight - _height + y);                // Only upper part will be displayed
    bmpHeight = _height - y;
  }
  file_pointer     = offset;
  address_pointer += offset;
  i = buffer;
  if(_tft_type != 2){
  // Change row address order
    write_command(ST7735_MADCTL);
    if(_tft_type == 0)
      write_data(0x48);
    if(_tft_type == 1)
      write_data(0x40);
  }
  setAddrWindow(x , y, x + bmpWidth - 1, y + bmpHeight - 1);
  for(row = 0; row < bmpHeight; row++){
    for(col = 0; col < bmpWidth; col++){
      if(i >= buffer){
        i = 0;
        fat16_read_data(buffer, bmpdata);
      }
      b = bmpdata[i++];
      g = bmpdata[i++];
      r = bmpdata[i++];
      color = Color565(r, g, b);
      pushColor(color);
    }
    if(padding == 1){
      i = buffer;
      file_pointer    += row_size - padding_factor;
      address_pointer += row_size - padding_factor;
    }
  }
  if(_tft_type != 2){
  // Return row address order as it was
    write_command(ST7735_MADCTL);
    if(_tft_type == 0)
      write_data(0xC8);
    if(_tft_type == 1)
      write_data(0xC0);
  }
  // Good BMP
  return 0;
}
#endif
Ttelmah



Joined: 11 Mar 2010
Posts: 19506

View user's profile Send private message

PostPosted: Wed May 25, 2022 1:34 am     Reply with quote

First, change your #use delay, and get rid of the setup_oscillator.

#use delay(internal=64MHz)

//setup_oscillator(OSC_16MHZ|OSC_PLL_ON)

You are currently telling the compiler you are running at 16MHz, but
telling the oscillator to run at 64MHz.

Then use the hardware SPI.

Change in the setup (before you load the sd driver):

Code:

#define   SDCARD_PIN_SELECT  PIN_C2
#define   SDCARD_PIN_SCL     PIN_C3
#define   SDCARD_PIN_SDO     PIN_C5
#define   SDCARD_PIN_SDI     PIN_C4

#PIN_SELECT SDI=SDCARD_PIN_SDI
#PIN_SELECT SDO=SDCARD_PIN_SDO
#PIN_SELECT SCL=SDCARD_PIN_SCL
#define SDCARD_SPI_HW


This tells the driver you are loading to use _hardware_ SPI, and tells
the compiler to select this.
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Wed May 25, 2022 1:51 am     Reply with quote

Ttelmah wrote:
First, change your #use delay, and get rid of the setup_oscillator.

#use delay(internal=64MHz)

//setup_oscillator(OSC_16MHZ|OSC_PLL_ON)

You are currently telling the compiler you are running at 16MHz, but
telling the oscillator to run at 64MHz.

Then use the hardware SPI.

Change in the setup (before you load the sd driver):

Code:

#define   SDCARD_PIN_SELECT  PIN_C2
#define   SDCARD_PIN_SCL     PIN_C3
#define   SDCARD_PIN_SDO     PIN_C5
#define   SDCARD_PIN_SDI     PIN_C4

#PIN_SELECT SDI=SDCARD_PIN_SDI
#PIN_SELECT SDO=SDCARD_PIN_SDO
#PIN_SELECT SCL=SDCARD_PIN_SCL
#define SDCARD_SPI_HW


This tells the driver you are loading to use _hardware_ SPI, and tells
the compiler to select this.



Thank you for your help. I made the changes in the source code. The reading was reduced to 1 minute 35 seconds. But still very slow.

New Source Code:
Code:

#device PIC18F45K22
#include <18F45K22.h>

#define TFT_CS  PIN_B2
#define TFT_DC  PIN_B3
#define TFT_DATA PIN_D4
#define TFT_CLK PIN_D0

#define   SDCARD_PIN_SELECT  PIN_C2
#define   SDCARD_PIN_SCL     PIN_C3
#define   SDCARD_PIN_SDO     PIN_C5
#define   SDCARD_PIN_SDI     PIN_C4
#define SDCARD_SPI_HW

#define   DRAW_BMP_FROM_MMCSD_CARD               // Enable BMP draw from SD card
#define   pixel_buffer  350                      // Set pixel buffer to 500

      #FUSES NOWDT                    //No Watch Dog Timer
      #FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
      #FUSES NOPROTECT                //Code not protected from reading
      #FUSES BROWNOUT                 //Reset when brownout detected
      #FUSES NOPUT                    //No Power Up Timer
      #FUSES NOCPD                    //No EE protection
      #FUSES STVREN                   //Stack full/underflow will cause reset
      #FUSES NODEBUG                  //No Debug mode for ICD
      //#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
      #FUSES NOLVP
      #FUSES NOWRT                    //Program memory not write protected
      #FUSES NOWRTD                   //Data EEPROM not write protected
      #FUSES NOIESO                   //Internal External Switch Over mode disabled
      #FUSES FCMEN                    //Fail-safe clock monitor enabled
      #FUSES NOWRTC                   //configuration not registers write protected
      #FUSES NOWRTB                   //Boot block not write protected
      #FUSES NOEBTR                   //Memory not protected from table reads
      #FUSES NOEBTRB                  //Boot block not protected from table reads
      #FUSES NOCPB                    //No Boot Block code protection
      #FUSES NOMCLR                     //Master Clear pin enabled
      #FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled
      #fuses PLLEN
      #use delay(clock = 16000000)
      #use delay(internal=64MHz)
      #use fast_io(b)
      #use fast_io(c)
      #use fast_io(d)
#include <sdcard.c>                              // SD card diver source code
#include <fat16.c>                               // FAT16 library source code
#include <ST7735_TFT.c>                          // ST7735 TFT driver source code
char *txt = "erroSr";
char *txt_sd = "DONE";
char *txt_sd1 = "DRAW";
char *txt_sd2 = "DRAW DONE";
char bmp[6] = "A.bmp";

void main()
{
//setup_oscillator(OSC_16MHZ|OSC_PLL_ON);

set_tris_d(0b00010000);
set_tris_c(0b00010000);
set_tris_b(0);
delay_ms(500);
sdcard_init();
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  if(fat16_init() == 0){
    while(TRUE){
      drawtext(0, 10, txt_sd1, ST7735_YELLOW, ST7735_WHITE, 2);
      bmpDraw(0, 0, bmp);
      drawtext(0, 10, txt_sd2, ST7735_YELLOW, ST7735_WHITE, 2);     
    }
 }
  else
    drawtext(0, 10, txt, ST7735_YELLOW, ST7735_BLACK, 2);
}
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Wed May 25, 2022 2:26 am     Reply with quote

I noticed something. After using the #define SDCARD_SPI_HW command, it was reduced to 1 minute 34 seconds. So the PIC still works at 16MHZ. It can be 18F45K22 INT OSC 16MHZ. 16X4=64MHZ working with PLL. The processor is still running at 64MHZ.
Ttelmah



Joined: 11 Mar 2010
Posts: 19506

View user's profile Send private message

PostPosted: Wed May 25, 2022 3:01 am     Reply with quote

How old is your compiler?.
The newest compilers correctly program the PLL. However older compilers
sometimes did not.
If you have an old compiler then leave the #use delay at 64MHz,
but put back your setup_oscillator line.

As I said, you were clocking at 64MHz, but you were telling the compiler
you were running at 16MHz. Means any delays in the code would be wrong.

//#use delay(clock = 16000000)
#use delay(internal=64MHz)

You don't want the first of these. _One_ #use delay only.
You are running at 64MHz, not 16MHz.

Seriously, you are writing the data to the display byte by byte, and
the chip doesn't have the room to buffer things a lot, so times are
never going to be super fast.
You need to have at least a sector sized buffer, and read the SD a sector
at a time, not a byte at a time. Look also at rewriting the read code in
this to perform a block read, rather than a byte read when this is used.
Probably at least 10* faster.
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Wed May 25, 2022 8:58 am     Reply with quote

Ttelmah wrote:
How old is your compiler?.
The newest compilers correctly program the PLL. However older compilers
sometimes did not.
If you have an old compiler then leave the #use delay at 64MHz,
but put back your setup_oscillator line.

As I said, you were clocking at 64MHz, but you were telling the compiler
you were running at 16MHz. Means any delays in the code would be wrong.

//#use delay(clock = 16000000)
#use delay(internal=64MHz)

You don't want the first of these. _One_ #use delay only.
You are running at 64MHz, not 16MHz.

Seriously, you are writing the data to the display byte by byte, and
the chip doesn't have the room to buffer things a lot, so times are
never going to be super fast.
You need to have at least a sector sized buffer, and read the SD a sector
at a time, not a byte at a time. Look also at rewriting the read code in
this to perform a block read, rather than a byte read when this is used.
Probably at least 10* faster.


Compeiler version CCS 5.007.
When I use the use delay(internal=64MHz) command alone, I get an error.

#use delay(clock = 16000000);
and
setup_oscillator(OSC_8MHZ|OSC_PLL_ON);

When I set 32MHZ in the form of, I reduced the time to 55 seconds.
Ttelmah



Joined: 11 Mar 2010
Posts: 19506

View user's profile Send private message

PostPosted: Wed May 25, 2022 9:42 am     Reply with quote

Seriously, you need ro update your compiler. 5.007, was beta at best and
has huge numbers of problems
You need to set the clock in the delay statement to _match_ the clock
you are selecting.
If you select 8MHz, with PLL, your clock rate is 32MHz.
If you select 16MHz with PLL, your clock rate is 64MHz.
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Wed May 25, 2022 12:47 pm     Reply with quote

Ttelmah wrote:
Seriously, you need ro update your compiler. 5.007, was beta at best and
has huge numbers of problems
You need to set the clock in the delay statement to _match_ the clock
you are selecting.
If you select 8MHz, with PLL, your clock rate is 32MHz.
If you select 16MHz with PLL, your clock rate is 64MHz.


U re good human. Thanks for all information. Next morning, I try it.
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Fri May 27, 2022 2:36 am     Reply with quote

Hello again.
I got good results with 32 MHZ. But my 64MHZ clock frequency stays high. 48MHZ is my max clock frequency. We can run it internally as 8-16-32-64 with PLL. Can I run 48MHZ with internal oscillator? If yes, how can I do? I reading Datasheet but ı don' have a idea.. :/
Ttelmah



Joined: 11 Mar 2010
Posts: 19506

View user's profile Send private message

PostPosted: Fri May 27, 2022 2:59 am     Reply with quote

No.

Why can't you use 64Mhz?. Is it actually that something else is going to fast
when you select this?. Possibly the SPI to the display?. If so, turn this
down.

However the biggest improvement will be to switch to doing sector based
reads from the SD card. A byt wide read takes massively more time than
using sector based reading.
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Fri May 27, 2022 3:36 am     Reply with quote

I am having trouble reading SD CARD with 64 MHZ clock frequency. Communication is not possible. It actually takes a long time to read the card. After reading the file inside the card (55 seconds), it takes 2 seconds to draw on the screen.
temtronic



Joined: 01 Jul 2010
Posts: 9225
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri May 27, 2022 4:54 am     Reply with quote

while I don't use that PIC.... curious...

his compiler is 5.007 and that was real 'early', potentially a few 'bugs' in it and he's using a newer PIC with Pin select ability.

Perhaps create a very small, simple test program that runs the PIC at 64MHz and sends data out the SPI port at max speed. Look with scope to check the waveform. It should be possible to 'loopback' the data as well.

Also what are the pullup/down resistors for the SD card ?
eaydin



Joined: 25 May 2022
Posts: 10

View user's profile Send private message

PostPosted: Fri May 27, 2022 5:00 am     Reply with quote

hi temtronic.

I have new version compiler. 5.109 version.

sd card pin to pin serial connect on the board. dont have resistor.


Schematic:



TFT Module:
temtronic



Joined: 01 Jul 2010
Posts: 9225
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri May 27, 2022 5:29 am     Reply with quote

sigh, it's the SD card interface that needs the resistors, my mistake...
coffee's not ready....

nice new compiler, so I doubt 'bugs' there....

should still be able to code small loopback test program.
There may be max 'speed' issues with either the SD or LCD unit. That 'should' be listed somewhere on the spec sheets for them.

Hmm... are you powering the PIC with 3 volts ? Most (all ?) external devices are 3 volt, so you should power the PIC with 3V.
Ttelmah



Joined: 11 Mar 2010
Posts: 19506

View user's profile Send private message

PostPosted: Fri May 27, 2022 8:11 am     Reply with quote

Also, Proteus doesn't let us see how capacitors are laid out. The SD card
_requires_ a reasonably large capacitor (22uF or more) as close as
possible to it.
I had assumed that you are using 3.3v. Essential for an SD unless you add
buffering (sticky at the top of the forum about this).
Also, just how the connections are physically made becomes more and
more essential the faster you run the interface.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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