PSoC 4200M Low Power with WDTs

Summary

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.

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

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

CY8CKIT-042 modification for PSoC 4200M Low Power Measurement

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

CY8CKIT-042 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.

PSoC 6, FreeRTOS & SSD1306 OLED Display

Summary

This week I have been working with my friend Hassane El Khoury on his hobby project which has the SSD1306 in it. [if you want to know more about the project, leave a comment and maybe he will answer the question].  I have been helping him get a number of things going including this display which I have written about before.  This article describes the integration of the SSD1306 OLED Display into his project with the PSoC 6 & FreeRTOS.

Firmware Architecture

Hassane started his career as an Automotive System Designer, then he worked as an Applications Engineer, so he has “mad” skills in this area.  He is building his project with a PSoC 6 and is heavily using FreeRTOS (which is new for him).  He has been distracted with some other thing the last few years, so I have been serving as a consultant on the latest PSoC stuff to smooth the learning curve.  In this case PSoC6, FreeRTOS and the SSD1306 OLED Display.  I have only used the SSD1306 OLED Display with PSoC 4 and WICED, but I ASSUMED (incorrectly assumed… which I will talk about in the next article) that there would be no problems making it work in that environment.

Hassane is all about reuse and he had copied the design of this section of his project from my article on using queues with FreeRTOS.  It looks like this.

  1. There is a task that manages the I2C and talks directly to the SCB hardware and the I2C devices
  2. There is a task that manages the OLED

ssd1306 oled display architecture

Originally the OLED task was talking directly to the I2C via the SCB (the red line on the picture).  Remember from the article on the U8G2 library, the hardware abstraction layer looks like this.  Basically it

  1. Sends an I2C Start at the start of the U8G2 transaction
  2. Sends an I2C Stop at the end of the tranaction
  3. Send the number of bytes called for.
uint8_t u8x8_I2C_HW(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t *data;
    switch(msg)
    {
        case U8X8_MSG_BYTE_SEND:            
            data = (uint8_t *)arg_ptr;
            while( arg_int > 0 )
            {
                Cy_SCB_I2C_MasterWriteByte(I2Cm1_HW, *data, I2C_TIMEOUT, &I2Cm1_context);
  	            data++;
	            arg_int--;
            }
        break;
      
        case U8X8_MSG_BYTE_START_TRANSFER:
            Cy_SCB_I2C_MasterSendStart(I2Cm1_HW, u8x8_GetI2CAddress(u8x8)>>1, CY_SCB_I2C_WRITE_XFER, I2C_TIMEOUT, &I2Cm1_context);
         break;
        
        case U8X8_MSG_BYTE_END_TRANSFER:
            Cy_SCB_I2C_MasterSendStop(I2Cm1_HW, I2C_TIMEOUT, &I2Cm1_context);
        break;
    
        default:
            return 0;
    }
    
    return 1;
}

The problem with this design is that it takes over the I2C hardware and requires exclusive control.  Moreover, it leaves you program hung while the transaction is happening.  To solve this problem, there are three options

  1. “Assign” the SCB block to the exclusive use of this task (makes it hard to share with other I2C devices)
  2. Create a mutex for the SCB.  This will work, but it will not allow the task to “queue” transactions and move on
  3. Create a task to manage the SCB with a queue (the option that Hassane chose)

The I2C message queue holds complete transactions.  Each transaction (which he called I2Cm_transaction_t) looks like this:

typedef enum I2Cm_transactionMethod {
    I2CM_READ,
    I2CM_WRITE
} I2Cm_transactionMethod_t;

typedef enum I2Cm_registerAddresType {
    I2CM_NONE, 
    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)
    uint32_t *I2Cm_bytesProcessed;               // A return value with the number of bytes written
    uint32_t I2Cm_byteNum;                       // How many bytes are in the request
    SemaphoreHandle_t I2Cm_doneSemaphore;        // If you want a semaphore flagging that the transaction is done 
} I2Cm_transaction_t;

In order to fix the the HAL you just sets up a transaction at the start of the task:

   memset(&OLEDtransaction,0,sizeof(OLEDtransaction));
	OLEDtransaction.I2Cm_method = I2CM_WRITE;
	OLEDtransaction.I2Cm_address = OLED_ADDRESS_1;
	OLEDtransaction.I2Cm_regType = I2CM_NONE;
	OLEDtransaction.I2Cm_bytes = buff;
	OLEDtransaction.I2Cm_bytesProcessed = &numProcessed;	
    OLEDtransaction.I2Cm_doneSemaphore = xSemaphoreCreateBinary();

Then queue up the bytes into a buffer, and flush the buffer when the “U8X8_MSG_BYTE_END_TRANSFER” message comes

uint8_t u8x8_I2C_HW(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t *data;
    switch(msg)
    {
        case U8X8_MSG_BYTE_SEND:        
            data = (uint8_t *)arg_ptr;
            while( arg_int > 0 )
            {
                buff[buffCount] = *data;
                buffCount += 1;
                CY_ASSERT(buffCount < BUFFSIZE);              
  	            data++;
	            arg_int--;
            }
        break;
 
        case U8X8_MSG_BYTE_START_TRANSFER:
            buffCount = 0;
         break;
        
        case U8X8_MSG_BYTE_END_TRANSFER:
	        OLEDtransaction.I2Cm_byteNum = buffCount;
            I2Cm1RunTransaction(&OLEDtransaction);
        break;
    
        default:
        return 0;
    }
    
    return 1;
}

SSD1306 OLED Display

Now, the short block of test code works perfect:

    u8x8_Setup(&MyI2Cu8x8, u8x8_d_ssd1306_128x32_univision, u8x8_cad_ssd13xx_i2c, u8x8_I2C_HW, u8x8_gpio_and_delay_PSoC6);
        
    u8x8_InitDisplay(&MyI2Cu8x8);  
    u8x8_SetPowerSave(&MyI2Cu8x8,0);
    u8x8_ClearDisplay(&MyI2Cu8x8);
    u8x8_SetFont(&MyI2Cu8x8,u8x8_font_amstrad_cpc_extended_f);
    u8x8_DrawString(&MyI2Cu8x8,0,0," Killer Mustang ");
    u8x8_DrawString(&MyI2Cu8x8,0,1,"   Test OLED    ");
    u8x8_DrawString(&MyI2Cu8x8,0,2,"________________");

And I get a nice output (I wonder what a “Killer Mustang” is?)

In the next article Ill talk about the SS1306 and a problem that I created for myself.

PSoC 4200M WDT Long Deep Sleep

Summary

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();
    }
}

 

Serial Wire View with PSOC4

I use PSOC4 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 PSOC4: The boot loader and application. This is not supported by Cypress implementation of SWD.

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 PSOC4 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 PSOC4 to save die cost, foiling my great idea. Brief interlude to drink and bang head on desk.

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.

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

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.

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.

Going to the system tab of the .CYDWR file, we can change them to 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.

Can you see where we are going with this? Printf( ) data is now coming out of PSOC4 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

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.

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

PSoC 6 Introduction – Behind the Scenes

Summary

As you might have noticed, last week I did a bunch of work creating a live streaming video series about PSoC 6.  You can find the recorded video here.  If you have been living under a bridge you might not know that PSoC 6 is the new Cypress PSoC.  It includes a dual ARM Cortex M4/M0+, a Bluetooth Low Energy 5.0 Radio, CapSense plus all of the other stuff that PSoC is known for.  The chip is super cool and has gotten everybody and their brother excited about the future.

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

You will be seeing a bunch more from me using the CY8CKIT-062-BLE development kits.

PSoC 6 Webinar

Here are some behind the scenes pictures that were taken the day before and the day of the recording session.

In the studio on Wednesday night… why doesnt it work?

Getting Ready for the PSoC 6 Webinar

Yes, Mouser was sponsoring the event… thank you very much!

PSoC 6 and Mouser

Getting things setup the morning of the recording session.

The morning of the PSoC 6 Video Webinar

During the presentation (Im sure whatever I was saying was important)

Talking about PSoC 6

Some video taken from behind the scenes.

The Gospel of PSoC 6

The whole crew.  Thank you very much to Mouser and ON24.

The whole crew for the PSoC 6 Webinar

Lesson 5 – PSoC 6 Introduction: Bluetooth Low Energy

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

Project Description

This project is a PSoC 6 BLE extension of the previous project with CapSense and UART.

The BLE will:

  • Advertise the device with the name “P6Intro” and as having the CapSense Service
  • Will allow one connection (with no security)
  • A BLE CapSense Service with the CapSense Slider characteristic as “notify”
  • When the CapSense slider updates it will send a notification to any connected device

The CapSense will (same as before)

  • BTN0 will set the intensity to 0%
  • BTN1 will set the intensity to 100%
  • CapSense slider will set the intensity (0-100) when touched

The UART will (same as before)

  • UART Key ‘0’ turn the LED to intensity = 0%
  • UART Key ‘1’ turn the LED to intensity = 100%
  • UART Key ‘5’ turn the LED to intensity = 50%
  • UART Key ‘?’ will printout help

How does it work?

This device will act as a GAP Peripheral with a GATT database server with the CapSense Service.  The PSoC 6 BLE component will allow you to split the BLE function onto the CM0+ (Controller) and the CM4 (Host and Profiles).  On the CM4 you will build an event handler which will act each time something happens in the Bluetooth world e.g. a connection, a disconnection, a read or write.

(same as previous project) You can control the intensity of an LED with a pulse width modulator and a high frequency input clock.  If the duty cycle is “small” then the intensity of the LED is low (meaning dim).  If the duty cycle is “big” then the intensity is “high”.  A 0% duty cycle will be off, a 50% duty cycle will be in the middle and 100% duty cycle will be all on.  To make this work I will set the input clock to 1 MHZ and the PWM Period to 100.  The PWM Compare value will then be set between 0 and 100 to represent brightness in percent.

(same as previous project) The Cypress CapSense block has two sensing modes, mutual capacitance and self-capacitance.   The CY8CKIT-062-BLE has two Mutual Capacitance buttons and a 5-Segment Self or Mutual Capacitance Slider.

