You are here

PSoC Creator News and Information | Cypress Semiconductor

Feb 20, 2018

Barc is ready for Embedded World

So, finally, Barc is ready to go back to Germany. Last time I had the main program working well and I just had to tune the CapSense proximity sensors. Now I do not have to thread a USB cable through Barc's mouth the tuning process was pretty simple. I just right-clicked on the CapSense component to launch the tuner, then ran the noise tests, and played with the graph view of the signals to fine-tune the threshold values. Once I got them working I just sent the values back to the project and re-built it. Here is a picture of the raw signals (on top) and the recorded active/inactive values (below) as I put my hand against the left sensor (blue), then the right (orange), and finally above them both. The goal here is to optimize sensing range without being so sensitive that both sensors always fire (because then he'll almost always just back up when a hand is anywhere near him).

Tuning Barc's proximity sensors

While working, however, I figured that I had a couple of defects and had a good idea for one last feature. even though I said I would not do that! First the defects... at the start of the CapSense loop I had a simple if statement.

if( CAPSENSE_NO_BUSY == CapSense_IsBusy() )

This works OK but, if it is busy, then I run the risk of the state variable jumping between a CapSense state and the IR sensor. I also noticed that sometimes Barc would jump forward momentarily when I remove my hand from the side of his face. To fix this I changed the "if" to a "while" so that the code waits until there is CapSense data.

while( CapSense_IsBusy() )
{
    /* Wait for CapSense */
}

This means that the code always checks the CapSense state and the jumpiness goes away.

The next problem was a slight tendency for Barc to get over-excited and run around in circles! This was happening because the motors tend to cause the noise floor to rise and the automatic baseline calculation cannot keep pace. It's not like I ever claimed this was a well-designed CapSense design! But it is a little annoying so I added some code that counts loops and periodically resets the baselines.

/* Periodically re-intialize the CapSense baseline to avoid noise-induced runaway dog */
if( 0 == ( loop_count % BASELINE_RESET_RATE ) )
{
    CapSense_InitializeWidgetBaseline( CapSense_LEFT_WDGT_ID );
    CapSense_InitializeWidgetBaseline( CapSense_RIGHT_WDGT_ID );
}
CapSense_ScanAllWidgets();                  // Start the next scan (non blocking call)

Because Barc is going to a trade show I decided I needed to save a little power or I would be swapping batteries every hour. There are many ways to do that. The ideal method would be to track activity and, if Barc is left alone, go into a deep sleep state, waking periodically to check for a proximity event. But I'll be honest, I ran out of time, so I put in a quick and dirty fix instead. I just added a count of loops with Barc in the WAIT state and, when he's still for a while, I just stop the motors and sensors, and go into stop mode (the lowest power state). He does not start up automatically but a press of the RESET switch does the trick.

Barc and I will be at the Cypress booth during the show - from Tuesday 27th through Thursday 1st - so please stop by and give him a gentle pat on the head.

Here is whole program for your reference. the actual project with the schematic and resources set up is attached to the blog page.

Package iconBarc_2.cyprj_.zip

/*
                ****************
                **** Barc_2 ****
                ****************

Barc_2 adds an IR sensor for longer range forward visibility. He supports the following activities.

    LEFT    hand detected on the right proximity sensor
    RIGHT   hand detected on the left proximity sensor
    REVERSE hand detected above (both proximity sensors)
    CHASE   hand detected ahead (IR sensor)
    WAIT    no hand detected
*/

/*
Peripheral includes
*/
#include "project.h"

/*
Library includes (for abs())
*/
#include "stdlib.h"

/*
Infrared sensor and ADC defines
*/
#define IR_POWER_ON             (1)
#define IR_POWER_OFF            (0)
#define IR_SENSOR_SETTLE_TIME   (100)

#define IR_ADC_CHANNEL          (0)
#define IR_ADC_THRESHOLD_MV     (1800)

/*
H-Bridge and PWM Motor defines
*/
#define HBRIDGE_POWER_ON        (1)
#define HBRIDGE_POWER_OFF       (0)

#define SPEED_ILLEGAL           (-1000)

#define SPEED_WALK_LEFT         (38)
#define SPEED_WALK_RIGHT        (40)
#define SPEED_RUN_LEFT          (57)
#define SPEED_RUN_RIGHT         (60)
#define SPEED_STOP              (0)

/*
LED defines
*/
#define LED_ON                  (1)
#define LED_OFF                 (0)

/*
Main loop timing defines
*/
#define WAIT_DEBOUNCE_MS        (50)
#define WAIT_LOOPS_PER_SECOND   (1000/WAIT_DEBOUNCE_MS)
#define WAIT_LOOPS_PER_MINUTE   (60*WAIT_LOOPS_PER_SECOND)
#define BASELINE_RESET_RATE     (5*WAIT_LOOPS_PER_SECOND)
#define HEARTBEAT_PERIOD        (20)
#define HEARTBEAT_DUTY_CYCLE    (1)

