Project Library

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
OmAb_4672891
Level 2
Level 2
10 replies posted 5 replies posted 10 questions asked

Hello,


I am writing firmware for a collection of MCU's that network via CAN. As a result, there is a lot of redundant code used between the projects. (i.e, I use a CANUtility.c file to handle all CAN operations. All projects use CANUtility.c the same way, so if I have to make a change, i have to go to all projects and change its CANUtility.c).  To combat this, I thought I'd might make a project library that all projects can include. However I seem unsure on how to do this. It does not recognize certain data types (like uint16, uint8) as i have to include project.h which is not there. In addition, I am unable to reference hardware, such as Pin_1_Read(). Is there a way I can have a file included in each project so that I have to make a change once? I've looked at the tutorials but was unable to do this.

Thank you

0 Likes
1 Solution

As a solution to my recommendation creating a custom component, I created a component to have a name of the component which is controlled by the custom component.

For example, my use case uses one I2C master component "I2CM" which is shared with two software component instances "I2C_LCD" and "I2C_LED"  The component PCA9632 is my custom component.

schematic3.png

The I2C_LED instance has a parameter "I2cMasterInstanceName" containing the name of an I2C master component.

ledDriverConfiguration.png

This parameter can be referred by the source code.  For example, the header file is included from the custom component's API header as follows.

#if !defined(CY_I2C_PCA9632_`$INSTANCE_NAME`_H)

#define CY_I2C_PCA9632_`$INSTANCE_NAME`_H

#include "cytypes.h"

#include "cyfitter.h"

#include "`$I2cMasterInstanceName`.h"

Now, the MACROs in the I2C master component can be accessed from this software component.

// MACROs for handling I2C Master API names on SCB and UDB

#if (`$INSTANCE_NAME`_IS_SCB_MASTER_USED)

    #define `$INSTANCE_NAME`_MODE_COMPLETE_XFER         (`$I2cMasterInstanceName`_I2C_MODE_COMPLETE_XFER)

    #define `$INSTANCE_NAME`_WRITE_COMPLETE             (`$I2cMasterInstanceName`_I2C_MSTAT_WR_CMPLT)

    #define `$INSTANCE_NAME`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)  \

        `$I2cMasterInstanceName`_I2CMasterWriteBuf(slaveAddress, wrData, cnt, mode)

    #define `$INSTANCE_NAME`_MasterReadStatus()     `$I2cMasterInstanceName`_I2CMasterStatus()

#else

    #define `$INSTANCE_NAME`_MODE_COMPLETE_XFER         (`$I2cMasterInstanceName`_MODE_COMPLETE_XFER)

    #define `$INSTANCE_NAME`_WRITE_COMPLETE             (`$I2cMasterInstanceName`_MSTAT_WR_CMPLT)

    #define `$INSTANCE_NAME`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)  \

        `$I2cMasterInstanceName`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)

    #define `$INSTANCE_NAME`_MasterReadStatus()     `$I2cMasterInstanceName`_MasterStatus()

#endif /* `$INSTANCE_NAME`_IS_SCB_MASTER_USED */

In my component all I2C master component functions are redefined in the header file.

Please refer my repository for more details.

Regarding the MACRO referred at runtime, it is impossible because the MACROs are processed by a software named pre-processor and then passed to the compiler.

Regards,

Noriaki

View solution in original post

8 Replies
OmAb_4672891
Level 2
Level 2
10 replies posted 5 replies posted 10 questions asked

To be more specific, I want to set a DEVICEID variable in main. Depending on this DEVICEID variable I would like to comment out sections of code as Cypress does in generating API source code. For example,

#if DEVICEID ==1

     /* use this piece of code */

     codeA;
#elif DEVICEID == 2
     /* use this piece of code */

     codeA but defined differently;
#endif

However, when coming to call codeA in main, it just says undefined refernece.... I would export DEVICEID using extern and using a main.h file.

0 Likes

OmAb,

Maybe this link to a Cypress KBA will help:  Creating a Library Project as an Object Code – KBA90606

Len

Len
"Engineering is an Art. The Art of Compromise."
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

I tried the following experimental with CY8CKIT--044.

(1) In the workspace I created a folder named common_lib

     and in the common_lib, I created test.h and test.c

test.h

==================

#ifndef _TEST_H_

#define _TEST_H_

 

void codeA(void) ;

 

#endif /* _TEST_H_ */

==================

test.c

==================

#include "project.h"

