ESP32 Arduino allocate and use array of structs in PSRAM - arrays

I have a struct like this:
typedef struct {
boolean isExists = false;
uint16_t readBuffer[32] = {0};
uint16_t writeBuffer[13] = {0};
uint16_t ledBrgBuff[9] = {0};
uint16_t address = 0x0000;
uint16_t id = 0x0000;
uint16_t serial = 0x0000;
String kind = "RESERVED";
String name = "RESERVED";
uint16_t readNum = 21;
uint16_t writeNum = 2;
byte read_failCounter = 0;
byte write_failCounter = 0;
byte allowedFails = 10;
long lastWriteFailMSG = 0;
long lastReadFailMSG = 0;
boolean isTesting = false;
boolean criticalOff = false;
boolean onFault = false;
uint8_t data[RESPONSE_COUNT];
} expStruct;
I was using a dynamically allocated array of struct from ram before like this:
expStruct * expanders[MAX_EXPANDER] EXT_RAM_ATTR; // Globally
void fillStructsTest(){
// Fill all the array of structs with data from a json string from the filesystem
JsonArray hwConf = doc.as<JsonArray>();
memset(expanders, 0, sizeof(expanders)); // here I null all the struct in the array
for(byte i = 0; i < hwConf.size(); i ++){
JsonObject module = hwConf[i];
expanders[expCounter] = new expStruct;
expanders[expCounter]->address = module["address"].as<uint16_t>();
expanders[expCounter]->id = module["moduleid"].as<uint16_t>();
expanders[expCounter]->serial = module["serialnr"].as<uint16_t>();
expanders[expCounter]->kind = module["kind"].as<String>();
expanders[expCounter]->name = module["name"].as<String>();
expCounter++;
}
}
That worked pretty well. I could use my array of structs like this:
void useStructTest(){
if( expanders[0] != 0 ){
expanders[0]->name = "Test";
// I can reach every variable inside any of the structs in the array if the value on the index
// is not 0
}
}
Now I want to place this array of structs into PSRAM on my esp32.
I approached it like this:
typedef struct {
boolean isExists = false;
uint16_t readBuffer[32] = {0};
uint16_t writeBuffer[13] = {0};
uint16_t ledBrgBuff[9] = {0};
uint16_t address = 0x0000;
uint16_t id = 0x0000;
uint16_t serial = 0x0000;
String kind = "RESERVED";
String name = "RESERVED";
uint16_t readNum = 21;
uint16_t writeNum = 2;
byte read_failCounter = 0;
byte write_failCounter = 0;
byte allowedFails = 10;
long lastWriteFailMSG = 0;
long lastReadFailMSG = 0;
boolean isTesting = false;
boolean criticalOff = false;
boolean onFault = false;
uint8_t data[RESPONSE_COUNT];
} expStruct;
expStruct *expanders = (expStruct *) ps_malloc(MAX_EXPANDER * sizeof(expStruct));
void fill_PSRAM_StructsTest(){
// Fill all the array of structs with data from a json string from the filesystem
JsonArray hwConf = doc.as<JsonArray>();
// memset(expanders, 0, sizeof(expanders)); // I can't do this now
for(byte i = 0; i < hwConf.size(); i ++){
JsonObject module = hwConf[i];
// expanders[expCounter] = new expStruct; // I can't do this now either.
// also I must replace the -> with . because the compiler erroring me
expanders[expCounter].address = module["address"].as<uint16_t>();
expanders[expCounter].id = module["moduleid"].as<uint16_t>();
expanders[expCounter].serial = module["serialnr"].as<uint16_t>();
expanders[expCounter].kind = module["kind"].as<String>();
expanders[expCounter].name = module["name"].as<String>();
expCounter++;
}
}
Somewhere here my ESP crashing without a meaningful serial output, which is this:
Guru Meditation Error: Core 0 panic'ed (StoreProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400ec9a7 PS : 0x00060630 A0 : 0x80090915 A1 : 0x3ffdaee0
A2 : 0x00000000 A3 : 0x3f80afdc A4 : 0x00000000 A5 : 0x3ffc25fc
A6 : 0x00000000 A7 : 0x3ffdaf64 A8 : 0x800ec9a2 A9 : 0x3ffdaeb0
A10 : 0x00000001 A11 : 0x3f40203a A12 : 0x3ffdafd0 A13 : 0xf655c6f4
A14 : 0x3ffdadc0 A15 : 0x00000005 SAR : 0x00000010 EXCCAUSE: 0x0000001d
EXCVADDR: 0x0000006e LBEG : 0x4008d6b1 LEND : 0x4008d6c1 LCOUNT : 0xfffffff7
ELF file SHA256: 0000000000000000
Backtrace: 0x400ec9a7:0x3ffdaee0 0x40090912:0x3ffdb000
#0 0x400ec9a7:0x3ffdaee0 in mbusTask(void*) at lib/modbus-esp8266-3.0.6/src/Modbus.h:238
(inlined by) mbusTask(void*) at src/Own_Headers/busCommunication.h:559
#1 0x40090912:0x3ffdb000 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)
Rebooting...
ets Jul 29 2019 12:21:46
Should I allocate every index again in a function or what should I do to use it normally like before?