/*
Definition of the supported robot states
*/
typedef enum { WAIT, CHASE, REVERSE, LEFT, RIGHT } state_t;

/*
Function declarations
*/
void motor( int, int );

/*
Function:   main

Initialization:
    Turn on the CapSense (proximity sensors) and I2C for tuning
    Turn on the ADC (distance sensor)
    Turn on the motor PWM (outputs are held high to brake the motors)
    Power up the H-Bridge board
    Wait for hardware to settle

Main Loop:
    Read the ADC to detect an object in front
    Read Capsense to detect hands to the side or above
    Act on the detected state - move the robot
*/

int main(void)
{
    state_t state;                                      // Result of sensor scans
    int16 range = 0;                                    // ADC value
    uint32 loop_count = 0;                              // Counter for heartbeat and barking
    uint32_t wait_loop_count = 0;                       // Counter for time spent in WAIT state
    int left, right;                                    // CapSense widget states
    
    CyGlobalIntEnable;      
    
    /*
    Turn on the Capsense proximity detection (with I2C tuning)
    */
    EZI2C_Start();                                      // Turn on I2C (over kitprog bridge)
    EZI2C_EzI2CSetBuffer1( sizeof( CapSense_dsRam ),    // set up I2C buffer for tuning
                           sizeof( CapSense_dsRam ), 
                           (uint8 *)&CapSense_dsRam );
    CapSense_Start();                                   // Turn on CapSense
    CapSense_ScanAllWidgets();                          // Start scanning
    
    /*
    Turn on the IR sensor and ADC
    */
    Pin_IR_GND_Write( IR_POWER_OFF );                   // Make sure ground is low
    Pin_IR_Power_Write( IR_POWER_ON );                  // Turn on the sensor
    CyDelay( IR_SENSOR_SETTLE_TIME );                   // Allow time for sensor output to be valid
    
    ADC_IR_Sensor_Start();                              // Turn on the ADC
    ADC_IR_Sensor_StartConvert();                       // Start sampling (free running)
       
    /*
    Turn on the motors
    */
    PWM_Motor_Start();                                  // Turn on the 2-channel PWM
    motor( SPEED_STOP, SPEED_STOP );                    // Hold output high (no motion)
    Pin_SLP_Write( HBRIDGE_POWER_ON );                  // Turn on the H-bridge for the motors
    CyDelay( 1 );                                       // Allow time for FLT to go low (380us)

    
    /*
    Main loop - read the IR and CapSense sensors then set motor speed accordingly
    */
    for(;;)
    {   
        /*
        Start sensing - default state is WAIT (do nothing)
        */
        state = WAIT;
        
        /* Get an ADC value from the distance sensor and convert it to millivolts */
        if( ADC_IR_Sensor_IsEndConversion( ADC_IR_Sensor_RETURN_STATUS ) )
        {
            range = ADC_IR_Sensor_GetResult16( IR_ADC_CHANNEL );
            range = ADC_IR_Sensor_CountsTo_mVolts( IR_ADC_CHANNEL, range );
        
            /* Change the state if above the proximity threshold */
            if( range > IR_ADC_THRESHOLD_MV )
            {
                state = CHASE;
            }
        }
        
        /*
        CapSense - detect a hand to the left, right or overhead
        */
        while( CapSense_IsBusy() )
        {
            /* Wait for CapSense */
        }
            
        CapSense_ProcessAllWidgets();               // Get the scan results
        CapSense_RunTuner();                        // Tuning across EZI2C 
        
        /* Read the scan values */
        left = CapSense_IsWidgetActive( CapSense_LEFT_WDGT_ID );
        right = CapSense_IsWidgetActive( CapSense_RIGHT_WDGT_ID );
        
        /* Set the state if a hand is detected */
        if( left && right )
        {
            state = REVERSE;                        // Back up
        }
        else if( left )
        {
            state = RIGHT;                          // Turn away
        }
        else if( right )
        {
            state = LEFT;                           // Turn away
        }

        /* Periodically re-intialize the CapSense baseline to avoid noise-induced runaway dog */
        if( 0 == ( loop_count % BASELINE_RESET_RATE ) )
        {
            CapSense_InitializeWidgetBaseline( CapSense_LEFT_WDGT_ID );
            CapSense_InitializeWidgetBaseline( CapSense_RIGHT_WDGT_ID );
        }
    
        CapSense_ScanAllWidgets();                  // Start the next scan (non blocking call)
        
        /*
        State machine - act on the sensed state of the robot
        */
        switch( (int)state )
        {
            case RIGHT:
                /* reverse right motor, forward left */
                motor( SPEED_WALK_LEFT, -SPEED_WALK_RIGHT );
                wait_loop_count = 0;
            break;
            
            case LEFT:
                /* reverse left motor, forward right */
                motor( -SPEED_WALK_LEFT, SPEED_WALK_RIGHT );
                wait_loop_count = 0;
            break;
            
            case REVERSE:
                /* reverse both motors */
                motor( -SPEED_WALK_LEFT, -SPEED_WALK_RIGHT );
                wait_loop_count = 0;
            break;
            
            case CHASE:
                /* forward both motors, fast */             
                motor( SPEED_RUN_LEFT, SPEED_RUN_RIGHT );
                wait_loop_count = 0;
            break;
            
            case WAIT:
            default:
                /* stop both motors */
                motor( SPEED_STOP, SPEED_STOP );
                
                /* sleep if left alone too long */
                wait_loop_count++;
                if( wait_loop_count > WAIT_LOOPS_PER_MINUTE )
                {
                    /* Turn everything off */
                    Pin_Status_Write( LED_OFF );
                    Pin_IR_Power_Write( IR_POWER_OFF );
                    Pin_SLP_Write( HBRIDGE_POWER_OFF );
                    
                    /* Turn off the peripherals */
                    ADC_IR_Sensor_Stop();
                    PWM_Motor_Stop();
                    EZI2C_Stop();
                    CapSense_Stop();
                    
                    CySysPmStop();                      // Re-start via RESET switch
                }
            break;            
        }
        
        /* Create an inverse heart beat (mostly on, briefly off) with the LED */
        Pin_Status_Write( ( loop_count > HEARTBEAT_DUTY_CYCLE ) ? LED_ON : LED_OFF );
        
        loop_count++;
        if( loop_count > HEARTBEAT_PERIOD )
        {
            loop_count = 0;                             // Reset the loop counter
        }

        CyDelay( WAIT_DEBOUNCE_MS );                    // Let the motors run for a while to prevent "jitter"             
    }
}