The project will have four tasks

  • BLE – Will act as a BLE GATT Peripheral that advertises the CapSense Service with the CapSense Slider.  The value of the slider will ‘Notify’
  • UART – Read data from the keyboard and send messages to the PWM Task
  • CapSense – Read finger positions from the slider and buttons and send messages to the PWM Task
  • PWM – Will have an input queue, when a integer message is received it will set the PWM compare value to the integer received (0 >= msg <= 100)

How am I going to do it?

  1. Copy, Paste and Rename the CapSense UART Project
  2. Add and Configure the BLE Component
  3. Fix the Interrupts
  4. Update the main_cm0p. c to run the BLE radio
  5. Add three global variables to main_cm4.c
  6. Create a new task to process the BLE
  7. Create an Event Handler
  8. Make a Function to Send Notifications
  9. Update the capSenseTask to Send Values
  10. Add the bleTask to main function in main_cm4.c
  11. Program and Test

Copy, Paste and Rename the CapSense UART Project

Right click on the PSoC-6-Introdution and Paste

Right click and rename the project to be CapSense_BLE

When is asks you to rename… do it

It should look like this now.

Add and Configure the BLE Component

Go to the Component Catalog and find the Bluetooth Low Energy component and drag/drop it into your design schematic.

Configure the component to be a “Dual Core …” and set the  number of connections to 1.

Add the Capsense service to your server by clicking “Load Service…”

And selecting the “CapSense.service” file.  I created this file to have the right UUIDs and characteristics.

You can now look at the CapSense Slider service and the CapSlider characteristic

Set the name of our peripheral in the “GAP Settings”.  In this case I will call it P6INTRO.

Configure the advertising settings to advertise the name of the device and the CapSense Service.

Fix the Interrupts

Almost all of the interrupt sources in the PSoC 6 can be connected to one or both of the Cortex cores.  I think that you are probably crazy to attach it to both.  In this case the BLE interrupt needs to be connected to the “ARM CM0+”

Update main_cm0p. c to Run the BLE Radio Controller

#include <project.h>
int main(void)
{
    __enable_irq(); /* Enable global interrupts. */
  
   
    Cy_BLE_Start(0);
    
    /* Enable CM4.  CY_CORTEX_M4_APPL_ADDR must be updated if CM4 memory layout is changed. */
    Cy_SysEnableCM4(CY_CORTEX_M4_APPL_ADDR); 
    

    for(;;)
    {
        Cy_BLE_ProcessEvents();
    }
}

Add three global variables to main_cm4.c

The BLE Task will have a queue that the CapSense will use to send out changes in the CapSense Slider.

QueueHandle_t bleQueueHandle;
int connected=0;
int notify=0;

Create a new task to process the BLE

The. task will just start up the BLE processing on the CM4.  Then it will cause the BLE event manager to run.  If a new value of the CapSense slider is set, it will send out a notification using the “notifyCapSense” function

// bleTask handles the BLE connection (or not).. if the user connects and turns on
// notifications then it sends out new values of the CapSense slider
void bleTask(void *arg)
{
    (void)arg;   
    uint32_t msg;
    
    printf("Starting BLE Task\r\n");
    bleQueueHandle = xQueueCreate(1,sizeof(uint32_t));
    Cy_BLE_Start(customEventHandler);
    
    for(;;)
    {
        Cy_BLE_ProcessEvents();
        
        // CapSense task will put CapSense slider values in the bleQueueHandle
        if(xQueueReceive(bleQueueHandle,&msg,0) == pdTRUE)
            notifyCapSense((uint8_t)msg);
       
        taskYIELD();
    }
}

Create an Event Handler

You will need a task to handle the BLE processing on the CM4.  Each time a Bluetooth event occurs you will need to do “something”

// This event handler is called by BLE
void customEventHandler(uint32_t event, void *eventParameter)
{
     cy_stc_ble_gatts_write_cmd_req_param_t   *writeReqParameter;   

    switch (event)
    {
        case CY_BLE_EVT_STACK_ON:
        case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED: // Central disconnects
            Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST,CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
            connected = 0;
            notify=0;
        break;

        case CY_BLE_EVT_GATT_CONNECT_IND: // A Central connection is made
            connected = 1;
        break;
        
        case CY_BLE_EVT_GATTS_WRITE_REQ:
            writeReqParameter = (cy_stc_ble_gatts_write_cmd_req_param_t *)eventParameter;

            if(CY_BLE_CAPSENSE_CAPSLIDER_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_HANDLE ==  writeReqParameter->handleValPair.attrHandle)
                notify = writeReqParameter->handleValPair.value.val[0];
              
            Cy_BLE_GATTS_WriteRsp(writeReqParameter->connHandle);
        break;

        default: // Ignore all other events
        break;
    }
}

Make a Function to Send Notifications

// This function is run inside BLE Task and sends notification if you are connected and notifications are on
void notifyCapSense(uint8_t capValue)
{    
    if(!(connected && notify))
        return;
    
    cy_stc_ble_gatts_handle_value_ntf_t v1;
    v1.connHandle = cy_ble_connHandle[0];
    v1.handleValPair.attrHandle = CY_BLE_CAPSENSE_CAPSLIDER_CHAR_HANDLE;
    v1.handleValPair.value.len = 1;
    v1.handleValPair.value.val = &capValue;
    
    Cy_BLE_GATTS_Notification(&v1);
        
}

EDIT!!!!

If you look at the source code at GitHub you will find that I removed the variables “connected” and “notify” and I changed the notifyCapSense function.  This was done because the BLE PDL implementation was greatly improved and simplified. The new function iterates through the connections and sends the notification to all of them.  This was done to handle multiple connections.

// This function is run inside BLE Task and sends notification if you are connected and notifications are on
void notifyCapSense(uint8_t capValue)
{    
   
    unsigned int index;
    
    printf("Sending CapSense %d\r\n",capValue);
   
    cy_stc_ble_gatt_handle_value_pair_t handleValuePair;
    handleValuePair.attrHandle = CY_BLE_CAPSENSE_CAPSLIDER_CHAR_HANDLE;
    handleValuePair.value.val = &capValue;
    handleValuePair.value.len = 1;
    
    Cy_BLE_GATTS_WriteAttributeValueLocal(&handleValuePair);
   
    /* Iterate and send notiication to all the connected devices that are ready to receive notification */
    for(index = 0; index <CY_BLE_CONN_COUNT; index++)
    {     
        Cy_BLE_GATTS_SendNotification(&cy_ble_connHandle[index], &handleValuePair);
    }
        
}

Update the capSenseTask to Send Values

Inside of the capsenseTask you need to send new values of the slider to the BLE task via the Queue.  Meaning the BLE task needs to send out notifications, but it can only do that if the capsenseTask notifies it of changes.

sliderPos=CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
            if(sliderPos<0xFFFF) // If they are touching the slider then send the %
            {
                msg = sliderPos;
                xQueueSend(pwmQueueHandle,&msg,0); // Send message to PWM - change brightness
                xQueueSend(bleQueueHandle,&msg,0); // Send message to BLE
            }

Add the bleTask to main function in main_cm4.c

In the main function you need to start the BLE Task.

xTaskCreate( bleTask, "BLE Task",4096,0,3,0);

Program and Test

To test this project I will run the CySmart iOS App.  First, you can see all of the devices that are advertising

CySmart

Scroll left (or right) to find the “CapSense Slider”

When you see it, Press on the CapSense Slider Icon

Then put your finger on the slider and you should see it change

Here is a picture of the whole system.

PSoC 6 BLE CapSense & CySmart

Lesson 4 – PSoC 6 Introduction: CapSense

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

Project Description

This project is a demonstration of a PSoC 6 running an RTOS using CapSense and the UART to control the intensity of the Red LED with a Pulse Width Modulator (PWM)

The CapSense will:

  • BTN0 will set the intensity to 0%
  • BTN1 will set the intensity to 100%
  • CapSense slider will set the intensity (0-100) when touched

The UART will:

  • UART Key ‘0’ turn the LED to intensity = 0%
  • UART Key ‘1’ turn the LED to intensity = 100%
  • UART Key ‘5’ turn the LED to intensity = 50%
  • UART Key ‘?’ will printout help

How does it work?

You can control the intensity of an LED with a pulse width modulator and a high frequency input clock.  If the duty cycle is “small” then the intensity of the LED is low (meaning dim).  If the duty cycle is “big” then the intensity is “high”.  A 0% duty cycle will be off, a 50% duty cycle will be in the middle and 100% duty cycle will be all on.  To make this work I will set the input clock to 1 MHz and the PWM Period to 100.  The PWM Compare value will then be set between 0 and 100 to represent brightness in percent.

The Cypress CapSense block has two sensing modes, mutual capacitance and self capacitance.   The CY8CKIT-062-BLE has two Mutual Capacitance buttons and a 5 segment Self or Mutual Capacitance Slider.

The project will have three tasks

  • UART – Read data from the keyboard and send messages to the PWM Task
  • CapSense – Read finger positions from the slider and buttons and send messages to the PWM Task
  • PWM – Will have an input queue, when a integer message is received it will set the PWM compare value to the integer received (0 >= msg <= 100)

How am I going to do it?

  1. Create a new project by copy/paste the FreeRTOS-UART Project
  2. Configure Schematic for Higher Frequency Clock and Change PWM Settings
  3. Add a new PWM task and modify the UART
  4. Program and Test
  5. Edit the FreeRTOS.h and make a bigger heap
  6. Add the CapSense Buttons to the Schematic and Set the Pins
  7. Add the capsenseTask
  8. Program and Test
  9. Add the CapSense Slider to the Schematic and Set the Pins
  10. Update the capsenseTask
  11. Program and Test

Create a new Project by Copy/Paste of FreeRTOS-UART Project

Right click on the FreeRTOS-UART Project then select “Copy”

PSoC 6 Create New Project - Copy

Then right click on the Workspace and select “Paste”

PSoC 6 Create New Project - Paste

 

Right click on “FreeRTOS-UART-Copy01)” and rename it to “CapSense”

PSoC 6 Create New Project - Rename

Click “Rename” (all of these files will be regenerated so it doesn’t really matter)

PSoC 6 Create New Project - Rename

Close all windows by right clicking “Close All But This” on the start tab

PSoC Creator Close All Windows

Configure Schematic for Higher Frequency Clock and Change PWM Settings

If you blink the LED fast enough, you will not be able to see the “Blink” it will just appear to be bright or dim based on the duty cycle.  Start by changing the clock to 1 MHz.

