This work shows the design and implementation of an automated system for an Object Classifier and handling of passive elements such as resistors and capacitors, using the PSoC 4100S board and a conveyor belt controlled by capacitive sensors. The actuators are a stepper motor and a servo motor. I have also reused the scanner from an old printer.
In the 11th section I have added an object counter using the PSoC 62S2 WiFi BT board and ModusToolbox 3.0 (Adding AnObject Counter), and with the variant that now I count small bottles in the right container and electrolytic capacitors in the left container.
As a 3-minute introduction below I show you the journey of my project, in the first part I show you the object classifier and in the second part the added object counter.
To develop this project I was inspired in my previous post: "Robotic Arm Controlled with Capsense". This didactic strategy makes it possible to promote different aspects such as collaborative work, the integration of previous skills and knowledge, as well as the relationship of content with the solution of problems in a real context.
2. Getting StartedPSoC™ Creator 4.4 is an Integrated Design Environment that enables concurrent hardware and firmware editing, compiling and debugging of PSoC™ and FM0+ solutions. Applications are created using schematic capture using 150 peripheral Components.
The PSoC™ 4100S Pioneer Kit features the PSoC™ 4100S device - PSoC 4™ with an ARM™ Cortex™-M0+ core. The PSoC™ 4100S device features the fourth-generation, low-power CAPSENSE™ solution with up to 64KB flash, 8KB SRAM, 9 Programmable Analog Blocks, 8 Programmable Digital Blocks and 36 general purpose I/Os, all capable of CAPSENSE™ and 16 Smart I/Os. Documentation: https://www.infineon.com/cms/en/product/evaluation-boards/cy8ckit-041-41xx/
CAPSENSE™ technology:
- Doesn't involve moving parts and will not wear out over time.
- Can be completely sealed to prevent moisture from seeping in.
- Doesn't require force to operate.
- Results in reduced BOM cost.
- Offers more flexibility in button shape, size, and graphical representation for your overall design.
Below I show you the schematic diagram of the project:
How does it work?
- When I touch the capsense 0 button, then the band moves to the left side of the module by means of the stepper motor.
- The number of turns of the stepper motor is 1900 turns with a time of 2 milliseconds per step. All this is obtained experimentally.
- When it reaches the extreme left, the servo motor rotates the container with the electronic part (resistor or capacitor) so that it rolls on a platform inclined approximately 15 degrees. At the end of the path, the electronic piece will fall into a container. The displacement module will return to its original position, in the center.
- When I touch the capsense 1 button, then the band moves to the right side of the module.
- When it reaches the extreme right, the servo motor rotates the container with the electronic part (resistor or capacitor) so that it rolls on a platform inclined approximately 15 degrees. At the end of the path, the electronic piece will fall inside the second container. The displacement module will return to its original position, in the center.
The pinout of this board is shown below:
I have reused an old scanner to build the main module. So I have modified it as shown below:
- First disassemble the old scanner from a printer.
- Now I remove the upper part, the glass and the scanner card. We only leave the lower part and the stepper motor.
- I make the electrical connection of the PSoC™ 4100S board, the driver and the stepper motor of the old scanner.
- In the middle part of the scanner I mounted a support and a container. The design of the 3D pieces is attached in the download section.
- At the ends of the main module I have attached two platforms made of recycled wood. The dimensions are 16 x 7.5 cm and a 1 cm high tab.
- The final module is shown below.
Servo can rotate approximately 180 degrees (90 in each direction), and works just like the standard kinds but smaller. You can use any servo code, hardware or library to control these servos. According to the datasheet of the MG90S we have this graph that helps us to control the PWM signal and the duty cycle.
The communication of the desired position is done by transmitting a pulsed signal with a period of 20ms. The pulse width determines the position of the servo. In general, a pulse between 500-1000 us corresponds to 0º, a pulse of 1500 ms corresponds to 90º, and a pulse between 2000-2500us corresponds to 180º.
I experimented with this data, and the only value I adjusted was the 0° angle at 3% duty cycle, because the servo was vibrating and making noise at 2.5%. The final data is shown below:
Below I show you the servo motor while doing the experiments.
To rotate a bipolar stepper motor, pulses are applied in sequence to the windings, the sequence of these pulses, are applied externally with an electronic controller. Said controllers are designed in such a way that the motor can be kept in a fixed position and also so that it can be rotated in both directions.
There are three characteristics that are common in stepper motors: Voltage, Electric resistance and degrees per step.
Here is a quick guide for Stepper Motor Wire Color And Coil Pairs.
However, color codes are not always respected by manufacturers. So a test allowed me to find that my stepper motor works at 5 volts. I also found by means of an ohmmeter the two coils and the order of the pins as follows: pin 1 - yellow, pin 2 - orange, pin 3 - brown, and pin 4 - black
These motors have several windings that, to produce the advance of a step, must be fed in a suitable sequence. By reversing the order of this sequence, the motor turns in the opposite direction. The next table shows the sequence to generate the CW (Clockwise) and CCW (Counterclockwise) movements of a bipolar motor.
To get started with PSoC Creator, I recommend you check out this tutorial that guides you through the use of Capsense and PSoC Creator for beginners: https://www.hackster.io/Infineon_Team/infineon-technologies-capsense-using-psoc4-cyckit-041-41xx-f66c20
Once PSoC Creator is open, click on File - New - Project, and select PSoC 4100S device
Now select Empty schematic
and finally type Project name and Workspace name:
Now open the TopDesign.cysch file and insert the next components: CapSense, EZI2C, PWM, Clock, and six digital output. Also make the conextion of the Clock, PWM and Pin_to_servo components as shown below:
The CapSense component has configured the two buttons that we will use (Button0 and Button1):
The digital output pins have the same configuration and will be programmed by software (LED, Pin1, Pin2, Pin3 and Pin4):
I have added the EZI2Ccomponent in case of monitoring any of the system data. The configuration is 100 kbps and 16 bits of sub-address size:
I have added the Clockcomponent, set it to 500kHz and connected it to the PWM component:
The PWMcomponent is used to control the servo motor. For easy understanding, I made the calculations shown below.
Period = Fclk / Desired frequency
The frequency of the clock is 500000 Hz, and desired frequency of the servo motor is 50 Hz, so:
Period = 500000/50 = 10000
Compare = Period - (Desired duty X Period)
Compare (max) = 10000 - (5% x 10000) = 9500
Now insert on the PWM component next values: Period = 10000, and Compare = 9500:
Finally I added a digital output, I edited it with the name of Pin_to_servo, and we configured it as HW connection:
The pin mapping is shown in the image below:
- "Cmod" is connected to port P4(2)
- The capsense Button0 is connected to port P0(2)
- The capsense Button1 is connected to port P0(0)
- SCL is connected to port P3(0)
- SDA is connected to port P3(1)
- The red LED is on board connected to P3(4)
- Pin1 is connected to port P3(7)
- Pin2 is connected to port P3(5)
- Pin3 is connected to port P1(5)
- Pin4 is connected to port P1(3)
- Pin_to_servo is connected to port P1(0)
Now open main.c file and add the code from the Design Components, remember to keep the names of the peripherals (CapSense, LED, etc.) which will be used always as prefix for each API call.
// AUTHOR: GUILLERMO PEREZ GUILLEN
#include "project.h"
int main(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
/* Start component */
EZI2C_Start();
EZI2C_EzI2CSetBuffer1(sizeof(CapSense_dsRam), sizeof(CapSense_dsRam), (uint8 *)&CapSense_dsRam);
CapSense_Start();
CapSense_InitializeAllBaselines();
CapSense_ScanAllWidgets();
PWM_Start();
int compare_var = 750;
for(;;)
{
PWM_WriteCompare(compare_var);
/* Do this only when a scan is done */
if(CapSense_NOT_BUSY == CapSense_IsBusy())
{
/* Include Tuner */
CapSense_RunTuner();
/* Process all widgets */
CapSense_ProcessAllWidgets();
/* Scan result verification */
/* Add any required functionality based on scanning result */
if (CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID))
{
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
compare_var = 300;
PWM_WriteCompare(compare_var);
CyDelay(1000);
compare_var = 750;
PWM_WriteCompare(compare_var);
CyDelay(1000);
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
}
else if (CapSense_IsWidgetActive(CapSense_BUTTON1_WDGT_ID))
{
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
compare_var = 1250;
PWM_WriteCompare(compare_var);
CyDelay(1000);
compare_var = 750;
PWM_WriteCompare(compare_var);
CyDelay(1000);
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
}
else
{
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(0);
compare_var = 750;
PWM_WriteCompare(compare_var);
}
/* Start next scan */
CapSense_ScanAllWidgets();
}
}
}
/* [] END OF FILE */
The code is easy to understand. For example, As we saw in section five, 0° corresponds to 3% duty cycle, 90° corresponds to 7.5% duty cycle, and 180° corresponds to 12.5% duty cycle. So, we do the next:
The servo motor is configured to starts at 90°.
PWM_Start();
int compare_var = 750;
If the 0 button is activated, the stepper motor rotates 1600 steps counterclockwise according to section six (value obtained experimentally).
/* Add any required functionality based on scanning result */
if (CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID))
{
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
Now the servo motor rotates from 90° to 0°, stops one second and rotates to 90°.
compare_var = 300;
PWM_WriteCompare(compare_var);
CyDelay(1000);
compare_var = 750;
PWM_WriteCompare(compare_var);
CyDelay(1000);
Finally the stepper motor rotates clockwise and returns to the initial position.
for (int i = 1; i <= 1600; i++){ //1600
LED_Write(0); // LED on
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(0);
Pin2_Write(1);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(0); // LED on
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(0);
Pin4_Write(1);
CyDelay(2);
LED_Write(1); // LED off
Pin1_Write(1);
Pin2_Write(0);
Pin3_Write(1);
Pin4_Write(0);
CyDelay(2);
}
If you touch button 1 now the movements are opposite to those already explained.
The full project repository can be obtained in the download section or in this link: Object Classifier
10. TestIn the video below I show you the tests done with the module.
11. Adding An Object CounterNow, in this project phase we will use ModusToolbox 3.0which has enhanced support for multi-core project workflow. The release features dual-core device support, a new graphical tool for customer board support package (BSP) development, infrastructure support for ModusToolbox Packs and backend system improvements.
About ModusToolbox software you can find the documentation here: https://www.infineon.com/cms/en/design-support/tools/sdk/modustoolbox-software/
The PSoC™ 62S2 Pioneer Kit features the PSoC™ 62 MCU (CY8C624ABZI-S2D44): 150-MHz Arm Cortex-M4 and 100-MHz Arm Cortex-M0+ cores, 2MB of Flash, 1MB of SRAM, Secure Digital Host Controller (SDHC) supporting SD/SDIO/eMMC interfaces, programmable analog blocks, programmable digital blocks, Full-Speed USB, a serial memory interface, a PDM-PCM digital microphone interface, and industry-leading capacitive-sensing with CAPSENSE™.
Documentation: https://www.infineon.com/cms/en/product/evaluation-boards/cy8ckit-062s2-43012/
Below I show you the schematic diagram with which we will make our object counter.
How does it work?
- GP2Y0A51SK0F is a distance or proximity sensor. Distance range from 2 to 15 cm and analog type output. GP2Y0A51SK0F datasheet
- On the PSoC 62S2 board, the analog ports are configured as multichannel. Therefore, port P10_2 is connected to 0 volts as a reference (ground).
- Pin P10_0 is configured as an analog port and detects the distance of objects in the right bin. On the front I have placed a cardboard wall at a constant distance of 12cm. When the small boat passes in front of the sensor and the distance is minus 7cm, then it counts one unit.
- Pin P10_1 is configured as an analog port and detects the distance of objects in the left bin. In the same way, in the front part I have placed a cardboard wall at a distance of 12cm. When the electrolytic capacitor passes in front of the sensor and the distance is minus 7cm, then it counts one unit.
GP2Y0A51SK0F Sensor Calibration
- The data between the GP2Y0A51SK0F sensor output voltage and the distance are not linear, so I used a method to get a formula with excel.
- First we make experimental measurements of output voltage against distance on each sensor. These measurements are captured in a table in excel.
- Now we make a scatterplot using the data from the table above. The first value corresponds to the voltage (millivolts), and the second value corresponds to the distance (millimeters).
- Then, on a point of the graph we click with the right mouse button and select add trendline.
- In the drop down menu I chose potential trendline option.
- Through the displayed menu I ask Excel to display equation on chart. Below I show you the final result in the first sensor (channel 0).
- Now I show you the final result in the second sensor (channel 1).
- If you are looking for more detailed information about this method with excel, please check my post: Fitting a data set to a trend line using excel
Programming With ModusToolbox 3.0
Here I have used the "ADC_basic" example. First select the board in: File - New - ModusToolbox Application
So I changed the application name to Object_Counter as shown below:
Once the project is created, you have to change the code of the main.c file to:
// AUTHOR: GUILLERMO PEREZ GUILLEN
#include "cy_pdl.h"
#include "cyhal.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
#include "math.h"
/*******************************************************************************
* Macros
*******************************************************************************/
/* Macro for ADC Channel configuration*/
#define SINGLE_CHANNEL 1
#define MULTI_CHANNEL 2
/*
* Macro to choose between single channel and multiple channel configuration of
* ADC. Single channel configuration uses channel 0 in single ended mode.
* Multiple channel configuration uses two channels, channel 0 in single ended
* mode and channel 1 in differential mode.
*
* The default configuration is set to use single channel.
* To use multiple channel configuration set ADC_EXAMPLE_MODE macro to MULTI_CHANNEL.
*
*/
//#define ADC_EXAMPLE_MODE SINGLE_CHANNEL
#define ADC_EXAMPLE_MODE MULTI_CHANNEL
/* Channel 0 input pin */
#define VPLUS_CHANNEL_0 (P10_0)
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Channel 1 VPLUS input pin */
#define VPLUS_CHANNEL_1 (P10_1)
/* Channel 1 VREF input pin */
#define VREF_CHANNEL_1 (P10_2)
/* Number of scans every time ADC read is initiated */
#define NUM_SCAN (1)
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/*******************************************************************************
* Enumerated Types
*******************************************************************************/
/* ADC Channel constants*/
enum ADC_CHANNELS
{
CHANNEL_0 = 0,
CHANNEL_1,
NUM_CHANNELS
} adc_channel;
/*******************************************************************************
* Function Prototypes
*******************************************************************************/
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Multichannel initialization function */
void adc_multi_channel_init(void);
/* Function to read input voltage from multiple channels */
void adc_multi_channel_process(void);
/* ADC Event Handler */
static void adc_event_handler(void* arg, cyhal_adc_event_t event);
#else /* ADC_EXAMPLE_MODE == SINGLE_CHANNEL */
/* Single channel initialization function*/
void adc_single_channel_init(void);
/* Function to read input voltage from channel 0 */
void adc_single_channel_process(void);
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/*******************************************************************************
* Global Variables
*******************************************************************************/
/* ADC Object */
cyhal_adc_t adc_obj;
/* ADC Channel 0 Object */
cyhal_adc_channel_t adc_chan_0_obj;
/* Default ADC configuration */
const cyhal_adc_config_t adc_config = {
.continuous_scanning=false, // Continuous Scanning is disabled
.average_count=1, // Average count disabled
.vref=CYHAL_ADC_REF_VDDA, // VREF for Single ended channel set to VDDA
.vneg=CYHAL_ADC_VNEG_VSSA, // VNEG for Single ended channel set to VSSA
.resolution = 12u, // 12-bit resolution
.ext_vref = NC, // No connection
.bypass_pin = NC }; // No connection
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Asynchronous read complete flag, used in Event Handler */
static bool async_read_complete = false;
/* ADC Channel 1 Object */
cyhal_adc_channel_t adc_chan_1_obj;
/* Variable to store results from multiple channels during asynchronous read*/
int32_t result_arr[NUM_CHANNELS * NUM_SCAN] = {0};
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/*******************************************************************************
* Function Name: main
********************************************************************************
* Summary:
* This is the main function for CM4 CPU. It does...
* 1. Configure and initialize ADC.
* 2. Every 200ms read the input voltage and display input voltage on UART.
*
* Parameters:
* none
*
* Return:
* int
*
*******************************************************************************/
int main(void)
{
/* Variable to capture return value of functions */
cy_rslt_t result;
/* Initialize the device and board peripherals */
result = cybsp_init();
/* Board init failed. Stop program execution */
if (result != CY_RSLT_SUCCESS)
{
CY_ASSERT(0);
}
/* Enable global interrupts */
__enable_irq();
/* Initialize retarget-io to use the debug UART port */
result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX,
CY_RETARGET_IO_BAUDRATE);
/* retarget-io init failed. Stop program execution */
if (result != CY_RSLT_SUCCESS)
{
CY_ASSERT(0);
}
/* Print message */
/* \x1b[2J\x1b[;H - ANSI ESC sequence for clear screen */
printf("\x1b[2J\x1b[;H");
printf("-----------------------------------------------------------\r\n");
printf("PSoC 6 MCU: ADC using HAL\r\n");
printf("-----------------------------------------------------------\r\n\n");
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Initialize Channel 0 and Channel 1 */
adc_multi_channel_init();
#else /* ADC_EXAMPLE_MODE == SINGLE_CHANNEL */
/* Initialize Channel 0 */
adc_single_channel_init();
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/* Update ADC configuration */
result = cyhal_adc_configure(&adc_obj, &adc_config);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC configuration update failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
for (;;)
{
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Sample input voltage at channel 0 and channel 1*/
adc_multi_channel_process();
#else /* ADC_EXAMPLE_MODE == SINGLE_CHANNEL */
/* Sample input voltage at channel 0 */
adc_single_channel_process();
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/* 200ms delay between scans */
cyhal_system_delay_ms(200);
}
}
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/*******************************************************************************
* Function Name: adc_multi_channel_init
*******************************************************************************
*
* Summary:
* ADC Multichannel initilization. This function initializes and configures
* channel 0 and channel 1 of ADC.
*
* Parameters:
* void
*
* Return:
* void
*
*******************************************************************************/
void adc_multi_channel_init(void)
{
/* Variable to capture return value of functions */
cy_rslt_t result;
/* Initialize ADC. The ADC block which can connect to pin 10[0] is selected */
result = cyhal_adc_init(&adc_obj, VPLUS_CHANNEL_0, NULL);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC initialization failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
/* ADC channel configuration */
const cyhal_adc_channel_config_t channel_config = {
.enable_averaging = false, // Disable averaging for channel
.min_acquisition_ns = 1000, // Minimum acquisition time set to 1us
.enabled = true }; // Sample this channel when ADC performs a scan
/* Initialize a channel 0 and configure it to scan P10_0 in single ended mode. */
result = cyhal_adc_channel_init_diff(&adc_chan_0_obj, &adc_obj, VPLUS_CHANNEL_0,
CYHAL_ADC_VNEG, &channel_config);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC first channel initialization failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
/*
* For multichannel configuration use to same channel configuration structure
* "channel_config" to configure the second channel.
* For second channel to be set to differential mode, two inputs from Pins
* 10.1 and 10.2 are configured to be inputs.
*
*/
/* Initialize channel 1 in differential mode with VPLUS and VMINUS input pins */
result = cyhal_adc_channel_init_diff(&adc_chan_1_obj, &adc_obj, VPLUS_CHANNEL_1,
VREF_CHANNEL_1, &channel_config);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC second channel initialization failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
/* Register a callback to handle asynchronous read completion */
cyhal_adc_register_callback(&adc_obj, &adc_event_handler, result_arr);
/* Subscribe to the async read complete event to process the results */
cyhal_adc_enable_event(&adc_obj, CYHAL_ADC_ASYNC_READ_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true);
printf("ADC is configured in multichannel configuration.\r\n\n");
printf("Channel 0 is configured in single ended mode, connected to pin \r\n");
printf("P10_0. Provide input voltage at P10_0\r\n");
printf("Channel 1 is configured in differential mode, connected to pin \r\n");
printf("P10_1 and P10_2. Provide input voltage at P10_1 and reference \r\n");
printf("voltage at P10_2\r\n\n");
}
/*******************************************************************************
* Function Name: adc_multi_channel_process
*******************************************************************************
*
* Summary:
* ADC single channel process function. This function reads the input voltage
* from channel 0 and channel 1. Prints the input voltage on UART.
*
* Parameters:
* void
*
* Return:
* void
*
*******************************************************************************/
int32_t bottle_number = 0;
int32_t capacitor_number = 0;
void adc_multi_channel_process(void)
{
/* Variable to capture return value of functions */
cy_rslt_t result;
/* Variable to store ADC conversion result from channel 0 */
int32_t adc_result_0 = 0;
/* Variable to store ADC conversion result from channel 1 */
int32_t adc_result_1 = 0;
/* Variables distance */
/* Initiate an asynchronous read operation. The event handler will be called
* when it is complete. */
result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC async read failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
/*
* Read data from result list, input voltage in the result list is in
* microvolts. Convert it millivolts and print input voltage
*
*/
adc_result_0 = result_arr[CHANNEL_0]/1000;
adc_result_1 = result_arr[CHANNEL_1]/1000;
float sensor_left = 28915*pow(adc_result_0, -0.908);
float sensor_right = 58221*pow(adc_result_1, -0.998);
int32_t bottle = sensor_left;
int32_t capacitor = sensor_right;
if (bottle<70){bottle_number=bottle_number+1;}
else if (capacitor<70){capacitor_number=capacitor_number+1;}
else { }
printf("Bottle: %3ld \t Capacitor: %3ld\r\n", bottle_number, capacitor_number);
cyhal_system_delay_ms(10);
/* Clear async read complete flag */
async_read_complete = false;
}
/*******************************************************************************
* Function Name: adc_event_handler
*******************************************************************************
*
* Summary:
* ADC event handler. This function handles the asynchronous read complete event
* and sets the async_read_complete flag to true.
*
* Parameters:
* void *arg : pointer to result list
* cyhal_adc_event_t event : ADC event type
*
* Return:
* void
*
*******************************************************************************/
static void adc_event_handler(void* arg, cyhal_adc_event_t event)
{
if(0u != (event & CYHAL_ADC_ASYNC_READ_COMPLETE))
{
/* Set async read complete flag to true */
async_read_complete = true;
}
}
#else
/*******************************************************************************
* Function Name: adc_single_channel_init
*******************************************************************************
*
* Summary:
* ADC single channel initialization function. This function initializes and
* configures channel 0 of ADC.
*
* Parameters:
* void
*
* Return:
* void
*
*******************************************************************************/
void adc_single_channel_init(void)
{
/* Variable to capture return value of functions */
cy_rslt_t result;
/* Initialize ADC. The ADC block which can connect to pin 10[0] is selected */
result = cyhal_adc_init(&adc_obj, VPLUS_CHANNEL_0, NULL);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC initialization failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
/* ADC channel configuration */
const cyhal_adc_channel_config_t channel_config = {
.enable_averaging = false, // Disable averaging for channel
.min_acquisition_ns = 1000, // Minimum acquisition time set to 1us
.enabled = true }; // Sample this channel when ADC performs a scan
/* Initialize a channel 0 and configure it to scan P10_0 in single ended mode. */
result = cyhal_adc_channel_init_diff(&adc_chan_0_obj, &adc_obj, VPLUS_CHANNEL_0,
CYHAL_ADC_VNEG, &channel_config);
if(result != CY_RSLT_SUCCESS)
{
printf("ADC single ended channel initialization failed. Error: %ld\n", (long unsigned int)result);
CY_ASSERT(0);
}
printf("ADC is configured in single channel configuration\r\n\n");
printf("Provide input voltage at pin P10_0. \r\n\n");
}
/*******************************************************************************
* Function Name: adc_single_channel_process
*******************************************************************************
*
* Summary:
* ADC single channel process function. This function reads the input voltage
* and prints the input voltage on UART.
*
* Parameters:
* void
*
* Return:
* void
*
*******************************************************************************/
void adc_single_channel_process(void)
{
/* Variable to store ADC conversion result from channel 0 */
int32_t adc_result_0 = 0;
/* Read input voltage, convert it to millivolts and print input voltage */
adc_result_0 = cyhal_adc_read_uv(&adc_chan_0_obj)/1000;
printf("Channel 0 input: %4ldmV\r\n", (long int)adc_result_0);
}
#endif /* ADC_EXAMPLE_MODE == MULTI_CHANNEL */
/* [] END OF FILE */
Here I show you the configuration in multichannel mode and configuration of analog ports
#define ADC_EXAMPLE_MODE MULTI_CHANNEL
/* Channel 0 input pin */
#define VPLUS_CHANNEL_0 (P10_0)
#if ADC_EXAMPLE_MODE == MULTI_CHANNEL
/* Channel 1 VPLUS input pin */
#define VPLUS_CHANNEL_1 (P10_1)
/* Channel 1 VREF input pin */
#define VREF_CHANNEL_1 (P10_2)
Finally, the multichannel function shows you how to add the formulas obtained in the previous step and in the corresponding sensor. Finally print the data and restart the process every 10 milliseconds.
/*
* Read data from result list, input voltage in the result list is in
* microvolts. Convert it millivolts and print input voltage
*
*/
adc_result_0 = result_arr[CHANNEL_0]/1000;
adc_result_1 = result_arr[CHANNEL_1]/1000;
float sensor_left = 28915*pow(adc_result_0, -0.908);
float sensor_right = 58221*pow(adc_result_1, -0.998);
int32_t bottle = sensor_left;
int32_t capacitor = sensor_right;
if (bottle<70){bottle_number=bottle_number+1;}
else if (capacitor<70){capacitor_number=capacitor_number+1;}
else { }
printf("Bottle: %3ld \t Capacitor: %3ld\r\n", bottle_number, capacitor_number);
cyhal_system_delay_ms(10);
The full project repository can be obtained in the download section or in this link: Object Counter
Test
Below I show you the project already assembled and with the boards programmed and ready for a test.
Below I show you the video of the tests carried out with the object counter
12. BOM CostBelow I show you a estimate BOM cost. However you can reduce the total cost by replacing the rechargeable battery with a power supply, and reusing the stepper motor from the old recycled scanner.
The Excel document can be downloaded from the "CAD - Enclosures and custom parts" section or project repository ( BOM-classifier-counter.xlsx)
13. Conclusion- In this project I demonstrate the use of capsense sensors in Object Classifier process (resistors and capacitors).
- I have used the PSoC™ 4100S Pioneer Kit board, and it was programmed with PSoC™ Creator 4.4
- In 11th section, I have added an Object Counter with the PSoC 62S2 WiFi BT board and using ModusToolbox 3.0. To achieve this I have I have used and experienced the GP2Y0A51SK0F infrared distance sensors.
- To develop this project I was inspired by "Robotic Arm Controlled with Capsense". I think that this work has many applications in industry and workshops. Eg, in quality control its necessary to check products and deposit those that approve in a container for distribution and sale, and the others in a container for return.
Comments