FreeRTOS PSoC Examples (Part 1)

Summary: FreeRTOS PSoC Examples

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

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

In this article I will show FreeRTOS PSoC Examples:

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

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

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

Single Blinking LED

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

  • Installs the FreeRTOS vectors (lines 9-22)
  • Creates the LED Task (lines 41-48)
  • Starts the scheduler (line 50)
  • Runs the LED_Task until the end of time, blinking the LED (lines 25-33)
#include "project.h"
#include "FreeRTOS.h"
#include "timers.h"

extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
#define CORTEX_INTERRUPT_BASE          (16)
void setupFreeRTOS()
{
    /* Handler for Cortex Supervisor Call (SVC, formerly SWI) - address 11 */
    CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SVCall_IRQn,
        (cyisraddress)vPortSVCHandler );
    
    /* Handler for Cortex PendSV Call - address 14 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + PendSV_IRQn,
        (cyisraddress)xPortPendSVHandler );    
    
    /* Handler for Cortex SYSTICK - address 15 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SysTick_IRQn,
        (cyisraddress)xPortSysTickHandler );
}


void LED_Task(void *arg)
{
    (void)arg;
    
	while(1) {
        RED_Write(~RED_Read());
        vTaskDelay(500);
	}
}

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

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

UART Thread

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

FreeRTOS PSoC Examples - PSoC Schematic

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

  • Starts the UART
  • Gets a key from the keyboard (line 47)
  • If it is an ‘a’ it prints out “Working” (line 51)
  • If it is a ‘t’ it calls the function vTaskList which makes a dump of the process currently running (more on this later) (lines 54-60)
#include "project.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
#include "timers.h"

extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
#define CORTEX_INTERRUPT_BASE          (16)
void setupFreeRTOS()
{
    /* Handler for Cortex Supervisor Call (SVC, formerly SWI) - address 11 */
    CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SVCall_IRQn,
        (cyisraddress)vPortSVCHandler );
    
    /* Handler for Cortex PendSV Call - address 14 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + PendSV_IRQn,
        (cyisraddress)xPortPendSVHandler );    
    
    /* Handler for Cortex SYSTICK - address 15 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SysTick_IRQn,
        (cyisraddress)xPortSysTickHandler );
}


void LED_Task(void *arg)
{
    (void)arg;
    
	while(1) {
        RED_Write(~RED_Read());
        vTaskDelay(500);
	}
}

void UART_Task(void *arg)
{
    (void)arg;
    static char buff[500];
    UART_Start();
    UART_UartPutString("Started UART\n\r");
    char c;
    
	while(1) {
        c = UART_UartGetChar();
        switch(c)
        {
            case 'a':
            UART_UartPutString("Working\n\r");
            break;
            
            case 't':
                UART_UartPutString("********************************************\n\r");
                UART_UartPutString("Task          State   Prio    Stack    Num\n\r"); 
                UART_UartPutString("********************************************\n\r");
                vTaskList(buff);
                UART_UartPutString(buff);
                UART_UartPutString("*********************************************\n\r");
   
            break;
        }
        taskYIELD();
    }
}

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    
    setupFreeRTOS();
  
    /* Create LED task, which will blink the LED */
    xTaskCreate(
        LED_Task,       /* Task function */
        "LED Blink",    /* Task name (string) */
        200,            /* Task stack, allocated from heap */
        0,              /* No param passed to task function */
        1,              /* Low priority */
        0 );            /* Not using the task handle */
   
    
    /* Create UART Task which will control the serial port */
    xTaskCreate(
        UART_Task,       /* Task function */
        "UART",          /* Task name (string) */
        0x400,           /* Task stack, allocated from heap */
        0,               /* No param passed to task function */
        1,               /* Low priority */
        0 );             /* Not using the task handle */
   
    vTaskStartScheduler();
    while(1);
}

 

Testing the project

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

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

FreeRTOS PSoC Examples UART Serial Port

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

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

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

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

Implementing PSoC Printf Part Deux

Summary

After I wrote the first article on Implementing PSoC Printf I got feedback from several people who made good suggestions.  Fundamentally, both of the people making commentary suggested ways of improving the memory footprint of PSoC printf.  In this article I am going to explain 5 different implementations and the pros/cons of those implementations.

Scheme Flash Total RAM Heap Stack Data/BSS Comment
vsprintf 4838 2852 2048 128 676 A function using sprintf + macros
iotexpert sprintf 4806 2852 2048 128 676 A macro trick – not as robust as vsprintf
iprintf 2952 2580 2048 128 674 restricted to %s,%d,%c,%x
printf 6496 3492 2048 1024 420 Fully featured printf from previous article
iprintf newlib nano 6496 3492 2048 1024 420 Same as printf (iprintf is aliased to printf)

To get these numbers I used the following simple program to implement PSoC printf with all of the default setting for compiler etc.

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    UART_Start();
    
    printf("Int=%d  String=%s\n",1,"asdf");


    for(;;)
    {
    }
}

You can “git” all of these projects at the IoT Expert GitHub site git@github.com:iotexpert/SerialPrintf.git

vsprintf & DBG_PRINTF

The first suggestion that I got was from a follower in China named Helon Chan.   After a little bit of back and forth, he sent me this code, which I think is very simple and elegant.  The way that this PSoC printf works is that it:

  • Uses “vprintf” which is a form of  “sprintf”.   The “v” in “vsprintf” stands for va_args.  By using the va_arg version of sprinf he eased the calling of sprintf (lines 16-18)
  • Uses a loop to print the buffer onto the screen (line 23-26)

The other nice thing that he did is create a macro that you can turn on and off the printing of debug messages (lines 29-34)

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

#define MAX_FORMAT_BUFFER_SIZE	(255)
static uint8_t s_formatBuffer[MAX_FORMAT_BUFFER_SIZE];

void dbg_printf(char *format,...)                                      ///< Function Name        
{
	uint8_t iWriteNum = 0,i=0;	
	va_list  ap;
	
	if(!format)
		return;
	
	va_start(ap,format);
	iWriteNum = vsprintf((char *)s_formatBuffer,format,ap);
	va_end(ap);
	
	if(iWriteNum > MAX_FORMAT_BUFFER_SIZE)
		iWriteNum = MAX_FORMAT_BUFFER_SIZE;
	
	for(i=0;i<iWriteNum;i++)
    {
		UART_UartPutChar(s_formatBuffer[i]);                            ///< Uart Function        
	}
}

#define DBG_ENABLE                              (1)                                 ///< Enable Printf
#if DBG_ENABLE
    #define DBG_PRINTF(format,...)              dbg_printf(format,##__VA_ARGS__)
#else
    #define DBG_PRINTF(...)
#endif

IoT Expert Version of vsprintf

After I looked at the previous implementation I thought of a really simple way to do the same thing.  Here is my version of the vsprintf:

#define MAX_FORMAT_BUFFER_SIZE	(255)
static uint8_t s_formatBuffer[MAX_FORMAT_BUFFER_SIZE];
#define asprintf(...) sprintf((char *)s_formatBuffer,__VA_ARGS__); UART_UartPutString((const char *)s_formatBuffer)

My version works almost exactly the same and is the same size as the previous version.  The weakness is that it does not error check at all.  Also if you called it enough times it would eventually use more flash than the previous version.

iprintf – An integer restricted printf

I got a tweet from Jose Marcelino in which he references the Cypress Knowledge Base Article KBA87093 in which someone implemented a PSoC printf that works only with a restricted set of formatting codes, %d (integer), %s (string),%x (hex integer) and %c (character).

The function is very easy to use.  All you need to do is copy iprintf.c and iprintf.h into your project.  Then you can use it by calling “iprintf”.  Here is the source code for iprintf.h

#ifndef IPRINTF_H
#define IPRINTF_H
#include <project.h>
void iprintf(char8 *pszFmt,...);
#endif

And iprintf.c

#include "iprintf.h"

static void iputc(char8 c)
{
    UART_UartPutChar(c);
}

static uint8* change(uint32 Index)
{
    return (uint8*)("0123456789abcdef"+Index);
}