PSoC 6 Update Clock

Double click on the PWM_1

PSoC 6 Update TCPWM

Fix the Period to be 100 and the compare to be 50

PSoC 6 Update TCPWM

The LED on the CY8CKIT-062-BLE is active low which means that when it is 0 the LED is on.  I want the opposite behavior so I can configure the PWM output to be inverted on the advanced tab.  Click the button next to “Invert PWM Output”

PSoC 6 Update TCPWM Advanced

Add a new PWM Task and Modify the UART Task

Next, I will create the pwmTask function.  It will simply wait for someone to send a message to the pwmQueue.  The message is simply a uint32_t that represents the PWM compare value … aka the percent intensity of the LED.  When it gets the value, it will update the hardware.

Edit: In the comments below you can see that Charles asked that I clarify where these functions should go.  When I did this, I put them basically at the top of the file… just under the #includes.   The only thing that really matters is that they need to be put before they are used so that the compiler knows the function prototypes.  If you put them after they are used then you need to make forward declarations.

#include "queue.h"
QueueHandle_t pwmQueueHandle; 

// This task controls the PWM
void pwmTask(void *arg)
{
    (void)arg;
    
    uint32_t msg;
    
    printf("Starting PWM Task\r\n");
    pwmQueueHandle = xQueueCreate(1,sizeof(uint32_t));
   
    while(1)
    {
        xQueueReceive(pwmQueueHandle,&msg,portMAX_DELAY);
        Cy_TCPWM_PWM_SetCompare0(PWM_1_HW,PWM_1_CNT_NUM,msg);
   }
}

In the main function you need to start the pwmTask

xTaskCreate(pwmTask,"PWM Task",configMINIMAL_STACK_SIZE*8,0,3,0);

Next, I will update the uartTask

  • To not getchar’s unless there is something in the RX buffer of the UART (line 13).
  • Create a message aka a uin32_t based on the key pressed and send it with xQueueSend
  • If there was nothing there then taskYIELD to one of the other tasks (line 35)
// uartTask - the function which handles input from the UART
void uartTask(void *arg)
{
    (void)arg;
    
    char c;
    uint32_t msg;

    printf("Started UART Task\r\n");
    setvbuf(stdin,0,_IONBF,0);
	while(1)
	{
		if(Cy_SCB_UART_GetNumInRxFifo(UART_1_HW))
		{
		    c = getchar();
    		switch(c)
	    	{
		    	case '0': // send the stop message
			    	msg = 0;
		    		xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);               
		    	break;
		    	case '1': // Send the start message
		    		msg = 100;
		    		xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);
		    	break;
			    case '5':
				    msg = 50;
				    xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);
			    break;
			    case '?': // Print Help 
				    printf("s - stop PWM\r\nS - start PWM\r\n");
			    break;
		    }
		}
		taskYIELD();
    }
}

Program and Test

PSoC 6 Test PWM

Edit the FreeRTOS.h and make a bigger heap

The CapSense task is going to take more heap space that is currently allocated for FreeRTOS.  To fix this I need to edit the FreeRTOS.h and change the heap size to 48K (which I picked out of the air)

#define configTOTAL_HEAP_SIZE                   (48*1024)

Add the CapSense Buttons to the Schematic and Set the Pins

In order to add CapSense to the design, you find the CapSense component in the Component Catalog. Then you drag it into your schematic.

PSoC 6 Add CapSense Component

The next thing to do is configure the CapSense block.  I start by re-naming it to “CapSense”.  Then I click on the plus and add two “Button”s to the design.

PSoC 6 Add CapSense Buttons

Then I change the configuration to “CSX (Mutual-cap).

PSoC 6 Setup Mutual CapSense

On the CY8CKIT-062-BLE, there is a shared transmit line. This saves 1 pin.  You need to tell the CapSense component that you are using a shared Tx.  To do this click on Advanced -> Widget Details.  Then pick “Button1_tx” and set the Sensor Connection / Ganging to “Button0_Tx”

PSoC 6 Setup CapSense Tx

Once the CapSense is configured you need to Set the Pins.

PSoC 6 Fix Pins

Next you run “Generate Application” which will let PSoC Creator place and route the design plus bring in all of the required drivers.

Add the capsenseTask

Now create a new task function called capsenseTask it will

  • Use 4 variables to hold the previous and current state of the buttons (line 81-84)
  • Start the CapSense Block (line 88)
  • Start CapSense Scanning (line 89)
  • If the CapSense hardware is not busy then you can talk to it. (line 93)
  • Start by turning the raw data into useable information (line 96)
  • Read the state of the buttons (line 98-99)
  • If the button state has changed, then send a message to the PWM Task (line 101-110)
  • Save the state of the buttons (line 111-112)
  • Update the baseline information (line 114)
  • Start Scanning again (line 115)
  • If the CapSense block is busy then yield to the other tasks (line 118)
// capsenseTask
// Read buttons and slider using CapSense and send messages to pwmQueue
void capsenseTask(void *arg)
{
    (void)arg;
    
    uint32_t msg;
    
    int b0prev=0;
    int b1prev=0;
    int b0current=0;
    int b1current=0;
    int sliderPos;
    
    printf("Starting CapSense Task\r\n");
    
    CapSense_Start();
    CapSense_ScanAllWidgets();
        
    for(;;)
    {
        if(!CapSense_IsBusy())
        {
            CapSense_ProcessAllWidgets();
/*
            sliderPos=CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
            if(sliderPos<0xFFFF) // If they are touching the slider then send the %
            {
                msg = sliderPos;
                xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);
            }
*/
            b0current = CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID);
            b1current = CapSense_IsWidgetActive(CapSense_BUTTON1_WDGT_ID); 
            
            if(b0current && b0prev == 0) // If they pressed btn0
            {
                msg = 0; 
                xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);
            }
            if( b1current && b1prev == 0) // If they pressed btn0
            {
               msg = 100;
               xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);  
            }
            b0prev = b0current;
            b1prev = b1current;
               
            CapSense_UpdateAllBaselines();
            CapSense_ScanAllWidgets();
        }
        else
            taskYIELD();
            
    }    
}

And start the capSense task in main.

xTaskCreate( capsenseTask, "CapSense Task",2048*2,0,3,0);

Program and Test

PSoC 6 Test CapSense Buttons

Add the CapSense Slider to the Schematic and Set the Pins

To make the slider work you need to add a LinearSlider widget by pressing the “+” and selecting LinearSlider

PSoC 6 Add CapSense Slider

Once you have added the LinearSlider you need to set the pins for the new CapSense Slider

PSoC 6 Fix CapSense Slider Pins

Update the capsenseTask to Include the Slider

The changes are small to make the LinearSlider work.  You just need to

  1. Read the position of the slider (line 99)
  2. The slider will be 0xFFFF if there is no touch… in which case ignore it (line 100)
  3. If you got a touch, then make that a message and set it to the PWM (line 102-103)
          sliderPos=CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
            if(sliderPos<0xFFFF) // If they are touching the slider then send the %
            {
                msg = sliderPos;
                xQueueSend(pwmQueueHandle,&msg,portMAX_DELAY);
            }

Program and Test

PSoC 6 CapSense Slider

Lesson 3 – PSoC 6 Introduction: FreeRTOS and a Debugging UART

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

Project Description

This project will show you how to build a PSoC 6 project that is running FreeRTOS and printing information to the debugging UART

How does it work?

The PSoC 6 is a very capable, fast dual Coretex-M MCU.  In order to manage your design complexity that can be attacked with this chip, we gave you the ability to use a Real Time Operating System – FreeRTOS.  With a few simple clicks it will startup for you.

The CY8CKIT-062-BLE has a KitProg-2 Debugger/Programmer on board.  In addition to Program/Debug it can also serve as a USB <–> UART bridge which will allow you to open a terminal program on your PC to do Input/Output with a UART in the PSoC 6.

How am I going to do it?

  1. Copy “MyFirstDesign” to Start a New Project
  2. Modify the Build Settings to add Debug Printing and FreeRTOS
  3. Add the UART to the Schematic and Assign the Pins
  4. Create the UART Test Firmware
  5. Program and Test the Debug UART
  6. Modify firmware for FreeRTOS
  7. Program and Test
  8. A Tour of PDL for the TCPWM

Copy “MyFirstDesign” to Start a New Project

Right click on “MyFirstDesign” and select “Copy”

PSoC Creator Copy Project

Then right click on the workspace and select “Paste”

PSoC Creator Copy/Paste

Then right click on “MyFirstDesign_Copy_01” and select rename.  Then type in a reasonable name like “FreeRTOS-UART”

PSoC Creator Copy / Paste

PSoC Creator will ask you about some files.  Just click “Rename”

PSoC Creator Rename

Now you will have three projects in your workspace.  I typically click “Program” which will build the copied project and program the development kit… just to make sure that stuff still works.

PSoC Creator Workspace

Modify the build settings to add debug printing and FreeRTOS

You can build massively complex systems with PSoC 6.  In order to help manage that complexity I like to turn on debug printing so that “printf” works.  I also like to use a Real Time Operating System (FreeRTOS).  PSoC Creator has both of these things built in.  We called the printf functionality “Retarget I/O” because you need to “target” the Input / Output of the printf … aka STDIN and STDOUT to a peripheral in the PSoC.  The best peripheral to use for printf is the UART that is attached to your computer via kitprog2.

To add printf and FreeRTOS to your project you need to: Right click on the project and select “Build Settings…”

PSoC Creator Modify Build Settings

Click on the Peripheral Driver Library and then select “FreeRTOS” and “Memory Management heap_4”.  Then select “Retarget I/O”.  When you build your project this will cause PSoC Creator to add both of these to your project.

PSoC Creator Build Settings FreeRTOS

Select Build->Generate Application and notice that PSoC Creator added a number of files to your project including the FreeRTOS, stdio_user.c/.h and retarget_io

PSoC Creator retarget_io.c

Add the UART to the Schematic and Assign the Pins

In order to printf, getchar and putchar  you need to attach c standard library stdin and stdout to “something”.  The best “something” is the UART that is attached to the KitProg2 which is attached to your computer.  Then you can open a terminal program (like Putty) and read and write to the PSoC.

