//*********************************************************************
//
//    FAT32 MMC/SD ЗАГРУЗЧИК версия 1.10.
// 
// 1. Добавляем makefile.mak в проект. Идем 
//    Project ->Configuration options. 
//    На вкладке General устанавливаем галку Use external makefile. 
//    Нажмаем "…" находим makefile.mak.
//
// 2. В makefile.mak настраиваем параметры компиляции загрузчика: 
//    MCU - модель процессора
//    F_CPU - рабочая частота процессора
//    BOOTLDRSIZE - размер загрузчика 
//    BOOTLOADERSTARTADR - стартовый адрес загрузчика
//    TARGET - имя *.hex файла, получаемого при компиляции
//    OBJDIR - директория, в которую происходит компиляция загрузчика
//
// 3. В SD_routines.h настраиваем ногу, на которую вешается выбор чипа - 
// 	  это определения SD_CS_ASSERT и SD_CS_DEASSERT. Остальные ноги, на которые
//    вешается SD карта (SPI_DI, SPI_DO, SPI_Clock) устанавливаются
//    стандартными для интерфейса SPI. Незабывать назначить PORTX и DDRX в 
//	  процедуре main.
//
// 4. В megavar.h, если модель процессора отсутствует, настраиваем
//    размеры EEPROM и FLASH памяти в секторах для программирования: 
//    EE_SECTOR_FOR_READ и FL_SECTOR_FOR_READ для EEPROM и FLASH памяти
//    соответственно. 
// 
// 5. В main.с в процедуре int main(void) настраиваем условие сработки 
//    bootloader-a (например низкий сигнал на PINC). Имя файла для 
//    загрузки по этому событию "boot" (задается в loadboot()).
// 
// 6. SD карта должна быть отформатирована в системе FAT32. 
//	  Размер кластера 512 байт.
//    На карточке должны размещаться bin-файлы EEPROM и FLASH. 
//    Например "boot.ee" и "boot.fl" Расширение файла EEPROM - "ee", 
//    расширение FLASH - "fl". Файл EEPROM может отсутствовать. 
//    Файл FLASH должен быть всегда. Размеры файлов - постоянная 
//    величина равная EE_SECTOR_FOR_READ*512 и FL_SECTOR_FOR_READ*512 
//    для EEPROM и FLASH соответственно.
// 
// 7. Для программирования чипа используйте 
//    progeeprom32.bat (программирование загрузчика) и 
//    progHfuse32.bat (программирование фьюзов). 
//    Для программатора отличного usbasp 
//    скорректировать указанные файлы.
//
// 8. Со всеми вопросами обращаетесь на адрес matrex@rambler.ru.
// 
// 9. При написании программы использовались следующие материалы:
//    http://www.mikrocontroller.net/articles/MMC/SD_Bootloader_f%C3%BCr_AT_Mega
//    http://www.ulrichradig.de/home/index.php/avr/mmc-sd
//    http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html
//
//
//    ИСТОРИЯ ИЗМЕНЕНИЙ:
//
//    01.10.2011 v. 1.01.
//    Устранена ошибка в размере EEPROM памяти для ATMEGA32. 
//    Приведено в соответствие со спецификацией наименование типов 
//    памяти. Добавлена работа с EEPROM памятью.
//
//    08.12.2011 v. 1.10.
//    Изменена логика работы программы:
//		- если сработало условие сработки бутлодера (нажата клавиша)
//		  загружаем файлы "boot.fl" "boot.ee"
//		- иначе чтитаем содержимое файла "upload.bat" и загружаем тот
//		  файл, который записан в upload, Обязательное условие - 
//		  имя файла должно заканчиваться "непечатным символом", например
//		  zagrus(#0D)(#0A)
//
//    01.05.2012 v. 1.20.
//    Добавлена возможность работы с ATmega64
//		- если сработало условие сработки бутлодера (нажата клавиша)
//
//*********************************************************************



#include <util/delay.h>

#include <avr/boot.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>

#include "FAT32.h"
#include "SPI_routines.h"
#include "SD_routines.h"


#define FL_SECTOR_FOR_READ 120	// количество FLASH памяти в секторах для програмирования.
			   		          	// рассчитывается BOOTLOADERSTARTADR/512 (из makefile.mak)
#define EE_SECTOR_FOR_READ 4  	// количество EEPROM памяти в секторах для програмирования.
			   		          	// рассчитывается 1024/512 (1024 - размер FLASH из даташита)
#define cPathSIze 32 			// размер пути для загрузки файлов
#define STOPPOINT(x) eeprom_write_byte(0,x) // для отладки, находить точки останова
void (*app_start)(void) = 0x0000;



