C: Vad är bästa sättet att komma åt en fälten i en struktur?

Permalänk

C: Vad är bästa sättet att komma åt en fälten i en struktur?

Tro det eller ej, men jag håller på med en liten uppgift där jag måste använda någon typ utav "enkapsulering" inom programmeringsspråket C.

Låt oss säga att vi har denna struktur

struct MyStruct{ uint8_t A[2]; /* Index 0, sub index 0 -> sub index 1 - Read Only */ uint16_t B; /* Index 1, sub index 0 - Read Write */ uint16_t C[4]; /* Index 2, sub index 0 -> sub index 3 - Read Only */ uint32_t D[11]; /* Index 2, sub index 0 -> sub index 10 - Read Only */ uint32_t E; /* Index 3, sub index 0 - Write Only */ uint8_t F; /* Index 4, sub index 0 - Read Write */ uint8_t G; /* Index 5, sub index 0 - Read Write */ };

Så jag har alltså en struktur som jag vill komma åt. Detta ska vara till ett program för andra användare som ska ansluta mitt program via sockets och sedan så ska dom komma åt dessa variabler. Men det finns olika rättigheter på dessa variabler också.

Mitt förslag är att man skapar en så kallade if-satser för att komma åt en variabel. Exempelvis:

uint32_t *value; if(index == INDEX_0){ read_or_write(value, &myStruct.A[sub_index], false, ACCESS_READ, DATA_TYPE_U8); }else if(index == INDEX_1){ read_or_write(value, &myStruct.B, false, ACCESS_READ_WRITE, DATA_TYPE_U16); }

Sedan anropar man read_or_write med. Men detta sätt är bara såååå grötigt och det är nästan som jag själv ej förstår vad jag ens skriver. Det betyder att jag behöver råd och tips för att få mer erfarenhet när det kommer till att ha någon form utav "säker åtkomst" för variabler i en strukt.

static STATUS read_or_write(uint32_t *value, void *struct_value, bool write_value, ACCESS access, DATA_TYPE data_type_struct_value){ if(write_value){ if(access == ACCESS_READ_WRITE){ if(data_type_struct_value == DATA_TYPE_U8) *((uint8_t*) struct_value) = *value; if(data_type_struct_value == DATA_TYPE_U16) *((uint16_t*) struct_value) = *value; if(data_type_struct_value == DATA_TYPE_U32) *((uint32_t*) struct_value) = *value; return OD_STATUS_OK; } }else{ if(access == ACCESS_READ_WRITE){ if(data_type_struct_value == DATA_TYPE_U8) *value = *((uint8_t*) struct_value); if(data_type_struct_value == DATA_TYPE_U16) *value = *((uint16_t*) struct_value); if(data_type_struct_value == DATA_TYPE_U32) *value = *((uint32_t*) struct_value); return STATUS_OK; } } return STATUS_NOT_PERMITTED; }

Låt oss säga att ni får en förfrågan om att en klient vill ha 4 bytes från index 3 och sub index 0, vilket ÄR variabel E i detta fall.
Hur skulle ni ha gjort för att ni kan plocka fram denna variabel E på ett snyggt sätt där ni tar hänsyn till om variabeln är skrivskyddad i strukturen eller lässkyddad.

Jag tänkte först att skapa två funktioner. En för läsa, en annan för skriva. Men det blev mindre kod om jag gjorde som funktionen read_or_write visar. Men det här är inte snyggt.

Har ni några bra förslag?

Notera att

ACCESS_READ = 0x1, ACCESS_READ = 0x2, ACCESS_READ_WRITE = ACCESS_READ | ACCESS_WRITE

Permalänk
Medlem

Nu skriver jag mer c++ än c, men jag tycker oftast det är smidigt att skriva någon typ av meta-info som du kan använda. Tex:

