Percepio Tracealyzer RTT Streamport – PSoC4200M

Summary

In the last article I showed you how to install the Percepio Tracealyzer into your PSoC FreeRTOS project using snapshot mode.   This mode is very convenient as it has little CPU impact and does not require a communication interface.  But your trace it is limited in record time by how much RAM you are willing to dedicate to the trace.   For me, this was not very much because I am using a small PSoC with limited RAM.  In this article I am going to show you how to use the Segger JLink RTT library to support the Tracealyzer RTT Streamport.  With this library built in, the RTOS events will be sent in real-time to the Pecepio Tracealyzer for analysis.

JLink Real Time Transfer (RTT) Library

The engineers at Segger had a great idea.  Really great.  Given that they had direct access to the memory of the ARM M0 (for programming) that could be read and written without CPU intervention via the Coresite Debug Access Port, wouldn’t it be nice if there was a simple way to “send” and “receive” data without using another communication peripheral on the device.

So they built the Segger Real Time Transfer (RTT) Library which works with their JLink.  Here is a picture which I got from their website.

Segger JLink RTT

I will talk more about this library in a future article.

Installing the Percepio Tracealyzer RTT Streamport

I decided to use a copy of the blinking led project (called 1-BlinkingLED) to start the new project.  To copy the project just use CTRL-C and CTRL-V then rename the project to 1-BlinkingLED_RTT.

To make all of this work you need to add the streaming include files for the Tracealyzer RTT Streamport by right clicking “Add–>Exiting Item…”

PSoC Creator Add Existing

Then selecting “TraceRecorder–>streamports–>Jlink_RTT_include”

PSoC Creator Add Existing

Next you need to update the include path for the project by right clicking on the project and selecting “Build Settings”

PSoC Creator Change Build Settings

Then add the path to the TraceRecorder\streamports\JLink_RTT\include

PSoC Creator Update Include Path for Tracelyzer RTT Streamport

Next, add the .c files for the RTT library.

PSoC Creator - Add Tracelyzer RTT Streamport Library

Now that all of the required files are part of your project, you need to modify the “trcConfig.h” to switch to the stream recording mode (line 102).

/*******************************************************************************
 * Configuration Macro: TRC_CFG_RECORDER_MODE
 *
 * Specify what recording mode to use. Snapshot means that the data is saved in
 * an internal RAM buffer, for later upload. Streaming means that the data is
 * transferred continuously to the host PC. 
 *
 * For more information, see http://percepio.com/2016/10/05/rtos-tracing/
 * and the Tracealyzer User Manual.
 *
 * Values:
 * TRC_RECORDER_MODE_SNAPSHOT
 * TRC_RECORDER_MODE_STREAMING
 ******************************************************************************/
//#define TRC_CFG_RECORDER_MODE TRC_RECORDER_MODE_SNAPSHOT
#define TRC_CFG_RECORDER_MODE TRC_RECORDER_MODE_STREAMING

My PSoC 4M only has 16K of SRAM.  So, I reduce the memory footprint of the streaming buffer by changing it to 1000 byte from 5000 bytes in trcStreamingPort.h.

/*******************************************************************************
 * Configuration Macro: TRC_CFG_RTT_BUFFER_SIZE_UP
 *
 * Defines the size of the "up" RTT buffer (target -> host) to use for writing
 * the trace data, for RTT buffer 1 or higher.
 *
 * This setting is ignored for RTT buffer 0, which can't be reconfigured
 * in runtime and therefore hard-coded to use the defines in SEGGER_RTT_Conf.h.
 *
 * Default buffer size for Tracealyzer is 5000 bytes. 
 *
 * If you have a stand-alone J-Link probe, the can be decreased to around 1 KB.
 * But integrated J-Link OB interfaces are slower and needs about 5-10 KB, 
 * depending on the amount of data produced.
 ******************************************************************************/
//#define TRC_CFG_RTT_BUFFER_SIZE_UP 5000
#define TRC_CFG_RTT_BUFFER_SIZE_UP 1000

Run Tracealyzer

After the project is updated for the Tracealyzer RTT Streamport, I can program it into my CY8CKIT-044 development kit.  The next step is to start the Tracealyzer.  If you have previously run the program it will use your previous J-Link settings.  If you have not run it before then you need to setup the J-Link Settings

Percepio Tracelyzer

I am using a CY8CKIT-044 which has a CY8C4247xxx PSoC 4200M MCU onboard.

Configure JLink for Tracelyzer RTT Streamport

I use the default settings for the Segger RTT.

Tracelyzer RTT Streamport Stream Trace Settings

Once things are setup you can “Connect to Target System…”

Percepio Tracelyzer

Then click “Start Recording”

Percepio Tracelyzer

As you are recording the Tracealyzer shows the CPU usage.  In this case it is very close to 0% as all the program does is blink the LED and then sleep for 500ms.

Percepio Tracelyzer Recording

After I stop recording I can look at the trace.   The first thing to notice in this trace is that I can look at quite a bit more data.  In this case I recorded for about 22 seconds.

Percepio Tracelyzer

In the next article I am planning on doing a Streamport based on the PSoC UART which means I will not need to use a JLink.

As always you can find all of these projects on the IotExpert GitHub site or git@github.com:iotexpert/PSoC-Tracelyzer.git

Article Description
Percepio Tracealyzer & PSoC An Introduction to Percepio Tracealyzer on the Cypress PSoC
Percepio Tracealyzer RTT Streamport - PSoC4200M Make the JLINK RTT Library work with Tracealyzer
Percepio Tracealyzer PSoC UART Streamport Creating a UART Streamport
Percepio Tracealyzer - Analyzing the PSoC Tracealyzer Streamport Figure out what is broken in the UART Streamport
Percepio Tracealyzer - Using PSoC DMA to Fix the UART Streamport Implementing PSoC DMA to improve the CPU Utilization
Percepio Tracealyzer - Running on PSoC6 Porting the Percepio Tracealyzer to PSoC6

Percepio Tracealyzer & PSoC 4200M

Summary

As you have probably noticed, I have spent a significant amount of time in the last few months doing FreeRTOS projects.  One thing that I have been continuously frustrated about is a lack of analysis tools.  How much memory am I using?  How much stack and heap are free?  How long are tasks taking?  How do you get the priorities set right?  And on and on.  A couple of weeks ago I got an email from the guys at Percepio asking me if I wanted to try out the Percepio Tracealyzer.  I said yes… but that I was really busy and it was going to take a while for me to get to it.  So now I have… and this is the first article about the Percepio Tracealyzer.  Unfortunately the story involves me burning myself with a soldering iron but I suppose that isn’t their fault.

In this series of article’s I will instrument the FreeRTOS projects that I have been showing you with the Percepio Tracealyzer Recorder Library, then run the tool to see what is going on.  In this specific Article I will show you how to get the most basic Tracealyzer functionality going on a PSoC4200M.

Percepio Tracealyzer

The way that Percepio Tracelzer works is that you install a bit of code into your project called the Tracealyzer Recorder Library.   In streaming mode, the code creates a new task in FreeRTOS called “TzCtrl” which interacts with the FreeRTOS kernel to record all of the running task information into a communication link (e.g UART, SPI, JLINK RTT, etc.)  In snapshot mode, the kernel information is just written to an SRAM buffer inside of the PSoC.

This is the architecture picture from the Percepio website:

Percepio Tracelyzer Architecture

Updating CY8CKIT-044 PSoC4200M Development Kit

To make the Percepio Tracealyzer work you need to turn the “Snapshot or streaming” line from the picture above into a real connection.  In snapshot mode there are two ways (I think) that you can do it.

  1. You can stop the processor with the debugger, then write the region of memory with the trace buffer into an “.hex” or a “.bin” file.  Then read that file into Percepio Tracealyzer.  Unfortunately the debugger in PSoC Creator cannot create binary output files of regions of memory like that… so plan 1 is out the window
  2. You can attach a Segger JLINK to the PSoC via SWD.  Then, Tracealyzer knows how to talk to the JLink to read the memory.  This is what I am going to do for this Article.

One feature that is very cool about the Snapshot methodology is that Tracealyzer can search the output file and find the trace buffer.  The trace buffer data structure is marked by something that Tracealyzer can find.

When our very good devkit team team in India built the CY8CKIT-044 (which I have used and written about quite a bit), they installed a built in debugger/programmer called “KitProg“.  But they also understood that someone might want to connect some third part debugger like the JLINK.  So… they put the footprint for a 10-pin ARM debugging port onto the board. (see it in the lower left).  Actually if you look at the board, you can see two 10-pin footprints, the one at the top is connected to the PSoC5 (which serves as the KitProg).

CY8CKIT-044 PSoC 4200M Development Kit

Before I can use a JLINK I needed to do this … which unfortunately ended with me burning myself with the soldering iron… bad Alan.

CY8CKIT-044 PSoC 4200M Development Kit

Tracealyzer Snapshot Mode on Blinking LED Project

