Read/Write BMP file from/to SD card (Baremetal)

Problem 
   Write two functions called readImage/storeImage to read/store a simple bmp image from/into SD card in standalone.

Solution
To be able to work with SD card, we can use the library available at here  (reference)
Note that the following hardware configuration is required
"In the ZYNQ7 processing system the MIO pins 40-45 are connected to the SD card interface.
Since these pins are programmed using the GPIO interface, it is mandatory that the SD0 peripheral in the Zynq Block Design (accessing the same pins) is not selected. On the other hand, the GPIO peripheral needs to be selected." 

The following function can do the read and store simple bmp images.

storeImage

void storeImage(float *imageOut, const char *filename, int rows, int cols, 
                const char* refFilename) {


   unsigned char tmp;
   int offset;
   unsigned char *buffer;
   int i, j;

   int bytes;

   int height, width;



   //========================================
   //  Read/Write File
   //========================================

   UINT bw; // bytes written
   int retval; // return value for Gpio functions


   // Lookup GPIO config table
   XGpioPs_Config * ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);

   // Initialize GPIO
   if ( (retval = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr)) != XST_SUCCESS) {
  printf("Error initalizing GPIO fails\n"); return retval;}
   if ( (retval = XGpioPs_SelfTest(&Gpio)) != XST_SUCCESS) {
  printf("GPIO Self test fails\n"); return 1; }

   /*
*  Write files to SDC using FatFs SPI protocol
*/

   f_mount(&FatFs, "", 0); /* Give a work area to the default drive */




   //FILE *fp = fopen(filename, "rb");
   int state = f_open(&iFil, refFilename, FA_READ );
   if(state != FR_OK) {
      perror(refFilename);
      exit(-1);
  }

  f_lseek(&iFil, 10);
  f_read(&iFil, &offset, 4, &bw);

  f_lseek(&iFil, 18);
  f_read(&iFil, &width, 4, &bw);
  f_read(&iFil, &height, 4, &bw);
  f_lseek(&iFil, 0);

  buffer = (unsigned char *)malloc(offset);
  if(buffer == NULL) {
     perror("malloc");
     exit(-1);
  }
  f_read(&iFil, buffer, offset, &bw);


  state = f_open(&oFil, filename, FA_WRITE | FA_CREATE_ALWAYS );
  if(state != FR_OK) {
        perror(filename);
        exit(-1);
  }

  f_write(&oFil, buffer, offset, &bw);
  if(bw != offset) {
     printf("error writing header!\n");
     exit(-1);
  }


   // NOTE bmp formats store data in reverse raster order (see comment in
   // readImage function), so we need to flip it upside down here.  
   int mod = width % 4;
   if(mod != 0) {
      mod = 4 - mod;
   }
   //   printf("mod = %d\n", mod);
   for(i = height-1; i >= 0; i--) {
      for(j = 0; j < width; j++) {
         tmp = (unsigned char)imageOut[i*cols+j];
         f_write(&oFil, &tmp, sizeof(char), &bw);
      }
      // In bmp format, rows must be a multiple of 4-bytes.  
      // So if we're not at a multiple of 4, add junk padding.
      for(j = 0; j < mod; j++) {
         f_write(&oFil, &tmp, sizeof(char), &bw);
      }
   } 

   f_close(&oFil);
   f_close(&iFil);

   free(buffer);

}



readImage

float* readImage(const char *filename, int* widthOut, int* heightOut) {

   uchar* imageData;
   int height, width;
   uchar tmp;
   int offset;
   int i, j;

  UINT bw; // bytes written
  int retval; // return value for Gpio functions


  // Lookup GPIO config table
  XGpioPs_Config * ConfigPtr =     XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);

  // Initialize GPIO
    if ( (retval = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr)) != XST_SUCCESS) {
  printf("Error initalizing GPIO fails\n"); return retval;}
if ( (retval = XGpioPs_SelfTest(&Gpio)) != XST_SUCCESS) {
printf("GPIO Self test fails\n"); return 1; }

/*
*  Write files to SDC using FatFs SPI protocol
*/

f_mount(&FatFs, "", 0); /* Give a work area to the default drive */



   printf("Reading input image from %s\n", filename);
   //FILE *fp = fopen(filename, "rb");
   int state = f_open(&iFil, filename, FA_READ );
   if(state != FR_OK) {
       perror(filename);
       exit(-1);
   }

   f_lseek(&iFil, 10);
   f_read(&iFil, &offset, 4, &bw);

   f_lseek(&iFil, 18);
   f_read(&iFil, &width, 4, &bw);
   f_read(&iFil, &height, 4, &bw);

   printf("width = %d\n", width);
   printf("height = %d\n", height);




   *widthOut = width;
   *heightOut = height;    

   imageData = (uchar*)malloc(width*height);
   if(imageData == NULL) {
       perror("malloc");
       exit(-1);
   }
   f_lseek(&iFil, offset);

   int mod = width % 4;
   if(mod != 0) {
       mod = 4 - mod;
   }

   // NOTE bitmaps are stored in upside-down raster order.  


   // Read in the actual image
   for(i = 0; i < height; i++) {

      // add actual data to the image
      for(j = 0; j < width; j++) {
         f_read(&iFil, &tmp, sizeof(char), &bw);
         imageData[i*width + j] = tmp;
      }

      for(j = 0; j < mod; j++) {
         f_read(&iFil, &tmp, sizeof(char), &bw);
      }
   }

   // Then we flip it over
   int flipRow;
   for(i = 0; i < height/2; i++) {
      flipRow = height - (i+1);
      for(j = 0; j < width; j++) {
         tmp = imageData[i*width+j];
         imageData[i*width+j] = imageData[flipRow*width+j];
         imageData[flipRow*width+j] = tmp;
      }
   }

   f_close(&iFil);

   // Input image on the host
   float* floatImage = NULL;
   floatImage = (float*)malloc(sizeof(float)*width*height);
   if(floatImage == NULL) {
      perror("malloc");
      exit(-1);
   }

   // Convert the BMP image to float (not required)
   for(i = 0; i < height; i++) {
      for(j = 0; j < width; j++) {
         floatImage[i*width+j] = (float)imageData[i*width+j];
      }
   }

   free(imageData);

/*

   fflush(NULL);

*/



   return floatImage;
}