#include "main.h"

#include "test.h"

#ifdef DEVICEID_1

void codeA(void)

{

    UART_UartPutString("CodeA for the Device 1!\n\r") ;

}

#endif

#ifdef DEVICEID_2

void codeA(void)

{

    UART_UartPutString("CodeA for the Device 2!\n\r") ;

#endif

==================

(2) Then I created a projectA in the workspace

schematic

002-schematic.JPG

pins

003-pins.JPG

main.h

========================

#ifndef _MAIN_H_

#define _MAIN_H_

#include "project.h"

 

#define DEVICEID_1

 

#endif /* _MAIN_H_ */

========================

main.c

NOTE: Please focus only #includes and main(), other parts are print utilities.

========================

#include "project.h"

#include "main.h"

#include "test.h"

#include "stdio.h"

#define STR_LEN 64

char    str[STR_LEN+1] ;

void    print(char *str)

{

UART_UartPutString(str) ; /* PSoC 4 */

// UART_PutString(str) ;     /* PSoC 5 */

}

void cls(void)

{

    print("\033c") ; /* reset */

    CyDelay(20) ;

    print("\033[2J") ; /* clear screen */

    CyDelay(20) ;

}

void splash(char *prog_name)

{

    cls() ;

    if (prog_name && *prog_name) {

        print(prog_name) ;

    }

    print(" (") ;

    print(__DATE__) ;

    print(" ") ;

    print(__TIME__) ;

    print(")\n") ;

}

int main(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */

    UART_Start() ;

 

    splash("ProjectA") ;

    for(;;)

    {

        codeA() ;

        CyDelay(1000) ;

    }

}

========================

In the project A build setting added "../common_lib" as an include path.

005-project_a_build.JPG

(3) Then I created a projectB in the workspace

main.h

===============

#ifndef _MAIN_H_

#define _MAIN_H_

#include "project.h"

 

#define DEVICEID_2

 

#endif /* _MAIN_H_ */

===============

main.c

===============

#include "project.h"

#include "main.h"

#include "test.h"

#include "stdio.h"

#define STR_LEN 64

char    str[STR_LEN+1] ;

void    print(char *str)

{

UART_UartPutString(str) ; /* PSoC 4 */

// UART_PutString(str) ;     /* PSoC 5 */

}

void cls(void)

{

    print("\033c") ; /* reset */

    CyDelay(20) ;

    print("\033[2J") ; /* clear screen */

    CyDelay(20) ;

}

void splash(char *prog_name)

{

    cls() ;

    if (prog_name && *prog_name) {

        print(prog_name) ;

    }

    print(" (") ;

    print(__DATE__) ;

    print(" ") ;

    print(__TIME__) ;

    print(")\n") ;

}

int main(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */

    UART_Start() ;

 

    splash("ProjectB") ;

    for(;;)

    {

        codeA() ;

        CyDelay(1000) ;

    }

}

/* [] END OF FILE */

===============

In the project B build setting added "../common_lib" as an include path.

006-project_b_build_settings.JPG

(4) Then when I set the projectA as active project, the Tera Term log was

001-teraterm_logA.JPG

(5) Then when I set the projectB as active project, the Tera Term log was

004-TeraTerm-logB.JPG

I wonder if this is doing something you wanted to do?

moto

I will introduce a different kind of solution making a custom component.

Create a symbol element of a component with no terminals and specify the "API suffix" in the Properties

GS004747.png

And add a new "uint8" parameter "DEVICEID" in the Parameter Definition.

GS004748.png

If the symbol has a label like ID=`=$DEVICEID`

GS004749.png

you can see the device ID on your schematic.

GS004750.png

The component has no schematics but APIs only.  For example, there is a header file Utility.h

#ifndef `$INSTANCE_NAME`_H

#define `$INSTANCE_NAME`_H

#include <cytypes.h>

extern uint8 `$INSTANCE_NAME`_Add8(uint8 a, uint8 b);

extern char *`$INSTANCE_NAME`_GetDeviceName(void);

#endif /* `$INSTANCE_NAME`_H */

and a C source file Utility.c

#include "`$INSTANCE_NAME`.h"

uint8 `$INSTANCE_NAME`_Add8(uint8 a, uint8 b) {

    return a + b;

}

extern char *`$INSTANCE_NAME`_GetDeviceName(void) {

    #if ((`$DEVICEID`) == 1)

        return "ONE";

    #elif ((`$DEVICEID`) == 2)

        return "TWO";

    #else

        return "N/A";

    #endif

}   

Now, you can drop and drop your custom component on your schematic and the API functions can be used as follows.

    (void)UT_Add8(10,20);

    (void)UT_GetDeviceName();

Please note that the returned values are not used in my example.

Please refer the "Component Author Guide" document installed with the PSoC Creator for more details.

Regards,

Noriaki

OmAb_4672891
Level 2
Level 2
10 replies posted 5 replies posted 10 questions asked

Thank you everyone for your replies! I have studied all the approaches and they all got me closer to my goal. However I am left with 2 questions.

1) My issue was I coudln't find a way to call on generated API code within the libraries. for example using MACROs from CAN such as:
     msg[1]=CAN_1_RX_DATA_BYTE2(*receiveMailBoxNumber);