In a previous article I talked about the organization of PSoC Creator projects, specifically how to handle the situation where you want to include .h/.c files into your project but you don’t want to modify them.  This is exactly what I will do with the Percepio Tracealyzer Recorder Library.  The library has some files which I will want to leave intact and I will just make references to them in my project including trcKernelPort.h, trcPortDefines.h etc.  And there are some files which I will want to modify, which I will copy out of the Trace Recorder Library into my project for editing trcConfig.h, trcSnapShotConfig.h and trcStreamingConfig.h

To start the first Percepio Tracealyzer example I will use the basic “Snapshot Mode”.  There is good documentation on the Percepio Tracealyzer documentation website.  I will start by copying the PSoC Creator Workspace from the FreeRTOS articles from GitHub.  It has 9 projects in it, starting with the blinking led, going to a more complicated multi-thread project.  The next step is to download the FreeRTOS Tracealyzer library from the Perceio website into a parallel directory called “TraceRecorder”.

Make a new folder for the non-changing include files called “TraceRecorder” (for the files that I don’t want to change)

PSoC Creator Project Configuration

Next I will add the files that I don’t want to change to the project so that I can see them (meaning they are just referenced by PSoC Creator)

PSoC Creator Project Configuration - Add External Files

They reside in the directory TraceRecorder/include

PSoC Creator Project Configuration - Add External Files

Once I have the unchanging .h’s into my project, I need to add their directory to the include path.  To do this right-click on the project and select “Build Settings …”

PSoC Creator Project Configuration - Add External Files

Then click “Compiler” and then the “…” on the “Additional Include Directories” line

PSoC Creator Project Configuration - Compiler Options

Navigate until you get to TraceRecoder/include then press “Select Folder”

PSoC Creator Project Configuration - Update include path

Now your include path should look like this:

PSoC Creator Project Configuration - Include Path

Next, I want to copy the configuration files in the Windows Explorer to my project directory so that they become a changeable part of they project.  They reside in TraceRecorder/config (I just did a Ctrl-C to copy)

PSoC Creator Project Configuration - Add Trace Recorder Library

You want to paste them into your project directory

PSoC Creator Project Configuration - Add External Files

Then you need to add them to the project so that you can easily edit them by right clicking on the “Header Files” folder and selecting “Add–>Existing Item”

PSoC Creator Project Configuration - Add External Files

Then selecting them out of your project directory.

PSoC Creator Project Configuration - Add External Files

Now we need to add references to all of the .c files.  Start by making a new folder for the c-files called “TraceRecorder”

PSoC Creator Project Configuration - Add TraceRecorder

Then “Add–>Existing Item…”

PSoC Creator Project Configuration - Add External Files

Select the files from the TraceRecorder directory

PSoC Creator Project Configuration - Add TraceRecorder Files

Now your project should look like this

PSoC Creator Project Configuration - Project Configuration

Your project now has all of the necessary body parts so you next will modify the source files.  Start with modifying FreeRTOSConfig.h to include the TraceRecorder Library stuff.

/* Integrates the Tracealyzer recorder with FreeRTOS */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#endif

Then modify the trcConfig.h so that it has access to all of the CMSIS header files.

/******************************************************************************
 * Include of processor header file
 * 
 * Here you may need to include the header file for your processor. This is 
 * required at least for the ARM Cortex-M port, that uses the ARM CMSIS API.
 * Try that in case of build problems. Otherwise, remove the #error line below.
 *****************************************************************************/
//#error "Trace Recorder: Please include your processor's header file here and remove this line."
#include <project.h>

Then tell the TraceRecorder that we are using an ARM Cortex-M

/*******************************************************************************
 * Configuration Macro: TRC_CFG_HARDWARE_PORT
 *
 * Specify what hardware port to use (i.e., the "timestamping driver").
 *
 * All ARM Cortex-M MCUs are supported by "TRC_HARDWARE_PORT_ARM_Cortex_M".
 * This port uses the DWT cycle counter for Cortex-M3/M4/M7 devices, which is
 * available on most such devices. In case your device don't have DWT support,
 * you will get an error message opening the trace. In that case, you may 
 * force the recorder to use SysTick timestamping instead, using this define:
 *
 * #define TRC_CFG_ARM_CM_USE_SYSTICK
 *
 * For ARM Cortex-M0/M0+ devices, SysTick mode is used automatically.
 *
 * See trcHardwarePort.h for available ports and information on how to 
 * define your own port, if not already present.
 ******************************************************************************/
//#define TRC_CFG_HARDWARE_PORT TRC_HARDWARE_PORT_NOT_SET
#define TRC_CFG_HARDWARE_PORT TRC_HARDWARE_PORT_ARM_Cortex_M

Now, you need to modify the project’s stack and heap size setup

PSoC Creator Project Configuration - Design Wide Resources

The default snapshot buffer is to big to fit into PSoC SRAM, so I change the size to 400 records

/*******************************************************************************
 * TRC_CFG_EVENT_BUFFER_SIZE
 *
 * Macro which should be defined as an integer value.
 *
 * This defines the capacity of the event buffer, i.e., the number of records
 * it may store. Most events use one record (4 byte), although some events 
 * require multiple 4-byte records. You should adjust this to the amount of RAM
 * available in the target system.
 * 
 * Default value is 1000, which means that 4000 bytes is allocated for the
 * event buffer.
 ******************************************************************************/
#define TRC_CFG_EVENT_BUFFER_SIZE 400

Finally modify main to start the Trace Recorder (vTraceEnable on line 40)

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    
    setupFreeRTOS();
    vTraceEnable(TRC_START);

    /* Create LED task, which will blink the RED LED */
    xTaskCreate(
        LED_Task,       /* Task function */
        "LED Blink",    /* Task name (string) */
        200,            /* Task stack, allocated from heap */
        0,              /* No param passed to task function */
        1,              /* Low priority */
        0 );            /* Not using the task handle */
      
    vTaskStartScheduler();
    while(1); // get rid of the stupid warning
}

Testing the Output

After all of the setup is done you can build and program the development kit.

PSoC Creator Program Project

Plug in the Segger J-Link

CY8CKIT-044 + JLINK

Start Tracealyzer then setup the J-Link (click on J-Link Settings…)

Percepio Tracelyzer

Select “Select Device…”

Percepio Tracelyzer - JLINK Settings

Filter the list to “cypress” then choose the “CY8C4247xxx” (which is the chip that is on the C8CKIT-044)

Percepio Tracelyzer - JLINK Settings Select Device

Then Select “Jink->Read Trace”

Percepio Tracelyzer - Read Trace

Accept the defaults (this window only is only brought up the first time)

Percepio Tracelyzer - JLINK Memory Region

Then you will end up with a screen like this.  Each stripe of white/gray is 1 second in time.  The yellow line is where the startup occurred.  The red lines are where the “LED Blink” task was running.  This seems to make sense as you get a tiny red line 2x/second (remember in the source code we have a 500ms delay so that makes sense).

Percepio Tracelyzer - Console

Next I press the reset button on the devkit and re-run the trace.  Then I click on the second red line and the window on the right gives me information about that task.  You can see that the task ran for 53uS and took 23uS to start (time taken up in the FreeRTOS scheduler and Percepio Tracealyzer Recorder Library)

Percepio Tracelyzer - Console

Conclusion

Obviously the snapshot mode is limited by the size of the RAM buffer that you allocate but it saves you the hardware resources and timing constraints that are required to stream FreeRTOS data.  In the next several articles I will show you how to:

  • Use Segger JLINK RTT streaming
  • Make a streaming port
  • Use some of the other screens in the Percepio Tracealyzer

As always you can find all of these projects on the IotExpert GitHub site or git@github.com:iotexpert/PSoC-Tracelyzer.git

Article Description
Percepio Tracealyzer & PSoC An Introduction to Percepio Tracealyzer on the Cypress PSoC
Percepio Tracealyzer RTT Streamport - PSoC4200M Make the JLINK RTT Library work with Tracealyzer
Percepio Tracealyzer PSoC UART Streamport Creating a UART Streamport
Percepio Tracealyzer - Analyzing the PSoC Tracealyzer Streamport Figure out what is broken in the UART Streamport
Percepio Tracealyzer - Using PSoC DMA to Fix the UART Streamport Implementing PSoC DMA to improve the CPU Utilization
Percepio Tracealyzer - Running on PSoC6 Porting the Percepio Tracealyzer to PSoC6

PSoC FreeRTOS & Kionix KXTJ2 Accelerometer

Summary

In the previous PSoC FreeRTOS article I showed you a scheme for sharing the I2C between multiple tasks by sending “I2C Transactions” to a single I2C Master thread.  In this article I will modify the Accelerometer task to use the hardware interrupt pin from the KXTJ2-1009 to only read data when there is new data to be read (i.e. stop polling).  I will also modify the project to use “kxtj2.h” a header file which I downloaded from Rohm’s website with all of the register definitions.

Updates to the PSoC FreeRTOS Project Schematics

When I was looking at the schematics for the CY8CKIT-044 I noticed that the KXTJ2-1009 had one of the pins attached to the PSoC P1[6].  Even better that pin was labeled “INT”.  In the original PSoC FreeRTOS project I polled the values from the accelerometer, which is almost always a bad idea.

 