So, to do this you need to add a UART to your design schematic from the component catalog.  In the picture below I just typed “uart” in the search box.

PSoC Creator Debug UART

What pins on the PSoC 6 BLE is the UART attached to?  Well… if you turn over your CY8CKIT-062-BLE you will find a little map that show that the PSoC 6 BLE UART is attached to the PSoC 5LP UART.  The PSoC 5LP is programmed with the KitProg2 firmware.

PSoC 6 CY8CKIT-062-BLE

To attach those pins in your firmware you just double click on the “Pins” tab in the DWR.  Then you can select “Port” for the UART RX and TX.  Look at the picture below.

PSoC 6 PSoC Creator Pin Settings

Now that I have all of that done, I run “Build->Generate Application” to get all of the new settings into my project.

Create the UART Test Firmware

The next step in making the printf work is to “retarget” it to the UART we just put in the schematic.  Open “stdio_user.h” and add “include <project.h>” (line 130) and change IO_STDOUT_UART and IO_STDIN_UART to be UART_1_HW.  The name “UART_1” on lines 136-137

#include <project.h>
#include "cy_device_headers.h"

/* Must remain uncommented to use this utility */
#define IO_STDOUT_ENABLE
#define IO_STDIN_ENABLE
#define IO_STDOUT_UART      UART_1_HW
#define IO_STDIN_UART       UART_1_HW

Now edit your main_cm4.c to:

  • Turn on the UART with UART_1_Start() on line
  • Then a couple of printfs lines 24-25