/*
Function:   motor

Sets the direction register, which swaps the PWM output signal to the H-bridge inputs.
Changes the motor PWM compare value to control the speed.
Only updates the PWM if something needs to change.
*/
void motor( int left, int right )
{
    static int last_left = SPEED_ILLEGAL;               // Remember the previous speeds
    static int last_right = SPEED_ILLEGAL;
    
    /* Pack the directions into a 2-bit register */
    int dir = ( right >= 0 );
    dir <<= 1;
    dir |= ( left >= 0 );
    Reg_Direction_Write( dir );                         // Set the direction (control the OR gates)
    
    if( left != last_left )
        PWM_Motor_WriteCompare1( abs( left ) );         // Left motor speed
        
    if( right != last_right )
        PWM_Motor_WriteCompare2( abs( right ) );        // Right motor speed
        
    last_left = left;                                   // Remember for next call
    last_right = right;
}

 

Feb 08, 2018

Finishing Up the Barc Makeover

Do you know the rule about the last 10% of a project taking 90% of the time? I think I fell foul of it with Barc's makeover. I thought I had plenty of time before Embedded World (February 27 to March 1) and knew how to finish the project back in November. Time to relax - oh wait - it's February! Let's re-cap where things stand.

Barc is a mobile (wheeled) dog, driven by PWMs on the PSoC 4200M and the Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board. He has two CapSense proximity-sensing loops (aka wires and copper tape) wrapped around either side of his head, which shall be used to detect the presence of a hand to the left right or overhead. And he has an Adafruit GP2Y0A21YK0F IR distance sensor which I set up in my previous post, Barc Assembled to detect a hand in front of the dog.

With all the hardware assembled I just need to write the C program to make him behave the way I need. Here is a summary of the program.

  1. Initialize the hardware (before the main loop)
  2. Generate a heart-beat on the LED
  3. Read the sensors and detect the presence of a hand
  4. State machine to choose the appropriate movement

Initialize the Hardware

I turn on the hardware in a specific order, with the motors starting last, so that Barc doesn't get frisky before the the sensors are ready to tell him what to do. In the following code I start the CapSense and the I2C (which is only used for tuning) first. Then I power up the IR sensor (ground low and power high), allow a little time for it to settle (100ms is long enough for the sensor output to be reliable) and then turn on PSoC ADC so i can read it. Lastly I turn on the motors and set their speed to "STOP" using the motor() function.