CY8CKIT-044 Schematic

Then when you look in the data sheet you see that you ask for an interrupt when the data is ready “DRDYE”

KTXJ2-1009 Datasheet

And you can program the INT pin to behave how you want.  I chose:

  • IEN=1 (Enable interrupts)
  • IEA=1 (Make the interrupt active high)
  • IEL=1 (Pulse the pin)

KTXJ2-1009 Datasheet

After I figured all of that out, I modify the PSoC FreeRTOS Creator schematic to have the accel_int_pin (aka P1[6]) attached to an interrupt.

PSoC FreeRTOS Schematic

Then I write an ISR to handle the interrupt, which just resets the pin interrupt and sets a semaphore.

CY_ISR(accelIntHandler)
{
    accel_int_pin_ClearInterrupt();
    xSemaphoreGiveFromISR(accelIntSemaphore,NULL);
}

Finally register the interrupt and the start the semaphore

   accelIntSemaphore = xSemaphoreCreateBinary(); // Signal from the Accel that it is ready
   accel_int_StartEx(accelIntHandler);

Using the “kxtj2.h” Header File

When I looked on the Kionix website for the KXTJ2-1009 datasheet I noticed that there was a download link… which I assumed would take me to a place where I could download the datasheet.

KTXJ2-1009 Datasheet

But when I go to the download page I got the  pleasant surprise of finding a bunch of driver files… but even better there was a “Register Definitions .h/.py”

KTXJ2-1009 Datasheet Download

This file had a bunch of #defines for the registers in the chip.

/* registers */
// output register x
#define KXTJ2_OUTX_L 0x06
#define KXTJ2_OUTX_H 0x07
// output register y
#define KXTJ2_OUTY_L 0x08
#define KXTJ2_OUTY_H 0x09
// output register z
#define KXTJ2_OUTZ_L 0x0A
#define KXTJ2_OUTZ_H 0x0B
// This register can be used to verify proper integrated circuit functionality
#define KXTJ2_DCST_RESP 0x0C
// This register can be used for supplier recognition, as it can be factory written to a known byte value.
#define KXTJ2_WHO_AM_I 0x0F
// This register reports which function caused an interrupt.
#define KXTJ2_INT_SOURCE1 0x16
// This register reports the axis and direction of detected motion
#define KXTJ2_INT_SOURCE2 0x17
// This register reports the status of the interrupt

In addition it has the the bit fields for the registers

// 12.5Hz
#define KXTJ2_DATA_CTRL_REG_OSA_12P5 (0x00 << 0)
// 25Hz
#define KXTJ2_DATA_CTRL_REG_OSA_25 (0x01 << 0)
// 50Hz
#define KXTJ2_DATA_CTRL_REG_OSA_50 (0x02 << 0)
// 100Hz
#define KXTJ2_DATA_CTRL_REG_OSA_100 (0x03 << 0)
// 200Hz
#define KXTJ2_DATA_CTRL_REG_OSA_200 (0x04 << 0)
// 400Hz
#define KXTJ2_DATA_CTRL_REG_OSA_400 (0x05 << 0)
// 800Hz
#define KXTJ2_DATA_CTRL_REG_OSA_800 (0x06 << 0)
// 1600Hz
#define KXTJ2_DATA_CTRL_REG_OSA_1600 (0x07 << 0)
// 0.78Hz
#define KXTJ2_DATA_CTRL_REG_OSA_0P781 (0x08 << 0)
// 1.563Hz
#define KXTJ2_DATA_CTRL_REG_OSA_1P563 (0x09 << 0)
// 3.125Hz
#define KXTJ2_DATA_CTRL_REG_OSA_3P125 (0x0A << 0)
// 6.25Hz
#define KXTJ2_DATA_CTRL_REG_OSA_6P25 (0x0B << 0)

To use it all I did was add it to my project by right clicking on Header Files and selecting “Add–>Existing Item”

Add header file to PSoC FreeRTOS Project

Then I add the include “ktxk2.h” to the includes

#include "kxtj2.h"

Finally I can use the #define names instead of the hardcoded values for example to reset the chip you can write to the KXTJ2_CTRL_REG with the value  KXTJ2_CTRL_REG _SRST

    setupData[0] = KXTJ2_CTRL_REG2_SRST; // Software reset
    mytransaction.i2cm_register = KXTJ2_CTRL_REG2; 

Updates to the Accelerometer Task

With all of the updates in place I can now make the modifications to the PSoC FreeRTOS accel_Task to use the new interrupt structure.  The first thing to do is write into the reset register to force a software reboot.  The datasheet says that you need to:

  • Write 0x00 into the CTRL_REG1
  • Write KXTJ2_CTRL_REG2_SRST into KXTJ2_CTRL_REG2
  • Then WAIT until the value in KXTJ2_CTRL_REG2 is 0x0
    // Setup for initialization
    uint8_t setupData[1];
    mytransaction.i2cm_method = I2CM_WRITE;
    mytransaction.i2cm_byteNum = 1;
    mytransaction.i2cm_doneSemaphore = mySemaphore;
    mytransaction.i2cm_bytes = setupData;
    
    // reset the accelerometer
    setupData[0] = 0b00000000; 
    mytransaction.i2cm_register = KXTJ2_CTRL_REG1; 
    i2cm_runTransaction(&mytransaction);
  
    setupData[0] = KXTJ2_CTRL_REG2_SRST; // Software reset
    mytransaction.i2cm_register = KXTJ2_CTRL_REG2; 
    i2cm_runTransaction(&mytransaction);

    vTaskDelay(10); // From the datasheet power up time

    while(1) // delay until the accelerometer is reset
    {
        mytransaction.i2cm_method = I2CM_READ;  
        mytransaction.i2cm_register = KXTJ2_CTRL_REG2;
        i2cm_runTransaction(&mytransaction);
        
        if(setupData[0] & KXTJ2_CTRL_REG2_SRST)
        {
            vTaskDelay(10); // backup 10 ms
        }
        else
            break;  
    }
    

After the accelerometer chip is reset, then initialize the chip

    // initialize the interrupt 
    setupData[0] = KXTJ2_INT_CTRL_REG1_IEN | KXTJ2_INT_CTRL_REG1_IEA | KXTJ2_INT_CTRL_REG1_IEL;
    mytransaction.i2cm_register = KXTJ2_INT_CTRL_REG1; 
    i2cm_runTransaction(&mytransaction);
  
    // initialize the  DATA_CTRL_REG
    setupData[0] = KXTJ2_DATA_CTRL_REG_OSA_12P5;      
    mytransaction.i2cm_register = KXTJ2_DATA_CTRL_REG; 
    i2cm_runTransaction(&mytransaction);
 
    // Initalize the acceleromter CTRL_REG_1
    setupData[0] =   KXTJ2_CTRL_REG1_PC | KXTJ2_CTRL_REG1_RES | KXTJ2_CTRL_REG1_DRDYE | KXTJ2_CTRL_REG1_GSEL_2G  ; 
    mytransaction.i2cm_register = KXTJ2_CTRL_REG1; 
    i2cm_runTransaction(&mytransaction);
    
    // Setup for repeated reads
    mytransaction.i2cm_method = I2CM_READ;
    mytransaction.i2cm_register = 0x06;
    mytransaction.i2cm_byteNum = sizeof(myData);
    mytransaction.i2cm_doneSemaphore = mySemaphore;
    mytransaction.i2cm_bytes = (uint8_t *)&myData;

After that you can wait for the semaphore, then get the data

    while(1)  // Run the same transaction over and over
    {
        xSemaphoreTake(accelIntSemaphore,portMAX_DELAY);
        
        i2cm_runTransaction(&mytransaction);
        myData.x /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
        myData.y /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
        myData.z /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
        xs = (myData.x < 0? -1:1);  // Find the XSign
        ys = (myData.y < 0? -1:1);  // Find the YSign
        zs = (myData.z < 0? -1:1);  // Find the ZSign
            
        // The 1024 is a hardcode based on the range of the acceleromter
        x = (int)((float)myData.x / 1024.0 * 10.0) * xs; // Turn it into integer G * 10
        y = (int)((float)myData.y / 1024.0 * 10.0) * ys; // Turn it into integer G * 10
        z = (int)((float)myData.z / 1024.0 * 10.0) * zs; // Turn it into integer G * 10
       
        // Format data in x.x  
        sprintf(buff,"x=%s%d.%dg\ty=%s%d.%dg\tz=%s%d.%dg\n",(xs<0?"-":""),x/10,x%10,(ys<0?"-":""),y/10,y%10,(zs<0?"-":""),z/10,z%10);
        UART_UartPutString(buff); 
    }

As always you can git this PSoC FreeRTOS project at the IoT Expert GitHub site or git@github.com:iotexpert/PSoC-FreeRTOS-Examples.git The project name is “8-Shared-I2C_Interrupt”

