C how to base structs off of struct - c

I'm trying to write some code dealing with a register interface. The register interface is generic and used all over the chip I'm working with. There are several different types of instantiations which use a subset of the register interface.
I want to know, is there some way in C to create a struct with the master register interface, then have the various subsets be structs that only can access their used registers?
So, for example, let's call the different possible subsets A, B, and C.
The register interface may look something like this:
OFFSET NAME NEEDED BY
0x00 -- CFG A B C
0x04 -- VERSION A B C
0x08 -- SIZE B C
0x0C -- TYPE A C
0x10 -- USER A
So, using the same format that ARM uses for their SCS register interface, I can create the following struct for master:
typedef volatile struct {
uint32 cfg;
uint32 version;
uint32 size;
uint32 type;
uint32 user;
} master_t;
That part's relatively straightforward. But I'm having trouble thinking of a way to implement the a_t, b_t, and c_t types to have their contents still point to the right offsets, short of just creating separate structs in the exact same method as above (which would make it extremely painful to propagate changes/bugfixes; the actual use case has more like 50 total registers for master_t).
Essentially, my end goal is that if you pointed an a_t to the right memory address, then tried to write code that would (e.g.) try to access a_t.size, the compiler would throw an error. Is there any way this is possible in C?

Perhaps like this:
typedef struct
{
uint32 cfg;
uint32 version;
uint32 filler;
uint32 type;
uint32 user;
} SUB_A_T;
typedef struct
{
uint32 cfg;
uint32 version;
uint32 size;
} SUB_B_T;
typedef struct
{
uint32 cfg;
uint32 version;
uint32 size;
uint32 type;
} SUB_C_T;
typedef UNION
{
SUB_A_T a;
SUB_B_T b;
SUB_C_T c;
} MASTER_T;
To access:
MASTER_T master;
master.a.cfg = 1;
master.a.version = 2;
master.a.type = 3;
master.a.user = 4;
// master.a.size = 11 This line would cause a compiler error.
Or...
master.b.cfg = 10;
master.b.version = 20;
master.b.size = 30;
// master.b.type = 22 This line would cause a compiler error.
// master.b.user = 33 This line would cause a compiler error.
Or...
master.c.cfg = 100;
master.c.version = 200;
master.c.size = 300;
master.c.type = 400;
// master.c.user = 44 This line would cause a compiler error.

Related

need support utilizing FM_1808(F-RAM Memory) for the data extraction using structure pointers

I am working on field oriented control for PMSM. I'm using customized hardware, and unfortunetly i can't see the speed responses and current signal straight on the oscilliscope through pins. So i need to save data in FM_1808(F-RAM Memory) and then later uses FM_1808(F-RAM Memory) to plot the signals. I'm using structure to keep all data in one function with the pointer, pointing to that function. unfortunetly i can't utilized the FRAM quite well and unable to extract the data. i need help to find where i'm doing wrong that make problem in extracting the data. thanks your help will be highly appreciated. I have attached some lines of code for where i define structure, starting address and function to extract data.
define.c
#pragma DATA_SECTION(FRAMAdr,"FRAMData");
volatile struct FRAMAddress FRAMAdr;
linker cmd
page1
{
EXTFRAM : origin = 0x200000, length = 0x008000
}
Sections
{
FRAMData: > EXTFRAM PAGE = 1
}
define.h
struct FRAMAddress {
int16 Dat[32768]; // FRAM 16bit data & 16bit memory
};
extern volatile struct FRAMAddress FRAMAdr;
Uint16 *FRAMStartAdr = (Uint16 *)0x200000;
pheripheral.c
void FSave(Uint16 FAdr, int16 FD1, int16 FD2)
{
FRamAdr.Dat[FAdr] = FD1;
DELAY_US(0.2L);
FRamAdr.Dat[FAdr+5000] = FD2;
}
main.c
extern void FSave(Uint16 FAdr1, int16 FD1, int16 FD2);
{
if(FIndex<=5000)
{
FSave(FIndex, (int16)spd, (int16)(id));
FIndex++;
}
else
{
FSaveflag=0;
}
}
i would like to seek help regarding data value at memory addresses using FM_1808(F-RAM Memory).
I suspect you probably want something like this, assuming your FRAM is mapped at address 0x200000.
struct FRAM {
uint16_t Dat[32768];
};
struct FRAM * FRAMData = (struct FRAM *)0x200000;
void FSave(Uint16 FAdr, int16 FD1, int16 FD2)
{
FRAMData->Dat[FAdr] = FD1;
DELAY_US(0.2L);
FRAMData->Dat[FAdr+5000] = FD2;
}
Note that the datasheet I found online suggests the FM1808 has 32K 8-bit values, not 16-bit values, so you should look into that.
Edit: Also not sure your delay is needed or makes sense. What is the type of 0.2L? What parameter type does DELAY_US() take?

