Rosserial On Cypress PSoC4

From JR
Jump to: navigation, search

project notes, trying to bring up Rosserial on Cypress PSoC4.

See Also: RosOnAStick

[24-Feb-2014: updated for PSoC Creator v3.0 & ros-hydro]

Contents

Overview of PSoC4 development

The PSoC4 is a "programmable system on a chip", meaning it has a small amount of programmable logic, programmable interconnect, and "soft peripherals" in addition to CPU, memory, and a complement of fixed peripherals typical of a microcontroller. There is a $25 "Pioneer kit" dev board which was designed to be pin-compatible with Arduino shields. The CPU is a 48MHz ARM0, with 32KB of flash and 4KB of RAM.

No super-chip, but nicely priced and a good candidate for a ROS node. The PSoC4 chip alone retails ~$3.00 in small quantities.

Cypress provides a very slick IDE (no cost, Windows only) called PSoC Creator, which integrates development of the programmable logic and peripheral configuration with software development. There is no Cypress support for a linux environment, and very limited ROS support for Windows. So, a bit of a challenge here.

PSoC Creator wraps a toolchain which includes a GUI for data entry, proprietary configuration tools, a programmable-logic synthesis/place/route toolset (3rd-party?), the gcc ARM compiler, and a device programming tool that operates over USB. Here is my hybrid-OS plan for putting Rosserial on this platform.

  1. Define the programmable logic and peripherals in the Windows PSoC Creator tool
  2. Build a "stub" program in PSoC Creator. This will synthesize the programmable logic and automatically generate a set of source files providing Cypress APIs for the soft peripherals.
  3. Copy the generated source files from the Windows environment to Linux (or access through a shared disk)
  4. Write the PSoC application code in a catkin src/rosserial/rosserial_psoc4 directory parallel to the existing src/rosserial/rosserial_arduino directory
  5. Follow the rosserial-arduino model of generating rosserial source code into an catkin_ws/install directory. Copy the generated source files (make_libraries.py) into a library directory for the ARM build.
  6. Build the ARM binary using application source files, rosserial-generated files, and PSoC-Creator-generated files (using arm gcc 4.7.3 and libraries from Cypress) Note must deal with mixed c, c++ code base
  7. Copy the linked hex file back to the Windows environment
  8. Combine the hex program file with the metadata components of the original PSoC Creator hex file to build a complete chip-programming hex file (Maybe write a python script to perform the Cypress "cyhextool" function?)
  9. Use the PSoC Creator programming function to download to the board

OK, that's a long way around the barn, but maybe we can tighten it up later.

How To

This may not be the best way, but it's the way I worked out.

On your Windows machine:

1. Install the PSoC Creator software as described in the Pioneer Kit documents.

2. Make sure that Creator is in your PATH. This is necessary for using makehex.bat, later.

set PATH=%PATH%;"C:\Program Files\Cypress\PSoC Creator\3.0\PSoC Creator\bin"

3. Download the rosserial example workspace from github: RosserialPSoC4.png

4. Extract the zip file and open the rosserial_psoc4\rosserial_psoc4.cywrk workspace in PSoC Creator.

CreatorScreen.png

5. If you wish to change the microprocessor peripheral configuration, you must "clean and build" the project, which generates a set of c-language source files providing initialization and API access to the peripherals. (The "main.c" file in the Creator project is a dummy; we only need the auto-generated peripheral-API code.)

On your Ubuntu machine:

Host environment is Ubuntu 12.04 LTS, 64-bit. I am running it in a VirtualBox VM.

1. Install some basic build tools

sudo apt-get install build-essential dkms

2. Get the ROS Hydro distribution. http://wiki.ros.org/hydro/Installation/Ubuntu

3. More tools, and give yourself permission to use the serial port

sudo apt-get install python-rosinstall
sudo adduser <my-user-name> dialout

4. Get the gcc-arm 4.7 distribution, https://launchpad.net/gcc-arm-embedded/4.7/4.7-2013-q3-update/+download/gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar.bz2 into ~/Downloads

