/* A very simple and lightweight translator from CAN .dbc file to a C code which prints the CAN message in a human readable form Intended to generate small code suitable for microcontrollers Copyright (C) 2022 Jiri Pittner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include "can_devind.h" void generate_code(FILE *dbc,FILE *header,FILE *code, char *basename, FILE *happend, FILE *cappend) { //header file fprintf(header,"/*code generated by dbc2c - do not edit*/\n"); fprintf(header,"#ifndef __%s_H_\n#define __%s_H_\n\n",basename,basename); fprintf(header,"#include \"can_devind.h\"\n"); fprintf(header,"#if defined(__cplusplus) || defined(c_plusplus)\nextern \"C\" {\n#endif\n"); fprintf(header,"extern int8_t decode_can_message_%s(const CAN_MESSAGE *m, int multiplex, CAN_VALUE *v);\n",basename); if(cappend) fprintf(header,"extern void process_can_message_%s(const CAN_VALUE *v, int8_t nsignals);\n",basename); //this is user-supplied in the append.c file fprintf(header,"#if defined(__cplusplus) || defined(c_plusplus)\n}\n#endif\n"); //generate code fprintf(code,"/*code generated by dbc2c - do not edit*/\n"); fprintf(code,"#include \"%s.h\"\n",basename); fprintf(code,"#include \n#include \n#include \n#include \n#include \n\n"); fprintf(code,"\ static uint64_t reverse_byte_order(uint64_t x) {\n\ x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;\n\ x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;\n\ x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;\n\ return x;\n\ }\n\ \n\ "); fprintf(code,"int8_t decode_can_message_%s(const CAN_MESSAGE *m, int multiplex, CAN_VALUE *v)\n",basename); fprintf(code,"{\n"); fprintf(code,"int8_t nsignals=0;\n"); fprintf(code,"switch(m->id)\n\t{\n"); //process the dbc file char line[2048]; enum {searching=0, foundbo=1, processingbo=2,} state; state=searching; int currentid=0; int currentlength=0; char boname[1024]; int dbcline=0; int8_t ismultiplexed; int mx; int lastmx = -2; int isignal; while(fgets(line,2048,dbc)) { ++dbcline; switch(state) { case searching: { if(strncmp(line,"BO_ ",4)==0) { state=foundbo; isignal= -1; ismultiplexed= -1; int r=sscanf(line,"BO_ %d %s %d",¤tid,boname,¤tlength); if(r!=3) { fprintf(stderr,"Warning: wrong BO_ at line %d\n",dbcline); state=searching; break; } char *p=strchr(boname,':'); if(p) *p=0; fprintf(header,"#define %s_%s_CAN_ID %d\n",basename,boname,currentid); fprintf(code,"\n\n\tcase %d:\n\t\t{\n",currentid); fprintf(code,"\t\tif(m->length!=%d) {return -1; break;}\n",currentlength); fprintf(code,"\t\tuint64_t raw;\n"); } } break; case processingbo: case foundbo: { if(strncmp(line," SG_ ",5)==0) //generate code for the SG item { ++isignal; if(isignal>=MAXSIG) //too many { fprintf(stderr,"too many signals in a message (increase MAXSIG if needed)\n"); --isignal; goto skipsignal; } int bitfrom,bitlength,endianity; char issigned; float scale,offset; float minval,maxval; char sgname[1024]; char unitname[1024]; int r; mx= -1; if(ismultiplexed==1) r=sscanf(line," SG_ %s m%d : %d|%d@%d%c (%f,%f) [%f|%f] %s",sgname,&mx,&bitfrom,&bitlength,&endianity,&issigned,&scale,&offset,&minval,&maxval,unitname); if(ismultiplexed==0) r=sscanf(line," SG_ %s : %d|%d@%d%c (%f,%f) [%f|%f] %s",sgname,&bitfrom,&bitlength,&endianity,&issigned,&scale,&offset,&minval,&maxval,unitname); if(ismultiplexed== -1) { r=sscanf(line," SG_ %s m%d : %d|%d@%d%c (%f,%f) [%f|%f] %s",sgname,&mx,&bitfrom,&bitlength,&endianity,&issigned,&scale,&offset,&minval,&maxval,unitname); if(r==11) ismultiplexed=1; else { r=sscanf(line," SG_ %s : %d|%d@%d%c (%f,%f) [%f|%f] %s",sgname,&bitfrom,&bitlength,&endianity,&issigned,&scale,&offset,&minval,&maxval,unitname); if(r==10) ismultiplexed=0; else { fprintf(stderr,"Warning: wrong SG_ at line %d\n",dbcline); break; } } } if(r!= (ismultiplexed?11:10)) { fprintf(stderr,"Warning: wrong SG_ at line %d\n",dbcline); break; } if(state==foundbo && ismultiplexed==1) { state=processingbo; fprintf(code,"\n\t\tswitch(multiplex) {\n"); } if(ismultiplexed==1) { if(mx!=lastmx) { if(lastmx>=0) { fprintf(code,"\t\tnsignals=%d;\n",isignal); fprintf(code,"\t\tbreak; //multiplexed 1\n\n"); } fprintf(code,"\t\tcase %d:\n",mx); isignal=0; } lastmx=mx; } fprintf(code,"\t\t//process signal %s multiplex %d item %d\n",sgname,mx,isignal); fprintf(code,"\t\t{\n"); if(endianity==0) //big endian - reverse byte order and recompute bit offset { fprintf(code,"\t\traw = reverse_byte_order(m->data.word);\n"); int bitfrom2= (8 * (7 - bitfrom/8)) + bitfrom%8 - (bitlength-1); bitfrom=bitfrom2; } else //native little endian { fprintf(code,"\t\traw = m->data.word;\n"); } fprintf(code,"\t\traw >>= %d;\n",bitfrom); fprintf(code,"\t\traw &= 0x%llx;\n",(1ULL<id;\n",isignal); fprintf(code,"\t\tv[%d].pid = %d;\n",isignal,mx); fprintf(code,"\t\tv[%d].item = %d;\n",isignal,isignal); //store names (length terminated for safety) punitname[UNIT_NAME_LEN-1]=0; fprintf(code,"\t\tstrcpy(v[%d].unit,\"%s\");\n",isignal,punitname); boname[SIGNAME_LEN-1]=0; fprintf(code,"\t\tstrcpy(v[%d].groupname,\"%s\");\n",isignal,boname); sgname[SIGNAME_LEN-1]=0; fprintf(code,"\t\tstrcpy(v[%d].signame,\"%s\");\n",isignal,sgname); //generate multiplex PID defines in the header if(ismultiplexed==1 && isignal==0) { fprintf(header,"#define %s_%s_%s_MUX 0x%02x\n",basename,boname,sgname,mx); } //generate signal items in the header fprintf(header,"#define %s_%s_%s_SIG_ITEM %d\n",basename,boname,sgname,isignal); //now process the signal value //TODO - maybe handle optionally min and max values - but it would increase the size of generated code if(scale==1 && offset==0) //integer { if(issigned=='-') { fprintf(code,"\t\tv[%d].type = -1;\n",isignal); switch(bitlength) { case 4: case 8: fprintf(code,"\t\tint8_t tmp;\n"); fprintf(code,"\t\ttmp = raw & 0xff;\n"); break; case 12: case 16: fprintf(code,"\t\tint16_t tmp;\n"); fprintf(code,"\t\ttmp = raw & 0xffff;\n"); break; case 18: case 24: case 32: fprintf(code,"\t\tint32_t tmp;\n"); fprintf(code,"\t\ttmp = raw & 0xffffffff;\n"); break; default: fprintf(stderr,"Warning: not implemented data size at line %d\n",dbcline); break; } fprintf(code,"\t\tv[%d].value.int32= tmp;\n",isignal); } else { fprintf(code,"\t\tv[%d].type = %d;\n",isignal,(strlen(punitname)?1:0)); fprintf(code,"\t\tv[%d].value.uint32= raw;\n",isignal); } } else //float { switch(bitlength) { case 1: case 4: case 8: fprintf(code,"\t\t%s tmp;\n",issigned=='+'?"uint8_t":"int8_t"); fprintf(code,"\t\ttmp = raw & 0xff;\n"); break; case 12: case 16: fprintf(code,"\t\t%s tmp;\n",issigned=='+'?"uint16_t":"int16_t"); fprintf(code,"\t\ttmp = raw & 0xffff;\n"); break; case 18: case 24: case 32: fprintf(code,"\t\t%s tmp;\n",issigned=='+'?"uint32_t":"int32_t"); fprintf(code,"\t\ttmp = raw & 0xffffffff;\n"); break; default: fprintf(stderr,"Warning: not implemented data size at line %d\n",dbcline); break; } fprintf(code,"\t\tv[%d].type = 2;\n",isignal); fprintf(code,"\t\tv[%d].value.floatval= %ff+(%ff*tmp);\n",isignal,offset,scale); } fprintf(code,"\t\t}\n"); skipsignal: ; } else //terminate BO group { if(ismultiplexed==1) { fprintf(code,"\t\tnsignals=%d;\n",isignal+1); fprintf(code,"\n\t\tbreak; //multiplexed 2\n"); fprintf(code,"\t\tdefault: break;\n"); fprintf(code,"\t\t}// multiplex switch\n"); } else { fprintf(code,"\t\tnsignals=%d;\n",isignal+1); } fprintf(code,"\t\t}\n\t\tbreak; //BO group end\n"); state=searching; } } break; } } fprintf(code,"\tdefault: \n\t\treturn -2;\n\tbreak;\n"); //unrecognized BO fprintf(code,"\t}\n"); //end switch if(cappend) fprintf(code,"process_can_message_%s(v,nsignals);\n",basename); fprintf(code,"return nsignals;\n"); fprintf(code,"}\n"); //end decode_can_message if(cappend) { fprintf(code,"\n//end automatically generated part\n"); char line[1024]; while(fgets(line,1024,cappend)) fputs(line,code); } fflush(code); if(happend) { fprintf(header,"\n//end automatically generated part\n"); char line[1024]; while(fgets(line,1024,happend)) fputs(line,header); } fprintf(header,"\n#endif\n\n\n"); //double inclusion prevention fflush(header); } int main(int argc, char **argv) { if(argc<2) { fprintf(stderr,"Usage dbc2c file.dbc [handmade.h] [handmade.c]\n"); exit(1); } int len=strlen(argv[1]); char *p=argv[1]+len-4; if(strncmp(p,".dbc",4)) { fprintf(stderr,"Argument must be a *.dbc file\n"); exit(1); } char *basename = strdup(argv[1]); basename[p-argv[1]]=0; len=strlen(basename); FILE *dbc=fopen(argv[1],"r"); if(!dbc) { fprintf(stderr,"cannot read-open %s\n",argv[1]); exit(1); } char headername[len+3]; strcpy(headername,basename);strcat(headername,".h"); FILE *header = fopen(headername,"w"); if(!header) { fprintf(stderr,"cannot write-open %s\n",headername); exit(1); } char codename[len+3]; strcpy(codename,basename);strcat(codename,".c"); FILE *code = fopen(codename,"w"); if(!code) { fprintf(stderr,"cannot write-open %s\n",codename); exit(1); } FILE *happend=NULL; FILE *cappend=NULL; if(argc>=3) happend=fopen(argv[2],"r"); if(argc>=4) cappend=fopen(argv[3],"r"); generate_code(dbc,header,code,basename,happend,cappend); if(happend) fclose(happend); if(cappend) fclose(cappend); fclose(dbc); fclose(header); fclose(code); }