How to initialize an array of structs to a value other than zero like 0xFF?

I have an array of structs that I need to initialize at compile-time (no memset) to 0xFF. This array will be written as part of the program over erased flash. By setting it to 0xFF, it will remain erased after programming, and the app can use it as persistent storage. I've found two ways to do it, one ugly and one a workaround. I'm wondering if there's another way with syntax I haven't found yet. The ugly way is to use a nested initializer setting every field of the struct. However, it's error prone and a little ugly. My workaround is to allocate the struct as an array of bytes and then use a struct-typed pointer to access the data. Linear arrays of bytes are much easier to initialize to a non-zero value.
To aid anyone else doing the same thing, I'm including the gcc attributes used and the linker script portion.
Example struct:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512] __attribute__(( packed ));
} __attribute__(( packed ));
Initialize to 0xFF using the best way I know:
// Allocate the space as an array of bytes
// because it's a simpler syntax to
// initialize to 0xFF.
__attribute__(( section(".bootloader_data") ))
uint8_t bootloaderDataArray[sizeof(struct BlData_t)] = {
[0 ... sizeof(struct BlData_t) - 1] = 0xFF
};
// Use a correctly typed pointer set to the
// array of bytes for actual usage
struct BlData_t *bootloaderData = (struct BlData_t *)&bootloaderDataArray;
No initialization necessary because of (NOLOAD):
__attribute__(( section(".bootloader_data") ))
volatile const struct BLData_t bootloader_data;
Addition to linker script:
.bootloader_data (NOLOAD):
{
FILL(0xFF); /* Doesn't matter because (NOLOAD) */
. = ALIGN(512); /* start on a 512B page boundary */
__bootloader_data_start = .;
KEEP (*(.bootloader_data)) /* .bootloader_data sections */
KEEP (*(.bootloader_data*)) /* .bootloader_data* sections */
. = ALIGN(512); /* end on a 512B boundary to support
runtime erasure, if possible */
__bootloader_data_end = .;
__bootloader_data_size = ABSOLUTE(. - __bootloader_data_start);
} >FLASH
How to use the starting address, ending address and size in code:
extern uint32_t __bootloader_data_start;
extern uint32_t __bootloader_data_end;
extern uint32_t __bootloader_data_size;
uint32_t _bootloader_data_start = (uint32_t)&__bootloader_data_start;
uint32_t _bootloader_data_end = (uint32_t)&__bootloader_data_end;
uint32_t _bootloader_data_size = (uint32_t)&__bootloader_data_size;
Update:
It turns out that I was asking the wrong question. I didn't know about the (NOLOAD) linker section attribute which tells the program loader not to burn this section into flash. I accepted this answer to help others realize my mistake and possibly theirs. By not even programming the section, I don't have to worry about the initialization at all.
I've upvoted the union answers since they seem to be a good solution to the question I asked.
I would use a union of your struct together with an array of the correct size, then initialize the array member.
union {
struct BlData_t data;
uint8_t bytes[sizeof(struct BlData_t)];
} data_with_ff = {
.bytes = {
[0 ... sizeof(struct BlData_t) - 1] = 0xff
}
};
You can then access your struct as data_with_ff.data, defining a pointer or macro for convenience if you wish.
Try on godbolt
(Readers should note that the ... in a designated initializer is a GCC extension; since the question was already using this feature and is tagged gcc I assume that is fine here. If using a compiler that doesn't have it, I don't know another option besides .bytes = { 0xff, 0xff, 0xff ... } with the actual correct number of 0xffs; you'd probably want to generate it with a script.)
The sensible way to do this is to find the command in the linker script telling it to back off from touching that memory in the first place. Because why would you want it do be erased only to filled up with 0xFF again? That only causes unnecessary flash wear for nothing.
Something along the lines of this:
.bootloader_data (NOLOAD) :
{
. = ALIGN(512);
*(.bootloader_data *)
} >FLASH
If you truly need to do this initialization and in pure standard C, then you can wrap your inner struct inside an anonymous union (C11), then initialize that one using macro tricks:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
union {
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512];
uint8_t raw [512];
};
};
#define INIT_VAL 0xFF, // note the comma
#define INIT_1 INIT_VAL
#define INIT_2 INIT_1 INIT_1
#define INIT_5 INIT_2 INIT_2 INIT_1
#define INIT_10 INIT_5 INIT_5
/* ... you get the idea */
#define INIT_512 INIT_500 INIT_10 INIT_2
const struct BlData_t bld = { .raw = {INIT_512} };
This method could also be applied on whole struct basis, if you for example want to initialize a struct array with all items set to the same values at compile-time.

