C++ block question

Hello Mike,

Is there a way to execute a C++ block only when an external logical condition is met? It would also be nice if we could start the execution of it based on PWM clock signal to run digital control algorithms.

Thank you

I would just put an if statement about the code in the .DLL. Let block decide if it should execute or not.

Hello Mike, EMRod,

I am now experimenting similar hybrid simulation where the discrete time control algorithm
is embedded into the continuous time environment. I have tried before similar with Ltspice
but is was uncomfortable and limited (e.g. state machine) but the C++ block in QSPICE is
a really great feature.

I did similar what Mike suggested (the code is in an if statement) and the result is quite good.
You can see testOut variable is incremented only once so the algorithm runs once in each
control period and the time between executions.

This is a little awkward because I bound the the algorithm execution to the simulation time
propably the result will be better (the time between executions will be more equidistant) in
case of using external clock as you intended. So propably this is not the best way to do
similar simulation but I hope it gives idea to do it better.

WinterMime

extern "C" __declspec(dllexport) void untitled_x1(void **opaque, double t, union uData *data)
{
   double  IN    = data[0].d; // input
   double &OUT   = data[1].d; // output
   double &tdiff = data[2].d; // output

   static double tprev = 0;
   static double lastTick = -1;
   static int testOut = 0;
   //static int timerPeriod = 1000; // 1 kHz control (1ms period)
   static int timerPeriod = 8000; // 8 kHz control (125us period)
   static double timeDiff = 125E-6;

   if ((int)(t*timerPeriod) != lastTick)
   {
      lastTick = (int)(t*timerPeriod);
      if (lastTick > 0) timeDiff = t - tprev;
      tprev = t;

      // Control algorithm goes here...

      // Check executions.
      testOut++;
   }

   OUT = testOut;
   tdiff = timeDiff;
}

For executing something every rising edge of an external clock, I do it like this:
(you need to create the template with the Include a struct… setting checked)

struct sTEST2_X1
{
  // declare the structure here
  bool state;
};

extern "C" __declspec(dllexport) void test2_x1(struct sTEST2_X1 **opaque, double t, union uData *data)
{
   double  in  = data[0].d; // input
   bool    clk = data[1].b; // input
   double &out = data[2].d; // output

   if(!*opaque)
   {
      *opaque = (struct sTEST2_X1 *) malloc(sizeof(struct sTEST2_X1));
      bzero(*opaque, sizeof(struct sTEST2_X1));
   }
   struct sTEST2_X1 *inst = *opaque;

// Implement module evaluation code here:
   if(!(inst->state) && clk) {
      inst->state = 1;
      out = in;
   }
   else if ((inst->state) && !clk) {
      inst->state = 0;
   }
}

edit: looking at WinterMimes post - static variables for storage seem to work too. When I tried that a few days ago, it didn’t work correctly. I wish there was a documation of what can/should be done.

1 Like

Thank you, it is really do what is expected! In your solution the out variable is not written in each exectuion cycle which means it preserves its previous value.

The function block’s output resistance is defaulted to 1k, did you figured out how can we change it? Mike mentioned this can be changed but I did not find where.

EDIT: To my last question: add new attribute (right click, Add New Attribute) named Rout to the block with the desired value.

turns out static variables work fine, but the result may be unexpected when using the optionally generated Trunc function.
If I understood it correctly, it works like a preview, to see if something (to be specified, e.g. a state) changes next round and if it does, it discards the results and tries again with a shorter timestep. It works great to get precise edges e.g. for PWM generation. But, static variables will not get restored, what may be a problem, so the struct must be used.

If the local static variable soultion works without the Trunc function, I am happy with it.
(I saw the Trunc funtion in the example schematic but I do not know what is for, and
this option is even disabled when I create code block source.)

I am not familiar with DLL’s memory management. Is this dynamically allocated struct
a common method to create variables in a DLL? I declared in the DLL simple global
(outside function) variables and they presereved their value too. Could it work to declare
global variables instead of local static variables?

I am really satisfied with this code block function. Some, I hope useful, notes:

  • It would be useful if there will be an user editable Init function for one-time
    initalizations which can be called once at the start of the simulation by QSPICE.

  • If I use the source editor and I try to select text wiht mouse cursor there is
    a several character shift between the cursor and the selection.

  • In spice of the popularity of proportional font for code viewing it would be
    better have monospaced font (at least as option)

1 Like

The font requested by QSPICE in all the ASCII editors is “Courier New Bold”, the only monospaced font in Windows.

I’ve heard one other person couldn’t find Windows the correct font. Why is unresolved. It works on every Win 11 and Win 10 machine I’ve access to. You might try to rebuild reinstall and rebuild the font index.

If you try QSPICE on a different computer, you’ll see that the font is monospaced.

If that font isn’t found on your computer, the cursor position will not be correct.

I noticed in revision history the fix related to font type. I updated QSPICE and the fix worked, at least on my computer. Thank you, Mike.

I believe it had a problem on corrupted Windows installations. Like the the font table/cache was messed up.

I am just starting to play around with this as well. I agree that a slightly more obvious template would be helpful for other novices like myself!

I’d love to see something to the affect of:

typedef struct 
{
    float bar;
    float reg[4u];
} sFOO_t;

sFOO_t * init(sFOO_t * obj, union uData *data)
{
    sFOO_t *temp    = malloc(sizeof(sFOO_t));
    temp->bar       = data[0].f;
    temp->reg[0]    = data[1].f;
    temp->reg[1]    = data[2].f;
    temp->reg[2]    = data[3].f;
    temp->reg[3]    = data[4].f;
    
    return temp;
} 

(pardon any syntactical errors please… :sweat_smile:)

I suppose if a pattern like this was adopted, the initial uData argument passed to the actual user block would need to include an offset for any ports or constants to differentiate from object initializers, or just create a second uData pointer like “union uData *params” or something…

Just a thought!!! Going to continue playing around with QSpice this weekend, I can’t stress enough how relevant this feature is to some projects I am working on at my job and in my spare time!!! THIS RULES.

Thanks for the suggestion and the positive feedback. We’ll continue to improve QSPICE through comments like yours.