Implementing the C Function Call

malife
Expert
Posts: 56
Joined: Tue Apr 17, 2007 7:24 am
Location: Santa Cruz, CA
Contact:

Implementing the C Function Call

Postby malife » Tue Mar 18, 2008 1:17 am

Hello Lubin,
I've been looking at your blockset and it is really amazing. For me being a seasoned PIC and dsPIC programmer, it is just amazing how much leverage your tool gives. Congrats on a great product. One thing I am trying to implement is the QEI, now I've already read that you currently have no plans to suport it, and honestly I would much rather see you devote time to an interrupt driven (virtual buffer) UART, SPI and CAN.

So I think (correct me if I am wrong) that using the C-Fucntion Call shall allow me to completely use the QEI in the following way:

1.- Create a C function to initialize the QEI and call it once when the model starts execution; and,
2.- Create a C function to read the status of the QEI registers (specially POSCNT) and send the data to Simulink to further use it.

So I was wondering if you could post a simple example of the usage of the function call, and I will do the QEI Example and make it available here on the forum for your registered users.

Cheers

Mariano

malife
Expert
Posts: 56
Joined: Tue Apr 17, 2007 7:24 am
Location: Santa Cruz, CA
Contact:

Re: Implementing the C Function Call

Postby malife » Thu Mar 20, 2008 7:41 pm

Hello,
I am working on touching up the example for the QEI. I have a
couple of requests in order to make this happen and I think that it
would overall help make the usage of the C function call block more diverse.

1.- Function prototype: In my previous email I forgot to include one
detail. Sometimes you need to implement an interrupt (like in this
case for the QEI), but as I am sure you know if I implement an
interrupt it has to have a given prototype, which it does not work
since I must adhere to the required prototype by your function call
block.

2.- Interrupts sometimes require access to a global variable,
generally a switch to indicate something changed. So if you could do a
very simple (I think :-) ) source block that was a global variable,
then, I could change the value inside my interrupt and be done with
it.

I have probably not made myself clear, so I will put the scenario in
front of you and let you decide:

I initialize the QEI with this function:

Code: Select all

void QEIInit(void){
 ConfigIntQEI (QEI_INT_ENABLE &  // Enable interrupts
               QEI_INT_PRI_5);   // Set Priority to 5

 POSCNT = 0;                     // Set Current Position to 0
 MAXCNT = 0xFFFF;                // Use the full 16bit counter

 OpenQEI (QEI_DIR_SEL_CNTRL &    // UPDN bit decides direction count
          QEI_INT_CLK &          // Use FCY for clocking
          QEI_INDEX_RESET_DISABLE &
          QEI_CLK_PRESCALE_1 &    // 1:1 Prescale
          QEI_GATED_ACC_DISABLE &
          QEI_NORMAL_IO &        // QEI Control state of IO pin
          QEI_INPUTS_NOSWAP &    // Channels A and B are not swapped
          QEI_MODE_x2_MATCH &    // X2 mode with counter reset by match
          QEI_UP_COUNT &         // Read only for this mode
          QEI_IDLE_STOP,         // Stop in idle operation
          MATCH_INDEX_INPUT_PHASEA &
          QEI_QE_CLK_DIVIDE_1_16 &// Filter 1:16 divider
          QEI_QE_OUT_ENABLE);    // Enable the input digital filter

}


This is clearly doable using your c function call block

then, every time the QEI overflows or underflows I increment a global
variable that keeps track of the turn count of the motor (i.e. the
motor can do more than one turn). This happens inside an interrupt.

Code: Select all

void __attribute__((__interrupt__, no_auto_psv)) _QEIInterrupt(void){

// if it overflowed
 if (ReadQEI() < 0x3FFF){
   ct_turns++;
 } else {
   ct_turns--;
 }
 // clear the interrupt flag
 IFS2bits.QEIIF =0;
}


This I can not do it using the c function call block because: 1.- I can not
change the function prototype and 2.- I have no way of declaring from
simulink the global variable ct_turns.

Then I use it n the code to compute the actual position of the motor:

Code: Select all

thePID.y_m = (float)(ct_turns*65535 + POSCNT)*angle_ratio;
//where POSCNT is a register



Maybe you can suggest a work around this limitation (maybe adding an
external file from the project properties dialog?) or you can
implement the changes in the block.

I have attached a Simulink model Image of how I see it working inside
simulink if you were to modify/add the suggeted blocks.

Concretely in the C function call, I think it would be great if you
were to provide the following modifications:

1.- A way to override your default function prototype
2.- A way to decide whether it should be called once at the beginning
(which I think you can by setting the sample time to inf), every cycle
(by setting the sample time) or never (like an interrupt which is
called by the microcontroller interrupt engine).

