From 1a11ab00d898c4484bc0f518a1d53b1f4cbf5fb4 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Wed, 18 Apr 2018 23:17:25 -0400 Subject: [PATCH] SD cardgit status FAT32git status Beautiful! --- Makefile | 2 +- include/display.h | 3 + include/fat32.h | 31 +++++++ include/sdcard.h | 42 +++++++++ initrd/graph | 16 ++-- initrd/table | 85 ++++++++++++++++++ libinterp.a | Bin 66064 -> 68136 bytes src/display.c | 37 +++++++- src/fat32.c | 145 ++++++++++++++++++++++++++++++ src/gpio.c | 2 +- src/keypad.c | 50 +++++++++-- src/main.c | 43 +++++++-- src/script.c | 52 ++++++++++- src/sdcard.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++ src/stdlib.c | 7 +- 15 files changed, 706 insertions(+), 31 deletions(-) create mode 100644 include/fat32.h create mode 100644 include/sdcard.h create mode 100644 initrd/table create mode 100644 src/fat32.c create mode 100644 src/sdcard.c diff --git a/Makefile b/Makefile index d672a52..595694a 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ INITRD = initrd.img all: $(OUT) -$(OUT): $(OFILES) initrd/init libinterp.a +$(OUT): $(OFILES) initrd/* libinterp.a @echo " INITRD " $(INITRD) @rm -f $(INITRD) @$(AR) $(INITRD) initrd/* diff --git a/include/display.h b/include/display.h index b8afe38..2d74fce 100644 --- a/include/display.h +++ b/include/display.h @@ -92,4 +92,7 @@ void dsp_set_addr(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); */ void dsp_set_addr_read(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); +void dsp_sleep(void); +void dsp_wakeup(void); + #endif // DISPLAY_H_ diff --git a/include/fat32.h b/include/fat32.h new file mode 100644 index 0000000..39d22f6 --- /dev/null +++ b/include/fat32.h @@ -0,0 +1,31 @@ +#ifndef FAT32_H_ +#define FAT32_H_ + +#include + +typedef struct { + uint32_t size; + uint32_t start; +} file_t; + +/** + * Finds a FAT partition on the SD card. + * @return non-zero if partition found + */ +int fat_find(void); + +/** + * Searches for the given file, returning necessary information to use it. + * @param name the file's name + * @return a file data structure, null if not found + */ +file_t *fat_findfile(const char *name); + +/** + * + */ +char *fat_readfile(file_t *file); + +char *fat_getname(uint32_t index); + +#endif // FAT32_H_ diff --git a/include/sdcard.h b/include/sdcard.h new file mode 100644 index 0000000..8cdef58 --- /dev/null +++ b/include/sdcard.h @@ -0,0 +1,42 @@ +/** + * @file sdcard.c + * Provides a basic library for accessing an SD card + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SDCARD_H_ +#define SDCARD_H_ + +#include + +/** + * Attempts to initialize the SD card. + * Warning: Little to no error checking is done/handled properly. + */ +void sd_init(void); + +/** + * Reads in data from the SD card. + * LBAs are 512 bytes long. + * @param buf the pre-allocated buffer to store data in + * @param lba the lba to read from + * @param count the number of bytes to read + * @return the buffer + */ +uint8_t *sd_read(uint8_t *buf, uint32_t lba, uint32_t count); + +#endif // SDCARD_H_ diff --git a/initrd/graph b/initrd/graph index 8f9916a..949fd52 100644 --- a/initrd/graph +++ b/initrd/graph @@ -41,24 +41,24 @@ func(makegrid) { # makegrid() -clearcmd = "clear" +clearcmd = "" while (1) { rect(0, 0, 480, 40, 0) print("f(x) = ") - Fx = gets() + fx = gets() - if (Fx == clearcmd) { + if (fx == clearcmd) { makegrid() } else { # do function - x = xmin - while (x < xmax) { - y = solve(Fx) + X = xmin + while (X < xmax) { + y = solve(fx) y = 0 - y if ((y >= ymin) & (y <= ymax)) { - pixel(cx + x * xinc, cy + y * yinc, 511) + pixel(cx + X * xinc, cy + y * yinc, 511) } - x = x + 1 / xinc + X = X + 1 / xinc } } diff --git a/initrd/table b/initrd/table new file mode 100644 index 0000000..62961e3 --- /dev/null +++ b/initrd/table @@ -0,0 +1,85 @@ +ppos(0, 0) +array(table, 1) + +index = 0 +while (1) { + c = getkey() + # down + if (c == 25) { + rpos(0, 1) + index = index + 1 + } + # up + if (c == 24) { + rpos(0, -1) + if (index > 0) { + index = index - 1 + } + } + # right - insert + if (c == 26) { + print(" ") + rpos(-10, 0) + print("> ") + table.index = getf() + ppos(0, index) + print(index) + print(": ") + print(table.index) + print(" ") + index = index + 1 + ppos(0, index) + } + # plus - sum + if (c == 43) { + s = size(table) + j = 0 + sum = 0 + while (j < s) { + sum = sum + table.j + j = j + 1 + } + ppos(0, 17) + print(" ") + ppos(0, 17) + print("sum: ") + print(sum) + ppos(0, 0) + index = 0 + } + # * - product + if (c == 42) { + s = size(table) + j = 0 + product = 1 + while (j < s) { + product = product * table.j + j = j + 1 + } + ppos(0, 17) + print(" ") + ppos(0, 17) + print("product: ") + print(product) + ppos(0, 0) + index = 0 + } + # / - average + if (c == 47) { + s = size(table) + j = 0 + sum = 0 + while (j < s) { + sum = sum + table.j + j = j + 1 + } + average = sum / size(table) + ppos(0, 17) + print(" ") + ppos(0, 17) + print("average: ") + print(average) + ppos(0, 0) + index = 0 + } +} diff --git a/libinterp.a b/libinterp.a index 5b5da09a3d0ffc73319a57032f4b5a978a8522db..c5a233714d2a57a0216e8bb491d33643438db010 100644 GIT binary patch literal 68136 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℞;CAnb7Eg${WLaZ>!L8W9qZ+_cz&$Tc2+PeE2^{c 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�`$4GhF9!u~#wN!B*qO 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 literal 66064 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 +#include +#include +#include +#include + +static uint32_t partition_lba = 0; + +static uint8_t SectorsPerCluster; +static uint32_t RootDirCluster; +static uint32_t ClustersLBA; +static uint32_t FATStartLBA; + +int fat_find(void) +{ + uint8_t *block = malloc(512); + + // find FAT partition + sd_read(block, 0, 512); + for (unsigned int i = 450; i < 450 + 64; i += 16) { + if (block[i] == 0x0B || block[i] == 0x0C) { + partition_lba = *((uint32_t *)(block + i + 4)); + break; + } + } + + if (partition_lba == 0) + return 0; + + // read FAT volume id + sd_read(block, partition_lba, 512); + SectorsPerCluster = block[0x0D]; + uint16_t ReservedSectors = *((uint16_t *)(block + 0x0E)); + uint32_t SectorsPerFAT = *((uint32_t *)(block + 0x24)); + RootDirCluster = *((uint32_t *)(block + 0x2C)); + + FATStartLBA = partition_lba + ReservedSectors; + ClustersLBA = FATStartLBA + 2 * SectorsPerFAT; + + free(block); + return 1; +} + +uint32_t fat_cluster2lba(uint32_t cluster) +{ + return ClustersLBA + (cluster - 2) * SectorsPerCluster; +} + +int fat_namecmp(const char *fatname, const char *name) +{ + for (unsigned int i = 0; i < 8; i++) { + if (name[i] == '.' || name[i] == '\0') + break; + + if (toupper(fatname[i]) != toupper(name[i])) + return 0; + } + + return 1; +} + +file_t *fat_findfile(const char *name) +{ + uint8_t *block = malloc(512 * SectorsPerCluster); + sd_read(block, fat_cluster2lba(RootDirCluster), 512 * SectorsPerCluster); + + for (unsigned int i = 0; block[i * 32] != 0; i++) { + if (block[i * 32] == 0xE5 || (block[i * 32 + 11] & 0x0F) == 0x0F) + continue; + + if (fat_namecmp((char *)(block + i * 32), name)) { + uint32_t size = *((uint32_t *)(block + i * 32 + 28)); + uint32_t start = *((uint16_t *)(block + i * 32 + 20)) + << 16 | *((uint16_t *)(block + i * 32 + 26)); + file_t *file = malloc(sizeof(file_t)); + file->size = size; + file->start = start; + free(block); + return file; + } + } + + free(block); + return 0; +} + +char *fat_getname(uint32_t index) +{ + uint8_t *block = malloc(512 * SectorsPerCluster); + sd_read(block, fat_cluster2lba(RootDirCluster), 512 * SectorsPerCluster); + + uint32_t idx = 0; + for (unsigned int i = 0; block[i * 32] != 0; i++) { + if (block[i * 32] == 0xE5 || (block[i * 32 + 11] & 0x0F) == 0x0F) + continue; + + if (idx == index) { + char *name = strncpy(malloc(11), (char *)(block + i * 32), 11); + free(block); + return name; + } + idx++; + } + + free(block); + return 0; +} + +char *fat_readfile(file_t *file) +{ + if (file == 0) + return 0; + + uint8_t *block = malloc(512); + + uint32_t start = file->start; + uint8_t *buffer = malloc(file->size + 1); + uint32_t offset = 0; + buffer[file->size] = '\0'; + while (start != 0) { + // find in FAT + sd_read(block, FATStartLBA + (start / 128), 512); + uint32_t next = ((uint32_t *)block)[start % 128]; + + // read start cluster + uint32_t size = 512 * SectorsPerCluster; + if (file->size - offset < size) + size = file->size - offset; + sd_read(buffer, fat_cluster2lba(start), size); + offset += size; + + if ((next & 0x0FFFFFFF) == 0x0FFFFFFF) + start = 0; + else + start = next; + + } + + free(block); + return (char *)buffer; +} diff --git a/src/gpio.c b/src/gpio.c index db67465..151779d 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -23,7 +23,7 @@ void gpio_init(void) { // enable clocks - RCC->AHB2ENR |= 0xFF; + RCC->AHB2ENR |= 0x0F; } void gpio_pupd(GPIO_TypeDef *port, uint32_t pin, uint32_t pupd) diff --git a/src/keypad.c b/src/keypad.c index b00cf41..cb569e1 100644 --- a/src/keypad.c +++ b/src/keypad.c @@ -35,6 +35,8 @@ #define COL_3 GPIO_PORT(A, 12) #define COL_4 GPIO_PORT(C, 5) +#define BTN_SLEEP GPIO_PORT(C, 11) + #define ROWS 6 #define COLS 5 @@ -59,7 +61,7 @@ static const port_t keypad_cols[COLS] = { static const char keypad_map[ROWS * COLS * 4] = { "\x7F\0\0\0" "\xFF\0\0\0" "\xFF\x02\0\0" "\x19\0\0\0" "\x18\0\0\0" - "x\0\0\0" "\0\0\0\0" "\0\0\0\0" "\x1B\0\0\0" "\x1A\0\0\0" + "sin\0" "cos\0" "tan\0" "\x1B\0\0\0" "\x1A\0\0\0" "7\0\0\0" "8\0\0\0" "9\0\0\0" "(\0\0\0" ")\0\0\0" "4\0\0\0" "5\0\0\0" "6\0\0\0" "/\0\0\0" "*\0\0\0" "1\0\0\0" "2\0\0\0" "3\0\0\0" "-\0\0\0" "+\0\0\0" @@ -67,12 +69,12 @@ static const char keypad_map[ROWS * COLS * 4] = { }; static const char keypad_map_2nd[ROWS * COLS * 4] = { - "a\0\0\0" "b\0\0\0" "c\0\0\0" "d\0\0\0" "e\0\0\0" - "f\0\0\0" "g\0\0\0" "h\0\0\0" "i\0\0\0" "j\0\0\0" - "k\0\0\0" "l\0\0\0" "m\0\0\0" "n\0\0\0" "o\0\0\0" - "p\0\0\0" "q\0\0\0" "r\0\0\0" "s\0\0\0" "t\0\0\0" - "u\0\0\0" "v\0\0\0" "w\0\0\0" "x\0\0\0" "y\0\0\0" - "z\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\xFF\x01\0\0" + "\x7F\0\0\0" "pi\0\0" "X\0\0\0" "Y\0\0\0" "Z\0\0\0" + "A\0\0\0" "B\0\0\0" "C\0\0\0" "D\0\0\0" "E\0\0\0" + "F\0\0\0" "G\0\0\0" "H\0\0\0" "I\0\0\0" "J\0\0\0" + "K\0\0\0" "L\0\0\0" "M\0\0\0" "N\0\0\0" "O\0\0\0" + "P\0\0\0" "Q\0\0\0" "R\0\0\0" "S\0\0\0" "T\0\0\0" + "U\0\0\0" "V\0\0\0" "W\0\0\0" "\b\0\0\0" "\xFF\x01\0\0" }; #define KEY(r, c, i) map[r * COLS * 4 + c * 4 + i] @@ -142,6 +144,19 @@ void keypad_init(void) gpio_speed(p, pin, VERYHIGH); gpio_dout(p, pin, 0); } + + gpio_mode(BTN_SLEEP, OUTPUT); + gpio_dout(BTN_SLEEP, 0); + gpio_mode(BTN_SLEEP, INPUT); + gpio_pupd(BTN_SLEEP, PULLDOWN); + + //SYSCFG->EXTICR[2] |= 0x200; // C10 + EXTI->RTSR1 |= (1 << 11); + EXTI->EMR1 |= (1 << 11); // Allow *10 + EXTI->IMR1 |= (1 << 11); + + // enable IRQ in NVIC + ((uint32_t *)0xE000E100)[1] |= (1 << 8); } void keypad_start(void) @@ -160,3 +175,24 @@ int keypad_get(void) keypad_buffer_pos--; return key; } + +uint32_t sleep_pending = 0; +void EXTI15_10_IRQHandler(void) +{ + uint32_t PR1 = EXTI->PR1; + + if (gpio_din(BTN_SLEEP)) { + while (gpio_din(BTN_SLEEP)); + if (sleep_pending != 0) { + sleep_pending = 0; + extern void wakeup(void); + wakeup(); + } else { + sleep_pending |= 1; + } + return; + } + + EXTI->PR1 |= PR1; +} + diff --git a/src/main.c b/src/main.c index a86db6a..4380eae 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +62,7 @@ int main(void) keypad_init(); flash_init(); - gpio_mode(GPIOA, 5, OUTPUT); + //gpio_mode(GPIOA, 5, OUTPUT); // taken by sd // enable FPU SCB->CPACR |= (0xF << 20); @@ -71,9 +73,25 @@ int main(void) while (1); } +void sleep(void) +{ + dsp_sleep(); + *((uint32_t *)0xE000ED10) |= 4; // SLEEPDEEP + PWR->CR1 |= 2; + asm("wfi"); +} + +void wakeup(void) +{ + clock_init(); + dsp_wakeup(); +} + void kmain(void) { dsp_init(); + sd_init(); + fat_find(); dsp_cursoron(); keypad_start(); @@ -81,30 +99,37 @@ void kmain(void) task_start(task_status, 512); while (1) { - gpio_dout(GPIOA, 5, 1); - delay(250); - gpio_dout(GPIOA, 5, 0); - delay(250); + extern uint32_t sleep_pending; + if (sleep_pending != 0) { + sleep(); + while (sleep_pending) + delay(1); + } + //gpio_dout(GPIOA, 5, 1); + //delay(250); + //gpio_dout(GPIOA, 5, 0); + delay(100); } } instance *load_program(const char *name) { // load file - char *s = initrd_readfile(name); - if (s == 0) { + file_t *file = fat_findfile(name); + if (file == 0) { dsp_puts("can't find "); dsp_puts(name); goto fail; } + char *s = fat_readfile(file); instance *it = inewinstance(); script_loadlib(it); // read in, parse into script code char *linebuf = (char *)malloc(120); uint32_t i = 0, prev = 0, lc; - uint32_t size = initrd_filesize(name); + uint32_t size = file->size; int ret = 0; while (i < size) { for (; s[i] != '\n' && s[i] != '\0'; i++); @@ -121,6 +146,8 @@ instance *load_program(const char *name) prev = ++i; } free(linebuf); + free(s); + free(file); return it; fail: while (1); diff --git a/src/script.c b/src/script.c index 51a3bb7..5ba0c08 100644 --- a/src/script.c +++ b/src/script.c @@ -39,9 +39,11 @@ int script_puts(instance *it); int script_putchar(instance *it); int script_gets(instance *it); +int script_getf(instance *it); int script_delay(instance *it); int script_rect(instance *it); int script_ppos(instance *it); +int script_rpos(instance *it); int script_line(instance *it); int script_color(instance *it); int script_rand(instance *it); @@ -53,6 +55,8 @@ int script_program(instance *it); int script_free(instance *it); int math_sin(instance *it); +int math_cos(instance *it); +int math_tan(instance *it); void script_loadlib(instance *it) { @@ -61,8 +65,10 @@ void script_loadlib(instance *it) inew_cfunc(it, "print", script_puts); inew_cfunc(it, "putchar", script_putchar); inew_cfunc(it, "gets", script_gets); + inew_cfunc(it, "getf", script_getf); inew_cfunc(it, "getkey", script_getkey); inew_cfunc(it, "ppos", script_ppos); + inew_cfunc(it, "rpos", script_rpos); inew_cfunc(it, "pixel", script_pixel); inew_cfunc(it, "line", script_line); @@ -78,6 +84,8 @@ void script_loadlib(instance *it) inew_cfunc(it, "freemem", script_free); inew_cfunc(it, "sin", math_sin); + inew_cfunc(it, "cos", math_cos); + inew_cfunc(it, "tan", math_tan); } int math_sin(instance *it) @@ -88,6 +96,22 @@ int math_sin(instance *it) return 0; } +int math_cos(instance *it) +{ + variable *n = igetarg(it, 0); + variable *v = make_varf(0, cosf(n->value.f)); + ipush(it, (uint32_t)v); + return 0; +} + +int math_tan(instance *it) +{ + variable *n = igetarg(it, 0); + variable *v = make_varf(0, tanf(n->value.f)); + ipush(it, (uint32_t)v); + return 0; +} + int script_menu(instance *it) { char listbuf[4]; @@ -113,13 +137,15 @@ int script_menu(instance *it) return 0; } +#include + int script_filemenu(instance *it) { char listbuf[4]; char *fname; strncpy(listbuf, " : \0", 4); dsp_puts("Choose a file: \n"); - for (unsigned int i = 0; (fname = initrd_getname(i)) != 0; i++) { + for (unsigned int i = 0; (fname = /*initrd*/fat_getname(i)) != 0; i++) { listbuf[0] = i + '0'; dsp_puts(listbuf); dsp_puts(fname); @@ -163,6 +189,18 @@ int script_putchar(instance *it) return 0; } +int script_getf(instance *it) +{ + if (script_gets(it) != 0) + return -1; + + variable *s = (variable *)ipop(it); + s->value.f = strtof((char *)s->value.p, 0); + s->type = NUMBER; + ipush(it, (uint32_t)s); + return 0; +} + int script_gets(instance *it) { char *s = malloc(64); @@ -261,8 +299,14 @@ int script_line(instance *it) int script_ppos(instance *it) { - dsp_cpos(0, 0); - dsp_coff(igetarg_integer(it, 0), igetarg_integer(it, 1)); + dsp_coff(0, 0); + dsp_cpos(igetarg_integer(it, 0), igetarg_integer(it, 1)); + return 0; +} + +int script_rpos(instance *it) +{ + dsp_spos(igetarg_integer(it, 0), igetarg_integer(it, 1)); return 0; } @@ -305,7 +349,7 @@ extern instance *load_program(const char *name); int script_program(instance *it) { int initrdOffset = (int)igetarg(it, 0)->value.f; - char *name = initrd_getname(initrdOffset); + char *name = fat_getname(initrdOffset); dsp_rect(0, 0, 480, 300, 0); dsp_cpos(0, 0); diff --git a/src/sdcard.c b/src/sdcard.c new file mode 100644 index 0000000..6cf602b --- /dev/null +++ b/src/sdcard.c @@ -0,0 +1,222 @@ +/** + * @file sdcard.c + * Provides a basic library for accessing an SD card + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#define SCK GPIO_PORT(A, 5) +#define SO GPIO_PORT(A, 6) +#define SI GPIO_PORT(A, 7) +#define CS GPIO_PORT(B, 6) + +typedef struct { + uint8_t cmd; + uint32_t arg; + uint8_t crc; +} __attribute__ ((packed)) sdcmd_t; + +typedef struct { + uint8_t zero :1; + uint8_t param :1; + uint8_t address :1; + uint8_t erase :1; + uint8_t crc :1; + uint8_t badcmd :1; + uint8_t ereset :1; + uint8_t idle :1; +} __attribute__ ((packed)) r1_t; + +void flash_out(uint8_t byte) +{ + for (int i = 0; i < 8; i++) { + gpio_dout(SI, byte & (1 << (7 - i))); + gpio_dout(SCK, 1); + gpio_dout(SCK, 0); + } +} + +uint8_t flash_in(void) +{ + uint8_t byte = 0; + for (int i = 0; i < 8; i++) { + if (gpio_din(SO)) + byte |= 1 << (7 - i); + gpio_dout(SCK, 1); + gpio_dout(SCK, 0); + } + return byte; +} + +void sd_delay(void) +{ + gpio_dout(SI, 1); + uint8_t so; + int i = 0; + do { + so = flash_in(); + if (i < 8) + i++; + } while (i < 8 || so != 0xFF); +} + +uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg) +{ + sdcmd_t cmdp; + cmdp.cmd = (cmd & 0x3F) | 0x40; + cmdp.arg = arg; + + sd_delay(); + flash_out(cmdp.cmd); + flash_out(cmdp.arg >> 24); + flash_out(cmdp.arg >> 16); + flash_out(cmdp.arg >> 8); + flash_out(cmdp.arg); + flash_out(cmd == 8 ? 0x87 : 0x95); + + // wait for a zero + gpio_dout(SI, 1); + uint8_t r1; + do { + r1 = flash_in(); + } while (r1 & 0x80); + + return r1; +} + +void sd_init(void) +{ + gpio_mode(SCK, OUTPUT); + gpio_mode(SI, OUTPUT); + gpio_mode(CS, OUTPUT); + gpio_mode(SO, OUTPUT); + gpio_speed(SCK, VERYHIGH); + gpio_speed(SI, VERYHIGH); + gpio_speed(SO, VERYHIGH); + gpio_speed(CS, VERYHIGH); + gpio_pupd(SCK, PULLUP); + gpio_pupd(SI, PULLUP); + gpio_pupd(SO, PULLUP); + gpio_pupd(CS, PULLUP); + gpio_dout(SO, 0); + gpio_mode(SO, INPUT); + gpio_dout(CS, 1); + gpio_dout(SCK, 0); + gpio_dout(SI, 1); + + // init? cs and si are high + delay(10); + sd_delay(); + + // pull cs low, send cmd0 + gpio_dout(CS, 0); + uint8_t resp = sd_send_cmd(0, 0); + sd_delay(); + gpio_dout(CS, 1); + + if (resp != 0x01) + while (1); + + // do cmd8 + delay(10); + gpio_dout(CS, 0); + resp = sd_send_cmd(8, 0x1AA); + uint8_t ocr[4]; + for (int i = 0; i < 4; i++) + ocr[i] = flash_in(); + sd_delay(); + gpio_dout(CS, 1); + (void)ocr; + + // initialize + do { + // cmd55 + gpio_dout(CS, 0); + resp = sd_send_cmd(55, 0); + sd_delay(); + gpio_dout(CS, 1); + delay(10); + + // acmd41 + gpio_dout(CS, 0); + resp = sd_send_cmd(41, 0x40000000); + sd_delay(); + gpio_dout(CS, 1); + } while (resp != 0x00); + + // set block length + gpio_dout(CS, 0); + resp = sd_send_cmd(16, 512); + sd_delay(); + gpio_dout(CS, 1); +} + +uint8_t *sd_read_block(uint8_t *buf, uint32_t lba) +{ + if (buf == 0) + return 0; + + // send command + gpio_dout(CS, 0); + if (sd_send_cmd(17, lba) != 0) + return 0; + + // wait for block + gpio_dout(SI, 1); + uint8_t byte; + do byte = flash_in(); + while (byte == 0xFF); + if (byte != 0xFE) + return 0; + + // get block + uint32_t i = 0; + for (i = 0; i < 512; i++) + buf[i] = flash_in(); + + sd_delay(); + gpio_dout(CS, 1); + return buf; +} + +uint8_t *sd_read(uint8_t *buf, uint32_t lba, uint32_t count) +{ + if (buf == 0) + return 0; + + uint8_t *block_buf = malloc(512); + uint32_t i = 0; + while (count > 0) { + sd_read_block(block_buf, lba); + memcpy(buf + i, block_buf, (count < 512) ? count : 512); + if (count < 512) + count = 0; + else + count -= 512; + i += 512; + lba++; + } + + free(block_buf); + return buf; +} + diff --git a/src/stdlib.c b/src/stdlib.c index 4714e93..a47fb03 100644 --- a/src/stdlib.c +++ b/src/stdlib.c @@ -85,7 +85,6 @@ char *snprintf(char *buf, unsigned int max, const char *format, ...) float strtof(const char *s, char **endptr) { - (void)s; (void)endptr; float res = 0.0f; @@ -139,6 +138,12 @@ char *ftostr(char *buf, float f) if (buf == 0) return 0; + if (f == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return buf; + } + unsigned int i = 0; // offset // strip decimals, convert in reverse