|
| 1 | +/* |
| 2 | + * flashprofiles.c - IAP flash profile storage for T-962 reflow controller |
| 3 | + * |
| 4 | + * Stores reflow profiles in LPC2134 flash sector 10 (at 0x18000). |
| 5 | + * Each profile occupies a 256-byte block. Supports up to 32 profiles. |
| 6 | + * |
| 7 | + * Uses NXP IAP (In-Application Programming) ROM routines for erase/write. |
| 8 | + * Cache-erase-rewrite pattern: all valid profiles are cached in RAM, |
| 9 | + * sector is erased, then profiles are written back. |
| 10 | + */ |
| 11 | + |
| 12 | +#include <stdint.h> |
| 13 | +#include <stdio.h> |
| 14 | +#include <string.h> |
| 15 | +#include "LPC214x.h" |
| 16 | +#include "flashprofiles.h" |
| 17 | +#include "reflow_profiles.h" |
| 18 | +#include "vic.h" |
| 19 | + |
| 20 | +// IAP ROM entry point |
| 21 | +typedef void (*IAP)(unsigned int[], unsigned int[]); |
| 22 | +static IAP iap_entry = (void*)0x7ffffff1; |
| 23 | + |
| 24 | +// IAP command codes |
| 25 | +#define IAP_PREPARE_SECTORS 50 |
| 26 | +#define IAP_COPY_RAM_FLASH 51 |
| 27 | +#define IAP_ERASE_SECTORS 52 |
| 28 | + |
| 29 | +// On-flash profile block layout (256 bytes) |
| 30 | +typedef struct { |
| 31 | + uint32_t magic; |
| 32 | + char name[FLASH_PROFILE_NAME_LEN]; |
| 33 | + uint16_t temps[48]; // 96 bytes |
| 34 | + uint8_t _reserved[256 - 4 - FLASH_PROFILE_NAME_LEN - 96]; |
| 35 | +} FlashProfileBlock_t; |
| 36 | + |
| 37 | +// Validity cache (scanned at init) |
| 38 | +static uint8_t profile_valid[FLASH_PROFILE_MAX_SLOTS]; |
| 39 | +static int profile_count = 0; |
| 40 | + |
| 41 | +// 256-byte write buffer — must be word-aligned for IAP |
| 42 | +static uint8_t __attribute__((aligned(4))) flash_write_buf[FLASH_PROFILE_BLOCK_SIZE]; |
| 43 | + |
| 44 | +// ---- Internal helpers ---- |
| 45 | + |
| 46 | +static const FlashProfileBlock_t* flash_profile_ptr(int slot) { |
| 47 | + return (const FlashProfileBlock_t*)((uintptr_t)FLASH_PROFILE_BASE + |
| 48 | + (uint32_t)slot * FLASH_PROFILE_BLOCK_SIZE); |
| 49 | +} |
| 50 | + |
| 51 | +// IAP command/result arrays — placed in BSS (not stack) to avoid |
| 52 | +// corruption from IAP's 128-byte internal stack usage |
| 53 | +static unsigned int iap_command[5]; |
| 54 | +static unsigned int iap_result[3]; |
| 55 | + |
| 56 | +static int iap_erase_sector(void) { |
| 57 | + uint32_t save = VIC_DisableIRQ(); |
| 58 | + uint32_t mam_save = MAMCR; |
| 59 | + MAMCR = 0; // Disable MAM during IAP (required by NXP) |
| 60 | + |
| 61 | + // Prepare sector (must stay valid until erase) |
| 62 | + iap_command[0] = IAP_PREPARE_SECTORS; |
| 63 | + iap_command[1] = FLASH_PROFILE_SECTOR; |
| 64 | + iap_command[2] = FLASH_PROFILE_SECTOR; |
| 65 | + iap_entry(iap_command, iap_result); |
| 66 | + if (iap_result[0] != 0) { |
| 67 | + MAMCR = mam_save; |
| 68 | + VIC_RestoreIRQ(save); |
| 69 | + printf("\n[IAP] Prepare for erase failed: code %u\n", iap_result[0]); |
| 70 | + return -1; |
| 71 | + } |
| 72 | + |
| 73 | + // Erase sector |
| 74 | + iap_command[0] = IAP_ERASE_SECTORS; |
| 75 | + iap_command[1] = FLASH_PROFILE_SECTOR; |
| 76 | + iap_command[2] = FLASH_PROFILE_SECTOR; |
| 77 | + iap_command[3] = IAP_CCLK_KHZ; |
| 78 | + iap_entry(iap_command, iap_result); |
| 79 | + |
| 80 | + MAMCR = mam_save; |
| 81 | + VIC_RestoreIRQ(save); |
| 82 | + if (iap_result[0] != 0) { |
| 83 | + printf("\n[IAP] Erase failed: code %u\n", iap_result[0]); |
| 84 | + } |
| 85 | + return (iap_result[0] == 0) ? 0 : -1; |
| 86 | +} |
| 87 | + |
| 88 | +static int iap_write_block(uint32_t flash_addr, uint8_t* data) { |
| 89 | + uint32_t save = VIC_DisableIRQ(); |
| 90 | + uint32_t mam_save = MAMCR; |
| 91 | + MAMCR = 0; // Disable MAM during IAP (required by NXP) |
| 92 | + |
| 93 | + // Prepare sector (must stay valid until copy) |
| 94 | + iap_command[0] = IAP_PREPARE_SECTORS; |
| 95 | + iap_command[1] = FLASH_PROFILE_SECTOR; |
| 96 | + iap_command[2] = FLASH_PROFILE_SECTOR; |
| 97 | + iap_entry(iap_command, iap_result); |
| 98 | + if (iap_result[0] != 0) { |
| 99 | + MAMCR = mam_save; |
| 100 | + VIC_RestoreIRQ(save); |
| 101 | + printf("\n[IAP] Prepare for write failed: code %u\n", iap_result[0]); |
| 102 | + return -1; |
| 103 | + } |
| 104 | + |
| 105 | + // Copy RAM to Flash |
| 106 | + iap_command[0] = IAP_COPY_RAM_FLASH; |
| 107 | + iap_command[1] = flash_addr; |
| 108 | + iap_command[2] = (uintptr_t)data; |
| 109 | + iap_command[3] = FLASH_PROFILE_BLOCK_SIZE; |
| 110 | + iap_command[4] = IAP_CCLK_KHZ; |
| 111 | + iap_entry(iap_command, iap_result); |
| 112 | + |
| 113 | + MAMCR = mam_save; |
| 114 | + VIC_RestoreIRQ(save); |
| 115 | + if (iap_result[0] != 0) { |
| 116 | + printf("\n[IAP] Write to 0x%lX failed: code %u\n", (unsigned long)flash_addr, iap_result[0]); |
| 117 | + } |
| 118 | + return (iap_result[0] == 0) ? 0 : -1; |
| 119 | +} |
| 120 | + |
| 121 | +// ---- Cache for sector rewrite ---- |
| 122 | +// Compact cache — only stores the essential data (not the full 256-byte block) |
| 123 | +typedef struct { |
| 124 | + char name[FLASH_PROFILE_NAME_LEN]; |
| 125 | + uint16_t temps[48]; |
| 126 | +} ProfileCache_t; // ~116 bytes |
| 127 | + |
| 128 | +// Cache, erase, rewrite the entire sector with one slot changed |
| 129 | +static int rewrite_sector(int target_slot, const uint16_t* new_temps, |
| 130 | + const char* new_name, int deleting) { |
| 131 | + static ProfileCache_t cache[FLASH_PROFILE_MAX_SLOTS]; // ~3.7KB in BSS |
| 132 | + int cache_valid[FLASH_PROFILE_MAX_SLOTS]; |
| 133 | + memset(cache_valid, 0, sizeof(cache_valid)); |
| 134 | + |
| 135 | + // Step 1: Cache all existing profiles from flash |
| 136 | + for (int i = 0; i < FLASH_PROFILE_MAX_SLOTS; i++) { |
| 137 | + if (i == target_slot) { |
| 138 | + if (!deleting && new_temps) { |
| 139 | + if (new_name) { |
| 140 | + strncpy(cache[i].name, new_name, FLASH_PROFILE_NAME_LEN - 1); |
| 141 | + cache[i].name[FLASH_PROFILE_NAME_LEN - 1] = '\0'; |
| 142 | + } else { |
| 143 | + snprintf(cache[i].name, FLASH_PROFILE_NAME_LEN, "Flash #%d", i); |
| 144 | + } |
| 145 | + memcpy(cache[i].temps, new_temps, sizeof(uint16_t) * 48); |
| 146 | + cache_valid[i] = 1; |
| 147 | + } |
| 148 | + } else if (profile_valid[i]) { |
| 149 | + const FlashProfileBlock_t* p = flash_profile_ptr(i); |
| 150 | + memcpy(cache[i].name, p->name, FLASH_PROFILE_NAME_LEN); |
| 151 | + memcpy(cache[i].temps, p->temps, sizeof(uint16_t) * 48); |
| 152 | + cache_valid[i] = 1; |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + // Step 2: Erase the sector |
| 157 | + if (iap_erase_sector() != 0) { |
| 158 | + printf("\n[ERROR] Flash erase failed\n"); |
| 159 | + return -1; |
| 160 | + } |
| 161 | + |
| 162 | + // Step 3: Write all cached profiles back |
| 163 | + int new_count = 0; |
| 164 | + int target_ok = 0; |
| 165 | + for (int i = 0; i < FLASH_PROFILE_MAX_SLOTS; i++) { |
| 166 | + if (!cache_valid[i]) { |
| 167 | + profile_valid[i] = 0; |
| 168 | + continue; |
| 169 | + } |
| 170 | + memset(flash_write_buf, 0xFF, FLASH_PROFILE_BLOCK_SIZE); |
| 171 | + FlashProfileBlock_t* blk = (FlashProfileBlock_t*)flash_write_buf; |
| 172 | + blk->magic = FLASH_PROFILE_MAGIC; |
| 173 | + memcpy(blk->name, cache[i].name, FLASH_PROFILE_NAME_LEN); |
| 174 | + memcpy(blk->temps, cache[i].temps, sizeof(uint16_t) * 48); |
| 175 | + |
| 176 | + uint32_t addr = FLASH_PROFILE_BASE + (uint32_t)i * FLASH_PROFILE_BLOCK_SIZE; |
| 177 | + if (iap_write_block(addr, flash_write_buf) != 0) { |
| 178 | + printf("\n[ERROR] Flash write failed at slot %d\n", i); |
| 179 | + profile_valid[i] = 0; |
| 180 | + continue; |
| 181 | + } |
| 182 | + profile_valid[i] = 1; |
| 183 | + new_count++; |
| 184 | + if (i == target_slot) target_ok = 1; |
| 185 | + } |
| 186 | + profile_count = new_count; |
| 187 | + return target_ok ? 0 : -1; |
| 188 | +} |
| 189 | + |
| 190 | +// ---- Public API ---- |
| 191 | + |
| 192 | +void FlashProfiles_Init(void) { |
| 193 | + profile_count = 0; |
| 194 | + for (int i = 0; i < FLASH_PROFILE_MAX_SLOTS; i++) { |
| 195 | + const FlashProfileBlock_t* p = flash_profile_ptr(i); |
| 196 | + if (p->magic == FLASH_PROFILE_MAGIC) { |
| 197 | + profile_valid[i] = 1; |
| 198 | + profile_count++; |
| 199 | + } else { |
| 200 | + profile_valid[i] = 0; |
| 201 | + } |
| 202 | + } |
| 203 | + printf("\nFlash profiles: %d/%d slots used", |
| 204 | + profile_count, FLASH_PROFILE_MAX_SLOTS); |
| 205 | +} |
| 206 | + |
| 207 | +int FlashProfiles_GetCount(void) { return profile_count; } |
| 208 | +int FlashProfiles_GetCapacity(void) { return FLASH_PROFILE_MAX_SLOTS; } |
| 209 | + |
| 210 | +int FlashProfiles_IsValid(int slot) { |
| 211 | + if (slot < 0 || slot >= FLASH_PROFILE_MAX_SLOTS) return 0; |
| 212 | + return profile_valid[slot]; |
| 213 | +} |
| 214 | + |
| 215 | +const char* FlashProfiles_GetName(int slot) { |
| 216 | + if (!FlashProfiles_IsValid(slot)) return NULL; |
| 217 | + return flash_profile_ptr(slot)->name; |
| 218 | +} |
| 219 | + |
| 220 | +int FlashProfiles_Read(int slot, uint16_t temps[48], char name[FLASH_PROFILE_NAME_LEN]) { |
| 221 | + if (!FlashProfiles_IsValid(slot)) return -1; |
| 222 | + const FlashProfileBlock_t* p = flash_profile_ptr(slot); |
| 223 | + memcpy(temps, p->temps, sizeof(uint16_t) * 48); |
| 224 | + if (name) { |
| 225 | + memcpy(name, p->name, FLASH_PROFILE_NAME_LEN); |
| 226 | + name[FLASH_PROFILE_NAME_LEN - 1] = '\0'; |
| 227 | + } |
| 228 | + return 0; |
| 229 | +} |
| 230 | + |
| 231 | +int FlashProfiles_WriteProfile(int slot, const uint16_t temps[48], const char* name) { |
| 232 | + if (slot < 0 || slot >= FLASH_PROFILE_MAX_SLOTS) return -1; |
| 233 | + return rewrite_sector(slot, temps, name, 0); |
| 234 | +} |
| 235 | + |
| 236 | +int FlashProfiles_Delete(int slot) { |
| 237 | + if (slot < 0 || slot >= FLASH_PROFILE_MAX_SLOTS) return -1; |
| 238 | + if (!profile_valid[slot]) return 0; |
| 239 | + return rewrite_sector(slot, NULL, NULL, 1); |
| 240 | +} |
| 241 | + |
| 242 | +void FlashProfiles_BackupAll(void) { |
| 243 | + printf("\n# T-962 Profile Backup\n"); |
| 244 | + printf("# Paste this into serial after reflashing to restore.\n"); |
| 245 | + |
| 246 | + // EEPROM profiles (survive reflashing) |
| 247 | + printf("# --- EEPROM profiles ---\n"); |
| 248 | + Reflow_ExportProfile(5); // CUSTOM#1 |
| 249 | + Reflow_ExportProfile(6); // CUSTOM#2 |
| 250 | + |
| 251 | + // Flash profiles (erased on firmware update) |
| 252 | + printf("# --- Flash profiles (lost on update) ---\n"); |
| 253 | + for (int i = 0; i < FLASH_PROFILE_MAX_SLOTS; i++) { |
| 254 | + if (!profile_valid[i]) continue; |
| 255 | + const FlashProfileBlock_t* p = flash_profile_ptr(i); |
| 256 | + printf("save flash %d ", i); |
| 257 | + for (int t = 0; t < 48; t++) { |
| 258 | + if (t > 0) printf(","); |
| 259 | + printf("%d", p->temps[t]); |
| 260 | + } |
| 261 | + printf(",%s\n", p->name); |
| 262 | + } |
| 263 | + printf("# --- End of backup ---\n"); |
| 264 | +} |
0 commit comments