void upload(char *fileName, unsigned char memtype)
{
	struct dir_Structure *dir;
	unsigned long cluster, byteCounter = 0, fileSize, firstSector;
	unsigned char filesector, error, p;
	uint16_t  i, j, adr, *lpword;
	error = convertFileName (fileName); //convert fileName into FAT format
	if(error) return;
	dir = findFiles (fileName); //get the file location
	if(dir == 0) return;
	cluster = (((unsigned long) dir->firstClusterHI) << 16) | dir->firstClusterLO;
	fileSize = dir->fileSize;
	firstSector = getFirstSector (cluster);
		switch (memtype) // Какую память будем программировать
		{
		case 0:
			// программируем FLASH
			for(filesector=0; filesector < FL_SECTOR_FOR_READ; filesector++)
			{
   			SD_readSingleBlock(firstSector + filesector);
			lpword = (uint16_t*) buffer;
				for (i=0; i<(512 / SPM_PAGESIZE); i++)
				{
					adr = (filesector * 512) + i * SPM_PAGESIZE;
					boot_page_erase(adr);
					while (boot_rww_busy()) boot_rww_enable();
					for (j=0; j<SPM_PAGESIZE; j+=2) boot_page_fill(adr + j, *lpword++);
					boot_page_write(adr);
					while (boot_rww_busy())	boot_rww_enable();
				};
		  	cluster = getSetNextCluster (cluster, GET, 0);
			}; 
	 	break;
		default:
			// программируем EEPROM
			adr=0;
			for (filesector = 0; filesector < EE_SECTOR_FOR_READ; filesector++)
			{
   				SD_readSingleBlock(firstSector + filesector);
				for(j=0;j<512;j++)
				{
					eeprom_busy_wait();
					eeprom_write_byte(adr, buffer[j]);
					adr++;
				}
		  	cluster = getSetNextCluster (cluster, GET, 0);
			};
	    break;
		}		
}





unsigned char uploadfilename(char *fileName, char *load)
{
	struct dir_Structure *dir;
	unsigned long cluster, byteCounter = 0, fileSize, firstSector;
	unsigned char  error, p;
	uint16_t  i, j, adr, *lpword;
	error = convertFileName (fileName); //convert fileName into FAT format
	if(error) return;
	dir = findFiles (fileName); //get the file location
	if(dir == 0) return;
	cluster = (((unsigned long) dir->firstClusterHI) << 16) | dir->firstClusterLO;
	fileSize = dir->fileSize;
	firstSector = getFirstSector (cluster);
   	SD_readSingleBlock(firstSector);
	for(j=0;j<cPathSIze;j++)
		{
		if (buffer[j]<33) return 0;
		load[j]=buffer[j];
	//	eeprom_write_byte(j+1, buffer[j]);
		}
	return 0;
}





void loadboot()
{

	char f[]="boot.fl";     // имя файла FLASH
	char f1[]="boot.ee";     // имя файла EEPROM

/*  старая версия программы. на 64 меге давала переполнение памяти
	char filename[]="boot";     // имя файла, который будет загружатся если 
	char f[cPathSIze];
	char f1[cPathSIze];
	char ee=".ee";
	char fl=".fl";
	memset(f,0,sizeof(f));
	memset(f1,0,sizeof(f1));
	memcpy(f,filename,strlen(filename));
	strcat(f,fl);
	memcpy(f1,filename,strlen(filename));
	strcat(f1,ee);
*/

	upload(f,0); 				// загружаем FLASH
	upload(f1,1); 				// загружаем EEPROM

}


void loadfile()
{
	char fn[]="upload.bat";     	// имя файла, который содержит файл для загрузки
	char filename[cPathSIze];				// имя загружаемого файла
	char f[cPathSIze];
	char f1[cPathSIze];
	char ee=".ee";
	char fl=".fl";
	memset(filename,0,sizeof(filename));					
	if (uploadfilename(fn,filename)!=0) return; // если файл отсутствует
//	for(unsigned char u=0;u<sizeof(filename);u++) {eeprom_write_byte(u+32, filename[u]);}
	memset(f,0,sizeof(f));
	memset(f1,0,sizeof(f1));
	memcpy(f,filename,strlen(filename));
	strcat(f,fl);
	memcpy(f1,filename,strlen(filename));
	strcat(f1,ee);
	upload(f,0); 				// загружаем FLASH
	upload(f1,1); 				// загружаем EEPROM
}


int main(void)
{

	unsigned char error;
	unsigned char i;
	// запрет прерываний, отключение собаки
	cli();
	wdt_reset();
	wdt_disable();

	// настройка портов клавиатуры
 	PORTC = 0xFF; 	//pullup for keys
 	DDRC  = 0x00;	// x x x START ROTATE LEFT RIGHT DOWN

	// настройка портов
	PORTB = 0xFF;
	DDRB  = 0xF7; //MISO на вход, остальные на выход

	// настройка SPI init
	SPCR = (1<<MSTR)|(1<<SPE); // SPI config
	SPSR = (1<<SPI2X); // double speed


	SPI_transmit(0x55);

	// проверка работоспособности бутлодера. Моргалка на ногах PF
 	// PORTF = 0xFF;DDRF  = 0xFF; while (1) {PORTF=0; _delay_ms(300); PORTF=255; _delay_ms(300);}


	// попытка инициализировать карту
	cardType = 0;
	for (i=0; i<10; i++) {error = SD_init();if(!error) break;}
	if(error) app_start(); // ошибка - стартуем приложение
	_delay_ms(10);   //some delay
	error = getBootSectorData (); //read boot sector and keep necessary data in global variables
	if(error) app_start(); // ошибка - стартуем приложение


	if(PINC!=255) 				// условие сработки bootloader-а (нажата кнопка VK1)
    {
		loadboot();
	}
	else
	{
		loadfile();				// иначе загружать из файла
	}
	app_start();				// запуск приложения

}


