Note that “\033[2J\033[H” is just a VT100 escape sequence for clear screen and go home.

#include "project.h"
#include <stdio.h>

int main(void)
{
    __enable_irq(); /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

    PWM_1_Start();
    UART_1_Start();
    
    printf("3[2J3[H"); // Clear Screen
    printf("Test\r\n");
    
    
    for(;;)
    {
        /* Place your application code here. */
    }
}

Program and Test the Debug UART

You need to figure out which com port that the KitProg2 is attached to.  To do this run the device manager by pressing the “windows” button then typing “device manger”

Device Manager

Click on ports.  Then you can see that the programmer is “KitProg2” and that the UART is attached to COM9 (everyone will have a different number)

Device Manager

Run Putty (or  your favorite terminal program)

Putty

Press the program button on PSoC Creator and you should see your test print

Putty PSoC 6 UART

Modify firmware for FreeRTOS

When I look out the output window from the previous step you can see that there is a warning message from FreeRTOS.  Lets fix that warning (by deleting line 83).  While we are at it, lets change the heap size to 48K (48*1024)

Now lets turn on FreeRTOS. Double click on main_cm4.c to open it in the code editor.

Modify FreeRTOS Firmware

Add the following firmware to your project.

The uartTask is function that FreeRTOS uses to create a Task.  This task will control the uart. (which is amazingly enough why I named it uartTask)

  • The function call “setvbuff” turns of buffering on stdin… which means every character will immediately come through to your program
  • getchar just gets a character from the terminal
  • the ‘s’ and ‘S’ will start and stop the PWM (more to follow)

In main I

  • Create a task for the uartTask (line 56)
  • Start FreeRTOS (line 57)
/* ========================================
 *
 * Copyright YOUR COMPANY, THE YEAR
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF your company.
 *
 * ========================================
*/
#include "project.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"

// uartTask - the function which handles input from the UART
void uartTask(void *arg)
{
    (void)arg;
    char c;
    setvbuf(stdin,0,_IONBF,0);
    while(1)
    {
        c = getchar();
        switch(c)
        {
            case 's': // Stop the PWM
                printf("Stopped PWM\r\n");
                Cy_TCPWM_PWM_Disable(PWM_1_HW,PWM_1_CNT_NUM);
                
            break;
            case 'S': // Start the PWM
                printf("Started PWM\r\n");
                Cy_TCPWM_PWM_Enable(PWM_1_HW,PWM_1_CNT_NUM);
                Cy_TCPWM_TriggerStart(PWM_1_HW, PWM_1_CNT_MASK);
            break;
        }
    }
}


int main(void)
{
    __enable_irq(); /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

    PWM_1_Start();
    UART_1_Start();
    
    printf("3[2J3[H"); // VT100 Clear Screen
    printf("Test\r\n");
 
    // Mask a FreeRTOS Task called uartTask
    xTaskCreate(uartTask,"UART Task",configMINIMAL_STACK_SIZE,0,3,0);
    vTaskStartScheduler();  // Will never return
    
    for(;;) // It will never get here
    {
    }
}

How did I figure out how to start/stop the PWM?  To find the documentation for “PDL” aka the Peripheral Driver Library, you can right click on the component in the schematic and select “Open PDL Documentation”

PSoC 6 TCPWM

Or you can open “Help –> Documentation –> Peripheral Driver Library”

PSoC Creator Help

In the documentation I can see all kind of functions, including “Cy_TCPWM_PWM_Disable”

PSoC 6 PDL

Program and Test

PSoC 6 FreeRTOS Test

A Tour of PDL for the TCPWM

How did I figure out that the TCPWM_Type *base should be “UART_1_HW”?  ….. well …. You might ask yourself, “What does PWM_1_Start()” do?  It just calls PDL start function.  OK… so what does that do?  To find out you can right click on the “PWM_1_Start” function and select “Go To Definition” which will take you to the source code.

PSoC 6 Tour of PDL

You will end up in a file called “PWM_1.c”.  This file was created for you automatically by PSoC Creator.   This function is really simple.

  • If it has never been initialized then it calls init (lines 15-19)
  • Then it enables the TCPWM (line 22)
  • Then if there not an external pin on the PWM, then it causes a software trigger (line 25)
/*******************************************************************************
* Function Name: PWM_1_Start
****************************************************************************//**
*
*  Calls the PWM_1_Init() when called the first time and enables 
*  the PWM_1. For subsequent calls the configuration is left 
*  unchanged and the component is just enabled.
*
* \globalvars
*  \ref PWM_1_initVar
*
*******************************************************************************/
void PWM_1_Start(void)
{
    if (0U == PWM_1_initVar)
    {
        (void) Cy_TCPWM_PWM_Init(PWM_1_HW, PWM_1_CNT_NUM, &PWM_1_config);

        PWM_1_initVar = 1U;
    }

    Cy_TCPWM_Enable_Multiple(PWM_1_HW, PWM_1_CNT_MASK);
    
    #if (PWM_1_INPUT_DISABLED == 7UL)
        Cy_TCPWM_TriggerStart(PWM_1_HW, PWM_1_CNT_MASK);
    #endif /* (PWM_1_INPUT_DISABLED == 7UL) */    
}

So, you might ask yourself what is “PWM_1_HW”?  Well this is just the base address of the registers for the PWM_1.  Lets follow that.  Right click on it and go to definition.

PSoC 6 PDL

This will take you to PWM_1.h and you will see PWM_1_HW is setup as PWM_1_TCPWM_HW.  What is that?  Well do the right click thing again and find out.

/***************************************
*           API Constants
***************************************/

/**
* \addtogroup group_macros
* @{
*/
/** This is a ptr to the base address of the TCPWM instance */
#define PWM_1_HW                 (PWM_1_TCPWM__HW)

/** This is a ptr to the base address of the TCPWM CNT instance */
#define PWM_1_CNT_HW             (PWM_1_TCPWM__CNT_HW)

/** This is the counter instance number in the selected TCPWM */
#define PWM_1_CNT_NUM            (PWM_1_TCPWM__CNT_IDX)

/** This is the bit field representing the counter instance in the selected TCPWM */
#define PWM_1_CNT_MASK           (1UL << PWM_1_CNT_NUM)
/** @} group_macros */

#define PWM_1_INPUT_MODE_MASK    (0x3U)
#define PWM_1_INPUT_DISABLED     (7U)

PSoC 6 PDL

This will take you to “cyfitter.h” which is the output of the place and route.  It tell you that PWM_1_TCP is really TCPWM0

/* PWM_1_TCPWM */
#define PWM_1_TCPWM__CNT_HW TCPWM0_CNT0
#define PWM_1_TCPWM__CNT_IDX 0u
#define PWM_1_TCPWM__HW TCPWM0
#define PWM_1_TCPWM__IDX 0u

 

Lesson 2 – PSoC 6 Introduction: Your First Project

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

Project Description

This will be your first PSoC 6 project. You will build a simple blinking LED project that uses an internal clock and a Pulse Width Modulator (PWM) to drive the RED LED on the CY8CKIT-062-BLE.

How does it work?

The PWM will take as input a 1 KHz clock and divide it by 1000 (period=1000) aka 1 Hz with a 50% duty cycle (compare = 500) to create an LED that blinks at 1 Hz.

How am I going to do it?

  1. Start PSoC Creator and tour the Workspace
  2. Make a new PSoC 6 project
  3. Walk through Workspace
  4. Create a schematic including assigning pins
  5. Write the firmware
  6. Program the PSoC 6 BLE

Start PSoC Creator

When you first run PSoC Creator 4.2 you will get a screen that looks like this:

PSoC Creator

If you get a screen that looks like this, just click “No”.  This screen is asking for you to register the Keil Compiler and get a license.  This is only used for PSoC3

PSoC Creator Keil License

On the left is the Workspace Explorer which will contain all of the files & projects in your workspace… more to follow

The Start page has a bunch of clickable links to take you to learning, new projects, etc.  It also has a web browser that shows the stream of posts from the Cypress PSoC Creator blog.

PSoC Creator Start Page

If you loose this screen you can bring it back by going to View –> Other Windows –> Start Page

PSoC Creator - Start Window

If your windows get screwed up you can always Window->Reset Layout

PSoC Creator Reset Layout

The Output window will contain messages from PSoC Creator including errors, compiler output etc… more to follow

Make a New PSoC 6 Project

Start your first project with File –> New –> Project

PSoC Creator New Project

You need to choose to make a Design Project, a Library Project or a Workspace.  I want to make an actual design.  In order to know what chip the design is using I need to pick one of three options:

  1. Which development kit I have (PSoC Creator knows all of the Chips on the development kits)
  2. Which module (e.g. a bluetooth module)
  3. Which device

When you pick device it will let you start by picking the family (e.g. PSoC3/4/5/6) and then which specific device in the family.  The CY8CKIT-062-BLE has a PSoC 6 family PSoC 63 device on it.

PSoC Creator Specify Project Type

If you have previously built a project it will default to that device.

PSoC Creator New Project

If you need to switch just use the pulldown menu

PSoC Creator New PSoC 6 Project

If you have some specific device, you can use the “device selector” which has all of the 50 million options.   The screenshot has a section of the PSoC 6 devices.PSoC Creator Device Selector PSoC 6

An excellent trick is that you can pick the default device by right clicking, then choosing “Select Default Device” and the family.  The default device will almost always be the device with all of the options that is installed on the development kit.

PSoC 6 Device Selector

After the device is selected, you can setup the project to be exported to IAR, Keil, etc… (I am not going to show any of these but application note AN219434 talks about how to do it)

PSoC Creator Export IDEs

We give you the choice of building from a code example.

PSoC Creator Template

If you choose this option it will bring up the code example explorer

PSoC Creator Code Examples

If you click on a code example and the sample code tab in the window you can look and see how the firmware works without making a new project.

PSoC Creator PSoC 6 Code Examples

Or a pre-populated schematic which has a bunch of components that are already configured “normally” (whatever that means).  I tend to never do this, but it does prevent you starting with a blank project.

PSoC Creator PSoC 6 PrePopulated Schematic

Or an empty schematic (this is what I will do for all of the projects in this series)

PSoC Creator Empty Schematic

You need to specify the name of the workspace and the name of your project.

PSoC Creator New Project

Walk through Workspace

Starting with the Workspace Explorer.  PSoC Creator has the notion of a workspace which is a container for MULTIPLE projects.    When you double click any of the rows in the workspace explorer, PSoC Creator will open up that “File” for editing in the proper editor (code, schematic, etc)

Notice that in the picture below there are two projects.  The one in bold is called the “active” project… this is also known as the one that will program or build.  One of the great errors that people make is to edit a project other than the active one… then build and program… and then not understand why nothing is happening.

PSoC 6 Workspace Explorer

When you switch projects you need to right click on the project then select “Set As Active Project”

PSoC Creator Workspace Explorer

In the middle of the screen you will have a multiple tab thing.  Each tab represents one of the open documents.  There are different types of editors.  In the picture below you can see three documents are open.

PSoC Creator Schematic Editor

On the far right are the components that are available to be placed into your project. (more to follow).

PSoC 6 Component Catalog

Create a Schematic Including Assigning Pins

The first thing that I will do is make the blinking LED project.  The Hello World of MCUs.

(1) Place a digital output pin by dragging and dropping it from the component catalog.

PSoC Creator PSoC 6 Digital Output Pin

You can also find the digital output pin by typing into the search box

PSoC Creator Component Catalog

(2) Place a TCPWM

PSoC 6 TCPWM

(3) Place a Clock

PSoC 6 Schematic

(4) Wire the three of them together (press the wire symbol under the pointer symbol) or press “w” on the keyboard.  Then click on the pin boxes.

PSoC 6 Schematic

(5) Configure the pin to be called “RED” by double clicking and change the name

PSoC 6 Digital Output Pin

Now the schematic should look like this:

PSoC 6 Schematic

(6) Configure the clock to 1000 Hz

(7) Configure the TCPWM Period to 1000 and the Compare to 500 (this will divide the input clock by 1000 which will make a nice even blinking 1 Hz LED)

(8) Assign the RED Pin to P0[3]

Here is a picture of that section of the CY8CKIT-062-BLE

Write the Firmware and Program

Click on Build –> Generate Application which will place and route the design and then bring in all of the required Peripheral Driver Library (PDL) functions into your project.  You can see all of the steps that PSoC Creator is executing in the “Output Window”

The next step is to edit the main c file.  There are two in this chip.  One called “main_cmop.c” for the Cortex-Mo Plus and one called “main_cm4.c” for the Cortex-M4

In order for the TCPWM to work you need to turn it on, with the “PWM_1_Start()” function call.  You can do this in “main_cm4.c” which you can edit by double clicking it.


Program the PSoC 6 BLE

You can build and program the design by

  1. Pressing the little chip button (just to the left of the debug bug)
  2. Pressing Ctrl-F5
  3. Selecting Debug->Program

When you click program, PSoC Creator will bring up a window that looks like this.  It shows that there are two ARM MCUs connected to the programmer.  It doesn’t matter which one you select as the programming target because they are both connected to the same Flash (remember it is a shared memory multi processor)

After you click OK, PSoC Creator will compile all of the file and then program the chip

As soon as it is done, you will see a message “Finished Programming” and ” … successfully programed …”  Then you should see the PSoC 6 LED start blinking. (yes that is my finger … sorry)

Lesson 1 – PSoC 6 Introduction: Resources

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

Summary

This is an index with links to all of the PSoC 6 learning resources.  You can click the links to go the website or see screen captures of the resources.

  1. PSoC 6 Product Page
  2. PSoC 6 Documentation
  3. PSoC 6 Community
  4. CY8CKIT-062-BLE Development Kit Web Page
  5. CY8CKIT-062-BLE Development Kit Guide
  6. PSoC 6 Datasheet
  7. PSoC 6 Technical Reference Manuals
  8. Application Notes
  9. Videos
  10. Code Examples
  11. Knowledge Base
  12. PSoC Creator Help Page
  13. PSoC Creator Component Datasheet
  14. Peripheral Driver Library Documentation (Doxygen)

PSoC 6 Product Page

You can find the PSoC 6 Product landing page for PSoC 6 here

PSoC 6 Documentation

On the PSoC 6 Product Landing page there is a documentation tab that has links to all of the current documentation.

Community

Cypress has an active development community and forum.  It can be found here.

Development Kit Web Page

Every Cypress development kit has a web page that contains all of the information about it, including links to the documentation and store.  The CY8CKIT-062-BLE kit page is here.

CY8CKIT-062-BLE Development Kit Guide

PSoC 6 Datasheet

The PSoC 6 Datasheet is available on Cypress.com here.

Technical Reference Manual

Each of the PSoC 6 devices has a lengthy discussion of the Technical Resources.  These documents can be found here

Application Notes

You can get them all on our website… here is a link to the filtered list of PSoC 6 Application Notes.

The best application note is always the “Getting Started”.  In this case it is AN210781 “Getting Started with PSoC 6 MCU with Bluetooth Low Energy (BLE) Connectivity”

Code Examples

You can find all of the PSoC 6 code examples on the web.  In addition they are built into PSoC Creator.

Or in PSoC Creator:

Videos

Cypress has made a bunch of videos that take you step by step through an introduction to PSoC 6.  You can find them on the Cypress training website.

https://www.youtube.com/watch?v=vefexHmf_Us

Knowledge Base

The Cypress technical support team writes “Knowledge Base” articles when there are repeated issues reported by customers.  You can find them here.

PSoC Creator Help Page

There is a bunch of help on the PSoC Creator Help Page.

On the PSoC Creator Help page there are tons of resources including

PSoC Creator Component Datasheet

Every component in PSoC Creator can be right clicked -> datasheet

For example the datasheet for the UART

Peripheral Driver Library Documentation (Doxygen)

All of the APIs in the PDL are documented in a Doxygen generated HTML document.  You can get there from

  • Help -> Peripheral Driver Library (this link is live only when you have a PSoC 6 project open)
  • Right click on a component -> Open PDL Documentation

Lesson 0 – PSoC 6 Introduction: A Two Hour PSoC 6 Class

Summary

This is the top level web page for a two hour class about getting started with PSoC 6.  I was initially a bit worried about how much could be covered in only two hours… then I remembered how awesome PSoC Creator is at getting people going.  Originally, I was planning on writing a lab manual document describing all of the getting started exercises, but upon reflection, I decided to make a series of IoT Expert articles so that the content could be live.

I believe strongly that hands-on is best for real learning, so this class is built as four lab exercises for you to do by following along with my instructions.  I supplement the lab exercises with a survey of the PSoC 6 ecosystem… meaning all of the resources you can use to get help and learn.

You will need a few things for the class:

Todays virtual workshop agenda is as follows:

PSoC 6 Introduction

# Title Comment
0 A Two Hour PSoC 6 Class An introduction to the PSoC 6 class with links to all of the documents
1 Resources Links to all of the Cypress PSoC 6 information including videos, application notes etc.
2 Your First Project Learn how to build a PSoC 6 project and program your CY8CKIT-062-BLE development kit
3 FreeRTOS and a Debugging UART Build a project using FreeRTOS including a debug console with the UART
4 CapSense Build a project using the Mutual Cap Buttons and Self Cap Slider
5 Bluetooth Low Energy Build a CapSense project using BLE and CySmart

Since I did the webinar several things have happened

  1. Lesson 4: I fixed an error in the stack size for FreeRTOS
  2. Lesson 5: The PSoC Creator BLE PDL has been upgraded to greatly simplify the notifyCapSense function

All of the projects are available at git@github.com:iotexpert/psoc-6-introduction.git or www.github.com/iotexpert/psoc-6-introduction

PSoC Creator 4.2

The class is built on PSoC Creator 4.2 which is currently in beta.  Here is a screenshot (of the class workspace) that includes the example projects (which you can download).  It also shows that some of the components are still prototypes.  Don’t worry, Cypress will be releasing production silicon and final development software soon.

PSoC Creator

CY8CKIT-062-BLE

The class is built for the CY8CKIT-062-BLE.  Here is a picture of my development kit taken from the table at Starbucks where I am finishing the material for this class.

CY8CKIT-062-BLE PSoC 6 Development Kit

And here is the nice picture from our website (that the marketing guys made me use)

CY8CKIT-062-BLE

FreeRTOS FAT SL FileSystem Porting to PSoC4M

CY8CKIT-044 for FreeRTOS FAT FL FileSystem

Summary of FreeRTOS FAT SL FileSystem Port

In the previous article I discussed the Cypress 24V10 FRAM which I am going to use to store nonvolatile data for the FreeRTOS FAT SL FileSystem.  The “SL” in the name stands for “super light” and was built by HCC Embedded for, get this, embedded applications.

In this article I am going to show you how to build a media driver to make the FreeRTOS FAT SL FileSystem work.  In the next article I will talk about how to actually use the FreeRTOS FAT SL FileSystem on the CY8CKIT-044 using the Cypress FMV24V10 FRAM.

The media driver is pretty simple,  you just need to provide the following functions:

And two structures

  • F_DRIVER – Function pointers to the media driver functions
  • F_PHY – Information about the FRAM

To make all of this work I will start by copying the “ram” driver that was provided as an example (ill call mine framdrv_f.c).  The file that I started with resides in FreeRTOS-Plus/Source/FreeRTOS-Plus-FAT-SL/media-drv/ram/ramdrv_f.c

F_DRIVER Structure

The F_DRIVER structure mostly contains function pointers to the driver functions (which you create).  All throughout the FreeRTOS FAT SL FileSystem code it will do things like :

status = mdrv->getstatus( mdrv );

which calls the function in the media driver structure named “getstatus”.

Here is the exact definition of the F_DRIVER STRUCTURE (found in FreeRTOS-Plus-FAT-SL/api/api_mdriver.h) :

typedef struct F_DRIVER  F_DRIVER;

typedef int           ( *F_WRITESECTOR )( F_DRIVER * driver, void * data, unsigned long sector );
typedef int           ( *F_READSECTOR )( F_DRIVER * driver, void * data, unsigned long sector );
typedef int           ( *F_GETPHY )( F_DRIVER * driver, F_PHY * phy );
typedef long          ( *F_GETSTATUS )( F_DRIVER * driver );
typedef void          ( *F_RELEASE )( F_DRIVER * driver );

typedef struct F_DRIVER
{
  unsigned long  user_data;     /* user defined data */
  void         * user_ptr;      /* user define pointer */

  /* driver functions */
  F_WRITESECTOR          writesector;
  F_READSECTOR           readsector;
  F_GETPHY               getphy;
  F_GETSTATUS            getstatus;
  F_RELEASE              release;
} _F_DRIVER;

typedef F_DRIVER *( *F_DRIVERINIT )( unsigned long driver_param );

The user_data and user_ptr do not appear to be used anywhere in the FreeRTOS FAT SL FileSystem code, so I am not sure what they had in mind for those variables.

The F_PHY Structure

The other structure that is needed (sort of) is F_PHY.  This structure contains parameters that were originally meant for disk drives and are no longer used.  For the FRAM I will divide up the 128k array into “sectors” of “512” bytes.  For some reason which I don’t understand the sector size must be fixed to 512 bytes.  The history of FAT filesystems is incredibly messy, and I started to dig through it so that I understood the number, but I am not sure that there are enough hours in my life.

typedef struct
{
  unsigned short  number_of_cylinders;
  unsigned short  sector_per_track;
  unsigned short  number_of_heads;
  unsigned long   number_of_sectors;
  unsigned char   media_descriptor;

  unsigned short  bytes_per_sector;
} F_PHY;

As I wrote this article I didnt remember setting up the F_PHY structure… but things still worked (ill address this later in the article).

F_DRVERINIT()

This function sets up the function pointers and marks the in_use variable.

/****************************************************************************
 *
 * fram_initfunc
 *
 * this init function has to be passed for highlevel to initiate the
 * driver functions
 *
 * INPUTS
 *
 * driver_param - driver parameter
 *
 * RETURNS
 *
 * driver structure pointer
 *
 ***************************************************************************/
F_DRIVER * fram_initfunc ( unsigned long driver_param )
{
  ( void ) driver_param;

    UART_UartPutString("Calling init\n");

  if( in_use )
    return NULL;

  (void)psp_memset( &t_driver, 0, sizeof( F_DRIVER ) );

  t_driver.readsector = fram_readsector;
  t_driver.writesector = fram_writesector;
  t_driver.getphy = fram_getphy;
  t_driver.release = fram_release;

  in_use = 1;

  return &t_driver;
} /* fram_initfunc */

F_GETPHY()

This function returns information about the FRAM including the size (in sectors) and the size of the sectors (in bytes)

/****************************************************************************
 *
 * ram_getphy
 *
 * determinate ramdrive physicals
 *
 * INPUTS
 *
 * driver - driver structure
 * phy - this structure has to be filled with physical information
 *
 * RETURNS
 *
 * error code or zero if successful
 *
 ***************************************************************************/
static int fram_getphy ( F_DRIVER * driver, F_PHY * phy )
{
  /* Not used. */
  ( void ) driver;

  phy->number_of_sectors = maxsector;
  phy->bytes_per_sector = F_SECTOR_SIZE;

  return MDRIVER_RAM_NO_ERROR;
}

FRAM Helper Functions

In order to map sectors to the correct I2C address (remember the FRAM has two banks of memory in two different I2C addresses) and to the correct bank address, I created two function which map “sector” into address and I2C address.

static const unsigned long maxsector = FDRIVER_VOLUME0_SIZE / F_SECTOR_SIZE;
static const unsigned long halfsector = FDRIVER_VOLUME0_SIZE / F_SECTOR_SIZE / 2;

/****************************************************************************
 *
 * calcAddress
 *
 * This function takes a sector and returns the address in the bank for the
 * start of the secor
 *
 * INPUTS
 *
 * unsigned long sector - which logical sector in the FRAM
 * 
 * RETURNS
 *
 * The FRAM Address of the sector
 *
 ***************************************************************************/
static inline uint32_t calcAddress(unsigned long sector)
{
    if(sector < halfsector)
        return sector * F_SECTOR_SIZE;
    else
        return (sector-halfsector) * F_SECTOR_SIZE;
}

/****************************************************************************
 *
 * calcI2CAddress
 *
 * This function takes a sector from 0 --> maxsector and figures out which bank
 * the address exists.
 *
 * INPUTS
 *
 * unsigned long sector - which logical sector in the FRAM to write to
 * 
 * RETURNS
 *
 * The I2C Address of Bank 0 or Bank 1
 *
 ***************************************************************************/
static inline uint32_t calcI2CAddress(unsigned long sector)
{
    if(sector < halfsector)
        return 0x50; // I2C Bank 0 Address from the datasheet
    else
        return 0x51; // I2C Bank 0 Address - From the datasheet  
}

F_READSECTOR()

This function reads 512 bytes that are located in the sector into the RAM buffer pointed to by data.  Notice on lines 26 & 27 I put in debugging information.  In order to read you need to

  • Send a start (line 30)
  • Send the I2C address and the write bit
  • Set the address you want to read from (line 37-38)
  • Send a restart (line 41)
  • read the 512 bytes (lines 44-54)
  • On the last byte NAK (line 47-50)
/****************************************************************************
 *
 * fram_readsector
 *
 * This function reads 512 bytes into the SRAM at the pointer data
 *
 * INPUTS
 *
 * driver - driver structure
 * void *data - a pointer to the SRAM where the 512 bytes of data will be written
 * unsigned long sector - which logical sector in the FRAM to read from
 * 
 * RETURNS
 *
 * error code or MDRIVER_RAM_NO_ERROR if successful
 *
 ***************************************************************************/

static int fram_readsector ( F_DRIVER * driver, void * data, unsigned long sector )
{
    char buff[128]; // A scratch buffer for UART Printing
    (void)driver;
    uint16 address;
    uint32_t status;
    
    sprintf(buff,"Read sector %d\n",(int)sector);
    UART_UartPutString(buff);
    
    address = calcAddress(sector);
    status = I2C_I2CMasterSendStart( calcI2CAddress(sector),I2C_I2C_WRITE_XFER_MODE);
    if(status != I2C_I2C_MSTR_NO_ERROR)
    {
        UART_UartPutString("I2C Error\n");
        return MDRIVER_RAM_ERR_SECTOR;
    }
    int i;
    I2C_I2CMasterWriteByte((address>>8)&0xFF);
    I2C_I2CMasterWriteByte(address & 0xFF); // 
    
    
    I2C_I2CMasterSendRestart(calcI2CAddress(sector),I2C_I2C_READ_XFER_MODE);
    
    uint8_t d;
    for(i=0;i<F_SECTOR_SIZE;i++)
    {
        
        if(i != F_SECTOR_SIZE - 1)
            d = I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA);
        else
            d = I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA);
         
        *((uint8_t *)data + i) = d;
        
    }
    
    I2C_I2CMasterSendStop();
    
  return MDRIVER_RAM_NO_ERROR;
}