void iprintf(char8 *pszFmt,...)
{
    uint8 *pszVal;
    uint32 iVal, xVal, i = 0, buffer[12], index = 1;
    uint8 cVal;
    uint32 *pArg;
    pArg =(uint32 *)&pszFmt;

    while(*pszFmt)
    {
        if('%' != *pszFmt)
        {
            iputc(*pszFmt);
            pszFmt++;
            continue;
        }
        pszFmt++;

        if(*pszFmt == 's')
        {
            pszVal = (uint8*)pArg[index++];
            for(; *pszVal != '
#include "iprintf.h"
static void iputc(char8 c)
{
UART_UartPutChar(c);
}
static uint8* change(uint32 Index)
{
return (uint8*)("0123456789abcdef"+Index);
}
void iprintf(char8 *pszFmt,...)
{
uint8 *pszVal;
uint32 iVal, xVal, i = 0, buffer[12], index = 1;
uint8 cVal;
uint32 *pArg;
pArg =(uint32 *)&pszFmt;
while(*pszFmt)
{
if('%' != *pszFmt)
{
iputc(*pszFmt);
pszFmt++;
continue;
}
pszFmt++;
if(*pszFmt == 's')
{
pszVal = (uint8*)pArg[index++];
for(; *pszVal != '\0'; pszVal++)
iputc(*pszVal);
pszFmt++;
continue;
}
if(*pszFmt == 'd')
{
iVal = pArg[index++];
i = 0;
do{
buffer[i++] = iVal % 10;
iVal /= 10;
}while(iVal);
while(i > 0)
{
i--;
iputc(*change(buffer[i]));
}
pszFmt++;
continue;
}
if(*pszFmt == 'c')
{
cVal = (uint8)pArg[index++];
iputc(cVal);
pszFmt++;
continue;
}
if(*pszFmt == 'x')
{
xVal = pArg[index++];
i = 0;
do{
buffer[i++] = xVal % 16;
xVal /= 16;
}while(xVal);
if(i%2!=0)
buffer[i++]=0;
if(i<2)
buffer[i++]=0;
while(i > 0)
{
i--;
iputc(*change(buffer[i]));
}
pszFmt++;
continue;
}
if(pszFmt == '\0')
{
break;
}
}
}
'; pszVal++) iputc(*pszVal); pszFmt++; continue; } if(*pszFmt == 'd') { iVal = pArg[index++]; i = 0; do{ buffer[i++] = iVal % 10; iVal /= 10; }while(iVal); while(i > 0) { i--; iputc(*change(buffer[i])); } pszFmt++; continue; } if(*pszFmt == 'c') { cVal = (uint8)pArg[index++]; iputc(cVal); pszFmt++; continue; } if(*pszFmt == 'x') { xVal = pArg[index++]; i = 0; do{ buffer[i++] = xVal % 16; xVal /= 16; }while(xVal); if(i%2!=0) buffer[i++]=0; if(i<2) buffer[i++]=0; while(i > 0) { i--; iputc(*change(buffer[i])); } pszFmt++; continue; } if(pszFmt == '
#include "iprintf.h"
static void iputc(char8 c)
{
UART_UartPutChar(c);
}
static uint8* change(uint32 Index)
{
return (uint8*)("0123456789abcdef"+Index);
}
void iprintf(char8 *pszFmt,...)
{
uint8 *pszVal;
uint32 iVal, xVal, i = 0, buffer[12], index = 1;
uint8 cVal;
uint32 *pArg;
pArg =(uint32 *)&pszFmt;
while(*pszFmt)
{
if('%' != *pszFmt)
{
iputc(*pszFmt);
pszFmt++;
continue;
}
pszFmt++;
if(*pszFmt == 's')
{
pszVal = (uint8*)pArg[index++];
for(; *pszVal != '\0'; pszVal++)
iputc(*pszVal);
pszFmt++;
continue;
}
if(*pszFmt == 'd')
{
iVal = pArg[index++];
i = 0;
do{
buffer[i++] = iVal % 10;
iVal /= 10;
}while(iVal);
while(i > 0)
{
i--;
iputc(*change(buffer[i]));
}
pszFmt++;
continue;
}
if(*pszFmt == 'c')
{
cVal = (uint8)pArg[index++];
iputc(cVal);
pszFmt++;
continue;
}
if(*pszFmt == 'x')
{
xVal = pArg[index++];
i = 0;
do{
buffer[i++] = xVal % 16;
xVal /= 16;
}while(xVal);
if(i%2!=0)
buffer[i++]=0;
if(i<2)
buffer[i++]=0;
while(i > 0)
{
i--;
iputc(*change(buffer[i]));
}
pszFmt++;
continue;
}
if(pszFmt == '\0')
{
break;
}
}
}
') { break; } } }

iprintf newlib nano

I did not realize it, but “iprintf” was actually part of the c-standard library.  This was a version of printf that was optimized for doing integer printfs (like the previous example).  I read on a few sites around the internet about iprintf, however in newlib-nano iprintf is just an exact wrapper around printf so it yields exactly the same results.

Thanks again for all of the feedback.  If you have any other good ideas please email me or leave a comment on this thread.

Implementing PSoC Printf

Summary

In 1978, Brian Kernighan and Dennis Ritchie published their iconic book “The C Programming Language”.  For years this book was the de-facto standard for “C”, often referred to as “K&R”.  On page 6 they created the archetypical “first program” for programmers, Hello World.  This simple example has propagated into almost every programming language book since then.  Here is a picture of my copy of K&R which I bought while I was in high school and have been carrying around for more than 30 years (obviously).

The C Programming Language The Original Hello World

I teach people all over the world to program PSoC.  The first program I show people is always the Blinking LED.  The purpose of the Blinking LED is the same as Hello World, to show that you can make the whole development environment work correctly and make something happen.  Invariably, anyone who has ever programmed C always asks me “How do I printf?” and I always have to answer “Well…ummm… you can… but sort of can’t.. ummm you need to use UART_UartPutString or something … umm blah blah”.  It isn’t really very satisfying as an answer.  So, back to K&R, here is hello world from page 6 above.

#include <stdio.h>

main()
{
    printf("hello, world\n");
}

If you try to run this program on a PSoC, it will compile, build and program.  But, it won’t actually do anything.  There are a number of reasons why PSoC printf doesn’t work correctly, and this article will show you how to overcome those problems.

To make PSoC printf work you need to do three things

  1. Create an output device with a UART, SPI or something
  2. Provide the code that PSoC printf needs to output to that device (compiler specific)
  3. Give enough memory to PSoC printf so that it doesn’t hang

Output Device

The most common output device in embedded programming is a Virtual Com Serial port on your computer.  Almost all of the Cypress PSoC development kits have a “Kitprog” which serves as a USB <– –> UART bridge and will enumerate on your PC as a virtual com port.  To make printf work place a UART and then attach it to the correct pins.  In the picture below you can see that I chose an “SCB” a.k.a Serial Communication Block (of which most of the PSoC 4’s have at least two).   Then I configured it to 115200 baud, 8/n/1 (this is the default configuration).

PSoC PRINTF UART Configuration

You can also use the “UART” or the “Software Transmit UART” or for that matter the SPI or I2C master… but those implementations will all be slightly different in that the API to output a character is a little bit different in all of them.

Next you need to attach the UART RX/TX to the correct pins on the development kit.  All of our development kits have slightly different pin configurations.  You can find the pins either on the back of the boards (see pictures) or in the user guide (available on our website).

CY8CKIT-048
CY8CKIT-046
CY8CKIT-145
CY8CKIT-042
CY8KIT-042-BLE

For this example I will choose the CY8CKIT-042BLE.  From the picture above you can see that I need to assign RX/TX to P1.4 & P1.5

PSoC4 Pin Map

PSoC printf Code

In order for printf, to actually printf, the function needs to be able to output a character to the “screen”.  Each of the three different compilers that Cypress supports (IAR, MDK and GCC) have slightly different implementations of printf.  But you can just copy this code:

  1. Into your main.c
  2. or into some other .c which is part of your project (I frequently create a file called util.c)

Notice that you will need to change lines 27, 61 and 77 to have the name of your UART component (so that it can output the character)

#if defined(__ARMCC_VERSION)
    
/* For MDK/RVDS compiler revise fputc function for printf functionality */
struct __FILE 
{
    int handle;  
};

enum 
{
    STDIN_HANDLE,
    STDOUT_HANDLE,
    STDERR_HANDLE
};

FILE __stdin = {STDIN_HANDLE};
FILE __stdout = {STDOUT_HANDLE};
FILE __stderr = {STDERR_HANDLE};

int fputc(int ch, FILE *file) 
{
    int ret = EOF;

    switch( file->handle )
    {
        case STDOUT_HANDLE:
            UART_UartPutChar(ch);
            ret = ch ;
            break ;

        case STDERR_HANDLE:
            ret = ch ;
            break ;

        default:
            file = file;
            break ;
    }
    return ret ;
}

#elif defined (__ICCARM__)      /* IAR */

/* For IAR compiler revise __write() function for printf functionality */
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
    size_t nChars = 0;

    if (buffer == 0)
    {
        /*
         * This means that we should flush internal buffers.  Since we
         * don't we just return.  (Remember, "handle" == -1 means that all
         * handles should be flushed.)
         */
        return (0);
    }

    for (/* Empty */; size != 0; --size)
    {
        UART_UartPutChar(*buffer++);
        ++nChars;
    }

    return (nChars);
}

#else  /* (__GNUC__)  GCC */

/* For GCC compiler revise _write() function for printf functionality */
int _write(int file, char *ptr, int len)
{
    int i;
    file = file;
    for (i = 0; i < len; i++)
    {
        UART_UartPutChar(*ptr++);
    }
    return len;
}


#endif  /* (__ARMCC_VERSION) */   

Fix the Heap

If you run this program without giving printf more memory it will hang… and it won’t be obvious why it hung.  The first time I had to figure it out, I used the debugger to attach to the running PSoC.  When you do this, you will see a screen like this which gives you the nice hint that you are out of heap space.

PSoC 4 Debugger

In order to fix the heap, go to the “System” tab of the Design Wide Resources and increase the heap size to at least 0x400.

PSoC Design Wide Resources Heap

Now it works

The final thing that you need to do is turn on the interrupts and start the UART.  Here is the code:

main()
{
    CyGlobalIntEnable;
    UART_Start();
    printf("hello, world\n");
}

In order to see the output you need to run a serial program (I often use Putty) and attach to the correct “virtual com port” which you can find in the device manager (see the KitProg USB-UART (COM9).

Windows Device Manager

Putty Configuration

After programming you will see this.

Hello World

That is excellent now we have caught up to K&R circa 1978!

You can read more in part two.

PSoC4 Boot Sequence (Part 5) – Initializing the PSoC with initialize_psoc()

Summary – Initializing the PSoC

Well, I have finally reached the end of the booting the PSoC4 series.  Actually I am contemplating one more article after this one, but for now I am done.  In this article I talk about the function “initialize_psoc()” which is responsible for initializing the PSoC; it turns the PSoC into a PSoC.  The function does two basic things:

  • It moves the exception table out of the flash (at location 0x0000) and puts it into the SRAM
  • It calls cyfitter_cfg() which calls a bunch of functions to setup all of the configuration registers inside of the PSoC

Here is the actual function:

/*******************************************************************************
* Function Name: initialize_psoc
******************************************************************************
*
* This function is used to initialize the PSoC chip before calling main.
*
*******************************************************************************/
#if(defined(__GNUC__) && !defined(__ARMCC_VERSION))
__attribute__ ((constructor(101)))
#endif  /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) */
void initialize_psoc(void)
{
    uint32 indexInit;

    #if(CY_IP_CPUSSV2)
        #if (CY_IP_CPUSS_CM0)
            /***********************************************************************
            * Make sure that Vector Table is located at 0000_0000 in Flash, before
            * accessing RomVectors or calling functions that may be placed in
            * .psocinit (cyfitter_cfg and ClockSetup). Note The CY_CPUSS_CONFIG_REG
            * register is retention for the specified device family.
            ***********************************************************************/
            CY_CPUSS_CONFIG_REG &= (uint32) ~CY_CPUSS_CONFIG_VECT_IN_RAM;
        #endif /* (CY_IP_CPUSS_CM0) */
    #endif  /* (CY_IP_CPUSSV2) */

    /* Set Ram interrupt vectors to default functions. */
    for (indexInit = 0u; indexInit < CY_NUM_VECTORS; indexInit++)
    {
        CyRamVectors[indexInit] = (indexInit < CY_NUM_ROM_VECTORS) ?
            #if defined (__ICCARM__)
                __vector_table[indexInit].__fun : &IntDefaultHandler;
            #else
                RomVectors[indexInit] : &IntDefaultHandler;
            #endif  /* defined (__ICCARM__) */
    }

    /* Initialize configuration registers. */
    cyfitter_cfg();

    #if !defined (__ICCARM__)
        /* Actually, no need to clean this variable, just to make compiler happy. */
        cySysNoInitDataValid = 0u;
    #endif  /* !defined (__ICCARM__) */

    #if (CYDEV_PROJ_TYPE != CYDEV_PROJ_TYPE_STANDARD)

        /* Need to make sure that this variable will not be optimized out */
        if (0u == cyBtldrRunType)
        {
            cyBtldrRunType = 0u;
        }

    #endif  /* (CYDEV_PROJ_TYPE != CYDEV_PROJ_TYPE_STANDARD) */

    #if (CY_IP_CPUSS_CM0)
        /* Vector Table is located at 0x2000:0000 in SRAM */
        CY_CPUSS_CONFIG_REG |= CY_CPUSS_CONFIG_VECT_IN_RAM;
	#else
	(*(uint32 *)CYREG_CM0P_VTOR) = CY_CPUSS_CONFIG_VECT_ADDR_IN_RAM;
    #endif /* (CY_IP_CPUSS_CM0) */
}

Move the Exception Table

In a previous article I talked about setting up the exception table and programming it into the flash.  This leaves me with a previously unstated question: How do I change the address of an interrupt vector service routine? Does it have to remain static and known a priori when you write the firmware?  In the Cortex-M0 architecture the answer to that question is fascinating (in my opinion).  If you use the ARM CM0 “straight out of the box” the answer is you can’t.  However, in the PSoC implementation of the CM0’s our system architect created a register to allow you to move the vector table.

Before I explain how this happens I would like to give a nod to the Cypress “CPUSS Architect”.  He is a former NXP engineer from the Netherlands and is one of the most amazing individual talents I have ever known.  He understands how to balance the white-tower-purity of the CTO office and the overall system architecture with the roll-up-your-sleeves realities of making chips.

Anyway.  Maybe it is a common thing to do with the CM0 (I actually haven’t looked at other people’s implementations) but the register “CPUSS_CONFIG” changes the address logic in the CM0 to read from two different places when the vector table addresses are issues by the CPU.  What this means is when the bit “CPUSS_VECS_IN_RAM” is 0, the addresses 0x0 are read from the flash, and when the bit is set to 1, the address 0x0 are read from 0x20000000 (which is the RAM).

Here is a clip from the PSoC Technical Reference Manual:

PSoC4 TRM

Once you see this, it is easy to understand lines 496–>517.  They basically:

  • Set the CPUSS_VECS_IN_RAM bit to 0
  • Then copy the vector table from the flash to the RAM

Finally on lines 537–>542 it sets the bit to 1 so that the vectors are read from the flash.

The only other trick here is that the CM0+ (and CM3/CM4) have a register called “VTOR” which allows you the accomplish exactly the same thing with out having to modify the address logic in the core.

Initializing the PSoC: Call cyfitter_cfg()

As part of building a PSoC project, PSoC Creator runs a “fitter”.  The fitter is responsible for

  1. Configuring the Digital Signal Interconnect  (DSI) matrix
  2. Configuring the UDBs
  3. Configuring the Analog Routing
  4. Configuring the Clocks
  5. Assigning the blocks of the PSoC to pins and components

All of the fitter tasks get turned into C-code which are either #defines (in the case of the components) or actual c-functions and data (as in the case of 1-4).  The last step in the function “start_c()” is calling the “cyfitter_cfg()” function”.  The “cyfitter_cfg()” will grow and shrink depending on how you configure the UDBs and the DSI.  For example, the version below has a blank schematic.  In it you can see that:

  • Lines 247-251 write 0s to the configuration registers… basically turning everything off
  • Lines 254-265 enable the UDBs and routing
  • Line 274 turns on the clocks
  • Line 277 turns on the analog
/*******************************************************************************
* Function Name: cyfitter_cfg
********************************************************************************
* Summary:
*  This function is called by the start-up code for the selected device. It
*  performs all of the necessary device configuration based on the design
*  settings.  This includes settings from the Design Wide Resources (DWR) such
*  as Clocks and Pins as well as any component configuration that is necessary.
*
* Parameters:  
*   void
*
* Return:
*   void
*
*******************************************************************************/
CY_CFG_SECTION
void cyfitter_cfg(void)
{
	/* Disable interrupts by default. Let user enable if/when they want. */
	CyGlobalIntDisable;

	{

		CYPACKED typedef struct {
			void CYFAR *address;
			uint16 size;
		} CYPACKED_ATTR cfg_memset_t;

		static const cfg_memset_t CYCODE cfg_memset_list [] = {
			/* address, size */
			{(void CYFAR *)(CYDEV_UDB_P0_U0_BASE), 1024u},
			{(void CYFAR *)(CYDEV_UDB_DSI0_BASE), 1024u},
		};

		uint8 CYDATA i;

		/* Zero out critical memory blocks before beginning configuration */
		for (i = 0u; i < (sizeof(cfg_memset_list)/sizeof(cfg_memset_list[0])); i++)
		{
			const cfg_memset_t CYCODE * CYDATA ms = &cfg_memset_list[i];
			CYMEMZERO(ms->address, (size_t)(uint32)(ms->size));
		}

		/* HSIOM Starting address: CYDEV_HSIOM_BASE */
		CY_SET_XTND_REG32((void CYFAR *)(CYREG_HSIOM_PORT_SEL3), 0x0000EE00u);

		/* UDB_PA_3 Starting address: CYDEV_UDB_PA3_BASE */
		CY_SET_XTND_REG32((void CYFAR *)(CYDEV_UDB_PA3_BASE), 0x00990000u);

		/* Enable digital routing */
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL, CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL) | 0x02u);

		/* Enable UDB array */
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_WAIT_CFG, (uint8)((CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_WAIT_CFG) & 0xC3u) | 0x14u));
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL, (uint8)(CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL) | 0x16u));
	}

	/* Perform second pass device configuration. These items must be configured in specific order after the regular configuration is done. */
	/* IOPINS0_3 Starting address: CYDEV_GPIO_PRT3_BASE */
	CY_SET_XTND_REG32((void CYFAR *)(CYREG_GPIO_PRT3_PC), 0x00000D80u);


	/* Setup clocks based on selections from Clock DWR */
	ClockSetup();

	/* Perform basic analog initialization to defaults */
	AnalogSetDefault();

}