/*
Infrared sensor and ADC defines
*/
#define IR_POWER_ON             (1)
#define IR_POWER_OFF            (0)
#define IR_SENSOR_SETTLE_TIME   (100)
#define IR_ADC_CHANNEL          (0)
#define IR_ADC_THRESHOLD_MV     (1800)
/*
H-Bridge and PWM Motor defines
*/
#define HBRIDGE_POWER_ON        (1)
#define HBRIDGE_POWER_OFF       (0)
     /*
    Turn on the CapSense proximity detection (with I2C tuning)
    */
    EZI2C_Start();                                      // Turn on I2C (over kitprog bridge)
    EZI2C_EzI2CSetBuffer1( sizeof( CapSense_dsRam ),    // set up I2C buffer for tuning
                           sizeof( CapSense_dsRam ), 
                           (uint8 *)&CapSense_dsRam );
    CapSense_Start();                                   // Turn on CapSense
    CapSense_ScanAllWidgets();                          // Start scanning
    
    /*
    Turn on the IR sensor and ADC
    */
    Pin_IR_GND_Write( IR_POWER_OFF );                   // Make sure ground is low
    Pin_IR_Power_Write( IR_POWER_ON );                  // Turn on the sensor
    CyDelay( IR_SENSOR_SETTLE_TIME );                   // Allow time for sensor output to be valid
    
    ADC_IR_Sensor_Start();                              // Turn on the ADC
    ADC_IR_Sensor_StartConvert();                       // Start sampling (free running)
       
    /*
    Turn on the motors
    */
    PWM_Motor_Start();                                  // Turn on the 2-channel PWM
    motor( SPEED_STOP, SPEED_STOP );                    // Hold output high (no motion)
    Pin_SLP_Write( HBRIDGE_POWER_ON );                  // Turn on the H-bridge for the motors
    CyDelay( 1 );                                       // Allow time for FLT to go low (380us)

 

The motor() function is very important. It is how I adjust Barc's speed and direction from this part of the schematic.

Barc PSoC motor control

It's worth diving into this function a little bit because it makes the rest of the program so easy to write. Here's the code.

#define SPEED_ILLEGAL           (-1000)

void motor( int left, int right )
{
    static int last_left = SPEED_ILLEGAL;               // Remember the previous speeds
    static int last_right = SPEED_ILLEGAL;
    
    /* Pack the directions into a 2-bit register */
    int dir = ( right >= 0 );
    dir <<= 1;
    dir |= ( left >= 0 );
    Reg_Direction_Write( dir );                         // Set the direction (control the OR gates)
    
    if( left != last_left )
        PWM_Motor_WriteCompare1( abs( left ) );         // Left motor speed
        
    if( right != last_right )
        PWM_Motor_WriteCompare2( abs( right ) );        // Right motor speed
        
    last_left = left;                                   // Remember for next call
    last_right = right;
}

The function accepts two signed speed values, one each for the left and right motors. If the speed is negative then the motor has to run backwards. So a local variable, dir, is set according to the sign of the arguments and is written into the control register (Reg_Direction). Next it sets the PWM compare values to control the duty cycle and, as a result, the motor speeds. The abs() C run-time function just returns the absolute (positive) value of a signed argument. I use static local variables to remember the values of the speeds from the previous function call so that I only write to the PWM if I need to change the speed. This is what makes my main loop easy to write - I can call motor() as often as a like and it only touches the hardware if an actual speed change is required. You'll soon see how my state machine code is really simple and easy to read.

Generate a Heart Beat

Now that Barc has an on/off button I need to know when he is awake. I am using the blue LED on the kit to tell me when he is active. Rather than simply turning on the LED I decided to blink it periodically within the main loop so I know my code is still running. Bad code happens. Especially late at night on hobby projects! I wanted the proof of life test to be written in firmware - it is easy to generate a heart beat from a PWM but that keeps running even when I write really broken software. So I added a simple loop_count variable and update the LED based on its value - it's a software PWM.

#define WAIT_DEBOUNCE_MS        (50)
#define HEARTBEAT_PERIOD        (20)
#define HEARTBEAT_DUTY_CYCLE    (1)

    /* Create an inverse heart beat (mostly on, briefly off) with the LED */
    Pin_Status_Write( ( loop_count > HEARTBEAT_DUTY_CYCLE ) ? LED_OFF : LED_ON );
        
    loop_count++;
    if( loop_count > HEARTBEAT_PERIOD )
    {
        loop_count = 0;                             // Reset the loop counter
    }
    CyDelay( WAIT_DEBOUNCE_MS );                    // Let the motors run for a while to prevent "jitter"

 

Read Sensors

This is a three-step process that calculates the state variable, used in the state machine below. I start by setting the default state to WAIT so that Barc will stay put if no hand is detected. Next I read the ADC to determine if there is a hand out in front and either leave the state alone or alter it to CHASE. The last step is to read the proximity sensors and, if they are active, set the state to LEFT, RIGHT or REVERSE. Note that there is an inherent priority here - if both the ADC and CapSense sensors detect a hand, the CapSense wins. If I were to swap the order of the code then the priority would be reversed. It all depends on whether you want a dog that chases a little more than it shies away, or the other way around.

typedef enum { WAIT, CHASE, REVERSE, LEFT, RIGHT } state_t;