sorry for my english, you can't use String in PSRAM (dynamic)
you use: char name[100];
no String !!!
and ... copy from JSON to stuct :-)
strcpy(expanders[expCounter].name,
module["name"].as < string > ().c_str());

To move a ArduinoJsonDocument into PSRAM one needs to do it like so:
struct SpiRamAllocator {
void* allocate(size_t size) {
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
}
void deallocate(void* pointer) {
heap_caps_free(pointer);
}
void* reallocate(void* ptr, size_t new_size) {
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
}
};
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;
see here for a more detailed info.
To move any variable into PSRAM memory space one needs to declare it as below for the case of a uint16_t:
uint16_t* p_buffer_a = (uint16_t*) heap_caps_malloc(307200, MALLOC_CAP_SPIRAM );
where 307200 is the uint16_t byte size memory allocation to that specific variable p_buffer_a. The maximum element size for this array can be determined like this:
sizeof(p_buffer_a)/sizeof(uint16_t)
Mind you if one needs / wants to use Strings, it needs to rebuild the String class to accommodate PSRAM memory allocation.
Another way to move variable data into PSRAM is by wrapping it in a typedef struct.To allocate a buffer dynamically at runtime like the one below, one can declare it as follows:
typedef struct{
int len;
uint8_t buff[INTERRUPT_BUFFER_SIZE];
} Buffer;
this particular one stores a variable array named buff with element size of INTERRUPT_BUFFER_SIZE. Next is needs to declare a variable name that is stored directly into PSRAM:
Buffer *bufferA = (*Buffer) heap_caps_malloc( sizeof(Buffer), MALLOC_CAP_SPIRAM );
Followed by its initialization in the setup function
//setup() initialization
void setup(){
bufferA = new Buffer();
...
}
Its usage through the code is made this way "bufferA->field" instead of the regular traditional way "bufferA.field".

Related

Writing array to flash which is uint16_t. I am using a STM32L053 nucleo., get a hard fault error. How to convert uint16_t array to write to flash?

This is the how I'm trying to write to flash
Basically I pass my uint16_t array to a function called FLASH_WriteA which accepts the array, the destination array or the location and the length
void FLASH_WriteA(uint16_t * src, uint16_t * dest, uint16_t length_16)
{
uint32_t firstAddress= FLASH_GetFirstAddress((uint32_t)dest, PAGE_SIZE_BYTES); // gets the first address of that page
uint32_t offset = ((uint32_t)dest - seg) / 2; // offset is in words
HAL_FLASH_Unlock();
Flash_Erase_Page((uint16_t*)seg); // calls the page erase function
FLASH_Write_HAL((uint32_t *)&flashBuffer, (uint16_t*)seg, FLASH_TABLE_SIZE_BYTES); //FLASH_TABLE_SIZE_BYTES = 256 = size of array
HAL_FLASH_Lock();
}
HAL_StatusTypeDef Flash_Erase_Page(uint16_t* seg){//, uint16_t length){
//uint16_t pages = (length/page_size) + (int)((length % page_size) != 0);
uint32_t pages = 2; // page size for STM32L053 = 128 bytes
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = seg;
EraseInitStruct.NbPages = pages;
uint32_t PageError;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK) //Erase the Page Before a Write Operation
{
return HAL_ERROR;
}
}
void FLASH_Write_HAL(uint32_t *p_source, uint16_t* seg, uint16_t length)
{
uint32_t varToPass;
uint16_t wordcount=0;
int i;
for (i=0; i<length; i++){
varToPass = p_source[i] ;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,seg+wordcount,varToPass);
wordcount += 4;
// if (wordcount>=256){
// break;
// }
}
}