F_WRITESECTOR()

The write sector is very similar to the read.  To write the data you need to

  • Send a start (line 204)
  • Write the address you want to write to (line 205-206)
  • Write the 512 bytes (lines 208-214)
  • Send a stop (line 215)
/****************************************************************************
 *
 * fram_writesector
 *
 * This function takes 512 bytes of user input and writes to the FRAM in the
 *
 * INPUTS
 *
 * driver - driver structure
 * void *data - a pointer to the SRAM where the 512 bytes of data exists
 * unsigned long sector - which logical sector in the FRAM to write to
 * 
 * RETURNS
 *
 * error code or MDRIVER_RAM_NO_ERROR if successful
 *
 ***************************************************************************/

static int fram_writesector ( F_DRIVER * driver, void * data, unsigned long sector )
{
    (void)driver;
    char buff[128]; // A scratch buffer for UART Printing
    uint16 address;
    int i;
        
    sprintf(buff,"Wrote sector %d\n",(int)sector);
    UART_UartPutString(buff);
        
    address = calcAddress(sector);
    I2C_I2CMasterSendStart(calcI2CAddress(sector),I2C_I2C_WRITE_XFER_MODE);
    I2C_I2CMasterWriteByte((address>>8)&0xFF);
    I2C_I2CMasterWriteByte(address & 0xFF); // 
    
    for(i=0;i<F_SECTOR_SIZE;i++)
    {
        uint8_t d = *((uint8_t *)data +i);
       
        I2C_I2CMasterWriteByte(d); 
        
    }
    I2C_I2CMasterSendStop();
    
  return MDRIVER_RAM_NO_ERROR;
}

F_GETSTATUS()

As I wrote the article I noticed in the documentation that I needed to provide the “F_GETSTATUS” function.  The reason that I had not noticed before was that it was not in the media driver provided in the distribution.  That being said, my implementation always returns a 0 indicating OK.

/****************************************************************************
 *
 * fram_getstatus
 *
 * This function must return the status of the drive... F_ST_MISSING, F_ST_CHANGED
 * or F_ST_WRPROECT or 0 (for OK)
 * INPUTS
 *
 * driver_param - driver parameter
 *
 *
 * For the FRAM I dont support any of these other status's
 ***************************************************************************/

