Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Sample Program for Recipe Handling

robertw
2006-02-23
2012-06-11
  • robertw - 2006-02-23

    Hi All,

    I am wondering if anybody has a sample program for recipe control. We need to select and manage up to 100 recipes each with 100 words/variables. I am new to CoDeSys programming and appreciate your help.

    Any ideas are welcomed.

     
  • ndzied1 - 2006-02-24

    I have not done one in CoDeSys but in general, recipe systems involve arrays. Structured Text seems to be the easiest language to accomplish this.

    First you need a place to store the recipe information:

    awMyRecipeArray: ARRAY [1..100,1..100] OF WORD;
    

    Then you need a place to hold the current (active) recipe and the number of the current recipe

    iCurrentRecipe: INT;
    iSelectRecipe: INT;
    awCurrentRecipe: ARRAY [1..100] OF WORD;
    

    Finally you need code to move the current recipe back into the main storage array (in case it changed) and also to move the new recipe from the main storage area into the current area. Something like this....

    if iSelectRecipe <> iCurrentRecipe then
       for i := 1 to 100 do
          awMyRecipeArray[iCurrentRecipe,i] := awCurrentRecipe[i];
          awCurrentRecipe[i] := awMyRecipeArray[iSelectRecipe,i];
       end_for;
       (* Update the Current Recipe number so it doesn't get loaded again *)
       iCurrentRecipe := iSelectRecipe;
    end_if;
    
     
  • robertw - 2006-02-27

    Thanks Mate. With a few mods, this done the job well. Stay tuned for more questions as we blunder our way through codesys.

    Also, this is on an ABB PLC so many new adventures ahead.

    Thanks,

    Rob.

     
  • spfeif - 2006-03-01

    I have the following suggestion assuming that your recipe are stored in memory some where. I like to reference structures so that you can give the variables of the recipes descriptive names. I also believe it's faster than for loops by passing pointers. I included the project so you can play with it. But here is the jist of the code.

    Create a structure

    TYPE RECIPE :
    STRUCT
       wPorportional      :WORD;
       wIntergral         :WORD;
       wDerivative      :WORD;
       wScale         :WORD;
       wOffset         :WORD;
    END_STRUCT
    END_TYPE
    

    Create some variables in your program:

    VAR
       bNewRecipe         : BOOL;               (* When triggered causes a memory read *)
       dwSelection         :DWORD;               (* Selection of which recipe, visualization limits it from 0 to 3 *)
       dwOffset            :DWORD   := 16#A;      (* Offset since there are 5 words = 10 bytes = 16#A to move between each recipe*)
       tCurrentRecipe      :RECIPE;               (* Currently stored recipe *)
       tRecipeMemory      :POINTER TO RECIPE   := 16#82DCA0;   (* Base address of first recipe in memory *)
       tTempRecipe         :POINTER TO RECIPE;   (* Used as a temporary variable *)
    END_VAR
    

    Now change the contents of the structure each time the button is pressed and the index changes. Hope you get the idea.

    IF bNewRecipe THEN
       (* Move the address to the temp recipe *)
       tTempRecipe   := tRecipeMemory + (dwSelection * dwOffset);
       (* Store the contents of the memory into the current recipe *)
       tCurrentRecipe   := tTempRecipe^;
    END_IF
    (* Now you can reference variable like this in your code*)
    (*   tCurrentRecipe.wDerivative
       tCurrentRecipe.wIntergral
       tCurrentRecipe.wOffset
       tCurrentRecipe.wPorportional
       tCurrentRecipe.wScale*)
    

    That's it on an operator display as the user presses an arrow or something to scroll through the recipes he changes the index and selects accept. That was easy!!

     
  • spfeif - 2006-03-01

    Some how the file didn't get uploaded. Dummy me again no .pro allowed!!!

    RecipeManager.zip [8.85 KiB]

     
  • gmanche - 2009-01-20

    Hello,

    What I dont't understand in this program is how do you know the base address (82DCA0), is it a byte address like %MB100 ?

    I work with Moeller XC201 MIPS III

    tRecipeMemory :POINTER TO RECIPE := 16#82DCA0; ( Base address of first recipe in memory )

    Thanks

    Gildas THOMAS

     
  • spfeif - 2009-01-20

    Your PLC of choice will have some way of accessing memory. Whether it is by using addressing like in my example, a FB provided by your PLC vendor or something. The example was trying to reflect that you can read the contents of memory and store them in a consistent interface that makes it not only easy to read but can also have data that has varying variable sizes and it still works. The pressing of buttons only skips to the next structure in memory. I do not know the Moeller PLC but I would have to ask you the question how do you store retained data? Is it by R/W files (Windows CE)? Directly in some EEPROM/FLASH? If you have a FB that reads and writes to memory if you want to post a project with the FB I can give you back an example with your particular PLC vendor. Once you have the method of retrieving stored data you can apply the principals in this example. Let me know if you need further help.

     
  • gmanche - 2009-01-26

    Hi,

    isn't the CoDeSys instruction ADR whitch can do that ?

    It doesn't seem to work...

    Thanks

    Gildas

     
  • spfeif - 2009-01-26

    The ADR operator is for returning the pointer to a CoDeSys variable in memory. Can you use it to access Memory that CoDeSys is unaware of I'm not sure. I am not sure what your confusion is? You said you used the ADR operator but to do what? What are you trying to accomplish? If you can help me understand your situation I may be of better help.

     
  • spfeif - 2009-01-26

    The pointer to memory address 16#82DCA0 was just an arbitrary number it didn't mean anything. I guess I could have just used 0 as well. Recipes are normally stored in some type of non-volatile memory. You can change my example around to make it match your situation. I have assumed here that the structures are stored together in memory.Take for instance I have a manufacturer specific FB to read and write to FRAM memory on my PLC. I give it a starting address in bytes from 0 and the length in bytes to read it will return the bytes using the ADR operator. By knowing this initial offset in memory I know know where all the Recipes are stored because I know the size of the structure. It will work for RAM memory as well. So each time a user requests a different recipe I call FRAM_READ with the base address plus the users offset of which recipe and the number of bytes to read, the FB will pass it back into the structure.

     
  • alex87 - 2012-05-23

    Maybe an old converstion but....

    You said >Zitat:
    "By knowing this initial offset in memory I know know where all the Recipes are stored because I know the size of the structure."
    . My question: Is there an list of used adresses in RAM in Codesys that you have an review of adresses, else how do you know where to set the adress and if it is already used by system?

    regards

     
  • spfeif - 2012-05-23

    I'm not really sure of your question? Can you explain further? If I define a structure of structures or in this case:

    TYPE RECIPES :
    STRUCT
      RECIPE1 :RECIPE;
      RECIPE2 :RECIPE;
      RECIPE3 :RECIPE;
      RECIPE4 :RECIPE;
      RECIPE5 :RECIPE;
    END_STRUCT
    END_TYPE
    RETAIN
        MyRecipes :RECIPES;                                                                  (* Define it as retain memory*)
    END_VAR
    OR 
    MyRecipes AT%MW0 :RECIPES;                                                          (* Define it as a memory address *)
    OR
    MyRecipes :RECIPES;                                                                       (* Define it in RAM *)
    OR
    FRAM_READ (NumBytes := 50, StartAdr := 101, DST := ADR(MyRecipes )); (* Read it from some custom specific PLC memory location *)
    CurrentRecipe :RECIPE;
    wSelected_Index :word := 0; (* Zero based to reach the first structure *)
    wRecipeOffset  := 10; (* 5words * 2 = 10 bytes *)
    dwRecipeIndex     := ADR(MyRecipes ) + (wRecipeOffset  * Selected_Index);
    CurrentRecipe      := dwRecipeIndex^;  (* Deference the currently selected structure *)
    

    Does this help?

     
  • sarathbabu - 2012-06-11

    Hi

             Though it is a basic question I would like to know what is the use of offset here?Is it just for 100 bytes memory for each recipe
    
     
  • spfeif - 2012-06-11

    I think you miss typed? You stated 100 but it is 10? It is the number of bytes that each structure holds based on the data types that are in the structures. You are referring to my comment 5 words * 2 bytes = 10 bytes?

     

Log in to post a comment.