Can't pass array stored in .h file to C function

I am trying to implement a driver for a sensor(ST-VL53L5CX) on an AVR MCU (AVR128DB48).
The driver comes prewritten, with a few I2C functions for the user to fill in
(So the sensor can be implemented on different platforms).
The driver is made up of multiple c files. API.c, API.h, Buffers.h, Platform.c and Platform.h.
The firmware for the sensor is stored in the Buffers.h file as an array. e.g:
const uint8_t Sensor_Firmware[] = {0x05,...,0x01};
This needs to be written to the sensor.
To do this, I call a function I have written to write multiple bytes to the I2C line:
uint8_t WrMulti(VL53L5CX_Platform *p_platform,uint16_t
RegisterAdress,uint8_t *p_values,uint32_t size)
{
uint8_t count = 0;
uint32_t new_size = size + 2;
//Split Register address into 2 bytes
uint8_t temp0 = (uint8_t)((RegisterAdress & 0xFF00)>> 8);
uint8_t temp1 = (uint8_t)(RegisterAdress & 0x00FF);
//Create new array to hold Register Address and p_values
uint8_t temp_array[new_size] ;
for (int i = 0; i <new_size; i++)
{
temp_array[i] = 0;
}
//Fill temp_array with register address and p_values
temp_array[0] = temp0;
temp_array[1] = temp1;
for (int i = 2; i < (new_size); i++ )
{
temp_array[i] = p_values[count];
count++;
}
//I2C
uint8_t status = 255;
while(!I2C0_Open(p_platform->address)); // sit here until we get the bus..
I2C0_SetBuffer(temp_array,new_size);
I2C0_SetAddressNackCallback(I2C0_SetRestartWriteCallback,NULL); //NACK polling?
I2C0_MasterWrite();
while(I2C0_BUSY == I2C0_Close()); // sit here until finished.
/* This function returns 0 if OK */
status = 0;
return status;
}
This works when sending an array that is located in my main.c file.
e.g:
//buffer.h
const uint8_t Sensor_Firmware[] = {0x05,...,0x01};
//End buffer.h
.
//main.c
void main (void)
{
uint8_t I2C_address = 0x01;
uint8_t I2C_reg = 0x01;
uint32_t size = 16;
uint8_t temp_array[size];
for (int i = 0; i <size; i++)
{
temp_array[i] = Sensor_Firmware[i];
}
WrMulti(I2C_address, I2C_reg, &temp_array[0], size);
While(1)
{
;
}
}
//End main.c
It also work when passing an array from the .h file without the 'const' key word.
e.g:
//buffer.h
uint8_t Sensor_Firmware[] = {0x05,...,0x01};
//End buffer.h
.
//main.c
void main (void)
{
uint8_t I2C_address = 0x01;
uint8_t I2C_reg = 0x01;
uint32_t size = 16;
uint8_t temp_array[size];
WrMulti(I2C_address, I2C_reg, &Sensor_Firmware[0], size);
While(1)
{
;
}
}
//End main.c
It does not when I pass the 'Sensor_Firmwware[]' array (with the const key word) from the .h file to 'WrMulti'. It just ends up sending '0x00' for every byte that comes from 'Sensor_Firmware'.
Does anyone know why this is might be?
Kind regards

Weird correlation between I2C Transfer and Array size