Topic Description
FreeRTOS: A PSoC4 FreeRTOS Port An introduction to making FreeRTOS work on PSoC 4
FreeRTOS PSoC Examples Using multiple tasks in FreeRTOS
FreeRTOS Queue Example Using a queue to communicate between tasks
PSoC 6 FreeRTOS - The First Example Booting FreeRTOS on PSoC 6
FreeRTOS Binary Semaphore An first example of a binary semaphore
FreeRTOS Binary Semaphore (Part 2) Removing polling in the UART Task
FreeRTOS Counting Semaphore An example using a counting semaphore
PSoC FreeRTOS Reading I2C Sensors with a shared I2C Bus
PSoC FreeRTOS Task Notify A light weight scheme to replace Semaphores
PSoC FreeRTOS Task Notification Values A very light weight method to transfer one word of information into a task

PSoC FreeRTOS – Sharing the I2C Bus

Summary

I have gotten quite a few questions about my PSoC FreeRTOS series of Articles.  One question was: “I have a bunch of different I2C sensors, what is a good PSoC FreeRTOS scheme to share the I2C bus?”  When I thought about this question there were a number of possible firmware architectures that could be used:

  1. Each sensor has a thread, and there is a global Mutex to lock/unlock the I2C bus
  2. There is single thread that reads and writes to all of the sensors (oh…wait… that looks like the old bare metal while(1); )
  3. There is a single I2C thread that can take requests execute them, and then feed data back to the calling thread

After thinking about it a bit, I decided to use the 3rd scheme as it allows me to

  • Reuse the I2C interface code (all the sensors have a register map that you read/write)
  • Handle all of the timing issues in one place (instead of duplicated)
  • Isolate the I2C interface from all of the threads (could be replaced with SPI or something else later)

In this article I will show you:

  • The register based I2C interface that is in common use with sensors
  • The design of the PSoC FreeRTOS firmware with a single I2C interface thread
  • The PSoC FreeRTOS firmware implementation
  • A test case using the CY8CKIT-044

Interfacing with I2C Sensor Registers using PSoC FreeRTOS

The CY8CKIT-044 (on of my favorite Cypress kits) has two devices attached to the I2C bus, so it made a perfect test platform.  On the front there is a Cypress FM24V10 FRAM and a Kionix KXTJ2-1009 I2C Accelerometer.

CY8CKIT-044 Development Kit Front

And on the back there is a nice little picture on the silkscreen that gives the FRAM I2C Address as 0x50, the I2C Address of the Accelerometer as 0X0F and the I2C pins as P4.0/P4.1 CY8CKIT-044 Development Kit Back

When I probe the I2C Bus using the bridge control panel I see that three I2C devices respond, 0x0F (the KXTJ2-1009), 0x50/0x51 (the FRAM.)

Bridge control panel probing I2C bus on CY8CKIT-044

So, how do you talk to the devices?  It turns out that both of these devices respond to the very common I2C device scheme which is called “EZI2C” inside of Cypress.  The scheme goes like this:

  • The chip is organized into registers
  • Each register has an address
  • Addresses are either 8-bits or 16-bits
  • To write an address you can send a string of bytes like:  I2C Start, I2C Address + W, Register Address,  Value to write, Stop
  • To read an address you can send a string of bytes like:  I2C Start, I2C Address + W, Register Address, Restart, Read, Stop

A simplification is that you can keep writing (or reading) and it will automatically goes to the next address without you having to send the next address. The easiest thing to do is show an example with the FRAM.

The FRAM has 128K addresses so it uses 16-bit addresses.  For example to save the values 5,6,7,8 into the register 0x0123 you would send:

  • 0x50, W, 0x01, 0x02, 0x05, 0x06, 0x07, 0x08, Stop  (in the bridge control panel the command is W 50 1 2 5 6 7 8 p)

To read back those addresses you would send

  • 0x50, W, 0x01, 0x02, restart, read read read read Stop  (in the bridge control panel the command is W 50 1 2 R 50 x x x x p)

Writing to Cypress FRAM using Bridge Control Panel

The interface is similar for the Kionix KXTJ2-1009.  When you look at the datasheet you will see on page 17 their description of the EZI2C protocol:

Kionix KXTJ2-1009 Datasheet

Then on page 20 they start describing the registers, and on page 21 they describe two registers (called DCST_RESP and WHO_AM_I) that will always respond with known values.

Kionix KXTJ2-1009 Datasheet

To verify the operation I send read commands using the Bridge Control panel to those registers, which respond with 55 and 9 just like the datasheet says that they should.

Reading from Kionix KXTJ2-1009 Using Bridge Control Panel

Designing PSoC FreeRTOS FW for I2C Register Based Sensors

From the previous section(s) we know that

  1. Two totally different devices, a memory and an accelerometer, can be accessed using exactly the same protocol
  2. It may take a while to complete I2C transactions (4 bytes @ 400kb/s = 87.5uS).
  3. The I2C master is a “shared” interface on the board, but multiple threads may want to perform transactions

These facts lead up to the conclusion that it would be nice to:

  • Have a standard software interface to I2C devices that use the EZI2C protocol
  • Have a non-blocking thread to run the interface
  • Share the I2C
  • Use PSoC FreeRTOS (the whole point)

With all of that in mind I created “i2cmaster.h”, the public interface to a PSoC FreeRTOS thread that will run the EZI2C protocol.  It defines two public functions

  • i2cm_Task – The thread that will need to be started to run all of the transactions
  • i2cm_runTransaction – The thread safe public interface which will send the transaction to the i2cm_Task

To run a transaction you need to have a transaction.  I defined a new type called “i2cm_transaction”.  This type is a structure that contains all of the information that is required to perform a transaction.  The input is split into three sections:

  • The I2C Address, the Register Address (and type 8-bit or 16-bit), and if the transaction is a Read or a Write
  • The number of bytes you want to read/write and a pointer to the data
  • The number of bytes that were processed (an output of the task)… plus an optional semaphore that will be given if you want a blocking transaction
#include <project.h>
#include "FreeRTOS.h"
#include "semphr.h"

#ifndef I2C_MASTER_H
#define I2C_MASTER_H

    
typedef enum i2cm_transactionMethod {
    I2CM_READ,
    I2CM_WRITE
} i2cm_transactionMethod_t;

typedef enum i2cm_registerAddresType {
    I2CM_BIT8,
    I2CM_BIT16
 
} i2cm_registerAddressType_t;


typedef struct i2cm_transaction {
    i2cm_transactionMethod_t i2cm_method;        // I2CM_READ or I2CM_WRITE
    uint8_t i2cm_address;                        // The I2C Address of the slave
    i2cm_registerAddressType_t i2cm_regType;     // I2CM_8BIT or I2CM_16BIT
    uint16_t i2cm_register;                      // The register in the slave
    
    uint8_t *i2cm_bytes;                         // A pointer to the data to be written (or a place to save the data)
    int i2cm_byteNum;                            // How many bytes are in the request

    int *i2cm_bytesProcessed;                    // A return value with the number of bytes written 
    SemaphoreHandle_t i2cm_doneSemaphore;        // If you want a semaphore flaging that the transaction is done
 
} i2cm_transaction_t;

void i2m_Task(void *arg);                  // The main thread function... you should start it in the RTOS

void i2cm_runTransaction(i2cm_transaction_t *i2cm_Transaction); // The public interface

#endif

With the public interface setup you will be able to run code that does exactly the same thing as you did in the bridge control panel.  For instance you can query the accelerometer “WHO_AM_I” register:

                    mytransaction.i2cm_method = I2CM_READ;
                    mytransaction.i2cm_address = 0x0F;            // Acelleromter I2C Address
                    mytransaction.i2cm_regType = I2CM_BIT8;       // 8-bit addressing
                    mytransaction.i2cm_register = 0x0F;           // WHO_AM_I Register
               
                    mytransaction.i2cm_byteNum = 1;               // One byte
                    mytransaction.i2cm_bytes = data;
               
                    mytransaction.i2cm_bytesProcessed = &byteCount;
                    mytransaction.i2cm_doneSemaphore = mySemaphore;
                    i2cm_runTransaction(&mytransaction);
                    
                    sprintf(buff,"Bytes=%d WHO_AM_I=0x%02X\n",byteCount,data[0]);
                    UART_UartPutString(buff);          

Which will yield exactly the same result as the bridge control panel did:

PSoC FreeRTOS: I2C Thread output from Kionix KXTJ2-1009

Implementing a PSoC FreeRTOS I2C Master Library

From the previous section we know the public interface.  But how does it work?  When the PSoC FreeRTOS i2cm_Task starts it:

  • Starts up the hardware I2C block
  • It makes a queue.  The queue will hold transactions which are waiting to be processed.  This is how multiple processes will put requested transactions into the queue, and the I2C master will deal with them one at a time in the order they were placed in the queue.
  • It starts up the infinite loop and waits for a transaction to be placed in the queue.
