#include <SD.h>
#include <SoftSPIB.h>
#include <Wire.h>
#include <U8x8lib.h>
#include "button.h"
#define COUNT_PAGE 256
#define COUNT_WORDS 64
#define COUNT_BYTE_IN_WORD 2
//Стартовый индекс данных в массиве символов
#define START_INDEX_DATA 9
#define RESET_PIN 5
#define SDCARD_SS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_MISO_PIN 12
#define SDCARD_SCK_PIN 13
#define LOGO_HEIGHT 64
#define LOGO_WIDTH 128
#define BTN_SET A2
#define BTN_DOWN A1
#define BTN_UP A0
#define MAX_ROW 8
#define COUNT_MAIN_MENU 3
#define MODE_MENU_MAIN 0
#define MODE_MENU_FILE 1
#define MODE_MENU_PC 2
#define MODE_MENU_LOADING 3
//Максимально допустимое количество имен прошивок
#define MAX_COUNT_NAME 16
int firstIndex = 0;
int lastIndex = 0;
int countNameFiles = 0;
int selectFile = firstIndex;
char nameFiles[MAX_COUNT_NAME][13];
volatile byte mode = MODE_MENU_MAIN;
U8X8_SH1107_64X128_HW_I2C u8x8(8);
volatile bool loadingFlag = false;
// Create a new SPI port with:
// D2 = MOSI,
// D3 = MISO,
// D4 = SCK
// D5 - RESET
SoftSPIB mySPI(2, 3, 4);
byte cmdProgrammingEnable[4] = {0xAC, 0x53, 0x00, 0x00};
byte cmdChipErase[4] = {0xAC, 0x80, 0x00, 0x00};
byte menuItem = MAX_ROW - 1;
button btnSet(BTN_SET);
button btnDown(BTN_DOWN);
button btnUp(BTN_UP);
bool initSD = false;
File root;
void setup(){
nameFiles[0][0] = '-';
nameFiles[0][1] = '-';
nameFiles[0][2] = '-';
Serial.begin(9600);
u8x8.begin();
u8x8.setPowerSave(0);
u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
mySPI.begin();
mySPI.setBitOrder(MSBFIRST);
mySPI.setDataMode(SPI_MODE2);
pinMode(RESET_PIN, OUTPUT);
digitalWrite (RESET_PIN, HIGH);
showMenuMain();
mode = MODE_MENU_MAIN;
}
void loop(){
if(mode == MODE_MENU_MAIN){
if (btnDown.click()) {
menuItem--;
if((MAX_ROW - COUNT_MAIN_MENU) > menuItem ){
menuItem = MAX_ROW - 1;
}
selectorMenu(COUNT_MAIN_MENU, menuItem);
}
if (btnUp.click()) {
menuItem++;
if(menuItem >= MAX_ROW){
menuItem = MAX_ROW - COUNT_MAIN_MENU;
}
selectorMenu(COUNT_MAIN_MENU, menuItem);
}
if (btnSet.click()) {
switch(menuItem){
case MAX_ROW-1:
mode = MODE_MENU_FILE;
printNameFiles();
selectorMenu(lastIndex - firstIndex + 1, MAX_ROW - 1 - selectFile + firstIndex);
break;
case MAX_ROW-2:
mode = MODE_MENU_PC;
break;
case MAX_ROW-3:
mode = MODE_MENU_LOADING;
break;
default:
mode = MODE_MENU_MAIN;
break;
}
}
}
if(mode == MODE_MENU_FILE){
if(initSD == false){
initSD = true;
PrintDisplay("Init SD card...");
pinMode(SDCARD_SS_PIN, OUTPUT);
pinMode(SDCARD_MOSI_PIN, OUTPUT);
// Пытаемся проинициализировать модуль
if (SD.begin(SDCARD_SS_PIN)) {
root = SD.open("/");
readFilesWithHEX(root);
PrintDisplay("card init!");
delay(500);
shiftElement(0);
printNameFiles();
selectorMenu(lastIndex - firstIndex + 1, MAX_ROW - 1 - selectFile + firstIndex);
}
else{
PrintDisplay("Card failed");
}
}
if (btnDown.click()) {
if(selectFile < lastIndex){
selectFile++;
}
else if(selectFile == lastIndex){
shiftElement(1);
selectFile = lastIndex;
printNameFiles();
}
selectorMenu(lastIndex - firstIndex + 1, MAX_ROW - 1 - selectFile + firstIndex);
}
if (btnUp.click()) {
if( (selectFile > firstIndex) && (selectFile <= lastIndex)){
selectFile--;
}
else if(selectFile == firstIndex){
shiftElement(-1);
selectFile = firstIndex;
printNameFiles();
}
selectorMenu(lastIndex-firstIndex+1, MAX_ROW - 1 - selectFile + firstIndex);
}
if (btnSet.click()) {
if(selectFile == 0){
mode = MODE_MENU_MAIN;
showMenuMain();
}
else{
loadFile(nameFiles[selectFile]);
printNameFiles();
selectorMenu(lastIndex - firstIndex + 1, MAX_ROW - 1 - selectFile + firstIndex);
}
}
}
}
void loadFile(const char *s){
if (SD.begin(SDCARD_SS_PIN)) {
// Открываем файл, но помним, что одновременно можно работать только с одним файлом.
// Если файла с таким именем не будет, ардуино создаст его.
File dataFile = SD.open(s, FILE_READ);
// Если все хорошо, то записываем строку:
if (dataFile) {
digitalWrite (RESET_PIN, HIGH);
delay(1);
digitalWrite (RESET_PIN, LOW);
PrintDisplay("Erase chip");
//delay(22);
SoftSPIWriteString(cmdProgrammingEnable, 4);
SoftSPIWriteString(cmdChipErase, 4);
//delay(600);
//Читаем содержимое файла
String buffer = "";
while (dataFile.available()){
buffer = dataFile.readStringUntil('\n');//Считываем с карты весь текст в строку до символа окончания.
char charBuf[buffer.length()+1];
buffer.toCharArray(charBuf, buffer.length());
ParseDataString(charBuf);
}
dataFile.close(); //закроем файл
digitalWrite (RESET_PIN, HIGH);
PrintDisplay("Loaded!!!");
loadingFlag = false;
}
else {
// Сообщаем об ошибке, если все плохо
PrintDisplay("Err read SD");
}
delay(1000);
}
}
void shiftElement(int count){
while( ((firstIndex + count) >= countNameFiles) || ((firstIndex + count) < 0)){
if(count > 0){
count--;
}
else if(count < 0){
count++;
}
else{
break;
}
}
if((lastIndex + count) < countNameFiles)
firstIndex += count;
lastIndex = firstIndex;
while( ((lastIndex + 1) < countNameFiles) && ((lastIndex + 1 - firstIndex) < MAX_ROW) ){
lastIndex++;
}
}
void showMenuMain(){
u8x8.clear();
u8x8_draw_string_90(u8x8.getU8x8(), MAX_ROW-1, 0, "[ ] SD-LOAD");
u8x8_draw_string_90(u8x8.getU8x8(), MAX_ROW-2, 0, "[ ] PC-LOAD");
u8x8_draw_string_90(u8x8.getU8x8(), MAX_ROW-3, 0, "[ ] POWER-OFF");
selectorMenu(COUNT_MAIN_MENU, menuItem);
}
void selectorMenu(int rowCount, byte item) {
for(int index = MAX_ROW - rowCount; index < MAX_ROW; index++){
if(index == item){
u8x8_draw_string_90(u8x8.getU8x8(), index, 0, "[*]");
}
else{
u8x8_draw_string_90(u8x8.getU8x8(), index, 0, "[ ]");
}
}
u8x8.refreshDisplay();
}
void PrintDisplay(char *message){
static int8_t indexLine = 7;
if(indexLine < 0){
indexLine = 7;
u8x8.clear();
}
u8x8_draw_string_90(u8x8.getU8x8(), indexLine, 0, message);
u8x8.refreshDisplay();
indexLine--;
}
void readFilesWithHEX(File dir) {
int index = 1;
while (true) {
File entry = dir.openNextFile();
if ( (!entry) || (index >= MAX_COUNT_NAME) ) {
break;
}
char* s = entry.name();
char* arr = nameFiles[index];
if(isFileBIN(s)){
while(*s != '\0'){
*arr++ = *s++;
}
index++;
}
entry.close();
}
countNameFiles = index;
}
void printNameFiles(){
u8x8.clear();
u8x8.refreshDisplay();
for(int index = firstIndex; index <= lastIndex; index++ ){
u8x8_draw_string_90(u8x8.getU8x8(), MAX_ROW - (index - firstIndex + 1), 4, nameFiles[index]);
}
}
bool isFileBIN(char * nameFile){
int len = strlen(nameFile);
if(len < 4){
return false;
}
else if( (nameFile[len - 4] == '.') && (nameFile[len - 3] == 'H') && (nameFile[len - 2] == 'E') && (nameFile[len - 1] == 'X')){
return true;
}
return false;
}
void u8x8_draw_string_90(u8x8_t *u8x8, uint8_t x, uint8_t y, const char *s){
while(*s != '\0')
u8x8_draw_glyph_90(u8x8, x, y++, *s++);
}
void u8x8_draw_glyph_90(u8x8_t *u8x8, uint8_t x, uint8_t y, uint8_t encoding){
static uint8_t buf[8];
u8x8_get_glyph_data(u8x8, encoding, buf, 0);
u8x8_DrawTile(u8x8, x, y, 1, rotate90(buf));
}
static uint8_t *rotate90(uint8_t *buf){
static uint8_t rbuf[8];
uint8_t i, h;
uint8_t *p;
uint8_t j;
for( i = 0; i < 8; i++)
rbuf[i] = 0;
for( i = 0; i < 8; i++ ){
h = buf[i];
p = rbuf;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
*p>>=1; *p |= (h&128); h <<= 1; p++;
}
return rbuf;
}
//Здесь нужно будет еще вернуть ответ
//Но пока без ответа
void SoftSPIWriteString(byte arraySymbols[], byte length) {
for(int index = 0; index < length; index++){
uint8_t in = mySPI.transfer(arraySymbols[index]);
}
}
//Получение адреса страницы по адресу данных
int GetAddrPage(int addrData){
return addrData / (COUNT_WORDS * COUNT_BYTE_IN_WORD);
}
int ASCIIHexToInt(char hexchar){
return (hexchar >= 'A') ? (hexchar - 'A' + 10) : (hexchar - '0');
}
//Парсим строку hex-файла прошивки
//Пример строки: ":100000000C94A6000C94B8000C94B8000C94B800A2"
int ParseDataString(char* charBuf){
if(charBuf[0] != ':'){
return 1;
}
static byte addrPage = 0;
static int addrWord = 0;
byte getAddress = 0;
static int countWords = 0;
//static int sum = 0;
//Инициализируем количество данных в строке
int length_str = ASCIIHexToInt(charBuf[1]) << 4 | ASCIIHexToInt(charBuf[2]);
int addrData = ASCIIHexToInt(charBuf[3]) << 12 | ASCIIHexToInt(charBuf[4]) << 8
| ASCIIHexToInt(charBuf[5]) << 4 | ASCIIHexToInt(charBuf[6]);
//Если загрузка еще не начата, то адрес страницы расчитаем из файла.
if(loadingFlag == false){
loadingFlag = true;
addrPage = GetAddrPage(addrData);
addrWord = 0;
}
countWords += length_str/2;
if(length_str == 0){
byte cmdWriteProgramMemoryPage[4] = {0x4C, addrPage, addrWord-countWords, 0x00};
SoftSPIWriteString(cmdWriteProgramMemoryPage, 4);
PrintDisplay("length_str");
delay(500);
countWords = 0;
return 0;
}
//Расчитываем конечный индекс данных
//Т.к. каждый байт это два HEX-символа, то количество данных умножаем на дв
int lastIndexData = 2*length_str + START_INDEX_DATA - 1;
int index = START_INDEX_DATA;
while(index <= lastIndexData){
//getAddress = GetAddrPage(addrData);
if( (addrWord % COUNT_WORDS) == 0){
byte cmdWriteProgramMemoryPage[4] = {0x4C, addrPage, addrWord-countWords, 0x00};
SoftSPIWriteString(cmdWriteProgramMemoryPage, 4);
delay(6);
countWords = 0;
}
//Если адрес слова стал равен количеству слов на странице
//или текущей адрес страницы не равен адресу считываемых из файла данных,
//то отдаем команду на запись страницы,
//а также перейдем на другую страницу и обнулим адрес слова
if(addrWord == 0x100){
addrPage++;
addrWord = 0x00;
}
byte lowByte = ASCIIHexToInt(charBuf[index]) << 4 | ASCIIHexToInt(charBuf[index+1]);
byte highByte = ASCIIHexToInt(charBuf[index+2]) << 4 | ASCIIHexToInt(charBuf[index+3]);
index += 4;
byte cmdLoadProgramMemoryPageLowByte[4] = {0x40, addrPage, addrWord, lowByte};
byte cmdLoadProgramMemoryPageHighByte[4] = {0x48, addrPage, addrWord, highByte};
SoftSPIWriteString(cmdLoadProgramMemoryPageLowByte, 4);
SoftSPIWriteString(cmdLoadProgramMemoryPageHighByte, 4);
//Увеличим адрес на количество прочитанных байт
addrData += 2;
//Слово состоит из младшего и старшего байт
//Поэтому увеличим адрес слова только на единицу
addrWord++;
}
return 0;
}