static long fram_getstatus(F_DRIVER *driver)
{
    (void) driver;
    // F_ST_MISSING	The media is not present (it has been removed or was never inserted).
    //F_ST_CHANGED	Since F_GETSTATUS() was last called the media has either been removed and re-inserted, or a different media has been inserted.
    //F_ST_WRPROTECT	The media is write protected.
    
    return 0;
}

F_RELEASE()

This function simply set the in_use to 0;

/****************************************************************************
 *
 * fram_release
 *
 * Releases a drive
 *
 * INPUTS
 *
 * driver_param - driver parameter
 *
 ***************************************************************************/
static void fram_release ( F_DRIVER * driver )
{
  /* Not used. */
  ( void ) driver;

  /* Disk no longer in use. */
  in_use = 0;
}

In the next article I will show you the source code for an example project using the FreeRTOS FAT SL Filesystem.

FreeRTOS PSoC Template Project

Summary

In the last article I showed you clever FreeRTOS PSoC Component… and the talked about some of the issues that I had with it.  In this article I will talk about a self-contained FreeRTOS PSoC Template project that includes everything for FreeRTOS and Tracealyzer all in one project.  My idea was that when I start a new project I will just make a copy of the template project, rename it, then move on.

My FreeRTOS PSoC Template has

  • FreeRTOS with the files in in the same directory (i.e. not referenced externally)
  • Tracealyzer with the files in the same directory & all of the streamports including RTT and the UART DMA
  • All of the files organized into PSoC Creator folders
  • A template main.c
  • All of the build settings configured

This project is checked into GitHub and you can find it git@github.com:iotexpert/PSoC-FreeRTOS-Template.git

Building the FreeRTOS PSoC Template

I started the process by creating a blank PSoC project called “FreeRTOS_Template”.  I then went into the Windows Explorer and copied the entire FreeRTOS source directory into the FreeRTOS PSoC Template Project directory.  Then I copied the Percepio TraceRecorder library into the FreeRTOS PSoC Template project.

FreeRTOS PSoC Template Directory Structure

By having both of the source directories in my project it isolated this project from changes in the FreeRTOS or the TraceRecorder.  Obviously that is a double edged sword.  The next thing that I did was use the Windows Explorer to copy FreeRTOSConfig.h, trcConfig.h, trcSnapshotConfig.h and trcStreamingConfig.h into the project.   Once all of the files were in my project directory is was time to fix up the PSoC Creator Project.

To do this I created a folder called “FreeRTOS_Source” in the “Header Files” and “Source Files” folders in my template project.  You can do this by right clicking and selecting “Add->New Folder”.  Then I added all of the appropriate .h and .c files from FreeRTOS source directory.  This gives me the following project view:

FreeRTOS PSoC Template PSoC Creator Header Files

And Source Files (notice that I also added heap_4.c which I generally use for memory management)

FreeRTOS PSoC Template PSoC Creator Source Files

Then I add the configuration files FreeRTOSConfig.h, trcConfig.h, trcSnapshotConfig.h, trcStreamingConfig.h and trcStreamingport.h.  Next I do the same thing for the TraceRecorder library which makes my project look like this:

Then I modify the build settings to add the include directories:

FreeRTOS PSoC Template Build Settings

FreeRTOS PSoC Template PSoC Creator Include Path

Now you can modify the FreeRTOSConfig.h to include all of the Tracealyzer stuff:

/* A header file that defines trace macro can be included here. */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#endif

#endif /* FREERTOS_CONFIG_H */

Then I setup a new tab in the schematic to contain the DMA UART Streamport.  You can read all about the UART DMA Streamport in this article.

FreeRTOS PSoC Template PSoC Creator Streamport Schematic

By putting that part of the stream port on a separate schematic page I can now do a right click and disable the page when I am not using the Tracealyzer streamport.  Disabling a page of the schematic completely removes everything from the build.

PSoC Creator Schematic Disable Settings

Next I create files called FreeRTOS_Start.h/.c to put in the startup code:

#include <project.h>
#include "FreeRTOS.h"

extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);

#define CORTEX_INTERRUPT_BASE          (16)
void FreeRTOS_Start()
{
    /* 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 );
}

Finally I make a template main.c that starts everything and has a simple ledTask and instructions for changing Memory Management scheme and the TraceRecorder.

// This template has heap_4.c included in the project.  If you want a 
// different heap scheme then remove heap_4 from the project and add 
// the other scheme from FreeRTOS/Source/portable/memmag

// TraceRecorder
// There are methods
// 1. Snapshot mode
// 2. Streaming UART/DMA
// 3. Streaming JLINK RTT
//
// To use method 1: 
// - in FreeRTOSConfig.h make this line be 1 (currently line 45)
//#define configUSE_TRACE_FACILITY                1
// - in trcConfig.h configure the TRC_CFG_RECORDER 
// #define TRC_CFG_RECORDER_MODE TRC_RECORDER_MODE_SNAPSHOT
//
// To use method 2:
// - in FreeRTOSConfig.h make this line be 1 (currently line 45)
//#define configUSE_TRACE_FACILITY                1
// - in trcConfig.h configure the TRC_CFG_RECORDER
//#define TRC_CFG_RECORDER_MODE TRC_RECORDER_MODE_STREAMING
//
// This project currently has the PSoC UART Streaming Port
// add the TraceRecorder/streamports/PSoC_Serial/trcStreamingPort.c to the project
// add the TraceRecorder/streamports/PSoC_Serial/include/trcStreamingPort.h
// add the TraceRecorder/streamports/PSoC_Serial/include to the compiler include directories
// Enable the Trace_DMA schematic sheet and make sure UART pins are assigned correctly
// This port depends on the UART being named UART and the DMA being named "DMA"
//
// To use method 3: JLINK RTT
// Remove the previous streamport files from the project
// Remove the Streamport include path
// add the TraceRecorder/streamports/JLink_RTT/Segger_RTT.c to the project
// add the TraceRecorder/streamports/JLink_RTT/Segger_RTT_Printf.c to the project
// add the TraceRecorder/streamports/JLink_RTT/include/trcStreamingPort.h
// add the TraceRecorder/streamports/JLink_RTT/include/Segger_RTT.h
// add the TraceRecorder/streamports/JLink_RTT/include/Segger_RTT_Conf.h
// add the TraceRecorder/streamports/JLink_RTT/include to the compiler include directories


#include "project.h"
#include "FreeRTOS.h"
#include "timers.h"

// An example Task
void ledTask(void *arg)
{
    (void)arg;
    while(1)
    {
        RED_Write(~RED_Read());
        vTaskDelay(500);  
    }
}

int main(void)
{
    CyGlobalIntEnable;
    FreeRTOS_Start();
    
    #if ( configUSE_TRACE_FACILITY == 1 )
    vTraceEnable(TRC_START);
    #endif
    
    xTaskCreate(ledTask,"LED Task",configMINIMAL_STACK_SIZE,0,1,0);
    vTaskStartScheduler();  // Will never return
    while(1);               // Eliminate compiler warning
}

 

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

FreeRTOS PSoC Component

Summary

This weekend I found myself down a rabbit hole, beating my head on the wall trying to make a USB driver work properly inside of Parallels & Windows 10 on my Mac (which I still don’t have quite right).  While going through that excruciating process I ended up creating 3-4-5 different FreeRTOS PSoC projects, which although not difficult, is still a bit of a pain as it requires 1/2 dozen step.  After reflecting on it a while I decided that I needed something easier.  The obvious thing to do was to build a FreeRTOS PSoC Component, which I did, but eventually abandoned (Ill talk about that process in the next section).  After the component idea was abandoned I decided to just make a template project that could be used to start a FreeRTOS PSoC Project (the next Article).

In this article I will show you:

  • An example project using a clever FreeRTOS PSoC Component that I found on GitHub built by E2ForLife
  • A discussion of the implementation details of that FreeRTOS PSoC Component
  • A discussion of problems with that implementation which lead me to build a template project.

FreeRTOS PSoC Component Example Project

When I first started looking at FreeRTOS I wasn’t totally sure where to start.  When I googled FreeRTOS PSoC, one of the first hits was this repo which contains a PSoC Component that was built by “E2ForLife”.  I really liked the idea of a component based FreeRTOS implementation, as all you would need to do is place the components… and then away you go.  The more that I looked at the component the more that I liked what E2ForLife had done.  When I cloned his repo, there was “nothing” in it, but it turns out there is another branch called “Implement-PSoC5” which makes me think that he was partially done (he hasn’t been changed since August 2015)

First, his example project.  When you look at the schematic you see a nice looking FreeRTOS PSoC Symbol (obviously E2ForLife draws way better than me).

FreeRTOS PSoC Component

When you double click the component you see all of the stuff that is normally in FreeRTOSConfig.h which you can set using the GUI instead of the FreeRTOSConfig.h

FreeRTOS PSoC Component Configuration

When you “Generate Application” you can see that he setup all of the FreeRTOS files to come into the Generated Source automatically (though with different names)

FreeRTOS PSoC Component API

And his FreeRTOS PSoC Component example project is straight forward

#include <project.h>

xTaskHandle redTask;
xTaskHandle blueTask;
xTaskHandle greenTask;

void vRedTask( void* pvParameters);
void vGreenTask( void* pvParameters);
void vBlueTask( void* pvParameters);

int main()
{
    CyGlobalIntEnable; /* Enable global interrupts. */

	xTaskCreate(vRedTask,"red",80,NULL,3,&redTask);
	xTaskCreate(vGreenTask,"green",80,NULL,3,&greenTask);

	FreeRTOS_Start();
	
    for(;;);
}

void vRedTask( void* pvParameters)
{
	for(;;) {
		vTaskDelay( 125/portTICK_RATE_MS );
		RED_Write( ~RED_Read() );
	}
}

void vGreenTask( void* pvParameters)
{
	for(;;) {
		vTaskDelay( 219/portTICK_RATE_MS );
		GREEN_Write( ~GREEN_Read() );
	}
}

He built a function called “FreeRTOS_Start()” which installs the interrupt vectors then starts up the scheduler.

uint8 FreeRTOS_initVar = 0;