state_t state;                                      // Result of sensor scans
int16 range = 0;                                    // ADC value

    /*
    Start sensing - default state is WAIT (do nothing)
    */
    state = WAIT;
        
    /* Get an ADC value from the distance sensor and convert it to millivolts */
    if( ADC_IR_Sensor_IsEndConversion( ADC_IR_Sensor_RETURN_STATUS ) )
    {
        range = ADC_IR_Sensor_GetResult16( IR_ADC_CHANNEL );
        range = ADC_IR_Sensor_CountsTo_mVolts( IR_ADC_CHANNEL, range );
        
        /* Change the state if above the proximity threshold */
        if( range > IR_ADC_THRESHOLD_MV )
        {
            state = CHASE;
        }
    }
        
    /*
    CapSense - detect a hand to the left, right or overhead
    */
    if( CapSense_NOT_BUSY == CapSense_IsBusy() )
    {
        int left, right;
            
        CapSense_ProcessAllWidgets();               // Get the scan results
        CapSense_RunTuner();                        // Tuning across EZI2C 
           
        /* Read the scan values */
        left = CapSense_IsWidgetActive( CapSense_LEFT_WDGT_ID );
        right = CapSense_IsWidgetActive( CapSense_RIGHT_WDGT_ID );
            
        /* Set the state if a hand is detected */
        if( left && right )
        {
            state = REVERSE;                        // Back up
        }
        else if( left )
        {
            state = RIGHT;                          // Turn away
        }
        else if( right )
        {
            state = LEFT;                           // Turn away
        }
      
        CapSense_ScanAllWidgets();                  // Start the next scan (non blocking call)
    }

 

State Machine

Once I have the state from the sensors it is really easy to control behavior. It's a simple switch statement that calls motor() to make Barc run, turn or back off. I toyed with the idea of running a buzzer in the REVERSE state to sound like a growl. But it turns out the only buzzer I had in the ever-filling shoe box of random electronic parts I am inevitably collecting wasn't louder than the running motors... so I'll shelve that idea for a while!

        /*
        State machine - act on the sensed state of the robot
        */
        switch( (int)state )
        {
            case RIGHT:
                /* reverse right motor, forward left */
                motor( SPEED_WALK_LEFT, -SPEED_WALK_RIGHT );
            break;
            
            case LEFT:
                /* reverse left motor, forward right */
                motor( -SPEED_WALK_LEFT, SPEED_WALK_RIGHT );
            break;
            
            case REVERSE:
                /* reverse both motors */
                motor( -SPEED_WALK_LEFT, -SPEED_WALK_RIGHT );
            break;
            
            case CHASE:
                /* forward both motors, fast */             
                motor( SPEED_RUN_LEFT, SPEED_RUN_RIGHT );
            break;
            
            case WAIT:
            default:
                /* stop both motors */
                motor( SPEED_STOP, SPEED_STOP );
            break;            
        }

Barc lives again! The whole program is less than 300 lines of quite thoroughly documented code (I will attach the project in my next blog) so I am quite pleased with the implementation. I think there are plenty of other enhancements I could make but I think the sensible thing to do is to finalize the CapSense proximity tuning so I can be sure he'll be ready to go to Embedded World.

 

Feb 01, 2018

PSoC 4200M Low Power with WDTs

[re-printed from iotexpert.com]

The last few weeks of my technical life have been frustrating.  Nothing and I mean nothing has been easy.  A few weeks ago I replied to a comment about cascading the watch dog timers in the PSoC 4200M to extend the sleep time – the article was called “PSoC 4200M WDT Long Deep Sleep“.  I wanted to measure power to see what exactly was happening.  But the great technical gods were conspiring against me and I had tons of problems.  Well I have things sorted out.  In this article I will build a project using the PSoC 4200M Watch Dog Timers and show the power measurements on the CY8CKIT-043 and CY8CKIT-044.  I will include PSoC 4200M low power measurements at 5V and 3.3V as well as with both the ILO and the WCO as the source of the WDT.  To do this I:

  1. Created a PSoC 4200M low power project
  2. Modified the CY8CKIT-043M
  3. Reject the Fluke 175
  4. Read the Keysite 34465A Multimeter Manual
  5. PSoC 4200M Low Power Current Measurement

 

PSoC 4 Low Power Firmware

First I create a schematic.  This includes 4 digital output pins.  The GREEN, RED and BLUE are attached to the tri-color LED.  A UART to print the status of the startup.  And an interrupt connected to the WDT Interrupt.

PSoC 4200M Low Power Schematic

The next thing to do is to make sure that the WDT is being driven by the ILO.  I also turn off the three WDTs because I will configure them in the firmware.

PSoC 4200M Low Power Clock Configuration

In order to get low power you need to switch the debugging pins off i.e. make them GPIOs.  You can do this on the DWR System Screen.

PSoC Creator Design-Wide Resources Editor (DWR)

Finally write some firmware.  The firmware simply

  1. Blinks the LED 10 times
  2. Prints “Started” to the UART, waits until the buffer is flushed, then turns off the UART to make sure the power is low.
  3. Configure the Watch Dog Timers.  This could have been done on the Low Frequency Clock Screen (except for the cascading of timers which had to be done with firmware)
  4. Turn off the Pin, Deep Sleep the chip… and wait.  When an interrupt from the WDT occurs, it calls the ISR (next section)