So... i don't even know how to explain this...
I have a cc1310 Launchpad XL and a tiny EEPROM. My task is to write a library for easy transferring. In Code Composer Studio I'm using an Example project from TI with TIRTOS to test my functions. The weird thing is:
when i am declaring a uint8_t array larger than 304. my Transactions wont work. Itll only send 1 Byte and freezes.
Under 305, everything is fine.
Oh and im not even using the array. It just has to exist and nothing works.
void *mainThread(void *arg0)
{
uint32_t *pAddress;
int i;
uint8_t dat = 0;
uint32_t address = 0x00FEAA;
uint8_t data[305] = {0};
uint32_t datalen = sizeof(data);
for(i=0; i< 305; i++)
{
data[i]=dat;
dat++;
}
pAddress=&address;
int write = EEPROM_sWrite(pAddress, data,datalen);
return 0;
}
and
int EEPROM_sWrite(uint32_t *address, uint8_t *data, uint32_t datalen)
{
I2C_init();
//needed variables and initialization
uint8_t writebuf[258] = {0};
uint8_t blocksize = 0;
uint8_t *p = &writebuf[2];
if((*address+datalen)>maxAddress) //out of bounds?
{
return 1;
}
else
{
I2C_Transaction Transaction = {0};
Transaction.slaveAddress = slaveAddressA;
Transaction.writeBuf = writebuf;
Transaction.writeCount = blocksize;
Transaction.readBuf = NULL;
Transaction.readCount =0;
I2C_Params params;
I2C_Params_init(&params);
params.bitRate = I2C_400kHz;
params.transferMode = I2C_MODE_BLOCKING;
I2C_Handle Handle = I2C_open(0,&params );
//first block
if(*address>0xFFFF) //second half of the memory?
{
Transaction.slaveAddress = slaveAddressB;
}
blocksize = maxPagesize - (*address & 0xFF)+1;
//blocksize needs to be adjusted to the page size
writebuf[0] = (*address & 0xFF00) >> 8; //page address
writebuf[1] = (*address & 0xFF); //cell Address
if(datalen<=blocksize) //if it fits in a single page, just do it
{
memcpy(p,data,datalen);
//copies data to buffer (fills only needed cells in page)
Transaction.writeCount = datalen+2;
if(I2C_transfer(Handle, &Transaction))
{
I2C_close(Handle);
return 0;
}
else
{
I2C_close(Handle);
return 1;
}
}
memcpy(p,data,blocksize);//copies data to buffer (fills complete page)
Transaction.writeCount = blocksize+2;
if(!I2C_transfer(Handle, &Transaction))
{
I2C_close(Handle);
return 1;
}
usleep(10000);
//loop preparation
data+=blocksize;//shifts pointer forward
datalen-=blocksize; //reduces blocksize
writebuf[0]++; //next page
writebuf[1] = 0; //start cell is now 0 each time
//nth block
while(datalen>maxPagesize) //cut down to page sized blocks and write it down
{
//copy 256 bytes of data to buffer
memcpy(p,data,maxPagesize);
//send it
Transaction.writeCount = maxPagesize+2;
if(!I2C_transfer(Handle, &Transaction))
{
I2C_close(Handle);
return 1;
}
usleep(10000);
//preparation
data+=maxPagesize;
datalen-=maxPagesize;
//checks if it exceeds the first memory half
if(writebuf[0]==0xff)
{
Transaction.slaveAddress=slaveAddressB;
writebuf[0]=0;
}
else
{
writebuf[0]++; //next page
}
}
//last block
//copy last data
memcpy(p,data,datalen);
//send it
Transaction.writeCount = datalen+2;
if(!I2C_transfer(Handle, &Transaction))
{
I2C_close(Handle);
return 1;
}
I2C_close(Handle);
return 0;
}
}
edit:
#define maxAddress 0x1FFFF
#define maxPagesize 0xFF
forgot them..
A Reddit user had the right idea!
Turns out it was a simple stack overflow.
The Stack size of the thread was 1024. As i changed it to 2048, everything works.

Convert uint8_t array to structure in C

