sdlog.c (11753B)
1 /* sdlog.c - log de diagnostico y partida guardada en la tarjeta SD de la PicoCalc. 2 * 3 * Para no meter un driver de FAT completo, los archivos de log y de partida 4 * (APP_SD_LOG y APP_SD_SAV de app_config.h, ej. CATAN.LOG y CATAN.SAV) 5 * se crean de antemano en la PC (tamano fijo) y aca solo se escribe DENTRO de 6 * ellos: se buscan en el directorio raiz FAT32, se sigue su cadena de clusters 7 * y se reescriben sectores. Nunca se toca la FAT ni el directorio. 8 * 9 * Log: cada arranque agrega texto despues de lo ultimo escrito; el resto 10 * del archivo queda en espacios. 11 * 12 * SD por SPI0: MISO GP16, CS GP17, SCK GP18, MOSI GP19 (esquematico PicoCalc). */ 13 #include <string.h> 14 #include "pico/stdlib.h" 15 #include "hardware/spi.h" 16 #include "sdlog.h" 17 #include "app_config.h" 18 19 #define SD_SPI spi0 20 #define SD_MISO 16 21 #define SD_CS 17 22 #define SD_SCK 18 23 #define SD_MOSI 19 24 25 static bool sd_hc; /* direccionamiento por bloque (SDHC/SDXC) */ 26 static bool mounted, ready; 27 static uint8_t sec[512]; /* sectores de FAT y directorio */ 28 static uint32_t fat_lba, data_lba, spc, root_cluster; 29 static uint32_t log_cluster, log_size; 30 static uint32_t cur_sector_idx; /* indice de sector dentro del log */ 31 static int cur_off; /* bytes usados en ese sector */ 32 static uint8_t wbuf[512]; /* sector del log en curso */ 33 34 /* ------------------------------------------------------------ SPI SD */ 35 36 static uint8_t xfer(uint8_t b) 37 { 38 uint8_t r; 39 spi_write_read_blocking(SD_SPI, &b, &r, 1); 40 return r; 41 } 42 43 static void cs(bool on) { gpio_put(SD_CS, on ? 0 : 1); xfer(0xFF); } 44 45 static uint8_t cmd(uint8_t c, uint32_t arg, uint8_t crc) 46 { 47 xfer(0xFF); 48 xfer(0x40 | c); 49 xfer((uint8_t)(arg >> 24)); xfer((uint8_t)(arg >> 16)); xfer((uint8_t)(arg >> 8)); xfer((uint8_t)arg); 50 xfer(crc); 51 uint8_t r = 0xFF; 52 for (int i = 0; i < 10 && (r & 0x80); i++) r = xfer(0xFF); 53 return r; 54 } 55 56 static bool sd_init(void) 57 { 58 spi_init(SD_SPI, 400000); 59 gpio_set_function(SD_MISO, GPIO_FUNC_SPI); 60 gpio_set_function(SD_SCK, GPIO_FUNC_SPI); 61 gpio_set_function(SD_MOSI, GPIO_FUNC_SPI); 62 gpio_init(SD_CS); 63 gpio_set_dir(SD_CS, GPIO_OUT); 64 gpio_put(SD_CS, 1); 65 gpio_pull_up(SD_MISO); 66 for (int i = 0; i < 10; i++) xfer(0xFF); /* >74 clocks con CS alto */ 67 68 cs(true); 69 uint8_t r = 0; 70 for (int i = 0; i < 20 && r != 1; i++) r = cmd(0, 0, 0x95); 71 if (r != 1) { cs(false); return false; } 72 r = cmd(8, 0x1AA, 0x87); 73 bool v2 = r == 1; 74 if (v2) for (int i = 0; i < 4; i++) xfer(0xFF); 75 uint32_t t0 = to_ms_since_boot(get_absolute_time()); 76 do { 77 cmd(55, 0, 0x65); 78 r = cmd(41, v2 ? 0x40000000 : 0, 0x77); 79 } while (r != 0 && to_ms_since_boot(get_absolute_time()) - t0 < 1500); 80 if (r != 0) { cs(false); return false; } 81 sd_hc = false; 82 if (v2 && cmd(58, 0, 0xFD) == 0) { 83 uint8_t ocr = xfer(0xFF); 84 xfer(0xFF); xfer(0xFF); xfer(0xFF); 85 sd_hc = (ocr & 0x40) != 0; 86 } 87 cmd(16, 512, 0x15); 88 cs(false); 89 spi_set_baudrate(SD_SPI, 12000000); 90 return true; 91 } 92 93 static bool sd_read(uint32_t lba, uint8_t *buf) 94 { 95 cs(true); 96 if (cmd(17, sd_hc ? lba : lba * 512, 0xFF) != 0) { cs(false); return false; } 97 uint8_t t = 0xFF; 98 for (int i = 0; i < 100000 && t == 0xFF; i++) t = xfer(0xFF); 99 if (t != 0xFE) { cs(false); return false; } 100 uint8_t ff = 0xFF; 101 for (int i = 0; i < 512; i++) spi_write_read_blocking(SD_SPI, &ff, &buf[i], 1); 102 xfer(0xFF); xfer(0xFF); 103 cs(false); 104 return true; 105 } 106 107 static bool sd_write(uint32_t lba, const uint8_t *buf) 108 { 109 cs(true); 110 if (cmd(24, sd_hc ? lba : lba * 512, 0xFF) != 0) { cs(false); return false; } 111 xfer(0xFF); 112 xfer(0xFE); 113 spi_write_blocking(SD_SPI, buf, 512); 114 xfer(0xFF); xfer(0xFF); 115 uint8_t resp = xfer(0xFF); 116 uint8_t b = 0; 117 for (int i = 0; i < 500000 && b != 0xFF; i++) b = xfer(0xFF); /* esperar fin de escritura */ 118 cs(false); 119 return (resp & 0x1F) == 0x05; 120 } 121 122 /* ------------------------------------------------------------ FAT32 minimo */ 123 124 static uint32_t rd32(const uint8_t *p) { return p[0] | (p[1] << 8) | (p[2] << 16) | ((uint32_t)p[3] << 24); } 125 static uint16_t rd16(const uint8_t *p) { return (uint16_t)(p[0] | (p[1] << 8)); } 126 127 static uint32_t fat_next(uint32_t cl) 128 { 129 if (!sd_read(fat_lba + cl / 128, sec)) return 0x0FFFFFFF; 130 return rd32(sec + (cl % 128) * 4) & 0x0FFFFFFF; 131 } 132 133 static uint32_t cluster_lba(uint32_t cl) { return data_lba + (cl - 2) * spc; } 134 135 /* sector numero idx del archivo -> LBA (siguiendo la cadena de clusters) */ 136 static uint32_t file_lba(uint32_t first, uint32_t idx) 137 { 138 uint32_t cl = first, n = idx / spc; 139 while (n-- && cl >= 2 && cl < 0x0FFFFFF8) cl = fat_next(cl); 140 if (cl < 2 || cl >= 0x0FFFFFF8) return 0; 141 return cluster_lba(cl) + idx % spc; 142 } 143 144 static bool mount(void) 145 { 146 if (mounted) return true; 147 if (!sd_init() || !sd_read(0, sec)) return false; 148 uint32_t part = 0; 149 if (sec[510] == 0x55 && sec[511] == 0xAA && (sec[0x1C2] == 0x0B || sec[0x1C2] == 0x0C)) 150 part = rd32(sec + 0x1C6); 151 if (!sd_read(part, sec)) return false; 152 if (rd16(sec + 11) != 512 || rd16(sec + 22) != 0) return false; /* solo FAT32 */ 153 spc = sec[13]; 154 uint32_t rsv = rd16(sec + 14), nfat = sec[16], fatsz = rd32(sec + 36); 155 root_cluster = rd32(sec + 44); 156 fat_lba = part + rsv; 157 data_lba = fat_lba + nfat * fatsz; 158 mounted = true; 159 return true; 160 } 161 162 /* name en formato de directorio FAT: 8+3 en mayusculas, con espacios */ 163 static bool find_file(const char name[11], uint32_t *first, uint32_t *size) 164 { 165 if (!mount()) return false; 166 uint32_t cl = root_cluster; 167 for (int guard = 0; guard < 64 && cl >= 2 && cl < 0x0FFFFFF8; guard++) { 168 for (uint32_t s = 0; s < spc; s++) { 169 if (!sd_read(cluster_lba(cl) + s, sec)) return false; 170 for (int e = 0; e < 16; e++) { 171 const uint8_t *d = sec + e * 32; 172 if (d[0] == 0) return false; 173 if (d[0] == 0xE5 || (d[11] & 0x0F) == 0x0F || (d[11] & 0x18)) continue; 174 if (!memcmp(d, name, 11)) { 175 *first = ((uint32_t)rd16(d + 20) << 16) | rd16(d + 26); 176 *size = rd32(d + 28); 177 return *first >= 2; 178 } 179 } 180 } 181 cl = fat_next(cl); 182 } 183 return false; 184 } 185 186 /* ------------------------------------------------------------ log */ 187 188 bool sdlog_open(void) 189 { 190 ready = false; 191 if (!find_file(APP_SD_LOG83, &log_cluster, &log_size) || log_size < 1024) return false; 192 /* primer sector que todavia esta en blanco (arranca con espacio) */ 193 uint32_t nsec = log_size / 512; 194 uint32_t idx = 0; 195 for (; idx < nsec; idx++) { 196 uint32_t lba = file_lba(log_cluster, idx); 197 if (!lba || !sd_read(lba, sec)) return false; 198 if (sec[0] == ' ') break; 199 } 200 if (idx >= nsec) idx = 0; /* lleno: vuelve a empezar */ 201 cur_sector_idx = idx; 202 cur_off = 0; 203 memset(wbuf, ' ', sizeof wbuf); 204 ready = true; 205 return true; 206 } 207 208 bool sdlog_ready(void) { return ready; } 209 210 void sdlog(const char *line) 211 { 212 if (!ready) return; 213 int n = (int)strlen(line); 214 for (int i = 0; i <= n; i++) { 215 char c = i < n ? line[i] : '\n'; 216 wbuf[cur_off++] = (uint8_t)c; 217 if (cur_off == 512) { 218 uint32_t lba = file_lba(log_cluster, cur_sector_idx); 219 if (!lba || !sd_write(lba, wbuf)) { ready = false; return; } 220 cur_sector_idx++; 221 if (cur_sector_idx >= log_size / 512) cur_sector_idx = 0; 222 cur_off = 0; 223 memset(wbuf, ' ', sizeof wbuf); 224 } 225 } 226 /* escribir el sector parcial ya, por si despues se cuelga */ 227 uint32_t lba = file_lba(log_cluster, cur_sector_idx); 228 if (!lba || !sd_write(lba, wbuf)) ready = false; 229 } 230 231 /* ------------------------------------------------------------ partida guardada 232 * 233 * La partida se guarda en un archivo que se divide en dos mitades ("slots"). Cada slot arranca con una 234 * cabecera de 16 bytes: "CSV1", numero de secuencia, largo y checksum de los 235 * datos. Se guarda siempre en el slot que NO tiene el ultimo guardado, y el 236 * primer sector (el de la cabecera) se escribe al final: si se corta la 237 * energia a mitad de camino, el guardado anterior sigue entero. Al cargar se 238 * usa el slot valido con la secuencia mas alta. */ 239 240 #define SAV_HDR 16 241 242 static uint8_t sbuf[512]; /* sector de la partida (aparte de wbuf: el log queda intacto) */ 243 static uint32_t sav_cluster, sav_size; 244 static bool sav_found; 245 246 static bool sav_open(void) 247 { 248 if (!sav_found) 249 sav_found = find_file(APP_SD_SAV83, &sav_cluster, &sav_size) && sav_size >= 4 * 512; 250 return sav_found; 251 } 252 253 static uint32_t slot_sectors(void) { return sav_size / 512 / 2; } 254 255 static uint32_t sum_step(uint32_t h, uint8_t b) { return (h ^ b) * 16777619u; } /* FNV-1a */ 256 257 /* Lee el slot (hasta max bytes en buf, puede ser NULL) y verifica el checksum. 258 * Devuelve el largo guardado, o -1 si el slot no es valido. */ 259 static int slot_read(int slot, char *buf, int max, uint32_t *seq) 260 { 261 uint32_t base = (uint32_t)slot * slot_sectors(); 262 uint32_t lba = file_lba(sav_cluster, base); 263 if (!lba || !sd_read(lba, sbuf) || memcmp(sbuf, "CSV1", 4)) return -1; 264 uint32_t len = rd32(sbuf + 8), want = rd32(sbuf + 12); 265 *seq = rd32(sbuf + 4); 266 if (len > slot_sectors() * 512 - SAV_HDR) return -1; 267 uint32_t h = 2166136261u; 268 for (uint32_t k = 0, i = SAV_HDR; k < len; k++, i++) { 269 if (i == 512) { 270 i = 0; 271 lba = file_lba(sav_cluster, base + (SAV_HDR + k) / 512); 272 if (!lba || !sd_read(lba, sbuf)) return -1; 273 } 274 h = sum_step(h, sbuf[i]); 275 if (buf && (int)k < max) buf[k] = (char)sbuf[i]; 276 } 277 return h == want ? (int)len : -1; 278 } 279 280 /* slot con el ultimo guardado valido (-1 ninguno) y su secuencia */ 281 static int sav_latest(uint32_t *seq) 282 { 283 int best = -1; 284 uint32_t s; 285 for (int slot = 0; slot < 2; slot++) 286 if (slot_read(slot, NULL, 0, &s) >= 0 && (best < 0 || (int32_t)(s - *seq) > 0)) { best = slot; *seq = s; } 287 return best; 288 } 289 290 bool sdsave_available(void) { return sav_open(); } 291 292 int sdsave_load(char *buf, int max) 293 { 294 uint32_t seq = 0; 295 if (!sav_open()) return 0; 296 int slot = sav_latest(&seq); 297 if (slot < 0) return 0; 298 int n = slot_read(slot, buf, max, &seq); 299 if (n < 0) return 0; 300 return n < max ? n : max; 301 } 302 303 static void wr32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); } 304 305 bool sdsave_save(const char *data, int n) 306 { 307 if (!sav_open() || n < 0 || (uint32_t)n > slot_sectors() * 512 - SAV_HDR) return false; 308 uint32_t seq = 0; 309 int last = sav_latest(&seq); 310 int slot = last < 0 ? 0 : 1 - last; 311 uint32_t base = (uint32_t)slot * slot_sectors(); 312 uint32_t h = 2166136261u; 313 for (int k = 0; k < n; k++) h = sum_step(h, (uint8_t)data[k]); 314 315 /* sectores 1.. primero, el de la cabecera al final */ 316 uint32_t nsec = (uint32_t)(SAV_HDR + n + 511) / 512; 317 for (uint32_t s = nsec; s-- > 0;) { 318 memset(sbuf, 0, sizeof sbuf); 319 int from = (int)s * 512 - SAV_HDR; /* byte de data en sbuf[0] */ 320 for (int i = 0; i < 512; i++) { 321 int k = from + i; 322 if (k >= 0 && k < n) sbuf[i] = (uint8_t)data[k]; 323 } 324 if (s == 0) { 325 memcpy(sbuf, "CSV1", 4); 326 wr32(sbuf + 4, seq + 1); 327 wr32(sbuf + 8, (uint32_t)n); 328 wr32(sbuf + 12, h); 329 } 330 uint32_t lba = file_lba(sav_cluster, base + s); 331 if (!lba || !sd_write(lba, sbuf)) return false; 332 } 333 return true; 334 }