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
Related
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;
// }
}
}
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".
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(¶ms);
params.bitRate = I2C_400kHz;
params.transferMode = I2C_MODE_BLOCKING;
I2C_Handle Handle = I2C_open(0,¶ms );
//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.
I wrote the below program to iterate over all images in memory and dump their string tables.
#include <mach-o/dyld.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
uint32_t count = _dyld_image_count();
for (uint32_t i = 0 ; i < count ; i++) {
const char* imageName = _dyld_get_image_name(i);
printf("IMAGE[%u]=%s\n", i, imageName);
const struct mach_header* header = _dyld_get_image_header(i);
if (header->magic != MH_MAGIC_64)
continue;
struct mach_header_64* header64 = (struct mach_header_64*)header;
char *ptr = ((void*)header64) + sizeof(struct mach_header_64);
for (uint32_t j = 0; j < header64->ncmds; j++) {
struct load_command *lc = (struct load_command *)ptr;
ptr += lc->cmdsize;
if (lc->cmd != LC_SYMTAB)
continue;
struct symtab_command* symtab = (struct symtab_command*)lc;
printf("\t\tLC_SYMTAB.stroff=%u\n", symtab->stroff);
printf("\t\tLC_SYMTAB.strsize=%u\n", symtab->strsize);
if (symtab->strsize > 100*1024*1024) {
printf("\t\tHUH? Don't believe string table is over 100MiB in size!\n");
continue;
}
char *strtab = (((void*)header64) + symtab->stroff);
uint32_t off = 0;
while (off < symtab->strsize) {
char *e = &(strtab[off]);
if (e[0] != 0)
printf("\t\tSTR[%u]=\"%s\"\n", off, e);
off += strlen(e) + 1;
}
}
}
return 0;
}
It seems to randomly work for some images, but for others the stroff/strsize have nonsensical values:
LC_SYMTAB.stroff=1266154560
LC_SYMTAB.strsize=143767728
It seems to always be the same two magic values, but I'm not sure if this is system-dependent in some way or if other people will get the same specific values.
If I comment out the check for strsize being over 100MiB, then printing the string table segfaults.
Most images seem to have this problem, but some don't. When I run it, I get the issue for 29 images out of 38.
I can't observe any pattern as to which do and which won't. What is going on here?
If it is relevant, I am testing on macOS 10.14.6 and compiling with Apple LLVM version 10.0.1 (clang-1001.0.46.4).
As you already worked out, those are from the dyld_shared_cache. And the 0x80000000 flag is indeed documented, in the headers shipped with Xcode or any semi-recent XNU source:
#define MH_DYLIB_IN_CACHE 0x80000000 /* Only for use on dylibs. When this bit
is set, the dylib is part of the dyld
shared cache, rather than loose in
the filesystem. */
As you've also discovered, the stroff/strsize values do not yield usable results when added to the dyld_shared_cache base. That is because those are not memory offsets, but file offsets. This is true for all Mach-O's, it's just often the case that the segments of non-cached binaries have the same relative position in file and memory offsets. But this is definitely not true for the shared cache.
To translate the file offset into a memory address, you'll have to parse the segments in the shared cache header. You can find struct definitions in the dyld source.
Here's a program which prints out the contents of the string table of the dyld shared cache.
My original program in the question can be enhanced to skip dumping string table of images with MH_DYLIB_IN_CACHE set, and combined with this program to dump the shared cache string table. (All images in the shared cache share the same string table.)
#include <mach-o/dyld.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
const void* _dyld_get_shared_cache_range(size_t* cacheLen);
struct dyld_cache_header {
char magic[16];
uint32_t mappingOffset;
uint32_t mappingCount;
// Omitted remaining fields, not relevant to this task
};
struct dyld_cache_mapping_info {
uint64_t address;
uint64_t size;
uint64_t fileOffset;
uint32_t maxProt;
uint32_t initProt;
};
#ifndef MH_DYLIB_IN_CACHE
# define MH_DYLIB_IN_CACHE 0x80000000
#endif
// Finds first shared cache DYLD image. Any will do, just grab the first
const struct mach_header_64* findSharedCacheDyldImage(void) {
uint32_t count = _dyld_image_count();
for (uint32_t i = 0 ; i < count ; i++) {
const struct mach_header* header = _dyld_get_image_header(i);
if (header->magic != MH_MAGIC_64)
continue;
const struct mach_header_64* header64 = (const struct mach_header_64*)header;
if (!(header64->flags & MH_DYLIB_IN_CACHE))
continue;
return header64;
}
return NULL;
}
// Find first instance of given load command in image
const struct load_command* findFirstLoadCommand(const struct mach_header_64* header64, uint32_t cmd) {
const char *ptr = ((void*)header64) + sizeof(struct mach_header_64);
for (uint32_t j = 0; j < header64->ncmds; j++) {
const struct load_command *lc = (const struct load_command *)ptr;
ptr += lc->cmdsize;
if (lc->cmd == cmd)
return lc;
}
return NULL;
}
// Translates a shared cache file offset to a memory address
void *translateOffset(const struct dyld_cache_header *cache, uint64_t offset) {
const struct dyld_cache_mapping_info* mappings = (struct dyld_cache_mapping_info*)(((void*)cache) + cache->mappingOffset);
for (uint32_t i = 0; i < cache->mappingCount; i++) {
if (offset < mappings[i].fileOffset) continue;
if (offset >= (mappings[i].fileOffset + mappings[i].size)) continue;
return (void*)(mappings[i].address - mappings[0].address + (offset - mappings[i].fileOffset) + (uint64_t)cache);
}
return NULL;
}
int main(int argc, char** argv) {
size_t cacheLen;
const struct dyld_cache_header *cache = _dyld_get_shared_cache_range(&cacheLen);
const struct mach_header_64* sharedCacheDyldImage = findSharedCacheDyldImage();
const struct symtab_command* symtab = (const struct symtab_command*)findFirstLoadCommand(sharedCacheDyldImage,LC_SYMTAB);
const void *stringTbl = translateOffset(cache, symtab->stroff);
uint32_t off = 0;
while (off < symtab->strsize) {
const char *e = &(stringTbl[off]);
if (e[0] != 0)
printf("STR[%u]=\"%s\"\n", off, e);
off += strlen(e) + 1;
}
return 0;
}
I am working with MSP430 with FRAM Controller. And I Have to write a data from buffer over FRAM Location.
I have gone through bundled example of writing to FRAM.
unsigned long *FRAM_write_ptr;
unsigned long data;
#define FRAM_TEST_START 0xD000
data = 0x00010001;
void main
{
while(1)
{
data += 0x00010001;
FRAM_write_ptr = (unsigned long *)FRAM_TEST_START;
FRAMWrite(); // Endless loop
count++;
if (count > 100)
{
P1OUT ^= 0x01; // Toggle LED to show 512K bytes
count = 0; // ..have been written
data = 0x00010001;
}
}
}
void FRAMWrite(void)
{
unsigned int i=0;
for ( i= 0; i<128; i++)
{
*FRAM_write_ptr++ = data;
}
}
But When I tested example, It goes beyond the FRAM allocated section.
Is there any other method for writing on to FRAM.?