And then provide a small block that declares a global variable (with a
given name) and initializes it a to given value and returns the
current value each sample time.

Also, how do I add the dsPIC library QEI.h? From the project properties dialog?

Sorry for the extra long post, I hope I have made myself clear. I
will try to look for workarounds for this in the mean while.

Thanks Lubin!!
Attachments
CFunCallQEI.JPG
Suggested Modifications to the C-Function Block and addition of Constant Source
CFunCallQEI.JPG (44.58 KiB) Viewed 6192 times

malife
Expert
Posts: 56
Joined: Tue Apr 17, 2007 7:24 am
Location: Santa Cruz, CA
Contact:

Re: Implementing the C Function Call

Postby malife » Fri Mar 21, 2008 12:35 am

Hello All,
It is with great pleasure that I give you an example Simulink Model of how to use the C function call to implement the QEI in a dsPIC30F4011. This is proven to work and if anybody has a question feel free to post it. Attached to this are two files: the Simulink model and the C file. Note that if you roll your own you need to add the C file to the Simulink options. The example is fully interrupt driven and supports more than one turns (in any direction of course :-) )

I guess using the C function call is a great reason to buy any of the registered versions :-)

Hope this is helpful to anyone looking to use the QEI.
Attachments
myQEI.c
This is the C file. Make sure you add it to your model by: Simulation -> Configuration Parameters -> Custom C code -> Source file
(2.23 KiB) Downloaded 500 times
QEITest.mdl
Using a dsPIC30F4011 at 40MHZ
(34.99 KiB) Downloaded 346 times

LubinKerhuel
Site Admin - Expert
Posts: 616
Joined: Wed Mar 07, 2007 11:23 pm
Location: Marseille - France
Contact:

Re: Implementing the C Function Call

Postby LubinKerhuel » Fri Mar 21, 2008 1:34 am

Hi Mariano,

I was working on this post while you posted your solution.
I keep it there since it was a piece of work.

to resume,
I declared the global variable using a different way. (yours may be better for this example).
I do not like the way you initialize the QEI peripheral. It would make better C code to set its sampling time to inf !

------
It was quite long to make the example; I respond quickly, refer to the example for further details.

malife wrote:1.- A way to override your default function prototype

To implement a function that has no "interface" with the model (kind of internal function, like interrupt) . You do not need to use the C function call block. Just add the function in the C file that you declared in the Configuration Parameters dialog box.

malife wrote:2.- A way to decide whether it should be called once at the beginning
(which I think you can by setting the sample time to inf), every cycle
(by setting the sample time) or never (like an interrupt which is
called by the microcontroller interrupt engine).


You already have the solution! I think it is better to enter into the simulink logic rather than adding a option for initialization.
    initialization (call only once at startup) ==> sample time = inf
    call ==> sample time = sampling time desired
    Interrupt ==> do not need using a C function call block.

malife wrote:And then provide a small block that declares a global variable (with a
given name) and initializes it a to given value and returns the
current value each sample time.


It is possible to use block 'Data Store Memory', 'Data Read Memory' and 'Data Write Memory' which are block of the standard simulink library.
You must declare (in the Data Store Memory) the variable as global, assign its name in order to be able to use this variable in your C function written. In your C file, the variable will be declared as external. You must declare the variable with the same datatype.



The following example compile but has not been tested. I don't know if this implementation of QEI function is working. I guess modifications will be required. I hope that the example is not too hard to read.
The model and C file can be download at the end of the post.
I also attached a basic example (the one I sent to you Mariano) that shows how to use different calling method to transfer data between simulink and C functions (using pointer or direct value...this example is not described on this post).

I noticed that some images do not load correctly. It is a strange behaviour of the forum ; I guess there is too many image in this post. Reloading the page seems to gives results... (Press F5)

Model_QEItest2.png
Example of advanced C function call.
The C file implement the QEI interrupt and two function : QEIInit and GetQEI. QEIInit initialise the QEII peripheral ; GetQEI compute the position measured and pass it to the model. One data is passed from the interrupt to the C functioin through the global variable 'ct_turns' declared in the model.
C part writen by Mariano and modified by myself. Compile but functionality not tested!
Model_QEItest2.png (17.25 KiB) Viewed 6153 times


ConfigurationParameters.png
The external C file added must be declared in the Configuration Parameters dialog.
Additional .h definition file are declared in the .c file
ConfigurationParameters.png (22.34 KiB) Viewed 6145 times