int main(void)
{
    trigger_Write(1); // Measure Startup
 
    // Blink the LED when the PSoC Starts
    for(int i=0;i<10;i++)
    {
        BLUE_Write(~BLUE_Read());
        CyDelay(200);
    }
    BLUE_Write(0);
    
    UART_Start();
    UART_UartPutString("Started\n");
    isr_1_StartEx(wdtInterruptHandler);
 
    while(UART_SpiUartGetTxBufferSize()); // Wait until the UART has flushed - then turn off the power
    UART_Stop();
 
    CySysWdtUnlock(); // Enable modification of the WDT Timers
    
    // Turn off the WDTs (all three of them)
    CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK);
    CySysWdtDisable(CY_SYS_WDT_COUNTER1_MASK);
    CySysWdtDisable(CY_SYS_WDT_COUNTER2_MASK);
    
    // Make Timer 0 & 1 run with no interrupt, and 2 cause an interrupt
    CySysWdtSetMode(CY_SYS_WDT_COUNTER0,CY_SYS_WDT_MODE_NONE);
    CySysWdtSetMode(CY_SYS_WDT_COUNTER1,CY_SYS_WDT_MODE_NONE);
    CySysWdtSetMode(CY_SYS_WDT_COUNTER2,CY_SYS_WDT_MODE_INT);
    
    // Set the time to 32768/(64*64*2^6) = 32768/(2^18) = 0.125 = 1/8 Hz
    CySysWdtSetMatch(CY_SYS_WDT_COUNTER0, 64);
    CySysWdtSetMatch(CY_SYS_WDT_COUNTER1, 64);
    CySysWdtSetToggleBit(6);
    
    CySysWdtSetClearOnMatch(CY_SYS_WDT_COUNTER0,1); // When counter his period reset counter
    CySysWdtSetClearOnMatch(CY_SYS_WDT_COUNTER1,1);
 
    CySysWdtSetCascade(CY_SYS_WDT_CASCADE_01 | CY_SYS_WDT_CASCADE_12); // Cascade 0-1-2
    CySysWdtEnable(CY_SYS_WDT_COUNTER0_MASK | CY_SYS_WDT_COUNTER1_MASK | CY_SYS_WDT_COUNTER2_MASK );
    
    trigger_Write(0); // End measurement
 
    CyGlobalIntEnable; /* Enable global interrupts. */
    
    for(;;)
    {
        trigger_Write(0); // setup trigger
        CySysPmDeepSleep(); // wait for interrupt in deep sleep
    }
}

The ISR first turns on the trigger pin (which I will use in the next article to trigger the oscilloscope).  Then it determines which timer caused the interrupt.  Originally I was blinking different color LEDs when I was trying to figure out what was happening.  With the current configuration WDT0 and WDT1 will never trigger an interrupt.  Finally it clears the interrupt and returns.  When it returns the main loop will turn off the trigger pin and then deep sleep.

void wdtInterruptHandler()
{
    trigger_Write(1); // Chip is woken up
    uint32 reason = CySysWdtGetInterruptSource();
    
    if(reason & CY_SYS_WDT_COUNTER0_INT)
        RED_Write(~RED_Read());
  
    if(reason & CY_SYS_WDT_COUNTER1_INT)
        GREEN_Write(~GREEN_Read());
  
    if(reason & CY_SYS_WDT_COUNTER2_INT)
        BLUE_Write(~BLUE_Read());
  
    CySysWdtClearInterrupt(reason);  // Clear the WDT Interrupt
}

Modified the CY8CKIT-043

In order to measure current with the. CY8CKIT-043 you need to remove the 0 ohm resistor R22 and install a jumper (J4) so that you can measure the current into the board.

CY8CKIT-043 Schematic

 

CY8CKIT-043 Schematic

R22 is on the back of the development kit.  You can see that I circled it.

CY8CKIT-043 modification for PSoC 4200M Low Power Measurement

Then install a jumper into J4. I  just use a two pin break away header.

CY8CKIT-043 modification for PSoC 4200M Low Power Measurement

Fluke 175

According to the AN 86233 PSoC® 4 and PSoC Analog Coprocessor Low-Power Modes and Power Reduction Techniques I should be able to get 1.3uA in deep sleep 1.3mA in active.

PSoC 4200M Low Power Specs

I started with my trusty Fluke 175… but quickly realized that uA are not in the cards.  It appears that the lowest it can measure is 10’s of microamps.

PSoC 4200M Low Power Measurement with Fluke

It does fine with the active mode.

PSoC 4200M Low Power Measurement with Fluke

Keysight 34465A 6 1/2 Digit Multimeter

The good news is that I also have a Keysight 34465A 6/12 digit multimeter.  In the 1uA range it can measure +- 0.055% of 1uA or about 5nA (is that right?)

Keysight 34655A Specs

Keysight 34665A Current Measurement Ranges

It also doesnt change the input power supply very much.  In fact only 0.0011v in 3.3v is negligible.

Keysight 34465A Drop Out Voltage

PSoC 4200M Low Power Current Measurement