I'm beginner and I have a problem. I have this structure:
typedef struct {
char data[26];
int index;
Placa_baza pb; // char nume_placa[10], int index_placa;
} PC;
And a structure vector:
static PC computers[5] = { ... };
I need to have a vector of type uint8_t pc[5*sizeof(computers)] instead of the structure vector.
Is it well declared that way? :
uint8_t pc[5*sizeof(computers)]
How can I convert (cast) vector uint8_t pc[5*sizeof(computers)] to PC?
To use the uint8_t pointer to address the structure, how should it be written?
Thank you in advance.
Your pc array, which could serve as a backup for the PC data is too large: it is sufficient to define it as:
uint8_t pc[sizeof(computers)];
Or possibly:
uint8_t pc[5 * sizeof(PC)];
You can then copy computers to pc with:
memcpy(pc, computers, sizeof pc);
You could also use a pointer to access the pc array as an array of PC:
PC *p = (PC *)pc; // Don't do this!
Note however that this has undefined behavior as the byte array pc might not be properly aligned to access members of the PC structure, especially the index member and using such a pointer is a violation of the strict aliasing rule. It would be much better to define pc as PC pc[5]; and access this array via a uint8_t pointer of so required.
WARNING: Below program is just demonstration purpose, it may not behave same way with all compilers/systems. You can use it to test your compilers or systems behavior and modify accordingly.
In the below program am copying the contents from the structure computers to unit8_t.
as you can see its not easy and not portable, because we need to extract the data as per the boundaries of memory, allocated for variables.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
typedef struct
{
char nume_placa[10];
int index_placa;
}Placa_baza;
typedef struct {
char data[26];
int index;
Placa_baza pb;
}PC;
int main()
{
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(Placa_baza) = %zu\n", sizeof(Placa_baza));
printf("sizeof(PC) = %zu\n", sizeof(PC));
static PC computers[3] = { {"data1",1,"comp1", 0}, {"data2",2,"comp2", 1}, {"data3",3,"comp3", 2} };
printf("sizeof(computers) = %zu\n\n", sizeof(computers));
for(int i =0; i<3; i++)
printf("data = %s, index =%d, pb.nume_placa =%s, pb.index_placa =%d\n",
computers[i].data,
computers[i].index,
computers[i].pb.nume_placa,
computers[i].pb.index_placa
);
uint8_t uint8_t_pc[sizeof(computers)] = {0};
// for copying the contents from pc (uint8_t), used same variable names as that of structures
/* typedef struct { */
char data[26];
int index;
/* Placa_baza pb;
} PC; */
/* typedef struct
{ */
char nume_placa[10];
int index_placa;
/* }Placa_baza;
*/
printf("\n sizeof(uint8_t_pc) = %zu\n", sizeof(uint8_t_pc));
memcpy(uint8_t_pc,computers,sizeof(computers));
int count = 0;
uint8_t* temp = uint8_t_pc;
printf("\n **From uint8_t memory ***\n");
while(count < 3) {
memcpy(data, temp, 26);
// since there is a padding of 2 bytes , so extract from 28
memcpy(&index, temp+28, 4);
memcpy(nume_placa, temp+32, 10);
//again there is a padding of 2 bytes
memcpy(&index_placa, temp+44, 4);
printf("data = %s, index = %d, nume_placa =%s , index_placa =%d\n", data, index, nume_placa, index_placa);
temp = temp+sizeof(computers[0]);
count++;
}
return 0;
}
Output:
sizeof(int) = 4
sizeof(Placa_baza) = 16
sizeof(PC) = 48
sizeof(computers) = 144
data = data1, index =1, pb.nume_placa =comp1, pb.index_placa =0
data = data2, index =2, pb.nume_placa =comp2, pb.index_placa =1
data = data3, index =3, pb.nume_placa =comp3, pb.index_placa =2
sizeof(uint8_t_pc) = 144
**From uint8_t memory ***
data = data1, index = 1, nume_placa =comp1 , index_placa =0
data = data2, index = 2, nume_placa =comp2 , index_placa =1
data = data3, index = 3, nume_placa =comp3 , index_placa =2
online source
Update:
Indeed we can use offsetof to get the offset of any member of the structure, so the statements inside while can also be replaced by below statments.
memcpy(data, temp+offsetof(PC, data), sizeof(computers[count].data));
memcpy(&index, temp+offsetof(PC, index), sizeof index);
memcpy(nume_placa, temp+offsetof(PC, pb.nume_placa), sizeof computers[count].pb.nume_placa);
memcpy(&index_placa, temp+offsetof(PC, pb.index_placa), sizeof index_placa);

