What LCD and lib are you using?
Remember with u8g2 you can strip down fonts and include only the required characters, this greatly reduces space.
Mosts libs include a huge font with all ascii chars, then you might need only numbers, but it'll be stored whole into the flash.
Definitely, the crappy **_HandleTypeDefs** are using almost half of the ram, woudl be nice to get rid of them.
Switching the project to LL (LowLevel) libraries will do it, still provide a library to interface the hardware.
I did the test, ram and flash went down to 26%!
Only 32 bytes used by the system.
Then the stack and heap were using 256bytes each (The reserved sizes in the linker script), could be tracked down and reduced by taking care on the code.
If you'll be using stdlib functions (*printf*), then check the allocated heap with the mallinfo function I posted earlier (AFTER using those functions, heap get allocated on first call), assigning just a little more (i.e 32 bytes), and that would be it.
Otherwise, as fas as I know only the USB library allocates memory that way, you probably could set the heap to 0 in your code otherwise, but always check first with mallinfo.
Same for user stack:
Analyze the list / call graph and assign the heap a slightly larger value.
That way you'll be protecting the heap and user memory from colliding, yet wasting the minimal resources.
If you don't allocate large structures inside your functions (Or avoid long chained calls between functions, each one allocating more data), then stack usage will be minimal.
It's this behavior what increases the Stack a lot:
void func4(void){
uint8_t buffer4[128]; //<-- Another 128bytes on stack (512 now)
return; // Now all stack will be freed up
}
void func3(void){
uint8_t buffer3[128]; //<-- Another 128bytes on stack (384 now)
func3(); // Another function call, perhabs buffer3 was not required anymore
}
void func2(void){
uint8_t buffer2[128]; //<-- Another 128bytes on stack (256 now)
func3(); // Another function call, perhabs buffer2 was not required anymore
}
void func1(void){
uint8_t buffer1[128]; //<-- 128bytes on stack
func2(); // Stack not freed up, but calling another function, perhabs buffer1 was not required anymore
}
Which will be greatly minimized if you avoid the call-hierarchy, breaking the functions in a diferent way:
int func4(void){
uint8_t buffer4[128]; //<-- 128bytes on stack
do_whatever();
return result; // stack freed up
}
int func3(void){
uint8_t buffer3[128]; //<-- 128bytes on stack
do_whatever();
return result; // stack freed up
}
int func2(void){
uint8_t buffer2[128]; //<-- 128bytes on stack
do_whatever();
return result; // stack freed up
}
int func1(void){
uint8_t buffer[128]; //<-- 128bytes on stack
do_whatever();
return result; // stack freed up
}
void handle(void){
if( func1()=='do_func2' ){
if( func2()=='do_func3' ){
if( func3()=='do_func4' ){
func4();
}
}
}
}
Or by many other ways.
First example will forcefully need a stack of not less than 512bytes, second will only use 128.
Developing in LL will be a bit harder, but still much easier than managing the registers yourself.