Example Firmware in C
for
Cypress enCoRe devices
by Stephen Santarelli
November 19, 2003
IMPORTANT NOTE: A bug was
recently uncovered in the firmware. Sometimes commands from the host
that set LEDs are not
received properly. Read here for bugfix information. A
completely updated and improved version of this firmware will be posted
within the next few weeks--Please check back in.
sfs 4-15-04
This page describes some simple
firmware
wrtten in C
using the Byte Craft cross-compiler for Cypress USB M8
microcontrollers. This makes a handy starting point for the
firmware developer wishing to use C to develop a wide range of
USB peripherals within the HID class.
Introduction
Cypress makes some very functional yet economical single-chip
microcontrollers for creating low speed USB peripherals. The
enCoRe (enhanced Component Reduction) series devices are their latest
offerings, numbered CY7C632XXA, and CY7C637XX. They include
these features:
- An onboard USB SIE (serial interface engine), with support for 2
('632XXA)or 3 ('63743)endpoints
- Between 3K and 8K OTP PROM
- Between 96 and 256 bytes on-chip RAM
- A free running microsecond timer
- Two edge triggered timer capture registers to aqcuire microsecond
resolution timing of external signals
- I/O pins may be programmed for different drive levels , and may
be individually programmed to generate interrupts. There
are between 8 and 16 I/O pins, depending on the variant.
- SPI port
- Watchdog timer
- Internal oscillator (no crystal or resonator required)
- PS/2 interface to implement "legacy" keyboards and mice
- DIP and SOIC packages convenient for homemade prototypes or low
volume production.
See Choosing
the Right Device for your USB Application for a quick comparison
between between these devices and some other low speed MCUs make
by Cypress.
Why make a low speed device when you could just choose a high speed
chip? Well, not all USB devices benefit from high speed
communication, and the original USB developers were aware that for some
devices (e.g. mice and keyboards), it didn't make sense to require a
12Mbps transciever and a heavy shielded cable. Imagine
wiggling your computer mouse around with the same heavy cable that you
connect to your printer or scanner. Reduced complexity and
bandwidth translate to lower cost and easier product development..
Using the serial port for comparison, a low speed USB peripheral
can send and receive 800 bytes per second, roughly comparable in data
throughput to a serial port peripheral sending and receiving data
at 9600 bps, with start, stop and parity bits. USB looks even
better
than that if you realize that it also includes ACK/NAK handshaking,
and CRC checking, which would add more overhead to a serial link.
Going HID
One class, or subset, of USB devices is the HID (human interface
device). HIDs include things that you would think of as devices
to interface humans with computers, such as keyboards, mice, and
joysticks. Fortunately though, a HID is not limited to these
types of tasks. A USB voltmeter, photometer, or even a Geiger counter
could be defined as a HID.
Perhaps the biggest benefit of defining a device within the HID class
is that you can communicate with the device without a
custom device driver! There are API calls in Windows, MacOS, and
Linux that allow you to communicate with these devices in user space.
Here's another nice fact about HIDs: standard, predefined devices like
mice and keyboards are not platform specific. You can plug a
Macintosh
mouse into a Windows PC or Linux PC, and vice-versa.
Keyboards will work too, though the keys could have some different
labelling (command key on Mac vs. Alt key on PC). You can also
connect multiple keyboards and mice to a single computer.
I began working with the enCoRe series in Fall of 2002. My first
projects were pretty simple, so my firmware was written using the
assembler. Stuart
Allman's EDN article provides a
simple example of a custom HID, and was the basis for my early
firmware.
Often the bulk of the firmware for a HID is the bit stuffing
required to make the SIE do what it needs to. That code may be reused
for different projects.
Programming enCoRe in C
enCoRe devices belong to the M8 family of microcontrollers, which use a
RISC instruction set, and only have two useable registers:
the accumulator and the index register. Many operations require moving
data into the Accumulator, operating on it, then writing back to RAM.
The instruction set is very
simple, so writing code for these CPUs is not so much difficult as it
is monotonous, especially if you want to work with data types larger
than 8 bits wide. Why not use C? If you already know the
language, and the compiler doesn't add too much overhead, you will save
time and get the job done right.
If you are using enCoRe chips in your design, you almost certainly own
a Cypress CY3654+P05 platform kit. A C cross-compiler
written by Byte Craft is included with the development kit.
The compiler is versatile and creates efficient code. You
are free to use the rich selection of operators and constructs
available in C. A wide range of data types, 8, 16, 24, and 32 bit
signed and unsigned integers, plus 24 and 32 bit floating point
types . In addition there are two data types bit and bits
that make accessing specific I/O or register bits easier.
It is not difficult at all to blend assembly code
with C code, and the two CPU registers and all the I/O and control
registers are
accessible directly from within C code. Listing files
generated by the
compiler are compatible with Cydb, the debugger included with the
Cypress development kit. You can see the resultant op codes side
by side with your C code.
A library with some neat extras is included, including a software
UART, string manipulation functions, even functions for
communicating with LCD devices based on the ubiquitous Hitachi HD44780
LCD controller. There are macros and library functions that allow
simple reading and writing of external I/O pins. The linker is
efficient, and does not place unused library functions into ROM.
Control of I/O pins is simple. To read a port, just use something
like:
pinstate = PORT0.3;
to read bit 3 of port 0. To write to a port, you
usually want to
use one of the macros provided, as they use shadow registers.
This read/modify/write sequence is an important consideration with
these MCUs. The macros make things easy, for example:
port_clear_bit_PORT1(2);
will clear only bit 2 or port 1, leaving the other pins on port 1
unchanged.
Example Firmware in C
When starting a new embedded design, it is usually advantageous to look
at a simple working example, particularly with a USB peripheral, as
there are stringent requirements for the device to work properly.
It was disappointing to find that there were no complete firmware
applications written for enCoRe chips. Cypress has some app notes and
sample code for these on their website, but all in assembly code.
The two
application examples included with the compiler distribution, mouse.c
and keyboard.c, were written for other M8 devices. The mouse
example was written for a '63100A, which has very different I/O
register and bit definitions.
The keyboard example was written for a '63512. Fortunately,
this MCU has a register architecture almost the same as the
enCoRe series, particularly registers that control USB SIE
functions. None of the enCoRe chips have enough I/O
pins to implement a real keyboard, but I wasn't interested in building
one anyway. The goal here was to create a device that looked like a keyboard to the host
as a starting point for other HIDs.
The Cypress development system comes with an application board,
the CY3654D500, which has a socket for the MCU, a temperature sensor
IC, an EEPROM, a pushbutton, and two LEDs. My program
effectively turns this development board into a single key
keyboard, ignoring the EEPROM and thermal sensor.
Plug this
device into a computer, and it thinks you've plugged in a full
keyboard. Press the button, and it thinks you've pressed the 's'
key. It will work with Windows, MacOS, and Linux, provided you
are using versions that support USB. The firmware can easily be
modified so any other keycode
could be sent, including any key combination you could produce with a
real keyboard. Note that on USB enabled machines you may have
more than one keyboard
connected simultaneously, so you don't have to unplug your real
keyboard to test this device. You can even connect it to the USB
port of a computer that is using a PS/2 keyboard, or a laptop.
Take a look at the Cypress app
note on designing a USB keyboard for a full explanation of the data
packet format that a keyboard needs to send. Key codes may
be found in the HID
Usage Tables these are part of the USB specification.
The original keyboard.c program needed some modifications
just to compile without errors for my target device, a CY7C63743.
Then the matrix scanning code was removed, and replaced by a simple
read of the pushbutton, using a 30ms debounce.
When this button is pressed, a keycode is sent (in my example the
letter s). It doesn't send anything else until the button is
released. When that happens, keycode 0x00 is sent to notify the
host. The host's operating system keeps track of how long a key
has been pressed, and starts the typematic repeat if it's held down
long enough.
The ENUM LED is turned on when the device has been
enumerated.
The ACTIVE LED lights when the pushbutton is held down.
The
host does tell a keyboard which LEDs (NUM LOCK, SCROLL LOCK,
or CAPS
LOCK) should be lit via control transfers with Set_Report requests
on
endpoint 0, but these lights would only be turned on if the host
received the appropriate key code (NUMLOCK, CAPS LOCK,
etc) from this
particular keyboard. I didn't want to use my one available "key"
as one of these keys, I wanted to see the results of my keypress on the
computer screen.
I did not take the time to test suspend mode and remote wakeup, which
means it is guaranteed not to
work. When I have a chance, that will be fixed. If someone
can take some time and fix it, please send me a copy of the
source. There are unused variables and arrays that should also be
removed. Sorry, please ignore.
Caution:
when I was porting
this code over, I encountered some problems. The C63743.h file
which defines registers and pin assignments has some missing
definitions and errors as of compiler release 1.5.1. Port.h also
has problems. Nevertheless, encorekbd.c
works around these, and will compile and work properly with version
1.5.1 of the compiler, using header files and libraries belonging to
that version. I
haven't yet tried V1.20 (the version actually shipped with the
development
kit), though I suspect there may have been even more header file
issues. You may have to contact Byte Craft directly to get the
latest files.
There is a curious bug which cropped up while I was using this
compiler. Once in a while, during development, the firmware
will simply not
work right. After a lot of trial and error, I found that placing
one (or sometimes more) NOP() macros in the code will temporarily cure
the problem. Write some more code, and you will have to remove
the NOPs, and so forth. It turns out these NOPs can apparently go
anywhere in the program, and
they will still cure the problem. Byte Craft support is currently
looking into
this issue, and also fixing the header files.
Moving Forward
The idea here was to get a simple example of a HID as a
starting point for a bigger project. Though a one key
keyboard may not seem very useful, here are some ideas for projects that
could be made with little or no changes to the source.
To use this code as a basis for other USB HIDs, you will need to
modify the descriptor reports, the descriptor strings, get your own
VID/PID and add some of your own application specific code. The
HID descriptor is the most complex of these, but is explained thorougly
in USB Complete, by Jan
Axleson.
Read Using
the HID class eases the job of writing device drivers
by Stuart Allman for a great example of a user defined HID device,
including a simple Windows application written in C. The
firmware is written in assembler, but it isn't hard to modify
encorekbd.c to duplicate its functionality.
Links
- USB.org Universal
Serial Bus specifications, HID class definition, HID usage tables, etc.
Download encorekbd
IMPORTANT NOTE: A bug was
recently uncovered in the firmware. Sometimes commands from the host
that set LEDs are not
received properly. Read here
for bugfix information. A completely updated and improved version
of
this firmware will be posted within the next few weeks--Please check
back in.
sfs 4-15-04
Source code: encorekbd.c
ROM image, Intel hex format: encorekbd.hex
Requires Byte Craft Optimizing C Compiler for Cypress Semiconductor USB
M8 Microcontrollers. This version was built using compiler version
1.5.1, including updated header files and libraries. Based on
example code provide by Byte Craft, see source file for restrictions on
use.