Is it possible to create 'empty' members within a struct in C?

I'm tryin to create a few array-like structures for handeling MODBUS data & adressing.
I'm doing it within a structure so it's simpler later on to set certain values instead of referencing a datasheet to find out which address is coupled to the data.
Some addresses are left blank and are reserved for future use (I know this isn't specified in the MODBUS standard, the data shouldn't have to be exactly MODBUS specific).
I've created a struct which compiles on the software for the embedded device this should run on. However I wasn't quite sure it would run the way I imagined it, so I ran some test code on 2 different compiler. 1 being an online c-compiler which runs the way I excepected it to with just one warning at the reserved byte.
The 2nd one is ran within a visual studio c++ console application, which won't run cause it generates the error:
Error C2208 'unsigned short': no members defined using this type
This is the complete code which should run:
#include <stdio.h>
#include <stdint.h>
#define U16 uint16_t
#define UI unsigned int
struct t {
U16 t1;
union {
struct {
UI b_f1 : 2;
UI b_f2 : 2;
UI b_f3 : 2;
UI b_f4 : 2;
UI b_f5 : 2;
UI b_f6 : 2;
UI b_f7 : 2;
UI b_f8 : 2;
}f;
U16 c;
}t2;
U16; //Reserved space for later use <- this gives me the compiler error
U16 t3;
}test;
int main()
{
test.t3 = 12;
test.t2.f.b_f1 = 0; //0
test.t2.f.b_f2 = 2; //4 (8)
test.t2.f.b_f3 = 0; //16
test.t2.f.b_f4 = 0; //64
test.t2.f.b_f5 = 0; //256
test.t2.f.b_f6 = 0; //1024
test.t2.f.b_f7 = 2; //4096 (8192)
test.t2.f.b_f8 = 0; //16384
//tot: 8200
test.t1 = 3000;
int h; //test variable
for (int i = 0; i < (sizeof(test) / sizeof(U16)); i++) {
h = *(&test.t1 + (i*sizeof(U16));
printf("%d", h); //here I can now send every MODBUS address & data
}
return 0;
}
Setting the variables is not neccesarily needed, it's just for testing purposes
I excpected it will just generate a structure of 4 elements, one being a 0 pointer, that way the addressing is correct in a way that address 3 is not used and therefore 0 while address 4(t3) will contain more data.
Is there a way to make this work I planned to do, or should I just create some variables in between called Reserved1..Reserverd2..etc ?
A "declaration" like
U16; //Reserved space for later use
is simply not valid.
Just use some dummy name to solve your problem, like e.g.
U16 _reserved_; //Reserved space for later use
No you can't. A type on its own is not a valid declaration within a structure definition.
What you could do is use __LINE__ to help guarantee that the name you assign to the variable is unique:
#define _concat(a,b) a##b
#define concat(a,b) _concat(a,b)
and write
U16 concat(t, __LINE__);
which is unique insofar that the struct name t is unique.
Is there a way to make this work I planned to do?
No.
or should I just create some variables in between called Reserved1..Reserverd2..etc
Yes.

Initializing, constructing and converting struct to byte array causes misalignment

I am trying to design a data structure (I have made it much shorter to save space here but I think you get the idea) to be used for byte level communication:
/* PACKET.H */
#define CM_HEADER_SIZE 3
#define CM_DATA_SIZE 16
#define CM_FOOTER_SIZE 3
#define CM_PACKET_SIZE (CM_HEADER_SIZE + CM_DATA_SIZE + CM_FOOTER_SIZE)
// + some other definitions
typedef struct cm_header{
uint8_t PacketStart; //Start Indicator 0x5B [
uint8_t DeviceId; //ID Of the device which is sending
uint8_t PacketType;
} CM_Header;
typedef struct cm_footer {
uint16_t DataCrc; //CRC of the 'Data' part of CM_Packet
uint8_t PacketEnd; //should be 0X5D or ]
} CM_Footer;
//Here I am trying to conver a few u8[4] tp u32 (4*u32 = 16 byte, hence data size)
typedef struct cm_data {
union {
struct{
uint8_t Value_0_0:2;
uint8_t Value_0_1:2;
uint8_t Value_0_2:2;
uint8_t Value_0_3:2;
};
uint32_t Value_0;
};
//same thing for Value_1, 2 and 3
} CM_Data;
typedef struct cm_packet {
CM_Header Header;
CM_Data Data;
CM_Footer Footer;
} CM_Packet;
typedef struct cm_inittypedef{
uint8_t DeviceId;
CM_Packet Packet;
} CM_InitTypeDef;
typedef struct cm_appendresult{
uint8_t Result;
uint8_t Reason;
} CM_AppendResult;
extern CM_InitTypeDef cmHandler;
The goal here is to make reliable structure for transmitting data over USB interface. At the end the CM_Packet should be converted to an uint8_t array and be given to data transmit register of an mcu (stm32).
In the main.c file I try to init the structure as well as some other stuff related to this packet:
/* MAIN.C */
uint8_t packet[CM_PACKET_SIZE];
int main(void) {
//use the extern defined in packet.h to init the struct
cmHandler.DeviceId = 0x01; //assign device id
CM_Init(&cmHandler); //construct the handler
//rest of stuff
while(1) {
CM_GetPacket(&cmHandler, (uint8_t*)packet);
CDC_Transmit_FS(&packet, CM_PACKET_SIZE);
}
}
And here is the implementation of packet.h which screws up everything so bad. I added the packet[CM_PACKET_SIZE] to watch but it is like it is just being generated randomly. Sometimes by pure luck I can see in this array some of the values that I am interested in! but it is like 1% of the time!
/* PACKET.C */
CM_InitTypeDef cmHandler;
void CM_Init(CM_InitTypeDef *cm_initer) {
cmHandler.DeviceId = cm_initer->DeviceId;
static CM_Packet cmPacket;
cmPacket.Header.DeviceId = cm_initer->DeviceId;
cmPacket.Header.PacketStart = CM_START;
cmPacket.Footer.PacketEnd = CM_END;
cm_initer->Packet = cmPacket;
}
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier,
uint8_t *data){
CM_AppendResult result;
switch(identifier){
case CM_VALUE_0:
handler->Packet.Data.Value_0_0 = data[0];
handler->Packet.Data.Value_0_1 = data[1];
handler->Packet.Data.Value_0_2 = data[2];
handler->Packet.Data.Value_0_3 = data[3];
break;
//Also cases for CM_VALUE_0, 1 , 2
//to build up the CM_Data sturct of CM_Packet
default:
result.Result = CM_APPEND_FAILURE;
result.Reason = CM_APPEND_CASE_ERROR;
return result;
break;
}
result.Result = CM_APPEND_SUCCESS;
result.Reason = 0x00;
return result;
}
void CM_GetPacket(CM_InitTypeDef *handler, uint8_t *packet){
//copy the whole struct in the given buffer and later send it to USB host
memcpy(packet, &handler->Packet, sizeof(CM_PACKET_SIZE));
}
So, the problem is this code gives me 99% of the time random stuff. It never has the CM_START which is the start indicator of packet to the value I want to. But most of the time it has the CM_END byte correctly! I got really confused and cant find out the reason. Being working on an embedded platform which is hard to debugg I am kind of lost here...
If you transfer data to another (different) architecture, do not just pass a structure as a blob. That is the way to hell: endianess, alignment, padding bytes, etc. all can (and likely will) cause trouble.
Better serialize the struct in a conforming way, possily using some interpreted control stream so you do not have to write every field out manually. (But still use standard functions to generate that stream).
Some areas of potential or likely trouble:
CM_Footer: The second field might very well start at a 32 or 64 bit boundary, so the preceeding field will be followed by padding. Also, the end of that struct is very likely to be padded by at least 1 bytes on a 32 bit architecture to allow for proper alignment if used in an array (the compiler does not care you if you actually need this). It might even be 8 byte aligned.
CM_Header: Here you likely (not guaranteed) get one uint8_t with 4*2 bits with the ordering not standardized. The field my be followed by 3 unused bytes which are required for the uint32_t interprettion of the union.
How do you guarantee the same endianess (for >uint8_t: high byte first or low byte first?) for host and target?
In general, the structs/unions need not have the same layout for host and target. Even if the same compiler is used, their ABIs may differ, etc. Even if it is the same CPU, there might be other system constraints. Also, for some CPUs, different ABIs (application binary interface) exist.