cd ~/Downloads
bunzip2 gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar.bz2
tar xvf gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar
sudo mv gcc-arm-none-eabi-4_7-2013q3 /opt/

5. You may need

sudo apt-get install openjdk-7-jdk
sudo apt-get install ia32-libs

The last line, installing ia32-libs 32-bit compatibility libs for 64-bit Ubuntu, seems to be necessary in order for the gcc arm compiler to run. Without this you are likely to get a misleading "arm-none-eabi-gcc: No such file" error, despite the file being present.

6. Install rosserial_arduino, instructions here: http://www.ros.org/wiki/rosserial_arduino/Tutorials/Arduino%20IDE%20Setup#Installing_from_Source_onto_the_ROS_workstation , but get the files from chuck-h's forked repository:

  cd ~/catkin_ws/src
  git clone https://github.com/chuck-h/rosserial.git
  cd ~/catkin_ws
  catkin_make
  catkin_make install
  source ~/catkin_ws/install/setup.bash

These instructions will clone a rosserial git repository into your catkin workspace.

7. Copy the Cypress component library from your Windows machine "C:\Program Files\Cypress\PSoC Creator\3.0\PSoC Creator\lib\CortexM0\ARM_GCC_473\Debug\CyCompLib.a" to the Ubuntu machine "~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress/CyCompLib.a"

8. Copy the PSoC Creator output files for the HelloWorld example from within the PSoC Creator workspace directory: rosserial_psoc4/HelloWorld.cydsn to the Ubuntu machine "~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress/rosserial_psoc4/HelloWorld.cydsn", as described:

chuck@rosman:~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress/rosserial_psoc4$ cat readme.txt
This directory holds the output files produced by Cypress PSoC Creator, a Windows-only program

1. Open the rosserial-psoc4 workspace in Creator. It contains several example projects
2. Select a project (e.g. HelloWorld) and build it. This will produce
   a) a set of .c and .h source files in HelloWorld.cydsn/Generated_Source
   b) HelloWorld.cydsn/device.h
   c) many other files that we do not use
3. Copy the HelloWorld.cydsn directory from the Windows machine to this directory. It is only
   *necessary* to copy a) and b) files above, but moving the whole directory is ok.

You can copy the other example files (ADC, pubsub, etc.) in the same way.

We now have all the necessary files from the Windows-based PSoC Creator world on our Ubuntu machine. The build process goes as follows.

9. If you want to examine or change the example code, edit the "master" version at ~/catkin_ws/src/rosserial/rosserial_psoc4/src/ros_lib/examples/HelloWorld.cpp .

10. Run catkin_make to stage master source files to the install directory. This is done via predefined CMake scripts.

cd ~/catkin_ws
catkin_make install
Base path: /home/chuck/catkin_ws
Source space: /home/chuck/catkin_ws/src
Build space: /home/chuck/catkin_ws/build
Devel space: /home/chuck/catkin_ws/devel
Install space: /home/chuck/catkin_ws/install
####
#### Running command: "make cmake_check_build_system" in "/home/chuck/catkin_ws/build"
####
####
#### Running command: "make install -j4 -l4" in "/home/chuck/catkin_ws/build"
####
[ 11%] Built target rosserial_msgs_generate_messages_cpp
 ...
 ...

11. Run make_libraries.py to generate additional source files and copy everything into the Cypress directory where we will do the ARM-gcc build. This is effectively the same as the Arduino process described here.

cd ~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress
rm -rf libraries
rosrun rosserial_psoc4 make_libraries.py libraries

12. Cross-compile and link the code using make:

cd ~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress
make clean
make

Note that you can select the particular example-application to be built by editing the beginning of ~/catkin_ws/src/rosserial/rosserial_psoc4/src/Cypress/Makefile.

If there were no build errors, the end result of the make is the file HelloWorld.elf. This gets passed back to the Windows machine.

On your Windows machine again

1. Copy HelloWorld.elf into the PSoC Creator project directory as rosserial_psoc4\HelloWorld.cydsn\HelloWorld.elf.

2. Run cyelftool to make the final hex file