C file content (Modified versioin of Mariano's proposition):

Code: Select all

#include <p30f4011.h>
#include <qei.h>

/*------------------------------------------------------------------*/
void QEIInit(void){
  ConfigIntQEI (QEI_INT_ENABLE &  // Enable interrupts
                QEI_INT_PRI_5);   // Set Priority to 5

  POSCNT = 0;                     // Set Current Position to 0
  MAXCNT = 0xFFFF;                // Use the full 16bit counter

  OpenQEI (QEI_DIR_SEL_CNTRL &    // UPDN bit decides direction count
           QEI_INT_CLK &          // Use FCY for clocking
           QEI_INDEX_RESET_DISABLE &
           QEI_CLK_PRESCALE_1 &    // 1:1 Prescale
           QEI_GATED_ACC_DISABLE &
           QEI_NORMAL_IO &        // QEI Control state of IO pin
           QEI_INPUTS_NOSWAP &    // Channels A and B are not swapped
           QEI_MODE_x2_MATCH &    // X2 mode with counter reset by match
           QEI_UP_COUNT &         // Read only for this mode
           QEI_IDLE_STOP,         // Stop in idle operation
           MATCH_INDEX_INPUT_PHASEA &
           QEI_QE_CLK_DIVIDE_1_16 &// Filter 1:16 divider
           QEI_QE_OUT_ENABLE);    // Enable the input digital filter

}

/*------------------------------------------------------------------*/
long GetQEI()
{
   extern volatile unsigned int ct_turns;
   return (long) ct_turns * 65535 + POSCNT;
}

/*------------------------------------------------------------------*/
void __attribute__((__interrupt__)) _QEIInterrupt(void)
{
   extern volatile unsigned int ct_turns;
   // if it overflowed
     if (ReadQEI() < 0x3FFF){
       ct_turns++;
     } else {
       ct_turns--;
     }
    // clear the interrupt flag
     IFS2bits.QEIIF =0;
}


block_QEIInit.png
Dialog box of C call function for the QEIInit function. QEIInit function must be called only once at the initialisation. Defining sample time to 'inf' make the function to be called only once at startup
block_QEIInit.png (14.33 KiB) Viewed 6132 times


block_GetQEI.png
Dialog box of C call function for the GetQEI function. This function return the angular position, using the global variable modified by the QEI interrupt function
block_GetQEI.png (13.99 KiB) Viewed 6120 times


Dialog1_GlobalParam.png
Global Variable Dialog box 1
Dialog1_GlobalParam.png (17.68 KiB) Viewed 6132 times


Dialog2_GlobalParam.png
Global Variable Dialog box 2
Dialog2_GlobalParam.png (18.25 KiB) Viewed 6148 times



Considere downloading the QEI implementation from the previous post (realized and tested by Mariano)
Attachments
C_functionCall_Example.zip
Another example to demonstrate different call possiblity for the C function call block. No picture shown here
(10.39 KiB) Downloaded 368 times
QEI_CFunction_v02.zip
QEITest2 model (.mdl file) with the C file "QEI_Functions.c"
(10.28 KiB) Downloaded 371 times

malife
Expert
Posts: 56
Joined: Tue Apr 17, 2007 7:24 am
Location: Santa Cruz, CA
Contact:

Re: Implementing the C Function Call

Postby malife » Fri Mar 21, 2008 3:56 am

Great Example Lubin. Thanks for the tip on the ct_turns variable. I find that more elegant than declaring a "ghost" variable from the code since in my example directly from the Simulink Model you can't really see that there is a global variable. Anyways, between the two examples it should be now more than clear how to use the C Function Call. Thanks !

One thing though. I might be mistaken but I think that even if you put for sample time inf on the C Function call, it still is called on every step, at least it was on my case, but maybe I was doing something wrong. I had to import the code to MPLab and using the debugger figure out what was going on. That is why I choose the enabled system approach. Could you confirm that?

Thanks!

Mariano

LubinKerhuel
Site Admin - Expert
Posts: 616
Joined: Wed Mar 07, 2007 11:23 pm
Location: Marseille - France
Contact:

Re: Implementing the C Function Call

Postby LubinKerhuel » Wed Mar 26, 2008 1:23 am

Will check that. One advantage to initialize the way you do is that your initialization is last (not erased by the "standard initialisation code)

However, I noticed a possible bug on the file myQEI.c, in the initialisation function :

Code: Select all

void QEIInit(void)
{
   
  // Configure I/O Ports
  ADPCFG = 0xFFFF; //ADPCFG | 0x0030;       // Disable the A/D pins 4 and 5
  TRISB = 0xFFFF; //TRISB | 0x0030;        // configure pins 4 and 5 as inputs
...


about the ADPCFG and TRISB register, ONLY the concerned bit used by the peripheral QEI should be modified (when required). Modifying the whole register could erase configuration of other peripheral.
considere using binary mask (It is done in the comment I guess )

Lubin


Return to “Request for new functions / Peripheral / Chip support”

Who is online

Users browsing this forum: No registered users and 1 guest