struct FieldInfo { int size; int count; uint32_t access; }; FieldInfo fields[] = { {1, 2, READ_ONLY}, // A {2, 1, READ_WRITE}, // B {2, 4, READ_ONLY}, // B ... };

Då kan du skriva en funktion som tar en FieldInfo* och gör det du vill på ett generiskt sätt.

Permalänk
Skrivet av grovlimpa:

Nu skriver jag mer c++ än c, men jag tycker oftast det är smidigt att skriva någon typ av meta-info som du kan använda. Tex:

struct FieldInfo { int size; int count; uint32_t access; }; FieldInfo fields[] = { {1, 2, READ_ONLY}, // A {2, 1, READ_WRITE}, // B {2, 4, READ_ONLY}, // B ... };

Då kan du skriva en funktion som tar en FieldInfo* och gör det du vill på ett generiskt sätt.

Nu hänger jag inte med. Kan man göra så där i C?

Permalänk
Hedersmedlem
Skrivet av heretic16:

Nu hänger jag inte med. Kan man göra så där i C?

Ja (fast eventuellt får man skriva

struct FieldInfo fields[] = {

Annars kanske du kan kompilera som c++ (ofta kan man ju använda samma kod)?

Permalänk
Skrivet av Elgot:

Ja (fast eventuellt får man skriva

struct FieldInfo fields[] = {

Annars kanske du kan kompilera som c++ (ofta kan man ju använda samma kod)?

Jag skulle behöva använda mig lite av generics för att varje fält kan antingen vara u8, u16 eller u32.
Men problemet är att generics tar ju 4 byte extra minne. Känns ovärt då.

Permalänk
Medlem

En annan möjlighet med x operationer är att göra ett antal småfunktioner och lägga funktionspekare i en array.
Sedan anropar du func[rw][access][type](index)

Alla write till skyddade element kan ju bara returnera fekod oavsett typ. Osv.

Permalänk
Medlem

Släpp din storlekshysteri. Om inte du har jätteont om minne är det sällan värt jobbet. Koden för att hantera alla olika storlekar kommer att ta mer plats än det minne du eventuellt sparar in på att ha olika storlekar på ditt data. Speciellt om du använder funktions-templates och får flera kopior av samma funktion. Använd en tabell av uint32 och låt användaren skicka in ett index för att tala om vilket fält han vill läsa/skriva. Publicera

enum index { A = 0, B = 2, C = 3, D = 7, E = 18, F = 19, G = 20 };

Ha två separata bitvektorer för att hantera läs- och skrivrättigheter. Det blir mycket enklare.

Permalänk
Skrivet av pacc:

En annan möjlighet med x operationer är att göra ett antal småfunktioner och lägga funktionspekare i en array.
Sedan anropar du func[rw][access][type](index)

Alla write till skyddade element kan ju bara returnera fekod oavsett typ. Osv.

Skrivet av Ingetledigtnamn:

Släpp din storlekshysteri. Om inte du har jätteont om minne är det sällan värt jobbet. Koden för att hantera alla olika storlekar kommer att ta mer plats än det minne du eventuellt sparar in på att ha olika storlekar på ditt data. Speciellt om du använder funktions-templates och får flera kopior av samma funktion. Använd en tabell av uint32 och låt användaren skicka in ett index för att tala om vilket fält han vill läsa/skriva. Publicera

enum index { A = 0, B = 2, C = 3, D = 7, E = 18, F = 19, G = 20 };

Ha två separata bitvektorer för att hantera läs- och skrivrättigheter. Det blir mycket enklare.

Blir det enklare än så här? Försök gärna visa om man kan göra bättre Jag har använt lite "generics" när det kommer till *void pekare. Testa koden här: https://onlinegdb.com/9PuTnOL5e

#include <stdio.h> #include <stdbool.h> #include <stdint.h> struct OD_Communication{ uint32_t device_type; /* Index 0x1000 sub index 0x0 */ uint8_t error_register; /* Index 0x1001 sub index 0x0 */ uint32_t manufacturer_status_register; /* Index 0x1002 sub index 0x0 */ uint32_t pre_defined_error_field[11]; /* Index 0x1003 sub index 0x0 -> 0xA */ uint32_t COB_ID_sync_message; /* Index 0x1005 sub index 0x0 */ uint32_t communication_cycle_period; /* Index 0x1006 sub index 0x0 */ uint32_t synchronous_window_length; /* Index 0x1007 sub index 0x0 */ char manufacturer_device_name[30]; /* Index 0x1008 sub index 0x0 */ char manufacturer_hardware_version[30]; /* Index 0x1009 sub index 0x0 */ char manufacturer_software_version[30]; /* Index 0x100A sub index 0x0 */ uint16_t guard_time; /* Index 0x100C sub index 0x0 */ uint8_t life_time_factor; /* Index 0x100D sub index 0x0 */ uint32_t store_parameters[4]; /* Index 0x1010 sub index 0x0 -> 0x3 */ uint32_t restore_default_parameters[4]; /* Index 0x1011 sub index 0x0 -> 0x3 */ uint32_t COB_ID_time_stamp_object; /* Index 0x1012 sub index 0x0 */ uint32_t high_resolution_time_stamp; /* Index 0x1013 sub index 0x0 */ uint32_t COB_ID_emcy; /* Index 0x1014 sub index 0x0 */ uint16_t inhibit_time_emcy; /* Index 0x1015 sub index 0x0 */ uint32_t consumer_heartbeat_time[0x80]; /* Index 0x1016 sub index 0x0 -> 0x7F */ uint16_t producer_heartbeat_time; /* Index 0x1017 sub index 0x0 */ uint32_t identity_object[5]; /* Index 0x1018 sub index 0x0 -> 0x4 */ uint8_t synchronous_counter_overflow_value; /* Index 0x1019 sub index 0x0 */ uint32_t verify_configuration[3]; /* Index 0x1020 sub index 0x0 -> 0x2 */ uint32_t emergency_consumer_object[0x80]; /* Index 0x1028 sub index 0x0 -> 0x7F */ uint8_t error_behavior_object[2]; /* Index 0x1029 sub index 0x0 -> 0x1 */ }; typedef struct { struct OD_Communication od_communication; /* Communication objects */ } CANopen; typedef enum{ OD_ACCESS_READ = 0x1, OD_ACCESS_WRITE = 0x2, OD_ACCESS_READ_WRITE = OD_ACCESS_READ | OD_ACCESS_WRITE }OD_ACCESS; enum{ OD_DATA_TYPE_U8 = 0x3, OD_DATA_TYPE_U16 = 0x2, OD_DATA_TYPE_U32 = 0x0 }; typedef enum{ OD_STATUS_OK, OD_STATUS_NOT_PERMITTED, OD_STATUS_NOT_FOUND, OD_STATUS_WRONG_DATA_TYPE }OD_STATUS; enum { /* Communication parameter area */ OD_INDEX_DEVICE_TYPE = 0x1000, OD_INDEX_ERROR_REGISTER = 0x1001, OD_INDEX_MANUFACTURER_STATUS_REGISTER = 0x1002, OD_INDEX_PRE_DEFINED_ERROR_FIELD = 0x1003, OD_INDEX_COB_ID_SYNC_MESSAGE = 0x1005, OD_INDEX_COMMUNICATION_CYCLE_PERIOD = 0x1006, OD_INDEX_SYNCHRONOUS_WINDOW_LENGTH = 0x1007, OD_INDEX_MANUFACTURER_DEVICE_NAME = 0x1008, OD_INDEX_MANUFACTURER_HARDWARE_VERSION = 0x1009, OD_INDEX_MANUFACTURER_SOFTWARE_VERSION = 0x100A, OD_INDEX_GUARD_TIME_MS = 0x100C, OD_INDEX_LIFE_FACTOR = 0x100D, OD_INDEX_STORE_PARAMETERS = 0x1010, OD_INDEX_RESTORE_DEFAULT_PARAMETERS = 0x1011, OD_INDEX_COB_ID_TIME_STAMP_OBJECT = 0x1012, OD_INDEX_HIGH_RESOLUTION_TIME_STAMP = 0x1013, OD_INDEX_COB_ID_EMCY = 0x1014, OD_INDEX_INHIBIT_TIME_EMCY = 0x1015, OD_INDEX_CONSUMER_HEARTBEAT_TIME = 0x1016, OD_INDEX_PRODUCER_HEARTBEAT_TIME = 0x1017, OD_INDEX_IDENTITY_OBJECT = 0x1018, OD_INDEX_SYNCHRONOUS_COUNTER_OVERFLOW_VALUE = 0x1019, OD_INDEX_VERIFY_CONFIGURATION = 0x1020, OD_INDEX_EMERGENCY_CONSUMER_OBJECT = 0x1028, OD_INDEX_ERROR_BEHAVIOR_OBJECT = 0x1029, }; static OD_STATUS read_or_write(void *value, void *struct_value, bool write_value, OD_ACCESS access, uint8_t byte_size); OD_STATUS CANopen_OD_set_get_value(CANopen *canopen, uint16_t index, uint8_t sub_index, bool write_value, void *value, uint8_t byte_size){ struct OD_Communication *communication = &canopen->od_communication; switch(index){ case OD_INDEX_DEVICE_TYPE: return read_or_write(value, &communication->device_type, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_ERROR_REGISTER: return read_or_write(value, &communication->error_register, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_MANUFACTURER_STATUS_REGISTER: return read_or_write(value, &communication->manufacturer_status_register, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_PRE_DEFINED_ERROR_FIELD: return read_or_write(value, &communication->pre_defined_error_field[sub_index], write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_COB_ID_SYNC_MESSAGE: return read_or_write(value, &communication->COB_ID_sync_message, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_COMMUNICATION_CYCLE_PERIOD: return read_or_write(value, &communication->communication_cycle_period, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_SYNCHRONOUS_WINDOW_LENGTH: return read_or_write(value, &communication->synchronous_window_length, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_MANUFACTURER_DEVICE_NAME: return read_or_write(value, &communication->manufacturer_device_name, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_MANUFACTURER_HARDWARE_VERSION: return read_or_write(value, &communication->manufacturer_hardware_version, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_MANUFACTURER_SOFTWARE_VERSION: return read_or_write(value, &communication->manufacturer_software_version, write_value, OD_ACCESS_READ, byte_size); case OD_INDEX_GUARD_TIME_MS: return read_or_write(value, &communication->guard_time, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_LIFE_FACTOR: return read_or_write(value, &communication->life_time_factor, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_STORE_PARAMETERS: return read_or_write(value, &communication->store_parameters[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_RESTORE_DEFAULT_PARAMETERS: return read_or_write(value, &communication->restore_default_parameters[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_COB_ID_TIME_STAMP_OBJECT: return read_or_write(value, &communication->COB_ID_time_stamp_object, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_HIGH_RESOLUTION_TIME_STAMP: return read_or_write(value, &communication->high_resolution_time_stamp, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_COB_ID_EMCY: return read_or_write(value, &communication->COB_ID_emcy, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_INHIBIT_TIME_EMCY: return read_or_write(value, &communication->inhibit_time_emcy, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_CONSUMER_HEARTBEAT_TIME: return read_or_write(value, &communication->consumer_heartbeat_time[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_PRODUCER_HEARTBEAT_TIME: return read_or_write(value, &communication->producer_heartbeat_time, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_IDENTITY_OBJECT: return read_or_write(value, &communication->identity_object[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_SYNCHRONOUS_COUNTER_OVERFLOW_VALUE: return read_or_write(value, &communication->synchronous_counter_overflow_value, write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_VERIFY_CONFIGURATION: return read_or_write(value, &communication->verify_configuration[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_EMERGENCY_CONSUMER_OBJECT: return read_or_write(value, &communication->emergency_consumer_object[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); case OD_INDEX_ERROR_BEHAVIOR_OBJECT: return read_or_write(value, &communication->error_behavior_object[sub_index], write_value, OD_ACCESS_READ_WRITE, byte_size); default: return OD_STATUS_NOT_FOUND; } } static OD_STATUS read_or_write(void *value, void *struct_value, bool write_value, OD_ACCESS access, uint8_t byte_size){ switch(write_value){ case true: switch(access | OD_ACCESS_WRITE){ case true: switch(byte_size){ case OD_DATA_TYPE_U8: *((uint8_t*) struct_value) = *((uint8_t*) value); break; case OD_DATA_TYPE_U16: *((uint16_t*) struct_value) = *((uint16_t*) value); break; case OD_DATA_TYPE_U32: *((uint32_t*) struct_value) = *((uint32_t*) value); break; default: return OD_STATUS_WRONG_DATA_TYPE; } return OD_STATUS_OK; default: return OD_STATUS_NOT_PERMITTED; } break; case false: switch(access | OD_ACCESS_READ){ case true: switch(byte_size){ case OD_DATA_TYPE_U8: *((uint8_t*) value) = *((uint8_t*) struct_value); break; case OD_DATA_TYPE_U16: *((uint16_t*) value) = *((uint16_t*) struct_value); break; case OD_DATA_TYPE_U32: *((uint32_t*) value) = *((uint32_t*) struct_value); break; default: return OD_STATUS_WRONG_DATA_TYPE; } return OD_STATUS_OK; default: return OD_STATUS_NOT_PERMITTED; } } return OD_STATUS_NOT_FOUND; } int main(){ /* Deklarera objekt */ CANopen canopen = {0}; /* Sätt ett värde */ canopen.od_communication.device_type = 0xABCDEF; canopen.od_communication.error_register = 0xFE; /* Skriv ett värde */ uint32_t value1 = 0x3F; OD_STATUS status = CANopen_OD_set_get_value(&canopen, OD_INDEX_DEVICE_TYPE, 0, true, &value1, OD_DATA_TYPE_U32); /* Skriv ut värdet */ printf("value = 0x%X\n", value1); /* Skriv ut status */ switch(status){ case OD_STATUS_OK: printf("OK\n"); break; case OD_STATUS_NOT_FOUND: printf("NOT FOUND\n"); break; case OD_STATUS_NOT_PERMITTED: printf("NOT PERMITTED\n"); break; case OD_STATUS_WRONG_DATA_TYPE: printf("WRONG DATA TYPE\n"); break; } /* Läs av ett värde */ uint8_t value2 = 0; status = CANopen_OD_set_get_value(&canopen, OD_INDEX_ERROR_REGISTER, 0, false, &value2, OD_DATA_TYPE_U8); /* Skriv ut värdet */ printf("value = 0x%X\n", value2); /* Skriv ut status */ switch(status){ case OD_STATUS_OK: printf("OK\n"); break; case OD_STATUS_NOT_FOUND: printf("NOT FOUND\n"); break; case OD_STATUS_NOT_PERMITTED: printf("NOT PERMITTED\n"); break; case OD_STATUS_WRONG_DATA_TYPE: printf("WRONG DATA TYPE\n"); break; } return 0; }

Permalänk
Medlem

Challenge accepted.

Om vi nu gör som pacc föreslog och stoppar den relevanta informationen i en tabell kan vi bli av med massor av repeterad/likartad kod.

Varför ha en funktion för både set och get? Det är två helt olika operationer och den enda kod de delar med varandra är return-satsen i slutet på funktionen, som ändå aldrig kommer nås!

Vad är det som skiljer sig mellan alla dina olika fall i switchen? Offset i datastrukturen, storleken på accessen och skrivrättigheten. Stoppa detta i en tabell och dela upp get/set i två olika funktioner. De kan då göra en sak och göra det effektivt, man slipper en massa extra parametrar för att styra saker som nu sker automatiskt, funktionerna blir kortare, har färre olika fall och blir lättare att förstå och debugga.

Det mesta av koden är klippt direkt från ditt exempel. Jag måste erkänna att jag inte hanterar dina sub_index, men det gjorde å andra sidan inte din kod heller.

#include <stdio.h> #include <stdbool.h> #include <stdint.h> #include <stddef.h> struct OD_Communication{ uint32_t device_type; /* Index 0x1000 sub index 0x0 */ uint8_t error_register; /* Index 0x1001 sub index 0x0 */ uint32_t manufacturer_status_register; /* Index 0x1002 sub index 0x0 */ uint32_t pre_defined_error_field[11]; /* Index 0x1003 sub index 0x0 -> 0xA */ uint32_t COB_ID_sync_message; /* Index 0x1005 sub index 0x0 */ uint32_t communication_cycle_period; /* Index 0x1006 sub index 0x0 */ uint32_t synchronous_window_length; /* Index 0x1007 sub index 0x0 */ char manufacturer_device_name[30]; /* Index 0x1008 sub index 0x0 */ char manufacturer_hardware_version[30]; /* Index 0x1009 sub index 0x0 */ char manufacturer_software_version[30]; /* Index 0x100A sub index 0x0 */ uint16_t guard_time; /* Index 0x100C sub index 0x0 */ uint8_t life_time_factor; /* Index 0x100D sub index 0x0 */ uint32_t store_parameters[4]; /* Index 0x1010 sub index 0x0 -> 0x3 */ uint32_t restore_default_parameters[4]; /* Index 0x1011 sub index 0x0 -> 0x3 */ uint32_t COB_ID_time_stamp_object; /* Index 0x1012 sub index 0x0 */ uint32_t high_resolution_time_stamp; /* Index 0x1013 sub index 0x0 */ uint32_t COB_ID_emcy; /* Index 0x1014 sub index 0x0 */ uint16_t inhibit_time_emcy; /* Index 0x1015 sub index 0x0 */ uint32_t consumer_heartbeat_time[0x80]; /* Index 0x1016 sub index 0x0 -> 0x7F */ uint16_t producer_heartbeat_time; /* Index 0x1017 sub index 0x0 */ uint32_t identity_object[5]; /* Index 0x1018 sub index 0x0 -> 0x4 */ uint8_t synchronous_counter_overflow_value; /* Index 0x1019 sub index 0x0 */ uint32_t verify_configuration[3]; /* Index 0x1020 sub index 0x0 -> 0x2 */ uint32_t emergency_consumer_object[0x80]; /* Index 0x1028 sub index 0x0 -> 0x7F */ uint8_t error_behavior_object[2]; /* Index 0x1029 sub index 0x0 -> 0x1 */ }; typedef struct { struct OD_Communication od_communication; /* Communication objects */ } CANopen; typedef enum{ OD_STATUS_OK, OD_STATUS_NOT_PERMITTED, OD_STATUS_NOT_FOUND, OD_STATUS_WRONG_DATA_TYPE }OD_STATUS; enum { /* Communication parameter area */ OD_INDEX_DEVICE_TYPE = 0x1000, OD_INDEX_ERROR_REGISTER = 0x1001, OD_INDEX_MANUFACTURER_STATUS_REGISTER = 0x1002, OD_INDEX_PRE_DEFINED_ERROR_FIELD = 0x1003, OD_INDEX_COB_ID_SYNC_MESSAGE = 0x1005, OD_INDEX_COMMUNICATION_CYCLE_PERIOD = 0x1006, OD_INDEX_SYNCHRONOUS_WINDOW_LENGTH = 0x1007, OD_INDEX_MANUFACTURER_DEVICE_NAME = 0x1008, OD_INDEX_MANUFACTURER_HARDWARE_VERSION = 0x1009, OD_INDEX_MANUFACTURER_SOFTWARE_VERSION = 0x100A, OD_INDEX_GUARD_TIME_MS = 0x100C, OD_INDEX_LIFE_FACTOR = 0x100D, OD_INDEX_STORE_PARAMETERS = 0x1010, OD_INDEX_RESTORE_DEFAULT_PARAMETERS = 0x1011, OD_INDEX_COB_ID_TIME_STAMP_OBJECT = 0x1012, OD_INDEX_HIGH_RESOLUTION_TIME_STAMP = 0x1013, OD_INDEX_COB_ID_EMCY = 0x1014, OD_INDEX_INHIBIT_TIME_EMCY = 0x1015, OD_INDEX_CONSUMER_HEARTBEAT_TIME = 0x1016, OD_INDEX_PRODUCER_HEARTBEAT_TIME = 0x1017, OD_INDEX_IDENTITY_OBJECT = 0x1018, OD_INDEX_SYNCHRONOUS_COUNTER_OVERFLOW_VALUE = 0x1019, OD_INDEX_VERIFY_CONFIGURATION = 0x1020, OD_INDEX_EMERGENCY_CONSUMER_OBJECT = 0x1028, OD_INDEX_ERROR_BEHAVIOR_OBJECT = 0x1029, }; static uint32_t r8 (void *addr) { return *(uint8_t *)addr; } static uint32_t r16(void *addr) { return *(uint16_t *)addr; } static uint32_t r32(void *addr) { return *(uint32_t *)addr; } static void w8 (void *addr, uint32_t value) { *(uint8_t *) addr = value; } static void w16(void *addr, uint32_t value) { *(uint16_t *)addr = value; } static void w32(void *addr, uint32_t value) { *(uint32_t *)addr = value; } typedef struct { uint32_t offset; uint32_t (*read)(void *addr); void (*write)(void *addr, uint32_t value); } OD_Entry; #define O(M) offsetof(struct OD_Communication, M) #define UNUSED 0xffffffff static OD_Entry entries[] = { { O(device_type), r32, NULL }, { O(error_register), r8, NULL }, { O(manufacturer_status_register), r32, NULL }, { O(pre_defined_error_field), r32, NULL }, { UNUSED }, { O(COB_ID_sync_message), r32, w32 }, { O(communication_cycle_period), r32, w32 }, { O(synchronous_window_length), r32, w32 }, { O(manufacturer_device_name), r8, NULL }, { O(manufacturer_hardware_version), r8, NULL }, { O(manufacturer_software_version), r8, NULL }, { UNUSED }, { O(guard_time), r16, w16 }, { O(life_time_factor), r8, w8 }, { UNUSED }, { UNUSED }, { O(store_parameters), r32, w32 }, { O(restore_default_parameters), r32, w32 }, { O(COB_ID_time_stamp_object), r32, w32 }, { O(high_resolution_time_stamp), r32, w32 }, { O(COB_ID_emcy), r32, w32 }, { O(inhibit_time_emcy), r16, w16 }, { O(consumer_heartbeat_time), r32, w32 }, { O(producer_heartbeat_time), r16, w16 }, { O(identity_object), r32, w32 }, { O(synchronous_counter_overflow_value), r8, w8 }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { O(verify_configuration), r32, w32 }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { UNUSED }, { O(emergency_consumer_object), r32, w32 }, { O(error_behavior_object), r8, w8 }, }; typedef struct { uint32_t value; OD_STATUS status; } Result; OD_STATUS CANopen_OD_set_value(CANopen *canopen, uint16_t index, uint8_t sub_index, uint32_t value) { uint16_t entry = index - OD_INDEX_DEVICE_TYPE; if (entry > OD_INDEX_ERROR_BEHAVIOR_OBJECT - OD_INDEX_DEVICE_TYPE || entries[entry].offset == UNUSED) return OD_STATUS_NOT_FOUND; OD_Entry *e = &entries[entry]; if (e->write == NULL) return OD_STATUS_NOT_PERMITTED; e->write((char *)canopen + e->offset, value); return OD_STATUS_OK; } Result CANopen_OD_get_value(CANopen *canopen, uint16_t index, uint8_t sub_index) { uint16_t entry = index - OD_INDEX_DEVICE_TYPE; if (entry > OD_INDEX_ERROR_BEHAVIOR_OBJECT - OD_INDEX_DEVICE_TYPE || entries[entry].offset == UNUSED) return (Result) { .status = OD_STATUS_NOT_FOUND }; OD_Entry *e = &entries[entry]; if (e->read == NULL) return (Result) { .status = OD_STATUS_NOT_PERMITTED }; return (Result) { .value = e->read((char *)canopen + e->offset), .status = OD_STATUS_OK }; } static const char *status_string[] = { "OK\n", "NOT PERMITTED\n", "NOT FOUND\n", "WRONG DATA TYPE\n" }; int main(){ /* Deklarera objekt */ CANopen canopen = {0}; /* Sätt ett värde */ canopen.od_communication.device_type = 0xABCDEF; canopen.od_communication.error_register = 0xFE; /* Skriv ett värde */ uint32_t value1 = 0x3F; OD_STATUS status = CANopen_OD_set_value(&canopen, OD_INDEX_DEVICE_TYPE, 0, value1); /* Skriv ut värdet */ printf("value = 0x%X\n", value1); /* Skriv ut status */ printf(status_string[status]); /* Läs av ett värde */ Result res = CANopen_OD_get_value(&canopen, OD_INDEX_ERROR_REGISTER, 0); /* Skriv ut värdet */ printf("value = 0x%X\n", res.value); /* Skriv ut status */ printf(status_string[res.status]); return 0; }