However, if you had a more complicated schematic, like this one which uses a UDB and the DSI to implement a LUT

LUT Example

Then you will end up with a block of code that configures the DSI and UDB registers required to implement the LUT and route it to the pins.  You can see on lines 274-308 there is a table of register values which are copied into the architectural registers by the function call on line 333.  I started looking through the meaning of all of these register values in the TRM but realized that it didn’t matter.  PSoC Creator does a perfectly good job of setting up the UDB and getting it routed to the right place.

/*******************************************************************************
* Function Name: cyfitter_cfg
********************************************************************************
* Summary:
*  This function is called by the start-up code for the selected device. It
*  performs all of the necessary device configuration based on the design
*  settings.  This includes settings from the Design Wide Resources (DWR) such
*  as Clocks and Pins as well as any component configuration that is necessary.
*
* Parameters:  
*   void
*
* Return:
*   void
*
*******************************************************************************/
CY_CFG_SECTION
void cyfitter_cfg(void)
{
	/* Disable interrupts by default. Let user enable if/when they want. */
	CyGlobalIntDisable;

	{
		static const uint32 CYCODE cy_cfg_addr_table[] = {
			0x400F3007u, /* Base address: 0x400F3000 Count: 7 */
			0x400F3107u, /* Base address: 0x400F3100 Count: 7 */
			0x400F3301u, /* Base address: 0x400F3300 Count: 1 */
			0x400F4009u, /* Base address: 0x400F4000 Count: 9 */
			0x400F4102u, /* Base address: 0x400F4100 Count: 2 */
		};

		static const cy_cfg_addrvalue_t CYCODE cy_cfg_data_table[] = {
			{0x88u, 0x01u},
			{0xA0u, 0x01u},
			{0xB6u, 0x01u},
			{0xB8u, 0x80u},
			{0xBEu, 0x40u},
			{0xD8u, 0x04u},
			{0xDFu, 0x01u},
			{0x04u, 0x10u},
			{0x15u, 0x01u},
			{0x1Fu, 0x80u},
			{0x80u, 0x10u},
			{0x85u, 0x01u},
			{0xC0u, 0x40u},
			{0xC4u, 0x10u},
			{0xAFu, 0x80u},
			{0x00u, 0x20u},
			{0x04u, 0x08u},
			{0x57u, 0x20u},
			{0x80u, 0x04u},
			{0x84u, 0x10u},
			{0xC0u, 0x60u},
			{0xD4u, 0x40u},
			{0xE0u, 0x10u},
			{0xE4u, 0x80u},
			{0xAFu, 0x20u},
			{0xEEu, 0x10u},
		};



		CYPACKED typedef struct {
			void CYFAR *address;
			uint16 size;
		} CYPACKED_ATTR cfg_memset_t;

		static const cfg_memset_t CYCODE cfg_memset_list [] = {
			/* address, size */
			{(void CYFAR *)(CYDEV_UDB_P0_U0_BASE), 1024u},
			{(void CYFAR *)(CYDEV_UDB_DSI0_BASE), 1024u},
		};

		uint8 CYDATA i;

		/* Zero out critical memory blocks before beginning configuration */
		for (i = 0u; i < (sizeof(cfg_memset_list)/sizeof(cfg_memset_list[0])); i++)
		{
			const cfg_memset_t CYCODE * CYDATA ms = &cfg_memset_list[i];
			CYMEMZERO(ms->address, (size_t)(uint32)(ms->size));
		}

		cfg_write_bytes32(cy_cfg_addr_table, cy_cfg_data_table);

		/* HSIOM Starting address: CYDEV_HSIOM_BASE */
		CY_SET_XTND_REG32((void CYFAR *)(CYDEV_HSIOM_BASE), 0x00003003u);
		CY_SET_XTND_REG32((void CYFAR *)(CYREG_HSIOM_PORT_SEL3), 0x0000EE00u);

		/* UDB_PA_0 Starting address: CYDEV_UDB_PA0_BASE */
		CY_SET_XTND_REG32((void CYFAR *)(CYDEV_UDB_PA0_BASE), 0x00990000u);
		CY_SET_XTND_REG32((void CYFAR *)(CYREG_UDB_PA0_CFG8), 0x00010000u);

		/* UDB_PA_3 Starting address: CYDEV_UDB_PA3_BASE */
		CY_SET_XTND_REG32((void CYFAR *)(CYDEV_UDB_PA3_BASE), 0x00990000u);

		/* Enable digital routing */
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL, CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL) | 0x02u);

		/* Enable UDB array */
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_WAIT_CFG, (uint8)((CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_WAIT_CFG) & 0xC3u) | 0x14u));
		CY_SET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL, (uint8)(CY_GET_XTND_REG8((void *)CYREG_UDB_UDBIF_BANK_CTL) | 0x16u));
	}

	/* Perform second pass device configuration. These items must be configured in specific order after the regular configuration is done. */
	/* IOPINS0_0 Starting address: CYDEV_GPIO_PRT0_BASE */
	CY_SET_XTND_REG32((void CYFAR *)(CYDEV_GPIO_PRT0_BASE), 0x00000009u);
	CY_SET_XTND_REG32((void CYFAR *)(CYREG_GPIO_PRT0_PC), 0x00000C4Eu);

	/* IOPINS0_3 Starting address: CYDEV_GPIO_PRT3_BASE */
	CY_SET_XTND_REG32((void CYFAR *)(CYREG_GPIO_PRT3_PC), 0x00000D80u);


	/* Setup clocks based on selections from Clock DWR */
	ClockSetup();

	/* Perform basic analog initialization to defaults */
	AnalogSetDefault();

}

The “ClockSetup()” function does exactly what its name says.  It configures all of the clocks (and starts them) based on how you setup things in the Clocks Tab of the DWR.

The AnalogSetDefault() function does exactly what its name says…. with a blank design is a whole lot of nothing.  As you add things that use the analog resources on the chip, say for instance the OpAmps, it configures the registers require to implement those features in the chip.

OK, I am done with this set of articles.  I hope that they were useful.  It has certainly been an adventure digging through all of this code.

 

PSoC4 Boot Sequence (Part 4) – Linker trickery with __attribute_((constructor(101)))

Summary