void i2cm_Task(void *arg)
{
    
    // The argument is the size of the queue
    uint32_t queueSize = (uint32_t)arg; // cast the arg
    
    I2C_Start(); // turn on the I2C Master
     
    i2cm_transaction_t i2cm_transaction; // A scratch transaction
    i2cm_transactionQueue = xQueueCreate(queueSize, sizeof(i2cm_transaction_t));
    
    uint32_t rval; // all of the I2C functions return a uint32_t
    
    while(1)
    {
        int i;
 
        // Wait until something gets put in the queue
        if(xQueueReceive(i2cm_transactionQueue,&i2cm_transaction,portMAX_DELAY) == pdTRUE)

The public interface to the whole system is i2cm_runTransaction.  It takes the transaction structure and pushes it onto the queue for the main thread to process.  If the user has asked to wait for completion then it also holds until the semaphore is given by the main loop.

// the i2cm_runTransaction function is the public interace to the system
// This function is thread safe (it can be called from any thread)
// It will queue up an I2C tranaction and then wait (if there is a semaphore handle)
void i2cm_runTransaction(i2cm_transaction_t *i2cm_transaction)
{
    if(xQueueSend(i2cm_transactionQueue,i2cm_transaction,portMAX_DELAY) != pdTRUE)
    {
        // Highly bad
        CYASSERT(0); // 
    }
    if(i2cm_transaction->i2cm_doneSemaphore)
        xSemaphoreTake(i2cm_transaction->i2cm_doneSemaphore,portMAX_DELAY);
}

The last part of the system is the main transaction processing loop which:

  • Waits for a transaction
  • Sends a start, then write the I2C address and register address (lines 65-75)
  • If it is a write, then write the bytes (lines 80-85)
  • If it is a read, then read then read bytes (lines 89-102)
  • Send a stop (lines 105-106)
  • If the user asked for a semaphore, give it. (lines 112-113)
        // Wait until something gets put in the queue
if(xQueueReceive(i2cm_transactionQueue,&i2cm_transaction,portMAX_DELAY) == pdTRUE)
{
// Begining of an I2C transaction
*i2cm_transaction.i2cm_bytesProcessed = 0;
// First write the register address
rval = I2C_I2CMasterSendStart(i2cm_transaction.i2cm_address,I2C_I2C_WRITE_XFER_MODE);
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
if(i2cm_transaction.i2cm_regType == I2CM_BIT16) // if a 16 bit register then send the high
{
rval = I2C_I2CMasterWriteByte((i2cm_transaction.i2cm_register >> 8) & 0xFF);
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
}
rval = I2C_I2CMasterWriteByte((i2cm_transaction.i2cm_register ) & 0xFF); // send the low byte
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
// if w send ...
switch(i2cm_transaction.i2cm_method) // Look at the type of transaction they want to do...
{
case I2CM_WRITE:  // If it is a write... then just send out the bytes
for(i=0; i < i2cm_transaction.i2cm_byteNum;i++)
{
rval = I2C_I2CMasterWriteByte(i2cm_transaction.i2cm_bytes[i]);
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
*i2cm_transaction.i2cm_bytesProcessed += 1;
}
break;
case I2CM_READ: // If it is a read.. then do a restart with READ... then read n-1 bytes
rval = I2C_I2CMasterSendRestart(i2cm_transaction.i2cm_address,I2C_I2C_READ_XFER_MODE);
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
for(i=0; i < i2cm_transaction.i2cm_byteNum-1;i++)
{
i2cm_transaction.i2cm_bytes[i] = I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA);
if(i2cm_transaction.i2cm_bytes[i] & 0x10000000) goto cleanup;
*i2cm_transaction.i2cm_bytesProcessed += 1;
}
// Then read the last byte and NAK (saying you are done)
i2cm_transaction.i2cm_bytes[i2cm_transaction.i2cm_byteNum-1] = I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA);
if(i2cm_transaction.i2cm_bytes[i2cm_transaction.i2cm_byteNum-1] & 0x10000000) goto cleanup;
*i2cm_transaction.i2cm_bytesProcessed += 1;
break;
}
rval = I2C_I2CMasterSendStop();
if(rval != I2C_I2C_MSTR_NO_ERROR) goto cleanup;
cleanup:
CYASSERT(rval == 0); // something really bad happend
// If they ask for a semphore when done... then give it
if(i2cm_transaction.i2cm_doneSemaphore)
xSemaphoreGive(i2cm_transaction.i2cm_doneSemaphore);     
}

I2C Sensor Library on the CY8CKIT-044 (FRAM)

In order to test the FRAM  I define a buffer in the RAM of the PSoC called “data”.  Then I setup three commands that can be executed from the UART Thread which will:

  • q – read 4 bytes from register 0x0102 into the data[] array
  • w -write 4 bytes from the data[] array into the FRAM register 0x0102
  • i – increment the values in the data[] array.

The code is simple:

           case 'q':
mytransaction.i2cm_address = 0x50;
mytransaction.i2cm_regType = I2CM_BIT16;
mytransaction.i2cm_byteNum = 4;
mytransaction.i2cm_register = 0x0102;
mytransaction.i2cm_method = I2CM_READ;
mytransaction.i2cm_bytesProcessed = &byteCount;
mytransaction.i2cm_bytes = data;
mytransaction.i2cm_doneSemaphore = mySemaphore;
i2cm_runTransaction(&mytransaction);
sprintf(buff,"Bytes=%d 0=%d 1=%d 2=%d 3=%d\n",byteCount,data[0],data[1],data[2],data[3]);
UART_UartPutString(buff);                  
break;
case 'i': // This will increment the values (that you read presumably from the FRAM
data[0] += 1;
data[1] += 1;
data[2] += 1;
data[3] += 1;
sprintf(buff,"0=%d 1=%d 2=%d 3=%d\n",data[0],data[1],data[2],data[3]);
UART_UartPutString(buff);
break;
case 'w': //This will write 4 values to register 0 in the FRAM
mytransaction.i2cm_address = 0x50;
mytransaction.i2cm_regType = I2CM_BIT16;
mytransaction.i2cm_byteNum = 4;
mytransaction.i2cm_register = 0x0102;
mytransaction.i2cm_method = I2CM_WRITE;
mytransaction.i2cm_bytesProcessed = &byteCount;
mytransaction.i2cm_bytes = data;
mytransaction.i2cm_doneSemaphore = mySemaphore;
i2cm_runTransaction(&mytransaction);
sprintf(buff,"Wrote %d bytes\n",byteCount);
UART_UartPutString(buff);
break;

To test:

  • Press ‘q’ to read from the FRAM into data[]
  • Press ‘i’ to increment the values in data[]
  • Press ‘w’ to write the data[] back into the FRAM
  • Press ‘q’ to make sure that the values were written
  • Use the bridge control panel to read the data to make sure that it is right.

PSoC FreeRTOS: I2C Thread output from Cypress FRAM

Read/Write from Bridge Control Panel to Cypress FRAM

I2C Sensor Library on the CY8CKIT-044 (Accelerometer)

Finally, to test the Accelerometer I create a PSoC FreeRTOS thread which starts up and turns on the Accelerometer.  To turn on the accelerometer you need to write a 0b1000000 to register 0x1b (called the CTRL_REG_1)