yields an error. I was wondering if there was a way to include generated API code within the project libraries?

2) Is there a way to define a MACRO at run time? I have 4 types of PCB's. I would like to do conditional compiling depending on the type of board. The only way I can do that is through a MACRo. So I was wondering if there was a way I can set that macro at runtime, or have it identifiy some hardware on the board BEFORE it runs?

Thakn you again for all your help!!

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

> Thank you everyone for your replies! I have studied all the approaches and they all got me closer to my goal.

I'm glad hearing that.

> 1) ..

> ...

> yields an error. ..

What is the exact error message you are getting?

If you can provide us a minimum sample project with which we can reproduce the problem,

it will help us to help you.

> 2) Is there a way to define a MACRO at run time?

IMHO, MACRO is fixed at compile time, so you can not define a new MACRO at run time.

But if you have defined required MACROs at compile time

and let the run time decide which MACRO to be called may be possible.

Still you need to provide some clue for the CPU to realize which board it is running on.

moto

As a solution to my recommendation creating a custom component, I created a component to have a name of the component which is controlled by the custom component.

For example, my use case uses one I2C master component "I2CM" which is shared with two software component instances "I2C_LCD" and "I2C_LED"  The component PCA9632 is my custom component.

schematic3.png

The I2C_LED instance has a parameter "I2cMasterInstanceName" containing the name of an I2C master component.

ledDriverConfiguration.png

This parameter can be referred by the source code.  For example, the header file is included from the custom component's API header as follows.

#if !defined(CY_I2C_PCA9632_`$INSTANCE_NAME`_H)

#define CY_I2C_PCA9632_`$INSTANCE_NAME`_H

#include "cytypes.h"

#include "cyfitter.h"

#include "`$I2cMasterInstanceName`.h"

Now, the MACROs in the I2C master component can be accessed from this software component.

// MACROs for handling I2C Master API names on SCB and UDB

#if (`$INSTANCE_NAME`_IS_SCB_MASTER_USED)

    #define `$INSTANCE_NAME`_MODE_COMPLETE_XFER         (`$I2cMasterInstanceName`_I2C_MODE_COMPLETE_XFER)

    #define `$INSTANCE_NAME`_WRITE_COMPLETE             (`$I2cMasterInstanceName`_I2C_MSTAT_WR_CMPLT)

    #define `$INSTANCE_NAME`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)  \

        `$I2cMasterInstanceName`_I2CMasterWriteBuf(slaveAddress, wrData, cnt, mode)

    #define `$INSTANCE_NAME`_MasterReadStatus()     `$I2cMasterInstanceName`_I2CMasterStatus()

#else

    #define `$INSTANCE_NAME`_MODE_COMPLETE_XFER         (`$I2cMasterInstanceName`_MODE_COMPLETE_XFER)

    #define `$INSTANCE_NAME`_WRITE_COMPLETE             (`$I2cMasterInstanceName`_MSTAT_WR_CMPLT)

    #define `$INSTANCE_NAME`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)  \

        `$I2cMasterInstanceName`_MasterWriteBuf(slaveAddress, wrData, cnt, mode)

    #define `$INSTANCE_NAME`_MasterReadStatus()     `$I2cMasterInstanceName`_MasterStatus()

#endif /* `$INSTANCE_NAME`_IS_SCB_MASTER_USED */

In my component all I2C master component functions are redefined in the header file.

Please refer my repository for more details.

Regarding the MACRO referred at runtime, it is impossible because the MACROs are processed by a software named pre-processor and then passed to the compiler.

Regards,

Noriaki

Thank you very much for all your help!

0 Likes