/* ------------------------------------------------------------------------ */
void FreeRTOS_Init( void )
{
    /* 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 );
	
	FreeRTOS_initVar = 1;
}
/* ------------------------------------------------------------------------ */
void FreeRTOS_Enable( void )
{
	/* start the scheduler so the tasks will start executing */
	vTaskStartScheduler();	
}
/* ------------------------------------------------------------------------ */
void FreeRTOS_Start( void )
{
	if (FreeRTOS_initVar == 0) {
		FreeRTOS_Init();
	}
	FreeRTOS_Enable();
	
	/*
	 * After the scheduler starts in Enable(), the code should never get to
	 * this location.
	 */
	for (;;);
}

And when you run the FreeRTOS PSoC Component project you get the nice blinking LED (Red and Green)

CY8CKIT-042

FreeRTOS PSoC Component

After downloading the project, then opening it I first wanted to look at the components.  You can do this by clicking the “Components” tab in the workspace explorer.  It looks like he created (or was planning to create) several other components in addition to the “FreeRTOS”.

FreeRTOS PSoC Component

When you double click on the “FreeRTOS_v8_2.cysym” you will get an editable view of the symbol.

FreeRTOS PSoC Component Symbol

When you right click on the blank part of the canvas, PSoC Creator will bring up this menu.

PSoC Creator Configuration

On the properties menu you can configure which tab the component belongs to in the Component Catalog.  In this case he created a tab called “Community” which looks like this in the actual Component Catalog.

PSoC Creator Component Catalog

To make that happen he setup the “Doc.CatalogPlacement” to be “Community/Operating System/FreeRTOS” (on the properties menu)

FreeRTOS PSoC Component Parameter Configuration

The next thing that he did was add a whole bunch of Symbol Parameters which will let the user change the setup of FreeRTOS. Each  of these parameters show up as an editable field on the component customers (in the schematic).

FreeRTOS PSoC Parameter

In the picture above you can see that he created two new “enumerated” datatypes called “FreeRTOS_CheckStackOverFlowType” “FreeRTOS_MemMangType”.  In the picture below you can see the legal values for that type.  This lets him restrict the things that the user of the component can select when he places it in his schematic.

FreeRTOS PSoC Component Configuration

Here is what the component looks like when the user actually uses it.  You can see the legal fields (from above) and the legal values for those fields (from above)

FreeRTOS PSoC Component Configuration

The next thing that he did was copy all of the FreeRTOS files into component API.

FreeRTOS PSoC Component API

If you remember each FreeRTOS project needs to have a file called “FreeRTOSConfig.h”.  But, that file doesn’t appear to exist in the list above.  How is that?  Remember that when PSoC Creator builds a project it copies the API into your generated source directory, but it renames each of the files to be INSTANCE_NAME_filename.  In this example, “Config.h” will become “FreeRTOS_Config.h” (FreeRTOS is the instance name in the schematic)

In FreeRTOS each of the features of the RTOS turn into a #define that is 0 or 1.  When the component was created he modified “Config.h” so that each of the parameters on the component set values for the #defines in the “Config.h” file.  And there is a bunch of them so that must have taken a good bit of work.  Here is an example of a section of his “Config.h”.  Each place you see the back-tick $parameter back-tick PSoC Creator will substitute the value of the parameter (I am typing back-tick because WordPress interprets the actual symbol to mean something special and I don’t know how to get it into the text)

#if CY_PSOC4
    
    /* Port-dependent settings - not user-editable */
    #define configCPU_CLOCK_HZ          ( ( unsigned long ) CYDEV_BCLK__SYSCLK__HZ )
        
    /* Application settings - user editable */
    #define configMAX_PRIORITIES		( `$MAX_PRIORITIES` )
    #define configTOTAL_HEAP_SIZE		( `$TOTAL_HEAP_SIZE` )
    
#elif CY_PSOC5
    
    /* Device settings - not user-editable */
    #define configCPU_CLOCK_HZ			( ( unsigned long ) BCLK__BUS_CLK__HZ )
        
    /* Application settings - user editable */
    #define configMAX_PRIORITIES        ( `$MAX_PRIORITIES` )
    #define configTOTAL_HEAP_SIZE		( `$TOTAL_HEAP_SIZE` ) 
    
#else
    
    #error "This FreeRTOSConfig.h file is for PSoC 4 and PSoC 5LP devices only"
    
#endif

#define configUSE_PREEMPTION			`$USE_PREEMPTION`
#define configUSE_IDLE_HOOK				`$USE_IDLE_HOOK`
#define configUSE_TICK_HOOK				`$USE_TICK_HOOK`
#define configTICK_RATE_HZ				( ( portTickType ) `$TICK_RATE_HZ` )

Because each filename changes name, he had to go fix all of the #includes to look like this example from croutine.c (this was a bunch of work)

/* PSoC Component Customizations */

#include "`$INSTANCE_NAME`.h"
#include "`$INSTANCE_NAME`_task.h"
#include "`$INSTANCE_NAME`_croutine.h"

He also wanted to be able to have all of the FreeRTOS memory management schemes included in the project at one time.  To solve this problem he put an #if around all of the code in heap_1, heap_2 … that adds and removes the appropriate memory manager.

#if (`$MemManager` == 2)

.
.
.
.
.

#endif

A FreeRTOS PSoC Template Project

When I started working on FreeRTOS I knew that I wanted to use V9.0.  But the component was setup for V8.2.  So I launched into the process of converting the component.  This turned out to be a colossal pain in the ass because of the massive number of “small” changes.  In addition I didn’t really like having to change the names of key files (even the stupidly named semphr.h).  Moreover, the source code as a component debate was raging wildly inside of Cypress and in general is on the way out.  So I decided to implement this as a template project instead of a component.

All that being said, what E2ForLife did I continue to think was really clever.

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 Task Notification Value

Summary

In the previous article I showed you how to use the FreeRTOS task notification mechanism to replace a binary semaphore.  In this article I will build PSoC FreeRTOS Task Notification Value firmware.  Every FreeRTOS task has one built-in “uint32_t” which you can use to pass information between the task that sends the notification and the task that receives the notification.  In this article, instead of using the task notification as a binary semaphore, I will use it as a Queue of depth 1 that can hold one 32-bit word.  I will also show you a lesson that I learned about the interrupt registers in the SCB UART.

PSoC FreeRTOS Task Notification Value

I start by copying the project 9-TaskNotify and calling it 10-TaskNotifyValue.  I thought that it would be interesting to pass the cause of the UART interrupt to the UART Task, so that it could be told to the user.  To do this I call “xTaskNotifyFromISR” instead of “vTaskGiveFromISR”.   This function lets me set the value of the FreeRTOS Task notification value, is case to the bit mask of the cause of the UART interrupt.  The function also lets you specific if you want to

  • eNoAction – don’t do anything (this is what the xTaskNotifyFromISR does)
  • eSetBits – or the current notification value with the value you send
  • eIncrement – increment the notification value by 1 (in which case it ignore the value you send)
  • eSetValueWithOverwrite – replace the current notification value with the value passed in this function
  • eSetValueWithoutOverwrite – if there is no pending write, then write the value from this function into the notification value

In the UART_Task I take the value and then clear all of the bit (aka set it to 0) when I exit.

CY_ISR(uartHandler)
{
    uint32_t intrBits = UART_GetRxInterruptSourceMasked();
    UART_SetRxInterruptMode(0); // Turn off the Rx interrupt
    BaseType_t xHigherPriorityTaskWoken;
    xTaskNotifyFromISR( uartTaskHandle,intrBits,eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
void UART_Task( void *arg)
{
    (void)arg;
    char c;
    UART_Start();
    UART_SetCustomInterruptHandler(uartHandler);
    while(1)
    {
        uint32_t intrBits;
              
        xTaskNotifyWait( 0x00,         /* Don't clear any bits on entry. */
                         ULONG_MAX,          /* Clear all bits on exit. */
                         &intrBits, 
                         portMAX_DELAY );    /* Block indefinitely. */
      
        switch(intrBits)
        {
            case UART_INTR_RX_NOT_EMPTY:
                UART_UartPutString("Interrupt: FIFO Not Empty\n");
            break;
            
            case UART_INTR_RX_ERR:
                UART_UartPutString("Interrupt: Error\n");
            break;
                
            case UART_INTR_RX_FULL:
                UART_UartPutString("Interrupt: FIFO Full\n");
            break;
            
            default:
                UART_UartPutString("Interrupt: Unknown\n");
            break;
        }
        
        while(UART_SpiUartGetRxBufferSize())
        {
            c = UART_UartGetChar();
            UART_UartPutString("Char = ");
            UART_UartPutChar(c);
            UART_UartPutString("\n");
        }
        // re-enable the interrupt
        UART_ClearRxInterruptSource(UART_INTR_RX_NOT_EMPTY);
        UART_SetRxInterruptMode(UART_INTR_RX_NOT_EMPTY);
    }
}

New Learning

When I first wrote the program I had something “weird” happening.  Specifically it looked like I was getting the interrupt service routine called twice:

I originally wrote the code like this:

CY_ISR(uartHandler)
{
    uint32_t intrBits = UART_GetRxInterruptSourceMasked();
    UART_SetRxInterruptMode(0); // Turn off the Rx interrupt
    UART_ClearRxInterruptSource(UART_INTR_RX_NOT_EMPTY);
    BaseType_t xHigherPriorityTaskWoken;
    xTaskNotifyFromISR( uartTaskHandle,intrBits,eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

But, what happens is:

  1. Turn off interrupts
  2. Clear the interrupt source (meaning turn off the flag in the SCB that says there is a character in the Rx Buffer)
  3. One or so UART clock cycles later the flag is reset (because there is still something in the UART Rx Buffer)
  4. Send the notification to the UART Task
  5. The UART Task wakes up and processes the UART Rx Buffer
  6. The UART Task turns back on the interrupts
  7. The interrupt is called because the RX_NOT_EMPTY flag is still set (it is set until it is clear)
  8. The interrupt handler clears the flag (which isn’t reset this time because there is nothing in the Rx buffer)
  9. The interrupt handler sends the notification
  10. The UART Task wakes up again… prints out the Flag..
  11. The UART Task tries to read out of the Rx Buffer… but there isn’t anything in it.

Each time I start thinking that I know what I am doing, I find something else to learn.  I suppose that is what makes this whole thing fun though.

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