// This task manages the accelerometer
// 
void accel_Task(void *arg)
{
(void)arg;
SemaphoreHandle_t mySemaphore;
i2cm_transaction_t mytransaction;
char buff[128];
int x,y,z;
int xs,ys,zs;
int byteCount;
accel_t myData;
mySemaphore = xSemaphoreCreateBinary();
// True of all read/writes
mytransaction.i2cm_address = 0x0F;
mytransaction.i2cm_regType = I2CM_BIT8;
mytransaction.i2cm_bytesProcessed = &byteCount;
// Initalize the acceleromter
uint8_t setupData[] = {0b10000000};
mytransaction.i2cm_method = I2CM_WRITE;
mytransaction.i2cm_register = 0x1b; // CTRL_REG_1
mytransaction.i2cm_byteNum = sizeof(setupData);
mytransaction.i2cm_doneSemaphore = mySemaphore;
mytransaction.i2cm_bytes = setupData;
i2cm_runTransaction(&mytransaction);

Once the accelerometer is turned on, it will update 6 registers called XOUT_L/XOUT_H, YOUT_L/YOUT_Z, XOUT_L/ZOUT_H with the current X,Y and Z acceleration.  The data will be in the form of a 12-bit signed integer, left aligned (in other words the 4 least significant bits will be nothing).  I create a structure that I will use to hold the acceleration data.  Notice that I used “CY_PACKED” so that it will be not padded by the compiler.

CY_PACKED typedef struct accel {
int16_t x;
int16_t y;
int16_t z;
} CY_PACKED_ATTR accel_t;

Then I set up a read transaction of those registers, which are conveniently all right next to each other so you can read 6 consecutive bytes.

   // Setup for repeated reads
mytransaction.i2cm_method = I2CM_READ;
mytransaction.i2cm_register = 0x06;
mytransaction.i2cm_byteNum = sizeof(myData);
mytransaction.i2cm_doneSemaphore = mySemaphore;
mytransaction.i2cm_bytes = (uint8_t *)&myData;

Finally I repeatedly read the registers and print out the results.  The only trick is that I didn’t enable the floating point output libraries (they are huge), so I manually convert the floats to ints to be printed out.

    while(1)  // Run the same transaction over and over
{
i2cm_runTransaction(&mytransaction);
myData.x /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
myData.y /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
myData.z /= 16;  // The KXTJ2 Datasheet say lower 4 bits are X
xs = (myData.x < 0? -1:1);  // Find the XSign
ys = (myData.y < 0? -1:1);  // Find the YSign
zs = (myData.z < 0? -1:1);  // Find the ZSign
// The 1024 is a hardcode based on the range of the acceleromter
x = (int)((float)myData.x / 1024.0 * 10.0) * xs; // Turn it into integer G * 10
y = (int)((float)myData.y / 1024.0 * 10.0) * ys; // Turn it into integer G * 10
z = (int)((float)myData.z / 1024.0 * 10.0) * zs; // Turn it into integer G * 10
// Format data in x.x  
sprintf(buff,"x=%s%d.%dg\ty=%s%d.%dg\tz=%s%d.%dg\n",(xs<0?"-":""),x/10,x%10,(ys<0?"-":""),y/10,y%10,(zs<0?"-":""),z/10,z%10);
UART_UartPutString(buff);
vTaskDelay(1000); // delay for 1 second
}

And here is the screen shot of the output of the PSoC FreeRTOS with the kit sitting on my desk…. the output is about 1G down (which make sense)

PSoC FreeRTOS: Output of Kionix KXTJ2-1009 Accelerometer

As always you can git this PSoC FreeRTOS project at the IoT Expert GitHub site or git@github.com:iotexpert/PSoC-FreeRTOS-Examples.git

Topic Description
FreeRTOS: A PSoC4 FreeRTOS Port An introduction to making FreeRTOS work on PSoC 4
FreeRTOS PSoC Examples Using multiple tasks in FreeRTOS
FreeRTOS Queue Example Using a queue to communicate between tasks
PSoC 6 FreeRTOS - The First Example Booting FreeRTOS on PSoC 6
FreeRTOS Binary Semaphore An first example of a binary semaphore
FreeRTOS Binary Semaphore (Part 2) Removing polling in the UART Task
FreeRTOS Counting Semaphore An example using a counting semaphore
PSoC FreeRTOS Reading I2C Sensors with a shared I2C Bus
PSoC FreeRTOS Task Notify A light weight scheme to replace Semaphores
PSoC FreeRTOS Task Notification Values A very light weight method to transfer one word of information into a task

PSoC 4 Flash Write

Summary

Last week I got a question from a customer about how to write into the PSoC 4 Flash.  I sent him a fairly straight forward email message explaining the process in English… but after reflecting a little bit, I decided to write an example project as “C’ is often much clearer.  And yes I know that this article it doesn’t have much to do with IoT, but hopefully it is helpful to someone.  In this article I will explain:

  • The organization of the PSoC 4 Flash
  • The risks and issues of writing to the PSoC 4 Flash
  • A program that demonstrates reading and writing the PSoC 4 Flash
  • How to protect rows of the PSoC 4 Flash

Organization of the PSoC 4 Flash

The PSoC 4 Flash is organized into an array that starts at memory address 0x00 and has 128 byte rows.  Different versions of the chip will have different numbers of rows and possibly two arrays (in the case of PSoC 4 BLE II).  The exact configuration of your part is knowable in the firmware by a set of #defines that are built for you by PSoC Creator.  Here is a snapshot from the PSoC 4 System Reference Guide Chapter 8.

PSoC 4 Flash System Reference Guide

You can use that information along with the System call CySysFlashWriteRow() to write into the flash of your device.

PSoC 4 Flash Write API

Risks and Issues Writing the PSoC 4 Flash

There are several risks that you must take into account when performing a PSoC 4 Flash write (or for that matter writing Flash in any other MCU).

  • You must write a complete row at a time.  In PSoC 4 that is 128 bytes.  So, even if you want to write only 1 byte, you will need to read in all of the data surrounding that byte in the row and make sure you don’t overwrite something important.
  • You can easily write someplace bad and brick your part (e.g. if you overwrite your program, or exception vectors, or …).  You need to be very careful that you are writing in the correct place
  • It takes 20ms, which is a long time in the context of MCU speed, to write a row of flash and the MCU comes to a standstill while that is happening (all of the interrupts are off etc.)
  • The Flash will eventually wear out.  If you look at the data sheet for PSoC 4 you will find that the endurance of the Flash is 100K writes. If you are continually updating a place in the memory, you will eventually wear it out

PSoC 4 Datasheet

A PSoC 4 Flash Write Demo Project

To demonstrate the process of a PSoC 4 Flash write I created a project that:

  • Initializes a 128 byte array in the Flash (aligned to a complete row) with 0’s
  • Lets you read the Flash array into a RAM array by pressing a key on the keyboard
  • Lets you printout the values of the Flash array
  • Lets you printout the values of the RAM array
  • Lets you increment the values of the RAM array
  • Lets you write the RAM array into the Flash

Normally when you declare a variable in C it will be assigned to the RAM somewhere (either on the Stack or in the Data or BSS section of the RAM).  In order to have my array be assigned to the Flash, I declare the array to be “const”.  The first time I did this I did not include the “={0}” in the declaration, and the compiler decided to put the array in the RAM, but when I initialized one of the elements it assigned it to the Flash.  The CY_ALIGN() macro tells the linker to put the start of the “row” array on a 128 byte boundary.  In other words completely aligning to a Flash row.

// Allocate 1 row of flash... make it aligned to the row size
// and initializde it with 0's
// the const keeps it in the flash (not in the data segment)
const uint8_t row[CY_FLASH_SIZEOF_ROW] CY_ALIGN(CY_FLASH_SIZEOF_ROW) = {0};

At the start of main, I declare array called “data” which will reside in the RAM.  I will use this array as a buffer to read Flash data into, then change it, then write it back to the Flash.

We know that the Flash is arranged in rows of 128 bytes (well, actually rows of size CY_FLASH_SIZEOF_ROW).  To calculate the row number of the my array “row”, I use the Address of the row divided by the row size. (the CY_FLASH_BASE is 0x00 because the Flash starts at Address 0x00)

    uint8_t data[CY_FLASH_SIZEOF_ROW];
int rowNum;
// calculate which row in the flash that the "row" array resides
rowNum = ((int)row - CY_FLASH_BASE) / CY_FLASH_SIZEOF_ROW;

To make things a bit easier I create a function called “dump” which just prints the values of a “size “of memory in hex format.  The function just takes an address that you want to start dumping to the screen.

// This function dumps a block of memory into rows of hex ...16 bytes long in hex
void dump(uint8_t *array, int size)
{
int i,j;
j=0;
for(i=0;i<size;i++)
{
dbgprintf("%2X ",array[i]);
j=j+1;
if(j==16) // 16 values per line
{
j = 0;
dbgprintf("\n");
}
}
UART_UartPutString("\n");
}

In order to test this whole thing I use a UART to read from the keyboard and display to the screen.  The main function is just a loop that reads keys and then does a big “switch”.

When you press ‘f’ it dumps the Flash “row”

When you press ‘r’ it copies the Flash “row” into the array “data” (which resides in the RAM)

When you press ‘i’ it increments the values in the “data” array

When you press “R” it dumps out the values in the “data” array

When you press ‘q’ it prints out information about the rowNumber, row size etc.

   for(;;)
{
char c;
c = UART_UartGetChar();
switch(c)
{
case 'f':
UART_UartPutString("Dumping Flash\n");
dump((uint8_t *)row,CY_FLASH_SIZEOF_ROW);
break;
case 'r':
UART_UartPutString("Reading Flash into RAM\n");
memcpy(data,row,(int)CY_FLASH_SIZEOF_ROW);
break;
case 'i':
UART_UartPutString("Incrementing RAM Array\n");
data[0] = data[0] + 1;
for(i=1;i<(int)CY_FLASH_SIZEOF_ROW;i++)
{
data[i] = data[i-1]+1;
}
break;
case 'R':
dbgprintf("Dumping RAM Array\n");
dump(data,CY_FLASH_SIZEOF_ROW);
break;    
case 'q':
dbgprintf("Row Number = %d\n",rowNum);
dbgprintf("RowSize = %d\n",CY_FLASH_SIZEOF_ROW);
dbgprintf("Flash Address = %X\n",CY_FLASH_BASE);
dbgprintf("Row Address = %X\n",(unsigned int)row);
dbgprintf("Data Address = %X\n",(unsigned int)data);
break;

When you press ‘w’, it writes the “data” array into the “row” array in the Flash using the “CySysFlashWriteRow()” system call

            case 'w':
dbgprintf("Writing Flash from RAM Buffer\n");            
rval = CySysFlashWriteRow(rowNum,data);
if(rval == CY_SYS_FLASH_SUCCESS )
{
dbgprintf("Flash Write Sucess\n");
}
else if (rval == CY_SYS_FLASH_INVALID_ADDR)
{
dbgprintf("Flash Write Failed: Invalid Address\n");
}
else if (rval == CY_SYS_FLASH_PROTECTED)
{
dbgprintf("Flash Write Failed: Flash Protected\n");
}
else {
dbgprintf("Flash Write Failed: Unknown\n");
}
break;

Testing the Program

After I program the device, I first press “q” to find out the information about the PSoC 4 Flash array.  You can see that my “row” is the 46th row in the Flash and resides at address 0x1700 (as I type this I wish that I hadn’t mixed decimal and hex … and that I had called my “row” variable something other than “row”).  You can also see that the rows are 128 bytes and that the Flash array starts at address 0x0.  All that is good.

PSoC 4 Flash Write Demo

When I press ‘f’ to dump out the Flash array, you can see that my row has all 0’s in it.  Then I press ‘R’ to dump out my RAM array and you can see that it has garbly-gook in it (which makes sense as I didn’t initialize the array)

PSoC 4 Flash Write Demo

Next, I press ‘r’ to copy the PSoC 4 Flash array into the RAM buffer.  Then I press “i” to increment the values in the RAM array, then I dump it out.  Lastly, I press ‘w’ to write it to the PSoC 4 Flash.

PSoC 4 Flash Write Demo

Then I hit reset on the board and press ‘f’ to printout the PSoC 4 Flash array.  And I see success, the values were retained in the PSoC 4 Flash even after resetting the device. (or for that matter power cycling it)

PSoC 4 Flash Write Demo

Protecting Rows of the PSoC 4 Flash

One cool feature of the PSoC 4 Flash is that you can write protect it on a row by row basis.  Meaning that you can make it so that the PSoC 4 Flash writing hardware will not overwrite rows.  You do this by going to the “Flash Security” tab of the Design Wide Resources.  Then you have 1 box for each row in the flash where you can pick “W” for Full Protection and “U” for Writable.  In the screen show below you can see me changing the row “ox1700” to Write protection.  This is the row where my Flash array resides.

PSoC 4 Flash Security

After reprogramming my device I try to press ‘w’ and you can see that the Write fails.

PSoC 4 Flash Write Fail

As always my projects are available on the IoT Expert GitHub site git@github.com:iotexpert/PSoC4-Flash-Write-Example.git

FreeRTOS PSoC Examples (Part 1)

Summary: FreeRTOS PSoC Examples

Although in my last Article I said that I would get back to BLE Centrals this week, that turns out to not be the case because I have found myself down the FreeRTOS rabbit hole.  This week I will publish several FreeRTOS PSoC examples using PSoC 4 and PSoC 6.  I found myself here because I was working on BLE centrals on PSoC4, and I thought that I should try it on PSoC 6 to see how well it worked.  But, given the expansive capabilities of the chip, the reality is that PSoC 6 will almost certainly be programmed using an RTOS.  So, I decided to use FreeRTOS to try out the different features of PSoC 6.  But given that I don’t really know PSoC 6 yet … and I don’t really know FreeRTOS … I decided to experiment first on PSoC 4 examples first then once I understood what to with FreeRTOS, try the same things on PSoC 6.

Here is a video I took on Saturday in my office with three different PSoCs (PSoC 4 BLE, PSoC 4M, and PSoC 6) running FreeRTOS.  (yes my office looks like a tornado hit it).  The moral of the story is never underestimate the power of the blinking LED (to figure out what is going on).

In this article I will show FreeRTOS PSoC Examples:

  • Start with a single blinking LED Task
  • Add 2nd UART based command processor thread

In the upcoming articles I will add more FreeRTOS PSoC Examples:

  • Issue commands via a queue between the two threads
  • Interrupt processing & a Semaphore
  • Im not sure what else yet 🙂

Single Blinking LED

I start this process by following the instruction in my previous post to create a FreeRTOS example.  The project is straight forward, it does:

  • Installs the FreeRTOS vectors (lines 9-22)
  • Creates the LED Task (lines 41-48)
  • Starts the scheduler (line 50)
  • Runs the LED_Task until the end of time, blinking the LED (lines 25-33)
#include "project.h"
#include "FreeRTOS.h"
#include "timers.h"
extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
#define CORTEX_INTERRUPT_BASE          (16)
void setupFreeRTOS()
{
/* Handler for Cortex Supervisor Call (SVC, formerly SWI) - address 11 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SVCall_IRQn,
(cyisraddress)vPortSVCHandler );
/* Handler for Cortex PendSV Call - address 14 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + PendSV_IRQn,
(cyisraddress)xPortPendSVHandler );    
/* Handler for Cortex SYSTICK - address 15 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SysTick_IRQn,
(cyisraddress)xPortSysTickHandler );
}
void LED_Task(void *arg)
{
(void)arg;
while(1) {
RED_Write(~RED_Read());
vTaskDelay(500);
}
}
int main(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
setupFreeRTOS();
/* Create LED task, which will blink the LED */
xTaskCreate(
LED_Task,       /* Task function */
"LED Blink",    /* Task name (string) */
200,            /* Task stack, allocated from heap */
0,              /* No param passed to task function */
1,              /* Low priority */
0 );            /* Not using the task handle */
vTaskStartScheduler();
while(1); // get rid of the stupid warning
}

You can see the blinking LED in the video from above.

UART Thread

Once you have the blinking LED working, that means you have all of the environment setup etc… for some reason this always feels like a big step.  I then copy the BlinkingLED project and give it the name “UART-Thread”.  Then add a debugging UART to the schematic:

FreeRTOS PSoC Examples - PSoC Schematic

Now you can add the UART Thread to your project.  It is pretty simple:

  • Starts the UART
  • Gets a key from the keyboard (line 47)
  • If it is an ‘a’ it prints out “Working” (line 51)
  • If it is a ‘t’ it calls the function vTaskList which makes a dump of the process currently running (more on this later) (lines 54-60)
#include "project.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
#include "timers.h"
extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
#define CORTEX_INTERRUPT_BASE          (16)
void setupFreeRTOS()
{
/* Handler for Cortex Supervisor Call (SVC, formerly SWI) - address 11 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SVCall_IRQn,
(cyisraddress)vPortSVCHandler );
/* Handler for Cortex PendSV Call - address 14 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + PendSV_IRQn,
(cyisraddress)xPortPendSVHandler );    
/* Handler for Cortex SYSTICK - address 15 */
CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SysTick_IRQn,
(cyisraddress)xPortSysTickHandler );
}
void LED_Task(void *arg)
{
(void)arg;
while(1) {
RED_Write(~RED_Read());
vTaskDelay(500);
}
}
void UART_Task(void *arg)
{
(void)arg;
static char buff[500];
UART_Start();
UART_UartPutString("Started UART\n\r");
char c;
while(1) {
c = UART_UartGetChar();
switch(c)
{
case 'a':
UART_UartPutString("Working\n\r");
break;
case 't':
UART_UartPutString("********************************************\n\r");
UART_UartPutString("Task          State   Prio    Stack    Num\n\r"); 
UART_UartPutString("********************************************\n\r");
vTaskList(buff);
UART_UartPutString(buff);
UART_UartPutString("*********************************************\n\r");
break;
}
taskYIELD();
}
}
int main(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
setupFreeRTOS();
/* Create LED task, which will blink the LED */
xTaskCreate(
LED_Task,       /* Task function */
"LED Blink",    /* Task name (string) */
200,            /* Task stack, allocated from heap */
0,              /* No param passed to task function */
1,              /* Low priority */
0 );            /* Not using the task handle */
/* Create UART Task which will control the serial port */
xTaskCreate(
UART_Task,       /* Task function */
"UART",          /* Task name (string) */
0x400,           /* Task stack, allocated from heap */
0,               /* No param passed to task function */
1,               /* Low priority */
0 );             /* Not using the task handle */
vTaskStartScheduler();
while(1);
}

 

Testing the project

When I program the development kit, the LED starts blinking.  That is good.  Then I press ‘t’ which dumps out a table of the processes running.  The columns are

  • The name of the task
  • The state of the task (R-Running, B-Blocked, S-Suspended)
  • The priority (0-configMAX_PRIORITIES from FreeRTOSConfig.h)
  • The highwater mark of the stack usage of that thread.  In the UART thread I declared 0x400 (1024) so it appears I have used most of the RAM.
  • The task number

FreeRTOS PSoC Examples UART Serial Port

It is interesting that there are two tasks that I didnt create.  The FreeRTOS requires that there alway be some task, so it automatically creates an IDLE tasks which it runs when there is nothing else available to run.  It is possible to put the chip to sleep by adding to the IDLE task.  The other task is the Timer Service task which is what enables me to use the software delay (vTaskDelay).

If you have more FreeRTOS PSoC Examples that you are interested in please make a comment and maybe Ill create them.

As always, you can find the FreeRTOS PSoC Examples on the IoT Expert GitHub site or git@github.com:iotexpert/PSoC-FreeRTOS-Examples.git

Topic Description
FreeRTOS: A PSoC4 FreeRTOS Port An introduction to making FreeRTOS work on PSoC 4
FreeRTOS PSoC Examples Using multiple tasks in FreeRTOS
FreeRTOS Queue Example Using a queue to communicate between tasks
PSoC 6 FreeRTOS - The First Example Booting FreeRTOS on PSoC 6
FreeRTOS Binary Semaphore An first example of a binary semaphore
FreeRTOS Binary Semaphore (Part 2) Removing polling in the UART Task
FreeRTOS Counting Semaphore An example using a counting semaphore
PSoC FreeRTOS Reading I2C Sensors with a shared I2C Bus
PSoC FreeRTOS Task Notify A light weight scheme to replace Semaphores
PSoC FreeRTOS Task Notification Values A very light weight method to transfer one word of information into a task

PSoC4 Watch Dog Timer

Summary

I recently was working on a project that had a line of code that sometimes caused the system to hang.  One of the several possible fixes to my problem was to use a Watch Dog Timer (WDT).  I “knew” about the PSoC4 Watch Dog Timer(s) in the PSoC4 family but had never used them.  My experiments are the subject of this article.  I will address the following areas.

  • PSoC4 Watch Dog Timer: Introduction
  • PSoC4 Watch Dog Timer: Basic Usage
  • PSoC4 Watch Dog Timer: Callback
  • PSoC4 Watch Dog Timer: Custom ISR

All of these projects are available on my GitHub website of you can “git@github.com:iotexpert/PSoC4-WDTExamples.git”

PSoC4 Watch Dog Timer: Introduction

Inside of most of the PSoC4 family there are two 16-bit timers Timer0-1/WDT0-1 and one 32-bit timer called Timer2/WDT2.  These timers are driven by the Low Frequency Clock (LFCLK). In addition, they can be cascaded together to form a larger timer (I’m not sure why you would need a 64-bit timer) or they can be used in isolation as general purpose or WDTs.  You can configure these timers either through software or on the clocks tab of the design wide resources (DWR).  For some reason, which I don’t understand, Cypress choose not to use the optional ARM Cortex-M family Watchdog Timer.

I started this article by creating a PSoC4M project called “4MBlank.  In the screenshot below you can see that I have clicked the “Clocks” button in the DWR.  On the 2-3-4 line you can see the Watch Dog Timers.  To edit the low frequency clocks press the “Edit Clock” button. (as shown on the 2nd screen shot)

PSoC4 Watch Dog Timer: Edit Clocks

In the PSoC4M it is possible to drive the LFCLK with two different oscillators, the Internal Low-speed Oscillator (ILO) which is a very inaccurate RC Oscillator or with an external Watch Crystal Oscillator (WCO) which is very accurate.  The CY8CKIT-044 has the WCO on the board so I will be able to use it as the LFCLK source for my projects.  To select the LFCLK source use the dropdown menu (currently set on ILO).  Each of the timers can be put into one of four modes (from the LFCLK Datasheet)

  • Free Running Timer – Does not generate an interrupt or reset. You can read the counter and set an interrupt in the firmware to generate occasional timing loops.
  • Periodic Timer – Generates an interrupt on a match event but no reset. The timer wraps at the set divider value.
  • Watchdog – Generates a reset on a match event (counter should be cleared before reaching a match event to prevent a reset).
  • Watchdog (w/interrupts) – Generates an interrupt on a match event and generates a reset on a 3rd unserviced interrupt.

In addition it is possible to cascade the timers (to make large timer), however, this must be done through the APIs.  I will explain the “Timer ISR” box in the last example project.  To enable a Timer click the little box in the upper left hand corner.  (in the picture you can see Timer0 and Timer1 are enabled).  When you configure the timer you can select its function as well as its divider which sets how often it is triggered based on the frequency of the input source.

PSoC4 Watch Dog Timer: Configure Low Frequency Clocks

All of the documentation is available on the Help–>System Reference Guide.  Then click the “PSoC4 Low Frequency Clock” link.

PSoC4 Low Frequency Clock Documentation

PSoC4 Watch Dog Timer: Basic Usage

The first project shows the use of the WDT to reset the chip.  This project starts by configuring the WDT to use the WCO as its source clock and to have a period of about 1 second.  With this configuration if you don’t “feed” the WDT it will cause a chip reset in 3 seconds.

PSoC4 Watch Dog Timer: Basic Example

When the PSoC4 chip is reset you can find the cause of the reset by using the API “CySysGetResetReason”.  This will return the cause of the last reset

  • CY_SYS_RESET_WDT       – WDT caused a reset
  • CY_SYS_RESET_PROTFAULT – Occured protection violation that requires reset
  • CY_SYS_RESET_SW        – Cortex-M0 requested a system reset.

My program looks for a WDT Reset, and if that happens it turns on the Red LED and hangs the program with an infinite while loop.  If it was a normal (XRES or Power up) boot of the chip it will just run the infinite for loop.

In the main loop of the program you can see that it blinks the Blue LED quickly (5 Hz)… then if you don’t feed the WDT it resets the chip.  If you uncomment the “CySysWatchDogFeed” the chip will continue to blink the Blue LED until the end of time.

#include "project.h"
int main(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
{
CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK);
red_Write(0); // turn on the red LED
while(1);     // hang
}
for(;;)
{
blue_Write(~blue_Read());
// If dont feed the WDT it will cause a reset in ~3 seconds
//CySysWatchdogFeed(CY_SYS_WDT_COUNTER0);
CyDelay(100);
}
}

PSoC4 Watch Dog Timer: Callback

Instead of just reseting the chip on the 3rd match of the WDT timer, you can ask to be called via an interrupt when the match occurs.  To accomplish this, start by configuring one or more of the WDT Timers to a “Watchdog (w/ Interrupt)”.  When you select “w/Interrupt” you can either use an ISR that Cypress provides or you can create your own ISR.

PSoC4 Watch Dog Timer: CallBack Example

The ISR that Cypress provides does two basic things.  First, if you have registered your interest in being called back, it calls your callback subroutine.  After the three callbacks are done, it resets the counters and clears the WDT.  In the block of code below you can see on Line 20 that I register my call back.  The routine “wdtInterruptCallback()” is then called each time that WDT Timer expires, A.K.A about 1/second.  This makes the Blue LED bink on/off.  The other interesting thing in this code is that I show that the chip can be put into deep sleep and the WDT will continue to run.

#include "project.h"
void wdtInterruptCallback()
{
blue_Write(~blue_Read());   
}
int main(void)
{
CyGlobalIntEnable; 
if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
{
CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK); // Disable the WDT
red_Write(0); // Turn on the red LED
while(1);     // Hang
}
CySysWdtSetInterruptCallback(CY_SYS_WDT_COUNTER0,wdtInterruptCallback);
for(;;)
{
CySysPmDeepSleep(); // Put the chip into deep sleep.       
}
}