I wanted to measure Active, Sleep, Deep Sleep on the CY8CKIT-043 and CY8CKIT-44 with the WCO on and off.  In the two pictures below you can see that the system is taking between 3.6uA and 4.2uA in Deep Sleep.

PSoC 4200M Low Power Measurement

PSoC 4200M Low Power Measurement

Here are tables with all of the measurements.

5V - CY8CKIT-044
Mode WCO On WCO Off
Active 12.860mA 12.859mA
Sleep 5.339mA 5.335mA
Deep Sleep 4.206uA 1.078uA

 

3.3V – CY8CKIT-044
Mode WCO On WCO Off
Active 12.84mA 12.84mA
Sleep 5.328mA 5.327mA
Deep Sleep 4.096uA 0.940uA

 

 

5V – CY8CKIT-043

Mode WCO Off
Active 12.711mA
Sleep 5.352mA
Deep Sleep 3.632uA

 

 

 

 

 

 

 

 

 

The other thing that is pretty cool with the KeySite is that you can put it into “Trend” mode and it can display the current readings over a long period.  In the picture below I am flashing the LED which takes around 3.5mA @ 3.3v on this development kit.

Keysight Trend mode

 

 
Jan 26, 2018

PSoC 4200M WDT Long Deep Sleep

[re-printed from iotexpert.com]

Earlier this year I wrote an article about using the PSoC 4200M WDT Watch Dog Timers.  In it I described a bunch of things that you could do with the PSoC 4200M WDT to help you time events, reset the part etc.  Recently a user named JAGIR asked if I could generate interrupts slower than 2 seconds and have the PSoC 4200M in deep sleep.  The answer to both of those questions is yes.

In order to make this work you need to “cascade” two WDTs together, something you can only do with firmware.  I have updated my previous workspace with a new project called “LongDeepSleep” which you can “Git” on my GitHub website or you can “git@github.com:iotexpert/PSoC4-WDTExamples.git”

Configure the PSoC 4200M WDT Design

First add a digital output pin called “RED” to the schematic

PSoC 4200M WDT Schematic

Then assign it to P0[6]

PSoC 4200M WDT - DWR Pin Assignment

Go to the clocks tab of the design wide resources.  Then click on “Edit Clock”.

PSoC 4200M WDT Clocks

On the Low Frequency Clocks configuration page of the design wide resources turn on the two WDTs and setup the dividers.

PSoC 4200M WDT

System Reference Guide – PSoC 4200M WDT

All of the documentation for the PSoC 4200M WDT is in the “Low Frequency Clock aka cy_lfclk” of the system resources documentations.

PSoC 4200M WDT System Reference Guide

When you read a little bit down in the PSoC 4 Low Frequency clock documentation you will find “Note The EDT cascade options are not configurable using these panels but the APIs can be used to perform cascading of WDTs”

PSoC 4200M WDT

Then you search a little bit further down in the document and you will find the API CySysWDTSetCascade which will allow you to hook multiple 16-bit counters together to get 32 or more bits.

PSoC 4200M WDT - CySysWdtSetCascade

Write the PSoC 4200M WDT Firmware

The first example will blink the LED every 4 seconds.  Remember from the configuration above it has a 32KHz input clock with a divider of 1024 on WDT0 and a divider of 128 on WDT1.  That means you will get 32KHz / (1024 *128) = 0.25 Hz aka every 4 seconds.

#include "project.h"
 
void wdtCallback()
{
    RED_Write(~RED_Read());
    
}
 
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    CySysWdtSetInterruptCallback(CY_SYS_WDT_COUNTER1,wdtCallback);
    CySysWdtSetCascade(CY_SYS_WDT_CASCADE_01);
    
    for(;;)
    {
        CySysPmDeepSleep();
    }
}

You can also get different delays by changing the dividers using firmware.  In the example below it is 1Hz output.

#include "project.h"
 
void wdtCallback()
{
    RED_Write(~RED_Read());
}
 
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    CySysWdtSetInterruptCallback(CY_SYS_WDT_COUNTER1,wdtCallback);
    CySysWdtSetCascade(CY_SYS_WDT_CASCADE_01);
    
    CySysWdtSetMatch(0,512);
    CySysWdtSetMatch(1,64);
    // Total divide = 512*64 = 32768
    
    for(;;)
    {
        CySysPmDeepSleep();
    }
}

 

Jan 16, 2018

Serial Wire View with PSoC 4

[re-printed from iotexpert.com]

Self-confessed semiconductor geek and Roman historian, Darrin Vallis guest-blogs on Alan's iotexpert site about ARM's SWV feature.

I use PSoC 4 to invent all kinds of unique solutions for customers. Usually, they want them field upgradeable to deploy new features or fix bugs. Fortunately Cypress has a great I2C boot loader to meet this need, so I use the heck out of it.