Writing to allocated char buffer

I have following code:
framingStatus compressToFrame(char* inputBuffer, size_t inputBufferLength, char* frame, size_t* frameLength)
{
dword crc32 = GetMaskedCrc(inputBuffer,inputBufferLength);
size_t length = snappy_max_compressed_length(inputBufferLength);
char* compressed = (char*)malloc(length);
snappy_status status = snappy_compress(inputBuffer,inputBufferLength,compressed,&length);
if( status!=SNAPPY_OK )
return FS_ERROR;
frame[0] = 0x00; // Typ ramki skompresowany
frame[1] = length&0xff;
frame[2] = (length&0xff00)>>8;
frame[3] = (length&0xff00)>>16;
frame[4] = crc32&0xff;
frame[5] = (crc32&0xff00)>>8;
frame[6] = (crc32&0xff0000)>>16;
frame[7] = (crc32&0xff000000)>>24;
frame[8] = '\0'; // Pomoc dla strcat
strcat(frame,compressed);
*frameLength = length+8;
free(compressed);
return FS_OK;
}
Before calling this function I allocate memory for buffer named frame. All is ok, but assign instructions frame[x] = ... don't seem to write anything to buffer called frame and strcat concatenate compressed data to empty buffer without header I need.
Why assign instructions frame[x] = ... etc. don't give any result?
[EDIT:]
Can you suggest what function I have to use if I want to concatenate frame header with compressed data?
[EDIT2:]
Code presented below works just fine.
framingStatus compressToFrame(char* inputBuffer, size_t inputBufferLength, char* frame, size_t* frameLength)
{
dword crc32 = GetMaskedCrc(inputBuffer,inputBufferLength);
size_t length = snappy_max_compressed_length(inputBufferLength);
char* compressed = (char*)malloc(length);
snappy_status status = snappy_compress(inputBuffer,inputBufferLength,compressed,&length);
if( status!=SNAPPY_OK )
return FS_ERROR;
frame[0] = 0x00; // Typ ramki skompresowany
frame[1] = length;
frame[2] = length >> 8;
frame[3] = length >> 16;
frame[4] = crc32;
frame[5] = crc32 >>8;
frame[6] = crc32 >>16;
frame[7] = crc32 >>24;
memcpy(&frame[8],compressed,length);
*frameLength = length+8;
free(compressed);
return FS_OK;
}
You have
frame[0] = 0x00;
which is the same as
frame[0] = '\0';
No matter what you add after the first character, frame becomes a 0 length string.
strcat is for strings, not general binary bytes. Because frame first byte is zero, strcat will copy compressed starting at frame[0] and will stop copying when it sees a zero in compressed.
Try memcpy instead.
memcpy(&frame[8], compressed, length);
Also, since the length of frame is passed as an argument, you might want to be checking the total length you are copying to frame to make sure there's no illegal overwrite in that case.
As others already pointed out, you use binary data and not text strings. Therefore, strcat function is inappropriate here, use memcpy instead.
Furthermore, you should use unsigned char instead of plain char.
Additionally, you don't need to mask the values before shifting
frame[2] = (length&0xff00)>>8;
could be just
frame[2] = length >> 8;
And in this case, it is even buggy
frame[3] = (length&0xff00)>>16;
Same here
frame[5] = crc32 >> 8;
frame[6] = crc32 >> 16;
frame[7] = crc32 >> 24;
frame[0] = 0x00;
will make the first character as end of string character therefore your string frame is empty.
frame[0] = 0x00;
is same as writing,
frame[0] = '\0';

Resources