Error while trying to update array element

I am working on an embedded platform which does not have debugging features. So it is hard to say what is the error source.
I have defined in header file:
typedef struct cm_packet {
CM_Header Header; //header of packet 3 bytes
uint8_t *Data; //packet data 64 bytes
CM_Footer Footer; //footer of packet 3 bytes
} CM_Packet;
typedef struct cm_inittypedef{
uint8_t DeviceId;
CM_Packet Packet;
} CM_InitTypeDef;
extern CM_InitTypeDef cmHandler;
void CM_Init(CM_InitTypeDef *handler);
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier
, uint8_t *data, uint8_t length);
And somewhere in implementation I have:
uint8_t bufferIndex = 0;
void CM_Init(CM_InitTypeDef *cm_initer) { //init a handler
cmHandler.DeviceId = cm_initer->DeviceId;
CM_Packet cmPacket;
cmPacket.Header.DeviceId = cm_initer->DeviceId;
cmPacket.Header.PacketStart = CM_START;
cmPacket.Footer.PacketEnd = CM_END;
//initialize data array
uint8_t emptyBuffer[CM_MAX_DATA_SIZE] = {0x00};
cmPacket.Data = emptyBuffer;
cm_initer->Packet = cmPacket;
}
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier
, uint8_t *data, uint8_t length){
//some check to see if new data does not make Data overflow
uint8_t i;
/*** ERROR HAPPENS HERE!!!! ***/
handler->Packet.Data[bufferIndex++] = identifier;
//now add the data itself
for(i = 0; i < length; i++) {
handler->Packet.Data[bufferIndex++] = data[i];
}
//reset indexer
if(bufferIndex > 64) {
PacketReady(); //mark packet as ready
bufferIndex = 0
};
//return result
}
The idea is to update the Packet.Data from some other source codes which have access to the handler. For example some other sources can call that Append function to change Packet.Data. But as you see in the code, I have commented the place which causes the micro-controller to go in hard fault mode. I am not sure what is happening here. All I know is exactly at that line micro goes into hard fault mode and never recovers!
This might be a race condition but before anything else I want to know I have written correct c !!! code then I try to rule out other problems.
In function CM_Init, you are setting cmPacket.Data to point to a local array:
uint8_t emptyBuffer[CM_MAX_DATA_SIZE] = {0x00};
cmPacket.Data = emptyBuffer;
Accessing this memory address outside the scope of the function yields undefined behavior.
As #barak manos mentioned, the buffer supplied to Data is allocated on the stack.
When you get to CM_AppendData, you are writing over memory that is no longer dedicated to the buffer.
You may want to use malloc so that the buffer is allocated on the heap instead of on the stack. Just remember to call free so that you are not leaking memory.
If you can't use dynamic allocation, it's possible to dedicate some scratch memory for all the Data uses. It just needs to be static.
Hope that helps :)

Resources