Skip to content

Commit 740fac1

Browse files
authored
Merge pull request #7 from Lexithean/IAP
feat: re-add IAP flash profile storage using LPC2134 sector 7
2 parents 94e22a0 + 8d35dca commit 740fac1

4 files changed

Lines changed: 385 additions & 9 deletions

File tree

T-962-controller.ld

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
* Linker script for the T-962 controller improved firmware.
33
*
44
* Amended from a version generated by LPCXpresso v7.5.0
5+
*
6+
* LPC2134 Flash layout (128KB, 11 sectors):
7+
* Sectors 0-9: firmware (96KB contiguous)
8+
* Sector 10: reserved for IAP profile storage (0x18000+)
59
*/
610

711
GROUP(
@@ -13,12 +17,14 @@ GROUP(
1317

1418
MEMORY {
1519
/* Define each memory region */
16-
MFlash128 (rx) : ORIGIN = 0x0, LENGTH = 0x20000 /* 128K bytes */
17-
Ram16 (rwx) : ORIGIN = 0x40000000, LENGTH = 0x4000 /* 16K bytes */
20+
/* Firmware: sectors 0-9 (96KB). Sector 10 reserved for IAP profile storage. */
21+
MFlash128 (rx) : ORIGIN = 0x0, LENGTH = 0x18000 /* 96KB, sectors 0-9 */
22+
/* Top 256 bytes of RAM (0x40003F00-0x40003FFF) reserved for IAP/ISP (per UM10120) */
23+
Ram16 (rwx) : ORIGIN = 0x40000000, LENGTH = 0x3F00 /* 16KB - 256 bytes IAP */
1824
}
1925
/* Define a symbol for the top of each memory region */
20-
__top_MFlash128 = 0x0 + 0x20000;
21-
__top_Ram16 = 0x40000000 + 0x4000;
26+
__top_MFlash128 = 0x0 + 0x18000;
27+
__top_Ram16 = 0x40000000 + 0x3F00;
2228

2329
ENTRY(_start)
2430

src/flashprofiles.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
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+
}

src/flashprofiles.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef FLASHPROFILES_H_
2+
#define FLASHPROFILES_H_
3+
4+
#include <stdint.h>
5+
6+
// LPC2134 sector 10: last sector at 0x18000+
7+
// Each profile occupies one 256-byte block (IAP minimum write size)
8+
#define FLASH_PROFILE_SECTOR 10
9+
#define FLASH_PROFILE_BASE 0x18000
10+
#define FLASH_PROFILE_END 0x20000
11+
#define FLASH_PROFILE_BLOCK_SIZE 256
12+
#define FLASH_PROFILE_MAX_SLOTS 32 // 8KB / 256 bytes = 32
13+
#define FLASH_PROFILE_NAME_LEN 20 // Including null terminator
14+
#define FLASH_PROFILE_MAGIC 0x50524F46 // "PROF"
15+
16+
// LPC2134 runs at 55.296 MHz
17+
#define IAP_CCLK_KHZ 55296
18+
19+
// Initialize flash profile subsystem — scans sector for valid profiles
20+
void FlashProfiles_Init(void);
21+
22+
// Get number of valid profiles currently stored
23+
int FlashProfiles_GetCount(void);
24+
25+
// Get maximum capacity
26+
int FlashProfiles_GetCapacity(void);
27+
28+
// Read profile from flash slot (0-based). Returns 0 on success, -1 if invalid.
29+
int FlashProfiles_Read(int slot, uint16_t temps[48], char name[FLASH_PROFILE_NAME_LEN]);
30+
31+
// Write profile to flash slot. Erases sector and rewrites all profiles.
32+
int FlashProfiles_WriteProfile(int slot, const uint16_t temps[48], const char* name);
33+
34+
// Delete profile from flash slot. Returns 0 on success.
35+
int FlashProfiles_Delete(int slot);
36+
37+
// Check if a slot contains a valid profile
38+
int FlashProfiles_IsValid(int slot);
39+
40+
// Get the name of a flash profile (returns pointer to flash, or NULL)
41+
const char* FlashProfiles_GetName(int slot);
42+
43+
// Print all profiles (EEPROM + flash) as import-compatible backup text
44+
void FlashProfiles_BackupAll(void);
45+
46+
#endif /* FLASHPROFILES_H_ */

0 commit comments

Comments
 (0)