Esta página también está en español
In the subject called PI -Periferals and Interfaces- at the FIB (UPC, Public University of Catalonia) we used a development board at the practices. You connect it to a computer and transfer the programs, which can control the buttons, write on the LCD screen, or communicate with the computer or other devices.
We used proprietary programs which only worked on Windows, so I looked for a way to do the same but on any operating system, and with free (as in freedom) and gratis programs, so that anyone could study them.
This isn't finished, but it's a starting point to anyone who were looking for this. At least you won't need to start from zero.
It's a little tricky to understand what the board is. Starting from the most general, we have:
The board has been designed and set up by the teachers of the subject (PI); they bought some pieces at electronic shops and they put them together. Among these pieces are: dot matrix, buttons, memory (64 Kb), serial and USB connectors, and the μC AN2131QC, and furthermore some LEDs, resistors, transistors, and others.
The main component at the board is the microcontroller AN2131QC, from the company Cypress (which bought Anchor Chips, the original producer). The AN2131Q is a product of the EZ-USB family from Cypress. Other microcontrollers (AN2122T, AN2135S, etc) have somewhat different characteristics (more USB endpoints, or faster ports, for instance).
This microcontroller, AN2131QC, has several important components, such as a 8 Kb memory, a UART (it has two), and of course the processor, which is an improved 8051. The original 8051 was made by Intel at 1980, bit now is produced by other companies, like Atmel. It's an 8 bits CPU with 256 bytes of RAM, 2 or 3 timers, 5 or 6 interrupts, 4 ports (of 8 bits), and serial port. The improvements made by Cypress with their EZ-USB is that it runs faster, operates at 3.3V, has a third counter (16 bits), and more.
The good thing is that the assembler code of the 8051 CPU still works on the microcontroller AN2131, so the board used in PI can also be programmed with code compatible with the 8051. In addition, some extra functions must be used to access everything related to just the EZ-USB.
There exist other development boards which use the AN2131QC from Cypress, for example: MF3001, USB I2C/IO, USBSIMM; and if you don't like them, make one yourself.
To make our program run in the board, we had to follow these steps:
a=5;
doesn't mean that a
gets set the value 5, but
it might be a call to a hardware function.
Sometimes just changing the order of assignments
(b=0;a=0;
instead of a=0;b=0;
) solved the problems.
So the two tools used are: Keil (compiler) and the loader from Cypress. I haven't exhaustively studied them as they only run on Windows, but what I have tried is to find a way to do the same, but using only crossplattform, no cost, and hopefully free software (which we can study). And not a demo, like Keil (at some subjects, like SDMI students could not compile large projects since the demo was limited in size).
I explain this first because is the easiest part. We have an .hex
(already compiled) and want the board to run it.
In kernel 2.6, the ability to transfer to the EZ-USB board is already
incorporated in the kernel. You just need the program fxload
, and a
command like fxload -I DosPunts.hex
is sufficient to make the
board start running the just transferred code. In kernel 2.4 you
must use this driver.
So that you can test, here there is an example program: DosPunts.hex. It shines two points at the dot matrix, and does nothing else.
This needs a cross-compiler for the specific language we want. For C or C dialects, there are several 8051 compilers. Among them there's C51 (the one found in Keil, expensive and for MS-DOS) and sdcc (free software, gratis, crossplatform, and including a lot of other tools).
You can compile code written in several other languages, both high and low level. For ASM (assembler), the known ones are A51 (from Keil), asx8051 (from sdcc), and others.
The one I have decided to use in Linux is sdcc (it seems it's the most complete,
and in addition it's free software).
To create the hex
you have to do so:
file.c
sdcc file.c
, possibly passing it some options like -I
to specify where are the includes.
This creates a lot of files:
.asm
,
.ihx
,
.lnk
,
.lst
,
.map
,
.mem
,
.rel
,
.rst
,
.sym
.
If what you were compiling was assembler,
it's done with asx8051 -o file.asm
and after that, sdcc file.rel
.
ihx
to hex
with packihx file.ihx >file.hex
.
To compile projects which have several files, compile each one individually
with flag -c
and then link them together, putting the main.c first.
SDCC manual explains this better.
And something else:
due to the architecture of the PI board, sdcc
will need some parameters
like
--code-loc 0x0080 --xram-loc 0x1000 --iram-size 256 --model-small
(well, default model is small anyway).
I found these data at the Keil configuration at the laboratories.
All this works; the problem is that at the EZ-USB board, most of the functions needed to access the hardware are implemented in an extern library, called EZUSB.LIB. For instance, in this program, DosPunts.c, it can be seen that almost the whole code are custom functions (EZUSB_WriteI2C, EZUSB_Delay, ...).
At the examples we included (#include
) two header files:
ezregs.h
tells the address for each register in the board,
both for the internal microcontroller (8051) and for
the ones added by Cypress.
ezusb.h
declares the structures and functions specific to the
EZ-USB bourd (EZUSB_InitI2C, etc)
These files, and others found at the directory include of the program, also need modification to be able to compile without problems. The originals are here: include-orig.tar.gz, and the fixed version is in this folder: include-fix/ (also in this tar.gz). I hope I don't disturb anyone by distributing them.
The changes I made to them are:
On Windows, each program referenced the files in a different manner.
I have renamed them all to lowercase; for instance ezregs.h
(formerly EZRegs.h
) so that it is easy to work with them.
And by the way, I have indented them and removed the TABs.
At ezregs.h
we have the memory address by which each
SFR from the board
can be accessed.
sdcc
already provides a similar file,
at /usr/share/sdcc/include/8051.h
;
the addresses of both do agree, but the problem is that our micro
is an extended 8051 (not to be confused with the 8052), and
there are definitions which are missing in the file from sdcc.
For instance, in the original there's just SBUF
, but the one
from Cypress has SBUF0
and SBUF1
.
Anyway, the sdcc file is useful to learn the syntax which the register
definitions have to follow. It should be something like:
sfr at 0x8D TH1;
but the Keil format is
sfr TH1 = 0x8D;
.
Both use sfr
for register names
and sbit
for names of bits inside registers.
I have corrected this (by using regular expressions it's done very fast).
At ezregs.h
there's also the definition of registers found at the
EZ-USB board (not at the 8051), such as the ones we need to use the USB.
They are xdata
registers, so they have to be accessed as if they were
at the external RAM.
The bad point is that they are doing a filthy trick with preprocessor:
#ifdef ALLOCATE_EXTERN #define EXTERN #define _AT_ _at_ #else /* */ #define EXTERN extern #define _AT_ ;/ ## / #endif /* */ /* Register Assignments 3/18/99 TPM */ EXTERN xdata volatile BYTE OUT7BUF[64] _AT_ 0x7B40; EXTERN xdata volatile BYTE IN7BUF[64] _AT_ 0x7B80; EXTERN xdata volatile BYTE OUT6BUF[64] _AT_ 0x7BC0;
And like that, for all registers. The intention is the change _AT_
to ;//
to
ignore the right half of several lines.
This doesn't please sdcc
(warning: pasting "/" and "/" does not give a valid preprocessing token
);
and neither to me (in fact, gcc
also joins our complaining, with normal C).
I have changed that to something more elegant:
#ifdef ALLOCATE_EXTERN #define NEWEZREG(_name,_where,_size) volatile xdata at _where _size _name #else /* */ #define NEWEZREG(_name,_where,_size) extern volatile xdata _size _name #endif /* */ /* Register Assignments 3/18/99 TPM */ NEWEZREG(OUT7BUF[64],0x7B40,BYTE); NEWEZREG(IN7BUF[64],0x7B80,BYTE); NEWEZREG(OUT6BUF[64],0x7BC0,BYTE);
With that, ezregs.h
is fixed;
fx2regs.h
and fx.h
also need the same change.
These 6 files are included when you program in assembler:
They use the Keil syntax, but they are so seldom used that I preferred not to change them. I will talk of them again.
The functions to work with the components added by Cypress are in the library file EZUSB.LIB, and they are more or less these. When we worked with Keil (Windows), at each project we had to include the file EZUSB.LIB so that it got linked with the compiled code; the problem is that the original EZUSB.LIB is in the format which understands Keil, but that's not the same of the one used in sdcc libraries.
Fortunately and not hoping it, I found a diretory with all the source code of the EZUSB library in a Keil installation. But it's prepared to compile only with the environment and syntax of Keil (using C51 and A51). I hope I don't disturb anyone if I distribute this here.
After a lot of work, I ported all the C and ASM code of this library to a format which sdcc could understand. The result is in ezusb-fix/ (also in this tar.gz); will Cypress get angry if I publish fixes for their code?.
Despite the codes were simple, I have fixed several errors. Now I will explain the changes that were needed.
According to Cypress, EZUSB.LIB is created using C51
to compile each .c
separately and A51
for each .a51
, also separately.
Then you must create an empty library, with LIB51
, and add all of the
generated object files except the ones for
2200jmpt.a51
, FXJmpTb.a51
, renum.a51
, USBJmpTb.a51
.
According to sdcc, a .lib
is just a list of .rel
files,
which are the object code, result of the
individual compilation of each
.c
or .asm
(by using -c
).
It's so easy: just a list with the file names, one per line.
In addition, a .lib
from sdcc may also contain all the code
(so that it ends up being in just one file), and the format is
equally simple: by using sdcclib
, the content of each
rel
(which is plain text) is included, and they are
classified by using a tags system similar to HTML.
On the other hand, the Keil format is binary, and
not easy to understand (at least by me).
Then, my goal is to fix the 13 files
delay.c
,
delayms.a51
,
discon.c
,
EZRegs.c
,
get_cnfg.c
,
get_dscr.c
,
get_infc.c
,
get_strd.c
,
i2c.c
,
i2c_rw.c
,
resume.c
,
susp.a51
,
usbirqcl.a51
,
compile them separately, and join them in a librray to make my
own ezusb.lib
.
To compile the library on Linux, the #include
have to reference
the correct file (agreeing even with the upper and lower case).
I have changed in all files things like
#include "..\inc\Fx.h"
to #include <fx.h>
.
get_cnfg.c
, get_dscr.c
, get_infc.c
, get_strd.c
needed
the symbol NULL
, which is in stdlib.h
, not stdio.h
.
i2c.c
directly didn't need it.
It says this when compiling:
get_infc.c:16: warning: function return value mismatch from type 'struct __00010003 generic* ' to type 'struct __00010003 xdata-xdata* '
The relevant part of code is (the 16 is the return
):
INTRFCDSCR xdata* EZUSB_GetIntrfcDscr(BYTE ConfigIdx, BYTE IntrfcIdx, BYTE AltSetting) { ... INTRFCDSCR *intrfc_dscr; ... intrfc_dscr = (INTRFCDSCR xdata *)((WORD)config_dscr + config_dscr->length); return(intrfc_dscr); ...
The function should return a xdata*
("pointer to the RAM external to the 8051"),
but in the small
model we are using in compilation, local variables are placed
by default in internal RAM (storage type data
).
I don't think that USB descriptors are located in data
(the 128/256 bytes of internal RAM),
so declaring it xdata*
it will solve it:
INTRFCDSCR xdata *intrfc_dscr;
The error compiling i2c.c
:
i2c.c:5: error: extern definition for 'I2CPckt' mismatches with declaration. from type 'struct __00010007' to type 'volatile-struct __00010007'
Line to blame:
I2CPCKT volatile I2CPckt;
.
But at ezusb.h
it says that extern I2CPCKT I2CPckt;
, so that doesn't match.
There are two options: add volatile
wherever needed, or remove it where it appears.
I think that if they put it, there must be some reason, so I add it too to ezusb.h
and fx2.h
:
extern I2CPCKT volatile I2CPckt;
At i2c.c
and i2c_rw.c
there are some functions which return BOOL
:
/* en i2c.c */ BOOL EZUSB_WriteI2C_(BYTE addr, BYTE length, BYTE xdata *dat); BOOL EZUSB_ReadI2C_(BYTE addr, BYTE length, BYTE xdata *dat); /* en i2c_rw.c */ BOOL EZUSB_ReadI2C(BYTE addr, BYTE length, BYTE xdata *dat) BOOL EZUSB_WriteI2C(BYTE addr, BYTE length, BYTE xdata *dat)
According to ezusb.h
, it's said that typedef bit BOOL;
, but a function returning a single bit
is not allowed. In fact, the ones from i2c.c
return TRUE
and FALSE
, which
are defined (#define
) at ezusb.h
to 1
and 0
respectively, so they can
be normal integers.
The strange part is that the ones from i2c_rw.c
return constants like
I2C_OK
, I2C_NACK
, I2C_BERROR
,
with values 8, 7 and 6 respectively. I can't understand the meaning of that transformation to bit; sdcc is doing well by complaining because of that.
I have changed the return type to BYTE
, changing the headers at i2c.c
, i2c_rw.c
and
ezusb.h
and fx2.h
. In theory a byte can function in the same way as a bit: 0 means false, and different to 0, true.
But this change may make some older programs (which wanted to get a BOOL
) stop compiling now.
To complete EZUSB.LIB there still are 3 assembler files:
delayms.a51
,
susp.a51
,
usbirqcl.a51
,
which include (in the sense of #include
) to
ezregs.inc
,
even when there are 6 includes:
ezbits.inc
ezbits.inc
ezregs.inc
fx2regs.inc
fx.inc
macros.inc
reg320.inc
.
The syntax used by asx8051 (the compiler found in sdcc) is very different to the
one from A51 (the assembler compiler from Keil).
It's worth studying the .asm
which sdcc generates when it compiles C.
In asx8051 there are some differences respect from A51:
.globl
(or with tag::
).
Then you don't need the -g
when compiling.
.include
equ
) are done with NAME = value
.area CSEG (CODE)
before the code.
0xVALUE
, and binary
with 0bVALUE
. In A51 they have the letter at the end.
end
instruction at the bottom of each file.
EZUSB_Delay1ms
, for instance, at the assembler
there's only defined the symbol EZUSB_DELAY1MS
.
_
(it means
external symbol). sdcc adds it automatically when compiling C.
Instead of fixing the 3 .a51
, I have created 3 .asm
files with the
code written in the syntax of asx8051,
which -in my opinion- is clearer than A51's.
Since data from the include was scarcely used (just to obtain the address
of the SFR USBCS
, PCON
and EXIF
),
I have repeated the declaration of those registers where needed, and I haven't
used includes.
I hope to have converted the files correctly.
Once the 13 files (10 C and 3 ASM) have been compiled, there have been
no problems creating ezusb.lib
from the 13 .rel
with sdcclib
.
Using the corrected header files (include-fix/) and the corrected library (ezusb-fix/) I can now compile DosPunts.c with:
echo OPCS="--code-loc 0x0080 --xram-loc 0x1000 --iram-size 256 --model-small" sdcc -I ../include-fix -L ../ezusb-fix -l ezusb $OPCS DosPunts.c packihx DosPunts.ihx >DosPunts.hex
I tried simulating the hex with s51 (it comes with sdcc): s51 DosPunts.hex
.
It's not very appropriate since the PI board is not an 8051
(for instance, it has 256 bytes of RAM instead of 128).
In the simulation, s51 said:
... Error: invalid address 0x82 in memory iram. 0x04ad f6 MOV @R0,A Error: invalid address 0x81 in memory iram. 0x04ad f6 MOV @R0,A Error: invalid address 0x80 in memory iram. 0x04ad f6 MOV @R0,A
That's logical, since at the 8051 (which is what it's simulating), there are just 128 (0x7f) bytes of RAM, and beyond 0x80 there live the SFRs. You can blame the initialization code which sdcc adds to empty the RAM:
... ; starting with r0=0 and a=0 00005$: mov @r0,a djnz r0,00005$ ; _mcs51_genRAMCLEAR() end
This is like i=0; do { mem[i]=0; i--; } while (i!=0);
.
That's effective, but the simulator complains.
You may add --no-xinit-opt
when compiling to stop generating this
initialization code and avoid warnings (but face the consequences),
or ask s51 to perform as an 8052, which has the full 256 bytes:
s51 -t 8052
(even when our board is not an 8052).
hex
and load them
in the board.
sdcc
.
ezusb.lib
which can be used with sdcc
.
ezusb.lib
with sdcc.
ezusb.lib
, look for some other
compiler which accepts libraries in the Keil format.
There are some, but I haven't found any free one which runs on Linux.
It will be difficult to find something better than sdcc.
I started writing this with absolutely no knowledge about microcontrollers; if in the theory classes at PI they had taught us something about the practices, I could have gathered all this information faster.
I have read things like this 8051 tutorial, but I am still a novice on microcontrollers. With this document I just intend to help a bit the new students so that they don't start their investigations from zero. I recommend to study the source code of the free software projects (like sdcc); it's a wonderful way of learning which it's worth profiting.
And, by the way, let me say that the subject PI was a general joke (even when everyone in our school, FIB, knows that). We made the practices trying things randomly or copying them from somewhere else, since at our group there were no explanation or notes to take, and the chip specification (340 pages, in English) didn't help very much at the beginning.
We got to the point where we were reading from a barcode scanner without having the scanner, and without even knowing how do these readers work. A teacher said that we could try to simulate it with Hyperterminal, but that he could not make it work, so there wasn't very much we could try.
And that's the way it was... at the end, the hardest thing to do was not to pass the subject, but to learn something useful.
Oh, and everything I have written has license do what you want
,
since I distribute some code which I don't really know if I have the right
to modify.
I have no relation with Cypress or with my university; I am responsible of
everything told here.
Write me if something needs to be changed.
n142857 -at-g-m-a-i-l--dot-com
March-July 2005 (updated on 13-05-2006), Daniel Clemente Laboreo,