- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
The I2C_LED instance has a parameter "I2cMasterInstanceName" containing the name of an I2C master component.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
OmAb,
Maybe this link to a Cypress KBA will help: Creating a Library Project as an Object Code – KBA90606
Len
"Engineering is an Art. The Art of Compromise."
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
pins
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.
(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.
(4) Then when I set the projectA as active project, the Tera Term log was
(5) Then when I set the projectB as active project, the Tera Term log was
I wonder if this is doing something you wanted to do?
moto
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
And add a new "uint8" parameter "DEVICEID" in the Parameter Definition.
If the symbol has a label like ID=`=$DEVICEID`
you can see the device ID on your schematic.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
The I2C_LED instance has a parameter "I2cMasterInstanceName" containing the name of an I2C master component.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you very much for all your help!