PSoC4 Watch Dog Timer: Custom ISR

If you do not want to use the Cypress provided ISR you can create your own.  In order to do this you need to add the ISR and Global Signal components to your schematic:

PSoC4 Watch Dog Timer: Custom ISR Schematic

This will attach the WDT Interrupt Signal to the NVIC.  In your source code you will need to provide the handler function for isr_1.  My custom ISR is called “wdtInterruptHandler”.  All it does is toggle the Blue LED, then clear the WDT Interrupt.

#include "project.h"
void wdtInterruptHandler()
{
blue_Write(~blue_Read());
CySysWdtClearInterrupt(CY_SYS_WDT_COUNTER0_INT);
}
int main(void)
{
if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
{
red_Write(0); // turn on the red LED
while(1);     // hang
}
CyGlobalIntEnable; /* Enable global interrupts. */
isr_1_StartEx(wdtInterruptHandler);
CySysWdtEnable(CY_SYS_WDT_COUNTER0);
for(;;)
{
CySysPmDeepSleep();
}
}

PSoC4 Watch Dog Timer: Watch Dog Reset

As I was reading all of the documentation I started thinking about how I could write a program that could show that three unserviced interrupts would cause the reset.  I originally thought that I could put a counter in the ISR and then reset the the WDT on the 3rd call, however, that does not work because when you return from the ISR, the WDT Interrupt is still pending.  When the interrupt is still pending it just jumps right back into the ISR.  In order to show the three unserviced calls, I created the program below which starts toggling a pin as fast as possible when the ISR is called.  This loop ends only when the chip is reset.

#include "project.h"
void wdtInterruptHandler()
{
while(1) WDTSignal1_Write(~WDTSignal1_Read());
}
int main(void)
{
WDTSignal0_Write(1);
if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
{
CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK);
red_Write(0); // turn on the red LED
while(1);     // hang
}
isr_1_StartEx(wdtInterruptHandler);
CySysWdtEnable(CY_SYS_WDT_COUNTER0);
CyGlobalIntEnable; /* Enable global interrupts. */
for(;;)
{
}
}

I configured the period for the WDT to be ~3ms.  The yellow trace labeled “1” is a pin that is written to 1 by the main loop when the chip starts main().  You can see that the time from main starting until the ISR starts toggling is about 3ms.

PSoC4 Reset Example (start to ISR)

Then inside of the ISR you can see that the toggling goes on for about 6ms (a.k.a two WDT periods)

PSoC4 Reset Example

Finally, you can see the chip reset, then reboot, which takes about 2ms.  You do not see the rising edge in main on the last picture because it takes about 500ms for the WCO to stabilize and main to start.

PSoC4 Reset Example