cd rosserial_psoc4\HelloWorld.cydsn
cyelftool.exe -C "CortexM0\ARM_GCC_473\Debug\HelloWorld.elf" --flash_size 32768 --flash_row_size 128

This will create HelloWorld.hex and place it in the CortexM0\ARM_GCC_473\Debug subdirectory, where PSoC Creator expects its own final binary to be.

3. Open the project in PSoC Creator. Make sure the Pioneer Kit is connected to the USB port. Select the Debug/Program menu function (or ctrl-F5) to download the code into the Pioneer Kit.

Running ROS

Connections

The PSoC4 serial lines are connected to a logic-level serial-to-USB adapter:

PSoC serial.jpg

Top USB: power & programming (Windows machine)

Right-hand USB: Rosserial serial connection (Ubuntu machine) - Pioneer Kit J3 pins 7, 9, & 10

Operation

See http://wiki.ros.org/rosserial_arduino/Tutorials/Hello%20World

RosPSoC serial.png

Update 16 Sept 2013 / 16 Oct 2013

This code runs for a while but has been failing intermittently for me; the serial communications have been unreliable. I am working on two issues.

1. There seems to be a problem in Oracle Virtual box USB (ticket here). This is only a problem if you are trying to run inside a Virtualbox VM.

  • update 16-Oct: workaround for me has been to configure VirtualBox guest with single CPU and PIIX3 motherboard.

2. I think there is a bug in the Cypress UART driver generated code UART_UartGetByte(), where an RX interrupt occurring at just the wrong time will cause a character to be dropped. Hoping to have a patch soon.

  • update 16-Oct: What's happening is that it is possible for UART_UartGetByte() to return with a validly-received character in the low byte OR'd with a "buffer empty" error code in the higher bytes. A genuine buffer empty condition returns the same error code with low byte = 0, but since 0 is possibly a valid data byte this is ambiguous. A fix is to avoid UART_UartGetByte() and use lower-level calls, as in Uarts.cpp, Uarts.h at this commit

Notes

source code at https://github.com/chuck-h/rosserial

Rosserial needs only three things from its host environment: readchar, writechar, and system time in milliseconds. The first two come from the cypress UART API, and system time can come from the ARM SysTick timer (see Cypress App Note AN54460 project D).

The libgcc libraries for heap management (malloc, impure) are too big for this processor. That should be okay, many (most?) small microcontroller projects don't use dynamic memory allocation. The stock rosserial distribution uses dynamic memory allocation in the rosserial_msgs/RequestParam message, supporting the client's ability to read parameters from the ROS parameter server. Workaround: Support arrays without malloc().

The ARM-M0 processor does not support unaligned memory access (e.g. writing a 32-bit integer starting at an odd byte address). The rosserial serialization code uses unaligned accesses in several places. (Issue tracked here: https://github.com/ros-drivers/rosserial/issues/10). This can be fixed in make_library.py, see patch, which is already incorporated in the chuck-h git fork. (Equivalent is merged into rosserial upstream here.)

It's necessary to have a big enough RX serial buffer to collect traffic between calls to SpinOnce. The default Cypress UART configuration is an 8-byte hardware FIFO, which doesn't cut it. Specifying a larger buffer (e.g. 64 bytes) generates software FIFO code and pulls in UART_SCB_IRQ.c . Also, it is risky practice to put a big delay() call in the main loop (as in HelloWorld.cpp),

void loop()
{
  str_msg.data = hello;
  chatter.publish( &str_msg );
  nh.spinOnce();
  delay(1000);
}

as many incoming messages can queue up before spinOnce() gets called; better to spin constantly and use a timer to perform periodic operations (see pubsub.cpp for an example).

void loop()
{
  if ((int32_t)(SysTimer::millis()-next_report_time) > 0) {
    next_report_time += kReportIntervalMs;
    str_msg.data = hello;
    chatter.publish( &str_msg );
  }
  nh.spinOnce();
}

It is also a good idea to have a substantial TX buffer so that nodehandle doesn't stall in the middle of a publish, waiting for the characters to be emitted.

Personal tools
Namespaces

Variants
Actions
Puzzle Pieces
Wiki tools
Operations plan
For staff
Toolbox