Cypress has a great debugger built into PSoC Creator which fully supports all the ARM Serial Wire Debug protocols such as breakpoints, single step, memory, register viewing etc. However, when you are running a boot loader the debugger does not work! Why not? Because with a boot loader there are two applications resident in PSoC 4: The boot loader and application. This is not supported by Cypress implementation of SWD.

I2C Communication

Where does this leave you, the intrepid code developer, when debugging a boot loader project? Personally, I have used all kinds of methods: debug UART interface, debug I2C interface, bang out states on pins, debug Bluetooth interface … and on and on. You get the idea. All these methods burn a communications interface and require extra pins on the chip. Sometimes that’s not possible.

The issue recently came to a head when a customer very nearly in production experienced a boot loader failure. One system out of a few thousand was “bricked” when they tried to field  update in the lab. Their pinout is frozen, they can’t add new hardware so how do we look inside PSoC 4 and see what’s going on?

I woke up at 2 AM and thought “Ah Ha! SWV!” (Yes, I Am A Geek) Serial Wire View is an ARM native debug protocol that let’s you XRAY the insides of any ARM MCU with the right interface. SWV is a protocol which runs on the SWD pins (clock and data) but also needs the Serial Wire Output (SWO) pin. Cypress left the SWO pin and associated IP off of PSoC 4 to save die cost, foiling my great idea. Brief interlude to drink and bang head on desk.

ARM Coresight Achitecture

Fortunately, I don’t give up easily. At least my subconscious does not. Woke up the next night thinking “Ah Ha!” again. Wife was mildly annoyed, but tolerates my idiosyncrasies.

Cypress has a nice software UART transmitter implementation. I shamelessly stole it, modified for my purposes and created a custom component. (It’s pretty easy to do this by the way) Baud rate was modified to 230 KBps and the output pin forced to a specific pin with a control file.

Custom DTView component

Once the component is in place, you can use its _DView_Printf( ) API call to display any debug data. Here is an example:

DTView_Printf()

More about that output pin. Cypress sells a tool for programming and debugging PSoC called CY8CKIT-002, aka MiniProg3. The programming connector consists of VDD, GND, reset, SWD clock and SWD data as shown below.

Cypress Miniprog3 connections

Since we can’t use SWD protocol for debugging anyway, we can change the pins from SWD to normal GPIO. The pins still function for programming. By default they are in SWD mode as shown.

PSoC debug pins

Going to the system tab of the .CYDWR file, we can change them to GPIO.

PSoC debug pins reused as GPIO

Once we do that, the pins look like this. Here’s the trick. We now assign the TX output of our DTView component to pin 3[2], which is available on the SWD programming header, pin 5.

PSoC debug pins reused as GPIO

Can you see where we are going with this? Printf( ) data is now coming out of PSoC 4 on pin 3[2], easily accessible on our debug header. This is where MiniProg3 comes in. It can actually receive data as a 230 KBps RX UART on its XRES pin. Weird, right? By building a simple interface cable we can get the data from your debug header into MiniProg3.

MiniProg3 XRES —— SWD HEADER pin 5

MiniProg3   GND —— SWD HEADER pin 2

Hand-built SWV cable!

However, MiniProg3 does not show up as a COM port on your PC, so how do we the data? It needs to be accessed by a host application running the PP_COM API. This is documented under PSoC Programmer Component Object Model COM Interface Guide, Cypress specification 001-45209. If you installed PSoC Creator or Programmer, this document is actually on your PC under C:\Program Files (x86)\Cypress\Programmer\Documents. Engineers don’t like to read instructions. Amazing what you can find when you do.

I wrote a simple  console application which opens MiniProg3 using PP_COM, retrieves data from the serial RX pin via USB and displays it like a simple terminal program. Voila! You now have a serial debugger that works for any PSOC4 project using MiniProg3 as your USB to serial dongle.

Customer was really happy with this. We were able to immediately see his problem and fixed it in about 5 minutes.

DTViewer serial terminal emulator

Finally, here are all the source files

DTView Firmware : PSOC Creator example project and DTView component

DTViewer Binary : Installer for DTViewer console

ViewerSource : Complete source code for DTViewer console (Requires Visual Studio 2015)

That’s all. Have fun with the new debugging tool.

DTV

 

ALL CONTENT AND MATERIALS ON THIS SITE ARE PROVIDED "AS IS". CYPRESS SEMICONDUCTOR AND ITS RESPECTIVE SUPPLIERS MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THESE MATERIALS FOR ANY PURPOSE AND DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THESE MATERIALS, INCLUDING BUT NOT LIMITED TO, ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT OF ANY THIRD PARTY INTELLECTUAL PROPERTY RIGHT. NO LICENSE, EITHER EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, IS GRANTED BY CYPRESS SEMICONDUCTOR. USE OF THE INFORMATION ON THIS SITE MAY REQUIRE A LICENSE FROM A THIRD PARTY, OR A LICENSE FROM CYPRESS SEMICONDUCTOR.

Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms and Conditions of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms and Conditions of this site. Cypress Semiconductor and its suppliers reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.