In the previous articles I took you through the first part of the PSoC4 Boot Sequence, then I showed you how the exception vectors got into the flash, lastly I showed you the Start_C() function and how it initializes your program variables.  After that your main() should start, right?  But wait, when does the PSoC get initialized?  Is it magic?  This article answers that question and explains the evil linker trickery __attribute_((constructor(101)) that is used to call the “initialize_psoc()” function.

For many many reasons, one of my heroes is Brian Kernighan.  He just gets “it” and even better, he can explain “it” beautifully.  One of my favorite Kernighan quotes is “Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?”  Our business is hard, really hard and there is absolutely no call for doing things  that make it harder.  Don’t be deluded, obfuscation is not elegance.

Start_c()

As discussed previously, the Reset() function just calls Start_C() whose role in life is to get the C program initialized (the previous article).  Then it calls the function “__libc_init_array” (line 346) and finally it calls main (line 347).  The first time I saw this I thought, wow that is simple.  Then I started wondering, how and when does PSoC become PSoC?  When I look around in the Cm0Start.c file I see a function called “initialize_psoc” (which I will talk about in the next article).  But, there does not appear to be a call to “initialize_psoc()”.  Did I miss it?  Is it even run?

void Start_c(void)
{
    #ifdef CY_BOOT_START_C_CALLBACK
        CyBoot_Start_c_Callback();
    #else
        unsigned regions = __cy_region_num;
        const struct __cy_region *rptr = __cy_regions;

        /* Initialize memory */
        for (regions = __cy_region_num; regions != 0u; regions--)
        {
            uint32 *src = (uint32 *)rptr->init;
            uint32 *dst = (uint32 *)rptr->data;
            unsigned limit = rptr->init_size;
            unsigned count;

            for (count = 0u; count != limit; count += sizeof (uint32))
            {
                *dst = *src;
                dst++;
                src++;
            }
            limit = rptr->zero_size;
            for (count = 0u; count != limit; count += sizeof (uint32))
            {
                *dst = 0u;
                dst++;
            }

            rptr++;
        }

        /* Invoke static objects constructors */
        __libc_init_array();
        (void) main();

        while (1)
        {
            /* If main returns, make sure we don't return. */
        }

    #endif /* CY_BOOT_START_C_CALLBACK */
}

Debugging to initialize_psoc()

To answer the question, “where is initialize_psoc()” called, I put a breakpoint at that function.  When I run the debugger, you can see that it stops at the right place, and you can see in the call stack that the function is called by the function “__libc_init_array()”.  This is the point where things started to get really difficult in my life.

initialize_psoc function

__libc_init_array()

What in the world is this “__libc_init_array()”.  If you look in “Cm0Start.c” on line 219 you will see this block of code.  That means it is somewhere in some library (and not in this file).

/* The static objects constructors initializer */
extern void __libc_init_array(void);

My first thought was that it had something to do with initializing variables, but I realized that we had already done that.  So, what does it do? And how in the world does it call “initialize_psoc()” and where does it come from?  I started the search by right clicking on it and having PSoC Creator take me to the definition.  But, no luck, for some reason we didn’t include the source code for that function.  So, what does the function do?

Well, start by running the debugger and placing a breakpoint at “__libc_init_array”.  When you do that, you can look at the assembly language.  This is the place that I ended up spending a bunch of time as I had never written ARM assembly, and in fact the last time that I wrote any material amount of assembly was at Georgia Tech 26 years ago.

As I often do, I started by reading a few books and a Cypress application note.  Then I spent a few days writing simple C, compiling it, and looking at the results.  Specifically

Finally I was ready to dig back into this crazy code.  This function has three sections:

  • 0x0620 –> 0x0x0630 is a loop that loads an array of function pointers, then makes the function call to each one (line 0x0636)
  • 0x063c is a call to a function called “_init” (which I don’t know what it does)
  • 0x0640 –> 0x0x0658 is a loop that loads an array of function pointers, then makes the call to each one (line 0x0654)
0x00000620 <__libc_init_array>:
0x00000620 ldr	r3, [pc, #38]	; (65c <__libc_init_array+0x3c>)
0x00000622 push	{r4, r5, r6, lr}
0x00000624 movs	r5, #0
0x00000626 adds	r6, r3, #0
0x00000628 ldr	r4, [pc, #34]	; (660 <__libc_init_array+0x40>)
0x0000062A subs	r4, r4, r3
0x0000062C asrs	r4, r4, #2
0x0000062E cmp	r5, r4
0x00000630 beq.n	63c <__libc_init_array+0x1c>
0x00000632 lsls	r3, r5, #2
0x00000634 ldr	r3, [r6, r3]
0x00000636 blx	r3
0x00000638 adds	r5, #1
0x0000063A b.n	62e <__libc_init_array+0xe>
0x0000063C bl	6d8 <_init>
0x00000640 ldr	r3, [pc, #20]	; (664 <__libc_init_array+0x44>)
0x00000642 movs	r5, #0
0x00000644 adds	r6, r3, #0
0x00000646 ldr	r4, [pc, #20]	; (668 <__libc_init_array+0x48>)
0x00000648 subs	r4, r4, r3
0x0000064A asrs	r4, r4, #2
0x0000064C cmp	r5, r4
0x0000064E beq.n	65a <__libc_init_array+0x3a>
0x00000650 lsls	r3, r5, #2
0x00000652 ldr	r3, [r6, r3]
0x00000654 blx	r3
0x00000656 adds	r5, #1
0x00000658 b.n	64c <__libc_init_array+0x2c>
0x0000065A pop	{r4, r5, r6, pc}
0x0000065C .word	0x000006e4
0x00000660 .word	0x000006e4
0x00000664 .word	0x000006e4
0x00000668 .word	0x000006ec

What all of this means is that there are two arrays of function pointers.  The first array starts a 0x06e4 and ends at 0x6e4 (in other words it is empty).  The second array starts at 0x06e4 and ends at 0x06ec which means there are two function pointers.

When I look at the memory location 0x06e4 –> 0x06ec you see that there are two address 0x0129 and 0x01bc. (look at the memory window in the picture below).  When I scroll the assembly window to location 0x01bc, look what I find, “initialize_psoc”.

PSoC4 Boot Sequence - initialize_psoc

After all of that, I did what I probably should have done originally.  I googled “__libc_init_array source”.  One of the first few google hits was a link to the source code from CodeSourcery.  And it is indeed what I said it was:

  • A loop (lines 32-34) with function pointer calls
  • A call to “_init” (line 36)
  • Another loop (lines 38-40) with function pointer calls
/*                                             
 * Copyright (C) 2004 CodeSourcery, LLC                        
 *                                                            
 * Permission to use, copy, modify, and distribute this file   
 * for any purpose is hereby granted without fee, provided that
 * the above copyright notice and this notice appears in all            
 * copies.                                          
 * This file is distributed WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

/* Handle ELF .{pre_init,init,fini}_array sections.  */
#include <sys/types.h>

#ifdef HAVE_INITFINI_ARRAY

/* These magic symbols are provided by the linker.  */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));

extern void _init (void);

/* Iterate over all the init routines.  */
void
__libc_init_array (void)
{
  size_t count;
  size_t i;

  count = __preinit_array_end - __preinit_array_start;
  for (i = 0; i < count; i++)
    __preinit_array_start[i] ();

  _init ();

  count = __init_array_end - __init_array_start;
  for (i = 0; i < count; i++)
    __init_array_start[i] ();
}
#endif

Now I need to figure out how the address of the function “initialize_psoc” got into the memory in the table.

__attribute_((constructor(101)))

In the function “_libc_init_array” (above) it iterates through all of the function pointers that are in the array that starts at “__init_array_start” and ends at “__init_array_end”.  If you look at the linker script you will see that it puts the address of every function that is in the section ending with “.init_array.*”  That is cool but how does “intialize_psoc” get in that section?

      . = ALIGN(4);
      KEEP(*(.init))

      . = ALIGN(4);
      __preinit_array_start = .;
      KEEP (*(.preinit_array))
      __preinit_array_end = .;

      . = ALIGN(4);
      __init_array_start = .;
      KEEP (*(SORT(.init_array.*)))
      KEEP (*(.init_array))
      __init_array_end = .;

      . = ALIGN(4);
      KEEP(*(.fini))

      . = ALIGN(4);
      __fini_array_start = .;
      KEEP (*(.fini_array))
      KEEP (*(SORT(.fini_array.*)))
      __fini_array_end = .;

Well… if you look at the “initialize_psoc” function in Cm0Start.c you see a bunch of compiler garbly-gook.  It says if you are using the GNU compiler that you should add the attribute “constructor(101)” to the function.

/*******************************************************************************
* Function Name: initialize_psoc
****************************************************************************//**
*
* This function is used to initialize the PSoC chip before calling main.
*
*******************************************************************************/
#if(defined(__GNUC__) && !defined(__ARMCC_VERSION))
__attribute__ ((constructor(101)))
#endif  /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) */
void initialize_psoc(void)
{

But what does the “constructor” attribute do?  The answer to that can be found in the ARM Information Center.  Basically it says that it will call all functions with that attribute before main in the order of the attribute value (in this case 101).  The compiler achieves this objective by putting the function in the “.init_array” section.  Then the linker does its magic.

As I said earlier in this post I am really not a fan.  There is absolutely no reason why we should not have just called “initialize_psoc” right before we call “main”.   This would have greatly simplified this file as there must be a similar trick going on with IAR and MDK (but I am not going to go figure it out).  In addition hardcoding that 101 means that someone else might slam into it unexpectedly.

Oh well.  In the next article I will talk about what initialize_psoc actually does.

 

PSoC4 Boot Sequence (Part 3) – Preinitializing Variables before main()

PSoC4 Boot Sequence: Examine memory using debugger

Summary

In the previous articles, I took you through the first part of the PSoC4 Boot Sequence.  I traced from the power on of the chip to the beginning of the “Reset()” function.  Then in the 2nd article I showed you how the exception vectors were placed into the ROM (Flash).  In this article I will show you the steps taken to initialize the system so that your C program runs correctly.

PSoC4 Boot Sequence: Initialize C Global Variables

RAM is a volatile storage area, meaning that when the chip turns on it is in an indeterminate state.  But, what happens when you define a global variable that resides in the RAM, because it can change, and is also initialized.  Consider this block of code:

PSoC4 main.c

We know that when this program starts that the variable “aGlobal” has the value of 10.  But aGlobal can change, so it must be in the RAM and everything in the RAM is scrambled when the PSoC4 is Reset.  We are left with the question: How does the variable aGlobal get to be 10?  Lets start with the listing file for main (which as I discussed in the previous article is called main.lst and can be seen in the Results tab inside of folder “Listing Files”.  Here is the section of code that is material to the definition of aGlobal.

PSoC4 Assembly Listing File Global Variable

Line 87 is creates an assembler label called “aGlobal” (which matches our variable name).  Then line 88 inserts 4-bytes (aka the size of an integer) with the value (0x0000000A) [dont forget that it is little endian] into the current segment.  Line 82 defines the current segment to be “.data”.  Now lets look at the linker map file where you can see that the final address of “aGlobal” is 0x200000c4.

PSoC4 Linker Map File

If you run the debugger you can see that by examining the memory at 0x200000c4

PSoC4 Boot Sequence: Examine memory using debugger

But that address is in the RAM (not the flash).  So how does that address end up with the value of 0x0A like we assigned in the program?

PSoC4 Boot Sequence: Start_c()

It turns out that the only function called by the Reset() function is the Start_c() function. This function does four things

  • Initializes the memory (as it says on line 321)
  • Invoke static objects constructors (as it says on line 345)… what in the world does this mean? (the subject of the next two articles)
  • Calls main() a.k.a the start of your program
  • If something bad happens and you return from main then it does an infinite while(1); loop (lines 349-351)

PSoC4 Boot Sequence: Start_C() Function

PSoC4 Boot Sequence: Initialize the Memory

To answer the question how does 0x0A get into 0x200000c4 the first thing to do is look at the linker script.  After looking around in the linker script I find that .data section on lines 214-237.  On line 237 it has a tricky little command, specifically “>ram AT>rom”.  This tells the linker to assign addresses in the RAM region, but to load the data in ROM region.

PSoC4 Boot Sequence: The Linker File - .data Segment

For the purposes of initializing the memory, a __cy_region is a block of memory that has an initialized part (aka a .data section) and a part that is initialized to 0 (aka .bss section).  You can see this in the definition of the __cy_region structure which is declared on line 224 of Cm0Start.c

PSoC4 Boot Sequence: __cy_region structure

On line 322 of the function Start_C() you can see that there is a loop over all of the __cy_regions.  On lines 329-334 it copies from the ROM to the RAM.  The on lines 336-340 it initializes the BSS to 0.

After all of that we are left with is figuring out how an array of __cy_region structures get into the Flash and how the symbols __cy_regions and __cy_region_num are set.  For the answer to that question we are back to the linker script.  All of these symbols we have met earlier in this article.

PSoC4 Boot Sequence: linker script definition of __cy_regions array

Finally, __cy_region_num is defined on line 62.  This calculation works by calculating the size of the __cy_regions array and dividing it by a hardcoded value for the sizeof(__cy_region) (remember it is a pointer, pointer, int, int which are all 4bytes).  I am not a giant fan of this as if someone changed the structure definition in Cm0Start.c the hardcoded 16 will break.

PSoC4 Boot Sequence: _cy_region_num definition

The __cy_regions was made as an array to future proof the system.  I do not believe that there is ever more than one region initialized by this method.

In this next article I will answer the question “What in the world is that __libc_init_array() function?”

 

PSoC4 Boot Sequence (Part 2) – Creating the Exception Table using the Linker

Summary of the PSoC4 Boot Sequence

In the previous post I took you through the first part of the PSoC4 Boot Sequence.  I traced from the power on of the chip to the beginning of the “Reset()” function.  In this post I will trace the steps that were required to get the reset vector and stack pointer set and programmed into the flash of the PSoC4 at the right place.

We know that you program the chip with a hex file which is essentially a file with a list of addresses and values.  But how to you get a hex file?  And how do you get the Reset Vector and Stack Pointer into the hex file?  In order to create a hex file there are several steps required.

  1. Compile C –> Assembly Listing File then Assemble listing –> Objectfiles
  2. Link object files –> into ELF file
  3. Convert ELF –> Hex file

PSoC4 Boot Sequence: Compile and Assemble Program

Each C file in your project is turned into an ARM assembly language program by the compiler.  You can see the ARM assembly language programs in “.lst” files.  You can find your project listing files by clicking on the “Results” tab in the workplace explorer then selecting the “listing files” folder under your project.  In the screenshot below I am showing you the “Cm0Start.lst” which is the ARM assembly language file from the Cm0Start.c.  You will notice that there is one listing file for each of the C files in your project.

PSoC4 Boot Sequence: ARM Assembly Language

The next step in the process is for the Assembler to turns the listing files into object files.  Each object file has the hex bytes that represent your program and data.  It also defines which “section” that the code and data belong too.  The .0 also has a list of symbols that it doesn’t know the address of e.g. the address of functions and data in other files.  In order to see the object files you need to use the windows explorer (we don’t attach them to the workplace explorer)

PSoC4 Boot Sequence: Linker Configuration

The linker is responsible for:

  1. Combining all of the .o files into one file
  2. Assigning actual addresses for all of the symbols e.g. variables and functions

In order for the linker to do its job, it needs a linker script.  Cypress supports three toolchains: GCC, ARM MDK and IAR.  Each one of the toolchains has different syntax, form etc. for the linker script.  This is painful, but it is made worse by the fact that the linker script language is rather esoteric.  PSoC Creator makes a linker script for all three of the tool chains.  You can find those files called “cm0gcc.ld” (the GCC linker file), Cm0Iar.icf (the IAR linker file) and Cm0RealView.scat (the MDK linker file) in the workspace explorer under Generated_Source–>PSoC4–>cy_boot

PSoC4 Boot Sequence: Linker File

There is quite a bit going on in the linker file.  In fact the GCC linker file for PSoC4M is 470 lines long.  However, there are two basic commands in the linker file.

The “Memory” command which defines the address and size of the different types of memory in the system.  Each block of memory is sometimes called a “region”, but the Linker words “region” and “section” and “segment” are used somewhat erratically in the parlance.  In this PSoC4M there is 128K of Flash that starts at address 0x00000000 and 16K of RAM which starts at address 0x20000000.  You can see the “Memory” command on lines 29-33 of the GCC linker script.

PSoC4 Boot Sequence: Memory Layout

The second big command in the linker file is “SECTIONS”.  After the sections command there is a list of [input] section names and where they belong (aka their address).   When you look around on the internet you will find that sometimes the word “section” is used interchangeably with the word “segment”.  Unfortunately there does not appear to be a canonical definition of either of these words. There are three main section/segments

  • bss – this section contains variables which should be initialized to zero before main starts
  • data – this section contains variables which need to be initialized to some specific value
  • text – this section contains your program

Here is a screenshot of the part of the GCC linker file that includes the definition of where the PSoC4 ARM exception vectors are located.  This starts on line 72 where the “SECTIONS” command is issued.

PSoC4 Boot Sequence: Linker Section Table

The section command defines the a list of sections and the address of the sections.  Inside of the linker there is an “address pointer” which keeps track the current address in the system.  As it loads sections into the program it increments the address pointer so that it knows the final address of symbols.

In the above listing you see on line 87 that a section called “.text” belongs starting at address “appl_start”.  The linker code on lines 75–>80 is used to support bootloading programs with multiple application.  However, in this case we have only one application and no bootloader.  As a result nothing is loaded and “appl_start” is set to 0x00000000 which is also known as the first location in Flash.  Line 92 tells the linker to put the code in the section “*.romvectors” next.  Now we need to find the code that is marked in some .c file as belonging to the section “romvectors”.  I will come back to the linker script in a later article.

Cm0Start.c the Heart of the PSoC4 Boot Sequence

After digging through the beginning of the linker script and putting a breakpoint on the reset vector we know that the vector table is in a section named “romvectors” and we know that the 2nd entry in the table is the address of the function “Reset()”.  So that leaves us with the question:  Where are the “romvectors” defined?  After using the PSoC Creator search functionality I find this block of code.

PSoC4 Boot Sequence: Exception Vector Table

That nested mess of #defines and #ifdefs is a bit intimidating… but it was all put there to support the three different compilers.  If you boil it down, the first thing you will see is line 462 tells the compiler to put the next block of code into the “.romvectors” section.  Bingo! that is exactly what we were looking for.  Then on line 464 you can see that we define an array of type “cysisraddress”.  It turns out that “cyisraddress” is just a function pointer.  OK.

Now dig a little bit more and you can see that line 467 defines the address of the initial stack pointer as CY_SYS_INITIAL_STACK_POINTER.  This address is the last location in the RAM.  Then on line 471 we put the address of the Reset() function, then we put in two entries for the NMI and hard fault handers.  The “IntDefaultHandler” is an infinite while(1){} which hangs the processor.  This function is also in the Cm0Start.c file.

PSoC4 Boot Sequence: Default Exception Handler

Stack Pointer

Earlier I said “.. the address of the initial stack pointer.  This address is the last location in the RAM.”  How does that get to be?  Well if you hover over CY_SYS_INITIAL_STACK_POINTER you will find that it is a #define for the symbol “__cy_stack”.  Then when you look in the linker script you will find:

PSoC4 Boot Sequence: Stack Pointer Configuration

You can also see that the end of the heap is defined as the address of __cy_stack – 0x800.   The 0x800 must be the size of the stack… and it is… you define it in on the system resources tab of the “design wide resources”

PSoC4 Boot Sequence: Stack and Heap Configuration

The final thing to look at with regards to the stack is linker file definition of the stack (and heap).  On line 258 you can see that the stack starts at the address __cystack – 0x800.  This address is also known as the end of the RAM minus the size of the stack (remember that in ARM the stack builds down towards lower addresses)

PSoC4 Boot Sequence: Stack and Heap Linker Configuration

Test1.map

The last link in the chain is the linker map file.  You can find that file in the Test1–>CortexM0–>ARM_GCC_541–>Debug–>Listing Files directory.  The map file is message output file of the linker.  You can see the final resting place of all of the symbols. In the screenshot below you can see that the “.romvectors” segment started at address 0x0 and ended at address 0x10.  This makes sense as there are 4x 4 byte addresses (stack pointer, reset vector, NMI vector, Hard Fault vector)

PSoC4 Boot Sequence: Linker Map File

And the stack landed at 0x200038000 and ends at 0x20004000 which makes sense as we have 16K of SRAM aka 0x4000 bytes.

PSoC4 Boot Sequence: Linker Map for Stack

In the next post we will follow through the “Reset()” function and the initialization of the BSS&Data section as well as the C-Standard library.

 

PSoC4 Boot Sequence (Part 1) – Debugging to the Reset Vector

PSoC4 Boot Sequence: Examine memory using debugger

Summary

Recently I have been working on RTOSs.  While I was completing the port of FreeRTOS, I ran into something that I just didn’t understand.  So, I called a close friend who is a remarkable programmer.  Instead of telling me the answer, he said “I could tell you, or you could set a breakpoint on the reset vector and find out for yourself”.  This simple statement sent me into weeks of single stepping ARM assembly language, reading linker scripts, reading application notes and crawling through boot source code to figure out what is really happening during the PSoC4 Boot Sequence.

So, what happens when power is applied to a PSoC4?  I suppose that I could tell you to “Set a breakpoint on the reset vector and find out for yourself.”  Which would be a good thing for you to do, but it would’nt make for very interesting reading.  It also wouldn’t tell the whole story as what I am going to show you is what happens after the initial supervisory part of the boot occurs.

In this series posts I will take you through the process.

  1. Using the debugger: Setting a breakpoint on the reset vector of PSoC4 Boot Sequence
  2. Creating the Exception Table using the Linker
  3. Generated Source & cy_boot & Cm0Start.c
  4. RAM Vectors –> RAM Vectors
  5. Start_c
  6. initialize_psoc
  7. Solving the mystery of  __libc_init_array & a GCC trick

The whole process relies heavily on the linker script to get all of the code and vectors into the right place.  If you have never dug through a linker script, it can be a bit overwhelming, but don’t despair as I will explain the parts required to make this work.

All that being said, the PSoC4 Boot Sequence must:

  1. Move the Flash based exception vector table to the RAM (so that it can be changed)
  2. Startup the C Standard library environment
  3. Initialize the C-variables (from the Data segment) and zero the ones in the BSS segment.
  4. Move the table of PSoC flash based register settings into the appropriate volatile latches (make the PSoC a PSoC)

Using the debugger: Setting a breakpoint in the reset vector of PSoC4 Boot Sequence

The first thing that I did was create a blank PSoC4 M project with a blinking LED.  I then clicked the debug button which built the project, programmed it into the board, then started the debugger.  When you do that, here is what you will see.  You can see that the debugger by default boots the chip and runs until the first line in main (which is beyond the reset vector)

PSoC4 Debugger

But I want to break on the reset the vector.  I didn’t even know what function Cypress had setup for the reset vector or exactly where to look for it.  I knew that  I needed to figure out where the reset vector is located in the CM0 so that I could put a breakpoint on that location. Start by googling “arm cortex m0 reset vector”  The first google hit is an ARM page that shows the layout of all of the exception vectors:

Cortex-M0 Exception Vector Table

The reset vector is at address 0x04.  Now examine the memory in the debugger to find the address (which you can see is 0x00000011 – don’t forget that this processor is little endian). You can examine the memory by clicking the “Memory 1” tab at the bottom of the debugger.

PSoC4 Memory Dump of Exception Table

Create an address based breakpoint by using the debug->New Breakpoint menu.

PSoC4 Boot Sequence - Set Reset Breakpoint

Then press the reset button (which I highlighted in Red) on the debugger.  This will cause a reset of the chip, the PSoC4 ARM Cortex-M0 will load the program counter (PC) and start running. You will end up on a screen that looks like this:

PSoC4 Boot Sequence - Halt at Reset Vector

That is a pretty convenient place to stop.  The function is called “Reset” which makes good sense, and it is in a file called “Cm0Start.c”.  When I saw this, I thought that the whole exercise was going to be a cakewalk, little did I know that I was about to fall into a rabbit hole, that was going to take a few weeks to descend out of.

In the next post I will show you where the reset vector came from and how it and stack pointer got into the flash.

 

PSoC4 Watch Dog Timer

Summary

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

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

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

PSoC4 Watch Dog Timer: Introduction

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

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

PSoC4 Watch Dog Timer: Edit Clocks

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

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

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

PSoC4 Watch Dog Timer: Configure Low Frequency Clocks

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

PSoC4 Low Frequency Clock Documentation

PSoC4 Watch Dog Timer: Basic Usage

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

PSoC4 Watch Dog Timer: Basic Example

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

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

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

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

#include "project.h"

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    
    if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
    {
        CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK);
        red_Write(0); // turn on the red LED
        while(1);     // hang
    }

    for(;;)
    {
        blue_Write(~blue_Read());
        // If dont feed the WDT it will cause a reset in ~3 seconds
        //CySysWatchdogFeed(CY_SYS_WDT_COUNTER0);
        CyDelay(100);
          
    }
}

PSoC4 Watch Dog Timer: Callback

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

PSoC4 Watch Dog Timer: CallBack Example

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

#include "project.h"

void wdtInterruptCallback()
{
    blue_Write(~blue_Read());   
}


int main(void)
{
    CyGlobalIntEnable; 
    
    if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
    {
        CySysWdtDisable(CY_SYS_WDT_COUNTER0_MASK); // Disable the WDT
        red_Write(0); // Turn on the red LED
        while(1);     // Hang
    }
 
    CySysWdtSetInterruptCallback(CY_SYS_WDT_COUNTER0,wdtInterruptCallback);

    for(;;)
    {
        CySysPmDeepSleep(); // Put the chip into deep sleep.       
    }
}

PSoC4 Watch Dog Timer: Custom ISR

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

PSoC4 Watch Dog Timer: Custom ISR Schematic

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

#include "project.h"

void wdtInterruptHandler()
{
    blue_Write(~blue_Read());
    CySysWdtClearInterrupt(CY_SYS_WDT_COUNTER0_INT);
    
}


int main(void)
{
    
    if(CySysGetResetReason(CY_SYS_RESET_WDT) == CY_SYS_RESET_WDT )
    {
        red_Write(0); // turn on the red LED
        while(1);     // hang
    }
    
    CyGlobalIntEnable; /* Enable global interrupts. */
  
    
    isr_1_StartEx(wdtInterruptHandler);
    
    CySysWdtEnable(CY_SYS_WDT_COUNTER0);
   
    for(;;)
    {
        CySysPmDeepSleep();
          
    }
}

PSoC4 Watch Dog Timer: Watch Dog Reset

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

#include "project.h"

void wdtInterruptHandler()
{
    while(1) WDTSignal1_Write(~WDTSignal1_Read());
}

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

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

PSoC4 Reset Example (start to ISR)

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

PSoC4 Reset Example

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

PSoC4 Reset Example

FreeRTOS: A PSoC4 FreeRTOS Port

Summary

In my work life, I am working on some systems that will require more complicated firmware architectures built using real time operating systems.  As I need to get more familiar with RTOSs, I thought that I would go ahead and start using them in my PSoC 4 projects.  The logical one to start with is FreeRTOS.  In this article I will take you through the steps to create a PSoC4 FreeRTOS port.

  1. Introduce FreeRTOS
  2. Execute a PSoC4 FreeRTOS Port
  3. Create a PSoC4 FreeRTOS Test Project (the blinking LED)

FreeRTOS Background

FreeRTOS is a light weight, open-source, real time operating systems for embedded systems that was developed by RealTime Engineers.  Their basic business plan is to sell consulting and support services to semiconductor companies their customers.  All of the source code is available (mostly in plain C) and easy to use.  In addition there are ports to 35 MCUs (including all of the Cypress ARM chips).  FreeRTOS is frequently cited as the most popular embedded RTOS which is easy to understand as I have found it easy to use and very stable.

Execute a PSoC4 FreeRTOS Port

The port of FreeRTOS to PSoC4 is actually pretty trivial, once you figure it out.  But I suppose that is how things often go.  FreeRTOS comes preconfigured with a GCC version of an ARM Cortex-M0 port.  All that needs to be done is to hook the basic port to the correct PSoC4 systems.  There are generic porting instruction on the FreeRTOS site but these are my PSoC specific steps:

    1. Download FreeRTOS V9.0.0
    2. Add the include directories “FreeRTOS/Source/include” and “FreeRTOS/Source/portable/GCC/ARM_CM0” to your project by right clicking on the project and editing “Build Settings…”  You should add those directories to the “Additional Include Directories”Configuring the PSoC 4 FreeRTOS build settings
    3. Right click on the project and “Add Existing Item” so that you can add the .c & .h files from FreeRTOS/Source/portable/GCC/ARM_CM0  to your projectAdd the PSoC4 FreeRTOS Files to Project
    4. Add the .c files from FreeRTOS/Source/ to your project
    5. Add the .h files from FreeRTOS/Source/include to your project
    6. Add “heap_1.c” (or which ever memory manager you want) from FreeRTOS/Source/portable/MemMang
    7. Create the “setupFreeRTOS” function to install the Interrupt Service vectors required by FreeRTOS.
extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
#define CORTEX_INTERRUPT_BASE          (16)
void setupFreeRTOS()
{
    /* Handler for Cortex Supervisor Call (SVC, formerly SWI) - address 11 */
    CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SVCall_IRQn,
        (cyisraddress)vPortSVCHandler );
 
    /* Handler for Cortex PendSV Call - address 14 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + PendSV_IRQn,
        (cyisraddress)xPortPendSVHandler );    
 
    /* Handler for Cortex SYSTICK - address 15 */
	CyIntSetSysVector( CORTEX_INTERRUPT_BASE + SysTick_IRQn,
        (cyisraddress)xPortSysTickHandler );
}

8. Create the FreeRTOSConfig.h First, add a new file called FreeRTOSConfig.h file to the project (right click on the project and “add new item”.  This file contains a bunch of CPP macros to setup FreeRTOS.  You can get this file by copy/pasting from the the linked website into your blank FreeRTOSConfig.h file.
9. Modify FreeRTOSConfig.h I made the following changes to the default configuration file:

// Add the PSOC Creator Macros for the PSoC4 Registers 
#include "project.h"
// PSoC Creator creates a #define macro for the clock settings from the DWR
#define configCPU_CLOCK_HZ ( ( unsigned long ) CYDEV_BCLK__SYSCLK__HZ )
// SysTick Defaults to 1ms
#define configTICK_RATE_HZ                      1000
#define configSUPPORT_STATIC_ALLOCATION   0
#define configSUPPORT_DYNAMIC_ALLOCATION  1
#define configTOTAL_HEAP_SIZE               10240
#define configAPPLICATION_ALLOCATED_HEAP 0
// During an assert just put into a busy wait
#define configASSERT ( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

Create a PSoC4 FreeRTOS Test Project

The example project is a simple blinked LED.  It starts with a “Task” function which I call LED_Task.  This just reads the current value of the RED Led pin, inverts it, and writes it back.  Then it does and RTOS delay of 500 ms.  The main look just turns on the interrupt system, initializes the RTOS, Creates the LED Task, then starts the scheduler.  The Scheduler will never return.

void LED_Task(void *arg)
{
    (void)arg;
 
        while(1) {
        RED_Write(~RED_Read());
        vTaskDelay(500);
        }
}
 
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
 
    setupFreeRTOS();
 
    /* Create LED task, which will control the intensity of the LEDs */
    xTaskCreate(
        LED_Task,       /* Task function */
        "LED Blink",    /* Task name (string) */
        200,            /* Task stack, allocated from heap */
        0,              /* No param passed to task function */
        1,              /* Low priority */
        0 );            /* Not using the task handle */
 
    vTaskStartScheduler();
}

ThingSoC: Four I2C OLED Displays with PSoC4L

Pattern Agents are running a crowd funding effort for ThingSoC TSoC4L … help them here

Summary

In the previous ThingSoC post I took you through building the firmware to make the PSoC4L drive the ThingSoC I2C hub.  Now what?  When originally looking around I saw a picture with 4x LCDs connected to the I2C hub, which I thought was cool. In this post Ill show you how to do the same thing with PSoC and the U8G2 library which I talked about in this article. To do this I will:

  1. Create New PSoC4L Project + Integrate the U8G2 Library
  2. Update the PSoC4L Firmware
  3. Test the PSoC4L Firmware

Here is the picture:

4 I2C Displays, Inspiration

Create New PSOC4L Project + Integrate the U8G2 Library

Maybe it should have been obvious, but for some reason it never occurred to me to do a copy/paste to duplicate a project.  I guess that I’m slow that way.  To do this right click on a project in the workspace explorer and then do paste.

PSoC Creator Edit Build Settings

The next step is to integrate the firmware from the U8G2 Library. Start by “gitting” with “git@github.com:olikraus/u8g2.git”.  Then you need to add the directory to your project by right clicking the project and selecting “Build Settings”.  You need to add U8G2 Library to the additional include directories.

Add the U8G2 Library

Add the all of the .h and .c files by right clicking the project and selection “Add->Existing Item”

copy the u8g2 files into the PSoC4L project

Navigate to the U8G2 library and add all of the .c and .h files.  The last thing you need is to bring in the HAL that I wrote for PSoC and described in this post.  Specifically you need to bring in the two functions

  • psoc_gpio_and_delay_cb
  • u8x8_byte_hw_i2c

I suppose that I should package the whole thing up in a component.  But, Ill leave that as an exercise for the reader.

Update the PSoC4L Firmware

The cool thing about the whole setup with the I2CHUB is that it allows you to have 4 devices with the same I2C address attached to one PSoC SCB at the same time.  To get going with the firmware, I start by defining an array of u8x8_t structures to represent the 4 different displays attaches to the 4 different ports on the I2C hub. (line 186).  Then I create a function called setupDisplay that initializes the display etc. (lines 199-203).  The only trick in the firmware is that Dennis Ritchie defined arrays to run from 0-3 but the I2C busses are labeled 1-4,  this is the reason for subtracting 1 from the input lcd number.

PSoC4L Firmware to initialize the U8G2 Display

The next step is modifying command processors in the main loop.  Specifically, I will add the commands q,w,e,r to startup the displays on I2C ports 1,2,3,4.  And, I will add those commands to the help print out.

Modifying the PSoC4L CLI

Test the PSoC4L Firmware

In order to make a connection from the Grove 4-pin connectors to my breadboard I used Switch Doc Labs connectors which I bought from Amazon.com.  For some reason you can’t purchase them from the Switch Doc Labs website or the SeeedStudio website, but they are very handy.  As an unrelated side note I had never seen (or in fact ever heard of) Switch Doc Labs.  But their website has a bunch of tutorials about the Raspberry Pi and Grove ecosystem boards for use with IoT-ifying house plants.  Seemed pretty cool.

Grove to Breadboard Switch Doc Labs Breakout Cable

Now when I press “qwer” in my command console I get this:

PSoC4L Driving 4 OLED LCDs

You can find all of the projects in the TSoC Series at my GitHub ThingSoC repository git@github.com:iotexpert/ThingSoC.git

Index Description
ThingSoc: TSoC PSoC4L Development Kit from PatternAgents Introduction to ThingSoC
ThingSoC PSoC4L: Using & Debugging the PSoC4 Clock Debugging a beta firmware problem
ThingSoC: The TSoC GROVEY I2CHUB : I2C Hub/Switch Evaluating the ThingSoC I2C Hub
ThingSoC: Multiple I2C Displays Using the I2CHUB and U8X8 Library A project using ThingSoC I2C Hub & 4 OLED Displays

ThingSoC: The TSoC GROVEY I2CHUB : I2C Hub/Switch

Pattern Agents are running a crowd funding effort for ThingSoC TSoC4L … help them here

Summary

One of the boards that the Pattern Agents guys sent me was a TSOC GROVEY I2CHUB : I2C Hub/Switch.  In this post I will take you through the process that I went through to evaluate the board and build an interface to the ThingSoc TSoC 4L base board.

  1. Introduction to the TSoC Grovey I2CHUB
  2. Discussion of PCA9546A I2C
  3. Using the MiniProg-3 to interact with the PCA9546A
  4. Building a Command Line Interface (CLI) using the USB/UART
  5. Adding firmware to mimic the MiniProg-3 I2C list to the CLI
  6. Adding a “port change” feature to the CLI firmware

Introduction to the TSoC Grovey I2CHUB

The ThingSoC I2C Hub is a ThingSoC Compatible (not Compliant) board that serves as a bridge from the base board to 4 separate programmable I2C busses using an NXP/TI 9546A bridge chip.  Each of the I2C busses is attached via a SeeedStudio Grove connector.  Grove is an ecosystem of sensors (Humidity, Temperature etc.) that are all? mostly? I2C connected.

ThingSoC I2CHub

Discussion of PCA9546A I2C

The PCA9546A is made by TI and NXP (or whatever they are calling themselves these days) and can be purchased for about $0.70 from Digikey, Mouser, or Arrow.  The chip is pretty cool.  It will let you connect your I2C master to 1 of 4 I2C busses.  It will actually let you connect more than one bus at a time.  This chip enables you to connect multiple slaves with the same address to your I2C master.

Here is a simplified picture from TIs datasheet.

PCA9546A Block Diagram

To control the system you just need to write into the control register with a bit mask.  The bit masks contains which slaves busses you want switched on.  Here is a screen shot from NXPs datasheet:

PCA9546A Control Register

Using the MiniProg-3 to interact with the PCA9546A

I start the whole firmware journey by using a MiniProg-3 as an I2C<–>USB bridge.  This allows me to type I2C commands in the Bridge Control Panel which go directly to the chip (without writing firmware).  In the picture below you can see that I have the MiniProg-3 attached to the ThingSoC board using jumper wires.  I also use the MiniProg-3 to power the board.

Using a MiniProg-3 to Probe the ThingSoC I2C Hub

When I press “List”, the Bridge Control Panel probes all of the I2C addresses.  If a device ACKs, it prints out that fact in the output window.  In the picture below you can see that PCA9546A is attached at address 0x73.  I then read and write the control register, which also works as expected.

Bridge Control Panel

The address 0x73 makes sense as you can see that (in the picture above) there is a blob of solder on “A2” which pulls it low (instead of the default high).  Here is that part of the schematic:

ThingSoC Schematic for PCA9546A Address Selection

Building a Command Line Interface (CLI) over the USB/UART

Now I am  ready to build a command line interface using the USB <–> UART Bridge.  First, I make a schematic with the USB-FS (Full Speed), LED and I2C components.  Notice that the USBFS is setup as a USBUART.  The I2C is configured as a Master.

PSoC Creator Schematic for ThingSoC I2C Hub Firmware

The Pins are selected as:

PSoC Creator Pin Selection

Next is a helper function to printout text via the CDC UART:

PSoC Creator Firmware PrintBuff Helper Function

Then I write a little bit of firmware for the CLI.  The function just reads from the UART and, if the user has pressed a button, then it issues that command.

Lines 99-103 get the USBUART going, and deal with changes (e.g. unplug events).

The only thing that you might not have seen is the “Clear Screen”.  I just send out the escape sequence (old school) for clear screen and move home from the VT100 Escape Codes.

PSoC Creator Main Command Line Interface

Adding firmware to mimic the MiniProg-3 I2C list to the CLI

When you press “List” in the Bridge Control Panel, what happens?  Although I have been using the Bridge Control Panel for years I wasn’t exactly sure.  To find the answer, I plugged in my Saleae Logic 16 and watched the output.

Probing using MiniProg-3 and Saleae Logic-16

In this screenshot from the Saleae Logic output screen,  you can see that the Bridge Control Panel sends out a “Read 0x15” which was NAKed.

Saleae Logic 16 address 0x15 NAK

In this screen shot you can see that when the “Read 0x73” occurs, that the PCA9456A ACKs which tells the Bridge Control Panel that the device is there.

Saleae Logic 16 0x73 ACK

While I was writing the entry, I looked around at several implementations.  It appears that sometimes people send “Write Address” instead of “Read Address”.  I wasn’t sure exactly what was right, so I consulted Dr. Zwave.  He really should go by Dr. I2C since that is what he did at Cypress.  Anyway, he pointed out that you will hang the I2C bus if you try a Read and then don’t read the next byte.  With a write you don’t actually need to keep writing.  I decided to follow his instructions.

I have always liked the output of the Raspberry Pi I2CDetect command.  Here is a screen shot from one of my Raspberry Pi’s where a PSoC4 is attached on I2C Address 0x8.  I am not totally sure why the command does not probe addresses 0x00-0x02 (comment with the answer and Ill send you a devkit)

Raspberry Pi I2CDetect

The implementation of i2c_detect on the PSoC4L is pretty simple.  All I need to do is iterate through all of the addresses from 0->0x7F.  Then attempt to write.  If a device ACKs then printout the address, otherwise move on.  Instead of  one big loop, I split it up into two loops to make the printing a little bit simpler.  Here is the code:

PSoC Creator I2C Detect Firmware

Now, when I press “l” on the CLI, it probes all of the legal addresses on the I2C bus from the PSoC4L.  You can see that only the PCA9546A at address 0x73 is active.  How cool is that?

PSoC Creator Firmware I2CDetect

Adding a “port change” feature to the CLI firmware

The last block of code adds three functions which let me control the PCA9546A control register.

PSoC Creator PCA9546A Control Firmware

To test the whole thing, I attach the Saleae Logic Analzer to I2C Port1.  Then I probe to make sure that I can attach and unattached the bus.

Test Jig for PSoC Creator Firmware

You can find all of the projects in the TSoC Series at my GitHub ThingSoC repository git@github.com:iotexpert/ThingSoC.git

Index Description
ThingSoc: TSoC PSoC4L Development Kit from PatternAgents Introduction to ThingSoC
ThingSoC PSoC4L: Using & Debugging the PSoC4 Clock Debugging a beta firmware problem
ThingSoC: The TSoC GROVEY I2CHUB : I2C Hub/Switch Evaluating the ThingSoC I2C Hub
ThingSoC: Multiple I2C Displays Using the I2CHUB and U8X8 Library A project using ThingSoC I2C Hub & 4 OLED Displays

ThingSoC PSoC4L: Using & Debugging the PSoC4 Clock

Pattern Agents are running a crowd funding effort for TSoC … here

Summary

In the previous post I told you that I immediately blew away the factory firmware on the TSoC4L board.  I’m like that sometimes :-).  In this post I am going to take you through the entire process of getting and reprogramming the factory firmware, then finding and debugging a PSoC4 clock problem.  The rest of this post is:

  1. Getting the correct firmware back on the TSoC4L Board
  2. Running into a bug in the beta version of the firmware
  3. Using the debugger to find the bug
  4. Explaining the functionality of Low Frequency Clocks and the Watch Dog Timer
  5. The Pattern Agents bug fix

Factory Firmware

Getting things going again starts with “git”ing the firmware.  This can be done by “git clone git@github.com:PatternAgents/TSOC_PSoC4L.git”.  Once you have the repo cloned you will find a bunch of good stuff including:

  1. datasheets: All the datasheets
  2. documentation: The PatternAgents documentation (quick start)
  3. drivers: The windows USB <-> UART drivers
  4. eagleUp: JPG renderings of the PCB
  5. firmware: The PSoC Creator Project
  6. hardware: gerbers, boms etc
  7. images: renderings of the board, photos of the board etc
  8. revisions: old revisions of the Eagle project files
  9. The Eagle PCB Project including schematic and layout

TSoC4 L Files from GitHub

When you open the PSoC Creator Workspace you will find two projects

  1. A USB Bootloader – called “USBFS_Bootloader”
  2. The TSoC RSVP Firmware – called “rspvsis_4l_typ”

The system is supposed to

  1. Start the USB HID bootloader (so you could get new firmware).  Wait for 10 seconds for a bootloader host to start then if it doesnt … then start
  2. The RSVP Firmware

When you plug in the board you will see the TSoC4L enumerate as “Cypress ThingSoc Bootloader”.  Then, after 10 seconds, you should see it reboot, then enumerate as “Cypress USB UART”.  But, this was not happening for me.  Why?  I wasn’t sure.  The first thing that I did was add a blinking LED circuit that looked like this:

Blinking LED

But the LED was not blinking.  That meant that the firmware was not even getting to main i.e. it was hanging BEFORE main in the PSoC boot code.  But where and why?

Using the Debugger to Find the Bug

How do you figure out where the system is stuck if you can’t even use the “blinking LED” or “printf” debug?  Simple.  Use the debugger.  Start by programming the PSoC with your firmware.  Then click “Debug->Attach to running target”.

Starting the PSoC Debugger

The debugger will start.  Then you will be able to press the “Pause” button. which will take you to a screen that looks like this:

PSoC4 Clock Busy Wait Loop

The chip is stuck on line 234.  What does line 234 do?  That is a busy wait loop.  It is waiting for the Watch Dog Control Register to clear.  To explain that, I will need to take you through the PSoC4 Low Frequency Clocks.  First you should notice that the file we are in is named “cyfitter_cfg.c”.  What is this file?  If you look at the top, this is what PSoC Creator says.

This means that the “cyfitter_cfg.c” file is synthesized by PSoC Creator when you do a build based on what you configured when you setup your project.  This file contains a bunch of the startup code including the code that gets the clock systems going.  Line 234 is inside of a function called “ClockSetup”.  All of this code is called BEFORE you get to main().

The PSoC4 Clock(s)

To explain why we are stuck there you first need to double click on the “Clocks” tab in the Design Wide Resources.  When you do that it will take you to this screen which contains a bunch of information about the clocking system in the PSoC.

PSoC4 Clock Setup

In the screen above, you can see that there are three Timers in the Watch Dog Timer system (WDT) that are clocked (i.e. have a Source Clock of) by the Low Frequency Clock (LFCLK).  The LFCLK has its source as the Watch Crystal Oscillator (WCO).  If you double click the WCO line it will take you to a screen that is much easier to see what is going on, specifically the “Configure System Clocks” screen:

PSoC4 Clock - Low Frequency

In the screen above you can see that the LFCLK has two choices of oscillator sources, the “ILO” or the “WCO”.  ILO stands for Internal Low Speed Oscillator.  The ILO is a fairly inaccurate (+- 60%) RC oscillator that is built inside of the chip.   It is designed to be very low power which is why it is so inaccurate.  If you need a more accurate oscillator, then you should use an external oscillator, specifically a Watch Crystal Oscillator or WCO.  A WCO is a 32.768KHz crystal oscillator.  These watch crystal oscillators give you very precise clocks that can be used to drive real time clocks accurately.

The next thing to see is there are three Watch Dog Timers (WDT) numbered 0-2.  These times are driven by the “LFCLK”.  Here is a picture from the PSoC Technical Reference Manual.


PSoC4 Clock - WDT Block Diagram

So what is the “CYREG_WDT_CONTROL”?  You can find that by looking in the PSoC4 Registers TRM.

PSoC4 Clock WDT Control Register

The busy wait loop is waiting for bits 19,11,3 to be cleared.  Those bits are “WDT_RESETx”  They become 0 when the three WDT timers are cleared which happens “several LFCLK cycles” after the reset.  As a reminder, here is that block of code:

This still leaves us with the question, “What is the problem?”.  If you look on the back of the ThingSoC4L board you will see there is no Crystal populated on this board. (the blank footprint on the right side of the back).  The WCO is an optional feature from Pattern Agents.  If you need the more accurate timing, you need to solder on your own Crystal.

Backside of ThingSoC4L PCB

If there is no WCO crystal, then the WDT timer reset will never happen because LFCLK isn’t doing anything.  And, the busy wait loop will never end.  Mystery solved.

The Pattern Agents Bug Fix

To fix the startup problem, the Pattern Agents guys changed the clock configuration to

  1. Turn off the WDTs
  2. Use the ILO as the source of the LFCLK

PSoC4 Clock - Low Frequency Setup

You can find all of the projects in the TSoC Series at my GitHub ThingSoC repository git@github.com:iotexpert/ThingSoC.git

Index Description
ThingSoc: TSoC PSoC4L Development Kit from PatternAgents Introduction to ThingSoC
ThingSoC PSoC4L: Using & Debugging the PSoC4 Clock Debugging a beta firmware problem
ThingSoC: The TSoC GROVEY I2CHUB : I2C Hub/Switch Evaluating the ThingSoC I2C Hub
ThingSoC: Multiple I2C Displays Using the I2CHUB and U8X8 Library A project using ThingSoC I2C Hub & 4 OLED Displays

ThingSoC: TSoC PSoC4L Development Kit from PatternAgents

Pattern Agents are running a crowd funding effort for ThingSoC TSoC4L … help them here

Summary

Last week I got a box in the mail from my friend Tom Moxon in Oregon.  He runs a company called Pattern Agents.  I was excited when I saw the box because I love new development kits.  I was even more happy when I opened the box and found a ThingSoC PSoC4L kit.

Tom sent me three boards

  1. A ThingSoC PSoC4L
  2. A MiniProg Adaptor
  3. A ThingSoC I2C Hub

ThingSoC PSoC 4L Packaging

ThingSoC PSoC4L

The ThingSoC complies to a new open source stackable footprint called the ThingSoC Open Source Socket.  This ThingSoC board is driven by a PSoC4L.  The PSoC4L is the largest and most capable of the PSoC4 family.  This chip “completed” the high end of the PSoC4 family by adding more IOs, more Flash, more RAM and a USB controller.  The PSoC4L serves as the base MCU in the stack as well as providing a bridge to the PC via a USB <–> UART Connection.  The board also has a connection for a 3.7V LiPo battery to power the system.  In the picture below you can see the base board (on the left) with the programmer attachment (on the right).  The programmer attachment lets you attach a MiniProg-3 or a Uart Bridge or an ISSP connection (like a MiniProg-1).

thingsoc 4200l

Here is a picture of the backs of the boards.  On the back of the ThingSoC PSoC4L board you can see a place to solder a Cypress NVSRAM as well as a footprint for a Watch Crystal.

The backs of the ThingSoC PSoC4L

The Blinking LED

The first thing I did with the board (and no, reading the instructions wasn’t the first thing) was program the chip to blink the LED.  Obviously with a PSoC the best thing to do was make a schematic with a PWM driving the USER LED pin:

I used the default clock of 12MHZ, then I used the PWM pre divider to get the frequency down to something that I could see.

Lastly, I wrote a tiny bit of firmware to get things going.  Notice that I use the CySysPmSleep to put the CPU to sleep.  It never wakes up from this sleep (because there are no interrupt sources to wake it up).  But, if it did, it would go right back to sleep because of the loop.

After programming the board, the LED started right up.  That is good as it means everything makes sense.

When I programmed the Blinking LED project I overwrote Tom’s default firmware which is called “RISC”.  In the next post Ill talk more about the architecture of the ThingSoC PSoC4L.

You can find all of the projects in the TSoC Series at my GitHub ThingSoC repository git@github.com:iotexpert/ThingSoC.git

Index Description
ThingSoc: TSoC PSoC4L Development Kit from PatternAgents Introduction to ThingSoC
ThingSoC PSoC4L: Using & Debugging the PSoC4 Clock Debugging a beta firmware problem
ThingSoC: The TSoC GROVEY I2CHUB : I2C Hub/Switch Evaluating the ThingSoC I2C Hub
ThingSoC: Multiple I2C Displays Using the I2CHUB and U8X8 Library A project using ThingSoC I2C Hub & 4 OLED Displays

The Creek: PSoC 4 Creator Schematic + Firmware

In this post I will describe the PSoC Creator schematic and firmware that I built to run the PSoC4.  You can find the project on github.  After you open the project in PSoC Creator you will find two projects in the workspace explorer:

  1. p4ardino-creek: This project starts running 10 seconds after the chip powers up.  It uses PSoC Analog to Digital Convertor to read the pressure sensor and the temperature sensor.  It then convert the data to a readable form and stores it into the EZI2C buffer to be read by the Raspberry PI.
  2. p4bootloader: This project runs for 10 seconds when the chip is rebooted.  If it detects the Raspberry Pi host trying to load new firmware it will “bootload”.  If after 10 seconds it doesn’t hear anything, it will run the p4arduino-creek application.  I will describe how the bootloader works in the next post.

CreekWorkspace

p4ardunio-creek

The schematic for this project contains four sections

  1. The Analog to Digital convertor and the “external” schematic of the pressure current loop.  The elements in blue are external to the PSoC and are there just for documentation.
  2. The Red and Blue LED pins and the clock that drives the Red LED to blink
  3. The EZI2C which serves as the I2C Slave for the Raspberry Pi to read
  4. The Bootloadable component which allows this project to be boot loaded.

The configuration of each of elements is show below.

Creek Schematic

The ADC is configured to run as slowly as possible (1000 SPS).  I have also enabled the averaging which automatically averages 256 samples.  This effectively makes a low pass filter to remove noise.  This is possible because the pressure and temperature move very slowly as compared to the speed at which they are sampled.  The SAR ADC in the PSoC is inherently differential, I tell the ADC that I want the negative channel to be connected to a stable VREF.  This allows me to measure between 0V and 2*vref = 2.048 volts.

ADC Configuration 1

On the ADC channels TAB I enable two channels, both single ended, both with averaging turned on.  Channel 0 is connected to the pressure sensor (on Arduino pin A0 = PSoC Pin P2[0]) and channel 1 is connected to the TMP036 (on Arduino pin A1 = PSoC Pin P2[1])

ADC Configuration Screen 2

For this design I setup the PSoC4 to act as an EZI2C slave.  EZI2C means that the chip follows the EEPROM protocol which many many I2C masters understand.  The only thing that I configure in this case is to use the slave address of 0X08.

CreekEzI2C

For this project to be compatible with a bootloader it needs the bootloadble component instantiated.  In this case I use all of the default configurations.  When I make changes to the firmware in the future I will up-rev the version numbers.  These settings are here to prevent you from accidentally overwriting and newer rev of the firmware or putting in the wrong application type.

CreekBootLoadableConfig

In order for the bootloadable to work I need to tell it where its  “bootloader” image resides.  Remember from above that I put both projects in the same workspace.  So, what I need to do is browse through the directory hierarchy into the bootloader project and selects its ELF and HEX files.  If you want to read more about PSoC4 Bootloading you can read AN86526 entitled “PSoC 4 I2C Bootloader”

BootloableDependencies

One of the unique features of a PSoC is its ability to do hardware based design.  In this design I want a slowly blinking LED to indicate that the system is running.  For this task I have to configure the pin to let one of the internal clocks drive it.  To make this work you need to selected the output mode as “Clock”.

redLedPin

And you need to selected the “Out clock” as external.

redLedClock

Now, I want to turn off the Blue LED.  As the LED is active Low, to turn it off you write a 1 to the pin.  I do this as part of the setup of the pin by setting the initial drive state to high.

blueLED

The last step in the device configuration is to select the proper pins on the Design Wide Resources (DWR) pins tab.  Not setting the pins correctly is one of the most frequent errors that users make with PSoC Creator.

creekFirmwarePins

The firmware for the project is simple.  In the first section (lines 3-8) I define a packed structure to serve as the I2C buffer.  The __attribute__((packed)) tells the compiler to put all of the members of the structure in contiguous bytes.  In the structure I defined below, if I had not marked it is packed, it is likely that the compiler would have put two bytes of padding after the pressureCounts and two bytes after the centiTemp.  It would have done this to work align those datatypes to make the memory access more efficient.

The next section (lines 17-23) I initialize the system and start the components.

The the main body of the program (lines 27-43) I ask the ADC if it is done reading the voltages,  if not go on, if so then calculate the different values and store them in the I2C buffer.  The “CyEnterCriticalSection” prevents an I2C interrupt from reading the buffer before the data is completely written to prevent a partial read of the data.

CreekFirmwareMainC

In the next post I will explain boot loading and the p4bootloader project.

Index Description
The Creek: IOT for the Elkhorn Creek Introduction
The Creek: Solution Architecture 1.0 Overall architecture
The Creek: Creek Board 1.1 Eagle layout of the board
The Creek: Creek Board 1.0 – RCCA A discussion of the errors in the 1.0 board
The Creek: CYPI, a Raspberry Pi to Arduino Bridge PSoC4 <--> Raspberry Pi Bridge Board
The Creek: PSoC4 Creator Schematic and Firmware Firmware to interface with the temperature and pressure sensors
The Creek: Testing the Firmware Using tools to verify that the PSoC 4 Firmware is working correctly
The Creek: Testing the Bootloader Make sure that you can load new firmware into the PSoC
The Creek: Software Architecture All of the Raspberry Pi software connections
The Creek: Install MySql Instruction to configure MySql
The Creek: Install Tomcat Instruction to configure Tomcat JSP Server
The Creek: Data Collection Java (Part 1) The Java program that reads the I2C and saves it in the database
The Creek: Data Collection Java (Part 2) The Java program that reads the I2C and saves it in the database
The Creek: Create the Chart with JFreeChart Using open source Java charting software to create plots of the Creek Depth
The Creek: Flood Event Data Processor A batch program to create analyze the database and create a table of flood events
The Creek: Flood Event Web Page A batch program to create the flood event web page
The Creek: Creek Server 1.1 Updates to all of the back off server programs to integrate charts
The Creek: JSP Web Page for www.elkhorn-creek.org The JSP program to make the table and display the website
The Creek: Raspberry Pi Clock Stretching Sorting out a bug in the system having to do with the Broadcomm Raspberry Pi Master not functioning well with clock stretching
The Creek: Creek Server 1.2 Caching the web pages to make them faster