Sie sind auf Seite 1von 24

/

***********************************************************************
*******
C167CR CAN Ap. Note project
Main - a_canx1

This is an example program showing how to use the CAN interface on the
Keil
MCB167 evaluation board.

Copyright (c) 1997 Keil Software


***********************************************************************
*******/
#include <stdio.h>
#include <intrins.h>
#include "can_ifc.h"
#include "timer.h"
#include "can_msgs.h"

#define TIME_MO 1 /* message object number for time message */

/
***********************************************************************
*******
Setup CAN controller

Returns: nothing
-----------------------------------------------------------------------
------*/
void
setup_can(void)
{
begin_can_init();

/* These mask values tell the CAN controller that all bits in a
message id. are significant when comparing the id of a received
message
to the id's in the arbitration registers of the message objects.
*/
CAN_MASK_SHORT = 0xffff;
CAN_UMASK_LONG = 0xffff;
CAN_LMASK_LONG = 0xf8ff;

/* Since this program doesn't use message object 15, it is


unnecessary
to initialize the 'mask of last message' registers
(CAN_UMASK_LAST and
CAN_LMASK_LAST).
*/

CAN_MSGOBJ[TIME_MO].msg_ctl = MSGVAL_CLR;
CAN_MSGOBJ[TIME_MO].arbitr = ARBITR(CAN_TIME_MSG);
CAN_MSGOBJ[TIME_MO].msg_cfg = MSG_CFG(LEN_CAN_TIME_MSG,
CANDIR_TRANSMIT, 0);
/* We're not initializing the data field right now, so we set
CPUUPD to prevent the message from being transmitted in
response to a remote frame.
*/
CAN_MSGOBJ[TIME_MO].msg_ctl =
/* clear bits set bits */
INTPND_CLR &
RXIE_CLR &
TXIE_CLR &
MSGVAL_SET &
NEWDAT_CLR &
CPUUPD_SET &
TXRQ_CLR &
RMTPND_CLR;

/* CAN_IE_ must be set for any CAN interrupts to occur.


CAN_EIE_ must be set for CAN error status change interrupts to
occur.
*/
end_can_init(CAN_IE_ | CAN_EIE_);
}

/
***********************************************************************
*******
main

This program does the following:


Set up the CAN controller, with a message object for the time stamp
message
as a transmit object.
Setup a timer to generate periodic interrupts.
Loop forever, periodically updating the time stamp message object.

The CAN controller will transmit the time stamp message when it
receives a
remote frame for it, without any intervention from the CPU.

Returns: never
-----------------------------------------------------------------------
------*/
void
main(void)
{
printf("Program A start\n");

/* Set up */
setup_can(); /* set up CAN interface */
init_timer(); /* initialize timing */

/* Run */
while (1) { /* infinite loop */
unsigned long t;

t = timer();
update_can_transmit_message(TIME_MO, &t, LEN_CAN_TIME_MSG);
/* Put the processor in idle mode to conserve power.
The next interrupt (from the timer) will wake it up again.
*/
_idle_();
}
}

C167CR CAN Ap. Note project


Main - b_canx1

This is an example program showing how to use the CAN interface on the
Keil
MCB167 evaluation board.

Copyright (c) 1997 Keil Software


***********************************************************************
*******/
#include <stdio.h>
#include <intrins.h>
#include "can_ifc.h"
#include "can_msgs.h"

#define TIME_MO 1 /* message object number for time message */

/
***********************************************************************
*******
Setup CAN controller

Returns: nothing
-----------------------------------------------------------------------
------*/
void
setup_can(void)
{
begin_can_init();

/* These mask values tell the CAN controller that all bits in a
message id. are significant when comparing the id of a received
message
to the id's in the arbitration registers of the message objects.
*/
CAN_MASK_SHORT = 0xffff;
CAN_UMASK_LONG = 0xffff;
CAN_LMASK_LONG = 0xf8ff;

/* Since this program doesn't use message object 15, it is


unnecessary
to initialize the 'mask of last message' registers
(CAN_UMASK_LAST and
CAN_LMASK_LAST).
*/
CAN_MSGOBJ[TIME_MO].msg_ctl = MSGVAL_CLR;
CAN_MSGOBJ[TIME_MO].arbitr = ARBITR(CAN_TIME_MSG);
CAN_MSGOBJ[TIME_MO].msg_cfg = MSG_CFG(LEN_CAN_TIME_MSG,
CANDIR_RECEIVE, 0);
CAN_MSGOBJ[TIME_MO].msg_ctl =
/* clear bits set bits */
INTPND_CLR &
RXIE_CLR &
TXIE_CLR &
MSGVAL_SET &
NEWDAT_CLR &
MSGLST_CLR &
TXRQ_CLR &
RMTPND_CLR;

/* CAN_IE_ must be set for any CAN interrupts to occur.


CAN_EIE_ must be set for CAN error status change interrupts to
occur.
*/
end_can_init(CAN_IE_ | CAN_EIE_);
}

/
***********************************************************************
*******
main

What this program does:


Setup the CAN interface, with a message object for receiving time
message.
Loop forever {
Transmit time message remote frame.
Wait for time message to be received.
Process time message.
}

Returns: never
-----------------------------------------------------------------------
------*/
void
main(void)
{
unsigned long time_value;
unsigned long last_printed_time_value = 0ul - 1000ul;

printf ("Program B start\n");

/* Set up CAN interface */


setup_can();

/* Main loop */
while (1) {
/* Request time message.
We're going to detect receipt of the message by examining
NEWDAT
(there are other ways), so also clear NEWDAT to make sure we
process
a new time message, not an old one that has been waiting around
unprocessed.
*/
CAN_MSGOBJ[TIME_MO].msg_ctl = TXRQ_SET & NEWDAT_CLR;

/* Wait for updated time message


Note: Doing it this way, the program will hang here forever if
the
message is not received.
*/
while (!(CAN_MSGOBJ[TIME_MO].msg_ctl & NEWDAT_)) ;

/* Process the time message */

copy_received_can_message(TIME_MO, &time_value);
/* If this time message is at least one second later than the
last one
that was output then output it.
*/
if (time_value - last_printed_time_value >= 1000) {
printf("%lu\n", time_value);
last_printed_time_value = time_value;
}
}
}

/
***********************************************************************
*******
C167CR CAN Ap. Note project
Header file for timing

Copyright (c) 1997 Keil Software


***********************************************************************
*******/

#define TIMER_UNITS_PER_SECOND 1000

void
init_timer(void);

unsigned long
timer(void);

C167CR CAN Ap. Note project


Timing functions

This is an example program showing how to use the CAN interface on the
Keil
MCB167 evaluation board.

Copyright (c) 1997 Keil Software


***********************************************************************
*******/
#include <reg167.h>
#include <intrins.h>
#include "timer.h"

/* The following macro calculates the value to write to an interrupt


control
register. The arguments are:
ir: Interrupt request (0 or 1)
ie: Interrupt enable (0 or 1)
ilvl: Interrupt priority level (0, 1, ... 15)
glvl: Group level (0, 1, 2, or 3)
*/
#define CALC_IC(ir,ie,ilvl,glvl) ((ir) << 7 | (ie) << 6 | (ilvl) << 2 |
glvl)

static unsigned long timer_val;

/* The following three macros control the frequency of the timer


interrupt.
CPU_FREQUENCY_HZ must be the actual CPU frequency.
The prescaler will divide this frequency by
2 raised to the (PRESCALE_SELECTION + 2) power. PRESCALE_SELECTION
must be
an integer >= 0 and <= 7.
The timer counts down. When it underflows from 0x0000 to 0xFFFF, it
is
reloaded with RELOAD_VALUE (It is reloaded from the CAPREL register,
which has been initialized with RELOAD_VALUE.), and generates an
interrupt.

If you change the values of any of these macros, you need to also
change
TIMER_UNITS_PER_SECOND in timer.h to match the new timer interrupt
frequency. A compile-time check that TIMER_UNITS_PER_SECOND is the
correct value follows these macros.
*/
#define RELOAD_VALUE 624 /* value for CAPREL register */
#define PRESCALE_SELECTION 3 /* value for T6I field of T6CON
register */
#define CPU_FREQUENCY_HZ 20000000 /* CPU frequency in Hertz */

/* Check that the intended timer interrupt frequency,


TIMER_UNITS_PER_SECOND,
is equal to the calculated timer interrupt frequency.
Generate an error message if it is not.
*/
#if CPU_FREQUENCY_HZ != \
TIMER_UNITS_PER_SECOND * (4L << PRESCALE_SELECTION) * (RELOAD_VALUE
+ 1L)
#error TIMER_UNITS_PER_SECOND does not match calculated value.
#endif
/
***********************************************************************
*******
Initialize the timer.

Return: nothing.
-----------------------------------------------------------------------
------*/
void
init_timer(void)
{
CAPREL = RELOAD_VALUE;
T6 = RELOAD_VALUE;
T6CON = 0x80c0 | PRESCALE_SELECTION;

/* interrupt set up */
T6IC = CALC_IC(0, 1, 10, 0); /* enable interrupt at priority level
10, group level 0 */
}

/
***********************************************************************
*******
Gets the timer value.

Returns: Elapsed time since the timer was initialized, in


TIMER_UNITS_PER_SECOND * seconds.
-----------------------------------------------------------------------
------*/
unsigned long
timer(void)
{
long temp;

/* This code was written for a 16-bit processor (Siemens C167CR), so


the
32-bit variable timer_val cannot be processed (copied,
incremented, etc.)
in a single machine instruction. The _atomic_() and _endatomic_()
"functions" tell the compiler to generate an ATOMIC machine
instruction
to protect the sequence of instruction processing timer_val from
being
interrupted. If the timer interrupt (which increments timer_val)
occurred
after one word of timer_val had been copied but before the other
word had
been copied, the copied value could be erroneous. The problem
would
probably occur only sporadicaly, and be maddeningly difficult to
find.
*/

_atomic_(0);
temp = timer_val;
_endatomic_();
return temp;
}

/
***********************************************************************
*******
Interrupt service routine for the timer interrupt.
-----------------------------------------------------------------------
------*/
void
timer_interrupt(void) interrupt 0x26
{
/* See comment in timer() function above discussing interruptability
of
operations on timer_val.
This operation (incrementing timer_val in the interrupt service
routine) also needs to be uninterruptable, if any higher-priority
interrupt handlers make use of the time. Otherwise, such an
interrupt
could catch timer_val half-updated and get an erroneous time.
*/
_atomic_(0);
timer_val++;
_endatomic_();
}

***********************************************************************
*******
C167CR CAN Ap. Note project
Header file for CAN hardware (non RTX-166 ap. note only)

Copyright (c) 1997 Keil Software


***********************************************************************
*******/

#include "canregs.h" /* Describes CAN registers */

/* Given a message id, the following macro computes the value to write
to
the arbitr member of a CAN message object structure. If a standard
(i.e., 11-bit) identifier is being used, the significant bits are
28...18, not 10...0. If you are specifying 11-bit message ids in bit
positions 10...0 and wish to use this macro, make sure the id is an
unsigned long, and shift it left 18 bit positions, e.g.,
ARBITR((unsigned long)std_id << 18)
*/
#define ARBITR(id) ((unsigned long)(id) >> 21 & 0x000000ff | \
(unsigned long)(id) >> 5 & 0x0000ff00 | \
(unsigned long)(id) << 11 & 0x00ff0000 | \
(unsigned long)(id) << 27)

/* The MSG_CFG macro computes the value for a CAN object message
configuration register (msg_cfg member of the CAN_MSGOBJ[]).
dlc is data length code. must be 0...8.
dir is direction. 0 (CANDIR_RECEIVE) receive; 1 (CANDIR_TRANSMIT)
transmit
xtd is extended flag. 0 standard (11 bit) id; 1 extended (29 bit) id
*/
#define MSG_CFG(dlc,dir,xtd) ((dlc) << 4 | (dir) << 3 | (xtd) << 2)
#define CANDIR_RECEIVE 0
#define CANDIR_TRANSMIT 1

void
begin_can_init(void);

void
end_can_init(
unsigned interrupt_enable_flags);

int
copy_received_can_message(
int object_number,
void *buf);

int
update_can_transmit_message(
int object_number,
void *buf,
int length);

167CR CAN Ap. Note project


CAN message id's

These are not standardized, but must be defined for each network design.

Copyright (c) 1997 Keil Software


***********************************************************************
*******/

#define CAN_TIME_MSG (0x555ul << 18) /* 11-bit id: 101 0101 0101 */
#define LEN_CAN_TIME_MSG 4

C167CR CAN Ap. Note project


Low-level CAN functions

Copyright (c) 1997 Keil Software


***********************************************************************
*******/
#include <intrins.h>
#include "can_ifc.h"

/
***********************************************************************
*******
Start of CAN initialization

This function begins the process of initializing the CAN module.


It establishes the bit timing and marks all the message objects invalid.

The mask registers, and any message objects that will actually be used,
must
be set up separately, and then end_can_init() should be called.

Returns: nothing.
-----------------------------------------------------------------------
------*/
void
begin_can_init(void)
{
int object_number;

CAN_CTL_STAT = CAN_INIT_ | CAN_CCE_;

CAN_BIT_TIMING = BIT_TIMING;

/* Mark all CAN objects invalid */


for (object_number = 1; object_number <= 15; object_number++) {
CAN_MSGOBJ[object_number].msg_ctl = MSGVAL_CLR;
}
}

/
***********************************************************************
*******
End of CAN initialization

The CAN module has only a single interrupt request line and vector, for
many
possible causes of interrupts. For each possible type of CAN
interrupt, there
is an interrupt enable bit (EIE and SIE bits in the control/status
register,
and TXIE and RXIE bits in the message control registers for the CAN
message
objects). There is also an overall CAN interrupt enable (IE bit in the
control/status register) which must be set for the CAN module to
generate any
interrupts.

The bit-mapped interrupt_enable_flags argument to this function controls


the IE, SIE, and EIE bits in the CAN control/status register.
The CAN_IE_, CAN_SIE_, and CAN_EIE_ macros define the relevant bits and
may be
combined with addition ('+') or bitwise-or ('|') operators to form the
argument
value. The TXIE and RXIE bits in the message control registers must be
set
separately.

Returns: nothing.
-----------------------------------------------------------------------
------*/
void
end_can_init(
unsigned interrupt_enable_flags)
{
CAN_CTL_STAT = interrupt_enable_flags;
}

/
***********************************************************************
*******
Copy a message from a CAN object to a buffer.

This function ensures that the copied data come from a single version
of the
message. If a new version of the message is received while the copy is
in
progress, the function will start over, copying the new version.

The object_number argument specifies which of the 15 CAN objects


(1...15) will
be read.

The buf argument is a pointer to the buffer the message is to be copied


into.
Only the first n bytes of the buffer will be altered, where n is the
number of
bytes in the data portion of the CAN message, as specified by the data
length
code in the message configuration register (0 <= n <= 8). If you are
certain
what the data length of the message will be, you may allocate only that
many
bytes for the buffer. An 8-byte buffer is always adequate.

Returns: length of message data


-----------------------------------------------------------------------
------*/
int
copy_received_can_message(
int object_number,
void *buf)
{
volatile struct can_obj *cano;
int length;

cano = &CAN_MSGOBJ[object_number];

/* The CAN controller can update the message while the CPU is trying
to
process it. This could cause the CPU to use data that come partly
from the older message and partly from the newer message.
That could be very, very bad.
To prevent this, follow these steps:
(1) clear NEWDAT
(2) process the message (actually, just copy it to another
location
for more thorough processing later)
(3) Check NEWDAT: if NEWDAT is set, it means the CAN controller
has
updated the message (i.e., received a new message).
The copied message may be corrupt, so go back to step 1.
*/
do {
/* Clear NEWDAT */
cano->msg_ctl = NEWDAT_CLR;

/* Copy the message.


The Data Length Code for the message is in bits 7...4 of the
message configuration register.
*/
length = cano->msg_cfg >> 4;
/* If CAN_MSGOBJ[object_number] hasn't been properly initialized,
the
data length code could be greater than the maximum valid value
of 8. This function shouldn't be called for an uninitialized
CAN object, but mistakes sometimes occur during development.
This inexpensive check contains the error in an easily
identifiable
form, rather than allowing the excess data to overwrite
whatever
follows the buffer in memory.
*/
if (length > 8) {
length = 0; /* If this statement ever gets executed, you
have a problem. */
}
{
unsigned char *src;
unsigned char *dest;
unsigned char *stop;

src = cano->msg;
dest = (unsigned char *)buf;
for (stop = dest + length; dest != stop; ) {
*dest++ = *src++;
}
}
} while (cano->msg_ctl & NEWDAT_); /* Check NEWDAT */

return (length);
}

/
***********************************************************************
*******
Update a CAN transmit object with new data from a buffer.

This function ensures that a partially updated form of the message will
not be transmitted.

The object_number argument specifies which of the 15 CAN objects


(1...15) will
be updated.
The buf argument is a pointer to the buffer the message is to be copied
from.

The number of bytes of data to copy is specified by the length argument.


If length >= 0, the DLC (data length code) of the message object is
changed
to length, and length bytes are copied.
If length < 0, the number of bytes to copy is read from the DLC of the
message
object.
If length > 8, or length < 0 and the DLC of the message object is not
correctly
set up, then incorrect results will occur.

Returns: length of message data


-----------------------------------------------------------------------
------*/
int
update_can_transmit_message(
int object_number,
void *buf,
int length)
{
volatile struct can_obj *cano;

cano = &CAN_MSGOBJ[object_number];

cano->msg_ctl = NEWDAT_SET & CPUUPD_SET;

/* The Data Length Code of the message object and the variable
'length'
must match. Change one to equal the other.
The Data Length Code for the message is in bits 7...4 of the
message configuration register.
*/
if (length < 0) {
/* Get length from the message object */
length = cano->msg_cfg >> 4;
}
else {
/* Set data length code in message object */
cano->msg_cfg = cano->msg_cfg & 0xf | length << 4;
}

/* Copy the data */


{
unsigned char *src;
unsigned char *dest;
unsigned char *stop;

src = (unsigned char *)buf;


dest = cano->msg;
for (stop = dest + length; dest != stop; ) {
*dest++ = *src++;
}
}
cano->msg_ctl = CPUUPD_CLR;

return (length);
}

/
***********************************************************************
*******
CAN interrupt handler

There is a single interrupt vector for all interrupts generated by the


on-chip
CAN module. The INTID field of the CAN interrupt register (CAN_INTID)
identifies what caused the interrupt. Several interrupt-causing
conditions can
exist at once, but the INTID field will reveal only one of these at a
time.
The values of INTID and their corresponding interrupt causes are:

0: no interrupt pending
1: status change interrupt
2: message 15 interrupt
2+N: message N (1 <= N <= 14) interrupt

When several interrupt conditions exist, the one associated with the
lowest
value of INTID (other than 0) takes precedence, i.e., its value appears
in
the INTID field. Once this interrupt has been cleared, the next
interrupt
source has its value appear in INTID. You must clear all interrupt
sources
for the CAN module (i.e., get INTID to 0) before returning from the
interrupt
handler or no further CAN interrupts will be generated.

Since this example program doesn't use CAN message interrupts, this
function
doesn't really need to have any code to deal with them. Absolute
minimal
message interrupt handling is provided for the benefit of people who
want
to use this program as a skeleton for more elaborate CAN programs. Of
course,
this skeleton may be anatomically unfit for your project.

-----------------------------------------------------------------------
------*/
void
can_interrupt(void) interrupt 0x40
{
unsigned char interrupt_id;
int message_number;
while (1) {
switch (interrupt_id = CAN_INTID) {

case 0 : /* no interrupt */
return;

case 1 : /* status change interrupt */


/* Interrupts associated with changes in the CAN status
register
are handled here.

Reading the status register clears this interrupt condition.

If the SIE bit of the CAN control register is set, an


interrupt is
generated when the CAN controller updates the LEC field of
the CAN
status register.

If the EIE bit of the CAN control register is set, an


interrupt is
generated when the CAN controller changes the BOFF or EWRN
bits in
the CAN status register.

The only one of these interrupts we are interested in for


this
program is a change in the BOFF (Bus Off) bit.
*/
{ /* Bus Off recovery */
/* During bus off recovery, busoff_recovery_state contains
the
(non-zero) value to write into the CAN control register
when
recovery is complete. At all other times, it contains 0.
*/
static unsigned char busoff_recovery_state;
unsigned char boff; /* reading of BOFF bit in status
reg */

if ((boff = CAN_STAT & CAN_BOFF_) && busoff_recovery_state


== 0) {
/* Bus off condition just occurred. */
/* When Bus Off occurrs, the CAN module sets the BOFF
and INIT
bits in its control/status register. It begins the
bus off
recovery sequence when the CPU clears the INIT bit.

Remember the current value in the CAN control


register so we
can restore it later. (Except the INIT bit; it will
always
be set at this point, and we will always want to
restore it
cleared.)
Clear INIT bit to start bus off recovery, and clear
SIE (if
it were set) to disable SIE-controlled status change
interrupts. They are of no interest to us while the
bus off
condition exists.
*/
busoff_recovery_state = CAN_CTL & ~CAN_INIT_;
CAN_CTL = CAN_EIE_ | CAN_IE_;
}
else if (busoff_recovery_state && !boff) {
/* Bus off recovery just completed */
CAN_CTL = busoff_recovery_state; /* Restore control
register from before bus off */
busoff_recovery_state = 0; /* No longer in bus off
recovery */
}
}
break;

case 2 : /* message 15 interrupt */


message_number = 15;
goto handle_message_interrupt;

default : /* other message interrupts */


message_number = interrupt_id - 2;
handle_message_interrupt:
/* Clear the INTPND (interrupt pending) bit in the message
control
register for the message object.

For most applications, you will probably need to do more


than this,
and you probably need to do different things for different
messages. But you will always need to do this.
*/
CAN_MSGOBJ[message_number].msg_ctl = INTPND_CLR;
break;
}
}
}

C COMPILERS • REAL-TIME OS • SIMULATORS • EDUCATION • EVALUATION BOARDS


Programming the Siemens C167CR CAN Interface: A Real Life Case:
Constructing the Hardware
Application Note 115b - Hooking up the CAN hardware and running the software: Version 1.0
By Robert Boys, MIS
Keil Software, Inc.
1-800-348-8051 or rboys@keil.com
This application note will instruct you on how to connect two Keil single board computers together to
construct a working two node CAN network. This article has instructions on loading and using the
Keil
monitor and debugger to operate and evaluate the entire system. This note will also work on the
Phytec
KitCON 167 board that is sold by Siemens.
Introduction
Gary Culp and Robert Boys wrote an article on the CAN bus in the Siemens Contact magazine in
February 1998. The basis of the article was making a two node CAN network using two Keil
MCB167
evaluation boards. This article very briefly described the CAN bus, the Keil CAN libraries and RTOS,
the Keil C167CR microcontroller and the MCB167. It then described some unusual and interesting
aspects of programming the C167CR on-chip CAN controller. These were discovered during the
research phase of this article and deemed to be of enough interest to form the main content of the
article.
The original article has become Keil Application Note # 115 and is available on the Keil Website:
www.keil.com/~market/115.pdf. The note you are now reading continues the original article and
completes the experiment. It describes the actual steps required to connect the two boards together and
getting the software running to display the expected results.
The Keil Tool Chain - an overview
The Keil Tool Chain fully supports the entire Siemens C166 and the SGS Thomson ST10
microcontroller lines. The Keil Tool Chain includes a C Compiler, Macro Assembler, Linker/Locator,
µ Vision IDE, and the dScope Simulator and Target Monitor.
The Keil tool chain consists of the following executables:
C Compiler c166.exe
Assembler a166.exe
Linker L166.exe
Converter oh166.exe
dScope dsw166.exe (a Windows application)
µ Vision uvw166e.exe (a Windows application)
These files are located in the c:\c166eval\bin directory for the evaluation version when installed on
your
hard drive. The full version would locate them in the c:\c166\bin directory. They are DOS programs
except for µ Vision and dScope. Access to these programs from Windows is accomplished with
µ Vision. The entire tool set can be run from µ Vision or directly from DOS with your batch files.
The
Evaluation version is limited in code size to 4Kbytes. Other than these restrictions, all features operate
normally.
2
µ Vision IDE
µ Vision is a Windows based front end for the C Compiler and Assembler. It was developed in the
USA
as was the printed manual set. Compiler, Assembler and Linker options are set with simple mouse
clicks. µ Vision runs on Windows 3.1, 95 and NT. This Integrated Development Environment (IDE)
has
been expressly designed with the user in mind. A full function editor is included. All IDE functions
are
intuitive via pull down menus with prompted selections. An extensive Help utility is included.
External
executables can be run from within µ Vision. This includes emulator software.
C166 C Compiler for the entire Siemens 166/167 family and SGS Thomson ST10
The C166 ANSI compiler and A166 assembler are designed specifically for the Siemens 161, 163,
C164CI, 165,166, 167, 167CR, and future derivatives. The C166 easily integrates with the Keil
RTOS.
The C166 interfaces and passes debug information to the Keil dScope Simulator and all in-circuit
emulators. The Keil C166 provides the fastest and smallest code using industry benchmarks. The
ST10
family, which is a second source of the C166 - is supported.
dScope Simulator
dScope is a software simulator. Your code can be debugged either in software on your PC or in your
target hardware. When operated in conjunction with the Keil monitor installed in your target
hardware,
dScope becomes tScope. You have the ability to run/halt, set breakpoints, examine/change memory,
view the stack, view/set peripheral information and apply virtual external signals. dScope has a
Performance Analysis feature to ensure your code runs efficiently. dScope has a built-in
disassembler/assembler allowing you to make changes in your code without recompiling.
The evaluation version of the Keil 8051 tool set is restricted to a 2K code size and the code must be
located at 0x4000. The C166 version can produce up to 4K of code and does not have a starting
address restriction. Useful object code is produced. Other than these restrictions, the tool set works
exactly as the full version does. This allows you to fully evaluate the features and power of Keil
products on the Rigel board. The full version has no restrictions and is fully ANSI compliant.
FR166 Full-Function RTOS: Siemens 166/167 family and SGS Thomson ST10
The FR166 is a multitasking real-time operating system for the Siemens 166 family. You can manage
multiple tasks on a single CPU making your programs much easier to develop. The RTX166 Full
includes CAN libraries. The RTX166 Tiny is a subset of the RTX166 Full and is included with all
C166 C Compiler Kits. There are no royalty payments generated by using a Keil RTOS.
CAN Library - Controller Area Network
The RTX51 and RTX166 Full RTOS supports CAN controllers with the included libraries. The CAN
libraries are sold with the RTOS. The CAN interface is becoming popular for automotive and
industrial
markets. Both 11 and 29 bit identifiers are supported. Keil C166 and 8051 C compilers interface with
the RTOS and CAN libraries. Keil supports all CAN microcontrollers based on the Siemens C505C,
C515C, C164-CI, and C167CR. Future CAN products based on these 8051 or C16x Families are
easily supported due to the flexible Keil Compiler design. The Siemens 81C90 CAN peripheral chip is
supported with a new Keil evaluation board. Contact Keil Software for details.
STARTING THE TUTORIAL
The Software
What you need to get started
1) Two Keil MCB167 evaluation boards with the CAN physical-layer interface added. You may be
able to substitute Phytec or Rigel boards. You need only install the proper monitor and boot files.
2) Two PCs with the Keil C166 tools installed and one serial port available for communication with
MCB167 boards. The 4K evaluation version of the Keil Tool Set can be used.
3) Note: you can use only one PC by switching between this single PC and the two boards either
manually or with a RS-232 switch box
4) Appropriate cable for connecting the CAN interfaces of the two MCB167 boards.
3
The CAN programs
Keil provides C source code for the A and the B boards. A runs a counter that determines how long it
has been activated in seconds. It also scans the CAN bus looking for a message from B. A sends a
message requesting this timer value. If B receives this message, it sends the timer message to A. A
then prints this value to the serial port window on the Keil debugger dScope.
The C source code, project files, compiled object files and startup files are available as file cansoft.zip
on the Keil website or was embedded in the file with this document. This file contains all the files
necessary to reproduce the two programs described in this application note and are listed below.
File Description
a_canx1.c main() module for program A
b_canx1.c main() module for program B
timer.c timer functions
can_ifc.c CAN interface functions
timer.h header file for timer
can_ifc.h header file for CAN interface functions
canregs.h header file describing CAN hardware
can_msgs.h header file for CAN message id.'s
a_canx1.prj project file for program a_canx1
b_canx1.prj project file for program b_canx1
ax1_dsco.ini dScope initialization file for program a_canx1
bx1_dsco.ini dScope initialization file for program b_canx1
boot & monitor dScope monitor kernel for the Keil MCB167 board
compiled.zip compiled object files and listing files
Install these files in a directory of your choice. This tutorial used c:\can.
Various other files will be
produced as a result of the compiling and linking process of the Keil Tool
Set.
The Keil Tool Set - µ Vision, C compiler, linker, locator and dScope
This project uses the Keil µ Vision IDE to interface to the compiler and linker/locator and dScope to
interface to the two the Keil single board computers. You can use either one or two PCs to be the host
computer. The Keil evaluation software must be installed on each of these computers if you use two.
The evaluation software is capable of compiling and demonstrating this project.
Installing the Keil Software
Install the Keil Software development tools by completing the following steps:
1) Get the 2 disks from the Keil or Siemens CD-ROM, or the Web site,
www.keil.com/demo/ek166.htm or prepare to install them from the Keil CD-ROM in the directory
/ek166/.
2) To install the Keil EK166 evaluation software from the Keil CD-ROM, run the program
/ek166/setup.exe from within Windows. Use the default destination drive of c:\c166eval. If you
have downloaded two floppies from the web - install them in the usual manner.
3) If you ever need to reload a totally fresh copy of the Keil C Compiler - you must delete dsw166.ini
and uvw166e.ini from your c:\windows\ directory. These files are created and updated by µ Vision
and dScope to retain user settings. You may want to save the existing files under new names to
save your settings.
After you install the Keil development tools to Windows 3.11, a Keil program group or icons appears
on your desktop. (the Windows 95 version is shown in Figure 1). Recall dScope is the Keil Simulator
and µ Vision is the Keil Windows based user interface (or IDE = Integrated Development
Environment).
4
FIGURE 1
For Windows 95 you must install the two icons manually as follows. If the two icons are visible in a
small window drag them onto the desktop. If they are not visible, follow these instructions:
1) right mouse click while in the Desktop and select New/Shortcut
2) The executable file you want to enter as the Command Line is c:\c166eval\bin\uvw166e.exe
3) The suggested name is µ Vision.
4) Repeat this process for c:\c166eval\bin\dsw166.exe and name it dScope.
The full version of the software installs these icons to the Windows 95 desktop.
The Hardware
The Keil MCB167 does not come with the physical layer circuitry to connect directly to the CAN bus.
This layer is easily provided with a Philips 82C250 driver chip. A schematic is provided below as
Figure 2. Wire wrap this circuit on the prototyping area of the MCB167. The location of P4.6 and P4.5
on the MCB167 is illustrated in Figure 6.
P4
+5V
1
U1
PCA82C250
Philips
2
3
4
8
7
6
5
J1
A
CB
R1 120
SW1-7
2
7
6
P4.6
P4.5
C1
10μF
16V
SW1-8
Slope Control
22k
C167CR
CAN
Bus
FIGURE 2
This demonstration will work without R1 or the ground wire at SW1-8 connected but it is desired. The
22K slope control resistor can be directly connected from Pin 8 of U1 to ground. You will not need to
adjust the jumper positions on Pin 8 for this demonstration.
The on-chip CAN interface of the C167CR connects to the outside world through two pins on Port 4.
Port 4.5 is the CAN receive line and Port 4.6 is the CAN transmit line. These pins are shared by
address lines A21 and A22. These two modes are mutually exclusive. P4.5 and P4.6 can be CAN ports
or address ports. Not both at the same time. Addressing mode restrictions apply when using the CAN
interface.
Use the MCB167 default jumper settings and the standard 256K of RAM. J2 in particular must be
installed. This puts the C167 into the bootstrap mode upon a CPU reset. This is the method that
dScope
uses to load the monitor kernel into the RAM of the MCB167.
When mon166.dll is selected within dScope, the file boot is first loaded using the C167 bootstrap
loader. Boot then loads monitor into the RMB 167-CRI’s RAM. These two programs must be in the
c:\c167eval\bin directory. If you load the properly configured monitor kernel into on-board FLASH or
EPROM memory, you need to deselect J2. For information concerning the Keil monitor see
Readme.txt
in c:\c166eval\mon166.
5
Connecting it all together
1) Connect the power supply to the two boards
2) Complete the CAN physical layer connection between the two boards as appropriate for your setup.
Pins 7 and 6 of the 82C250 must be correctly connected together along with the ground wire. Use a
reasonable amount of wire. A few feet will do for this demonstration.
3) Connect the serial port of your PC to the COM connector of one of the MCB167 boards. If you are
using two PCs, connect the other serial cable to the second MCB167 and to the second PC.
Starting Everything Up
Starting µ Vision for the A board - the Keil IDE
Double-click on the µ Vision icon to start the user interface. The compiler, assembler, linker and
dScope
will be called from within µ Vision in this tutorial. After you invoke µ Vision, the window shown in
Figure 3 appears. From this window, you can create projects, edit files, configure the tool, assemble,
link, and invoke the debugger. Everything is pre-configured for you for this project.
1) Open the Project menu and choose Open Project.
2) Select c:\can\a_canx1.prj and press OK.
3) Click on the Build All icon (it has three arrows pointing downwards) or open Project and select
Make: Build Project.
4) If the program specified (a_canx1.c) contains any errors, they will be shown on the screen.
5) If there are no errors, the code is assembled and linked and the executable code is ready to be
downloaded to the board. The code to be downloaded to the board will be the name of the project
with no filename extension. In this case this will be a_canx1. Click on OK in the Project Status
dialog box and continue to the next section. Note a HEX file could have been created. Other files
are created to help the debugging process.
6) If a list of errors appears, use the editor to correct the error(s) in the source code and save the file.
Then, repeat this section: beginning at step 1. There should be no errors. If there are, something
serious has gone wrong. Reload all the software.
FIGURE 3
6
Downloading the A Software to the MCB167
You are now ready to start the dScope debugger to download and execute code to the Keil A board.
1) Press the reset button. It is labeled RESET and is beside the 5 volt regulator chip.
2) Click on the debugger icon on the µ Vision toolbar. This icon is a “d” seen through a magnifying
glass and is yellow and blue in colour. This is the same icon installed for dScope in your desktop
area. If you leave the mouse pointer on an icon for a few seconds, the word “debug” will appear.
A window similar to that shown in Figure 4 is displayed. You may want to move and resize the
windows. The window marked “Module: <none>” is the Debug window. The Command window
can be used to enter commands.
3) You may need to open, resize and /or move some windows to make your screen look something
like
Figure 4. The Debug, Command, and Serial I/O windows will be used. You may want to open the
Commands menu in the Debug Window and select Show Toolbar.
4) Note that the file ax1_dsco.ini configured and loaded dScope. This ASCII file can easily be edited.
Its location is specified in µ Vision in the Options menu under dScope Debugger. The object file
that was loaded into the MCB167 memory is a_canx1. This is the project name with no file
extension. The original C source code needs to be available for source-level debugging.
FIGURE 4
Running the A Software
1) Press Go
2) “Program A Start” with 4 characters beneath it will be displayed in the Serial I/O window. CAN
node A is successfully started. It is now waiting for a message from B (which is not started yet).
3) If you get errors - a section called Communication Setup - Help ! follows.
Starting µ Vision for the B board - the Keil IDE
Now you will start the B board. This board sends out a CAN message requesting a time stamp from A.
This time stamp will then be displayed in the Serial I/O window. The instructions are the same as for
the A board. Only the filenames change.
7
If using only one PC, move the serial COM cable to the second MCB167. The A board will continue
to
operate without intervention of dScope. Start at instruction 1) below.
If you are using two PCs; double-click on the µ Vision icon on the second PC to start the user
interface.
After you invoke µ Vision, the window shown in Figure 2 appears as before with A. Everything is
preconfigured
for you for this project.
1) Open the Project menu and choose Open Project.
2) Select c:\can\b_canx1.prj and press OK.
3) Click on the Build All icon (it has three arrows pointing downwards) or open Project and select
Make: Build Project.
4) If the program specified (b_canx1.c) contains any errors, they will be shown on the screen.
5) If there are no errors, the code is assembled and linked and the executable code is ready to be
downloaded to the board. The code to be downloaded to the board will be the name of the project
with no filename extension. In this case this will be b_canx1. Click on OK in the Project Status
dialog box and continue to the next section. Note a HEX file could have been created. Other files
are created to help the debugging process.
6) If a list of errors appears, use the editor to correct the error(s) in the source code and save the file.
Then, repeat this section: beginning at step 1. There should be no errors. If there are, something
serious has gone wrong. Reload everything again. The software has been thoroughly tested.
Downloading the B Software to the MCB167
You are now ready to start the dScope debugger to download and execute code to the Keil B board.
1) Press the reset button.
2) Click on the debugger icon on the µ Vision toolbar.
3) You may need to open, resize and /or move some windows to make your screen look something
like
Figure 4. The Debug, Command, and Serial I/O windows will be used. You may want to open the
Commands menu in the Debug Window and select Show Toolbar.
4) Note that the file bx1_dsco.ini configured and loaded dScope. This ASCII file can easily be edited.
Its location is specified in µ Vision in the Options menu under dScope Debugger.
Running the A Software
1) Press Go
2) “Program B Start” will be displayed in the Serial I/O window on the B PC as shown below. Every
second a new number indicating in seconds how long A has been running will be displayed in the
Serial I/O window shown below. CAN node B is started. B requests A send the time stamp, then B
displays it. You now have a running CAN node to experiment with. Congratulations !
3) If you get errors - a section called Communication Setup - Help ! follows.
4) If you get no errors and the Serial windows indicate that both program A and B have started but no
numbers appear in the B Serial window: there is probably something wrong with the physical CAN
network. The next section describes testing each CAN node to help you track the problems.
The first 11 seconds of A running and displaying on B
8
COMMUNICATIONS SETUP - Help !
You need to follow these instructions if proper communication was not established. After a series of
attempts, dScope will error out as indicated in the No Target System Found ! dialog box.
Select the appropriate configuration. Select the appropriate COM port for your system. The speed
must
be 9600 for the C167 with a 5 Mhz crystal. Select 19,200 if 9600 does not work.. This depends on
which version of the monitor you have installed. Monitors for 19.2, 38.4, and 57.6 Kbaud are
available
from Keil.
After you set the COM port and baud rate correctly, click on Try Again.
FIGURE 5
If you receive Figure 5 as an error message in dScope; the wrong version of the monitor kernel is
loaded into the MCB167 memory. dScope uses the files boot and monitor to load into the Keil board
RAM. The correct files must be loaded into your hard drive where the Keil evaluation software
(EK166) was loaded. (default = c:\c166eval\bin). These files are default with the Keil software.
Copies are available in the file cansoft.zip that came with this document and it is also available at
www.keil.com/~market/.
The serial FIFO buffer in Windows 95 can cause transmission problems. dScope may have problems
completing the communication initialization process. This can be intermittent. The FIFO can be
disabled under "controlpanel/system/device_manager/Port Settings/Advanced". Make sure “Use
FIFO buffers” in this menu is not activated.
Notes on using RESET in dScope and the MCB167
1) dScope in simulator mode: When you are using dScope as a simulator (i.e. no target hardware),
pressing the 'reset' button does not cause a running simulation to stop at the current point of
execution. Reset starts the application from the beginning address (0) again. This situation is
evident in example 15 where a reset causes the program to re-run. Press the “Stop” button to halt a
program normally.
2) dScope in monitor mode (tScope): The monitor is running in the target hardware. A tScope 'reset'
sets the IPC to zero and does some other initializations if no user application was started. It is not
as good as a hardware reset. The best method to stop an application that is running is to press the
“STOP” button rather than the “RESET” button in tScope.
'stop' tries to stop a running application when the 'use serial interrupt' option is enabled - or - if not
enabled, a dialog box is displayed where you can select the next step. This has the advantage of
seeing the 'infinite loop' where your program is stuck. With reset, you are starting at address 0
again.
3) The Keil board does have a hardware NMI button.
9
Testing the MCB167 CAN Interface
At this point you will have two boards with the physical layer installed, the Keil Tool Set installed,
and
the CAN sample programs copied to the c:\can directory. In order to make sure the boards are working
correctly; a simple test is provided. This may save you plenty of time. You will need an oscilloscope
or
other method of determining the existence of a 1 Mhz TTL level data stream.
This test will allow you to determine if each board is capable of sending a CAN message on to the
physical layer. You can view a TTL level waveform with an oscilloscope when program B is running.
You do not need to have the other node working or connected. It is best if they are not connected.
1) Connect the board you want to test to the PC COM port and start program B as described above.
Press GO to run the B program. “Program B Start” will be displayed in the Serial I/O window.
2) Measure P4.6 (transmit) with an oscilloscope. Figure 6 illustrates the position of these points. You
should see a digital data signal of 4.5 volts p-p as shown in Figure 7.
3) If the 82C250 is connected, a similar data signal of about 4 volts will be shown on P4.5 (receive).
4) Note that if you stop the CPU with the STOP button, this signal does not stop. This demonstrates
the fact that the CAN controller does not need CPU intervention to operate.
5) Check the physical CAN signals on Pins 6 and 7 of the 82C250. Each signal will be about 1.5 and
2 volts p-p respectively. Remember these outputs/inputs are a differential pair, so in order to see
the true waveform, you will need to use a two channel scope set to ADD with one channel inverted
from the other. This will give the same signal as Figure 6 but at about 2 volts p-p.
6) Test the other board in the same fashion.
FIGURE 6
FIGURE 7
Notes:
1) If P4.6 and P4.5 are not connected to anything; P4.6 would sometimes not put any signal out. The
CPU had to be stopped, the dScope RESET activated and GO pressed again. The dScope reset is
the icon with the small red “T” pointing into a circle on the dScope toolbar or the RESET button on
the Toolbox window. Sometimes I had to reload the object file again. A signal would never appear
on the Receive (P4.4) in this case.
10
2) If I connected a resistor ( 2k or so) between P4.6 and P4.5, the appearance of a signal on the
Transmit pin would be appear more consistently and a smaller signal would appear on the Receive
pin P4.5.
3) If the 82C250 was connected; the system became very stable. Only switching a relatively heavy
load would cause the signals to disappear. A RESET and then a GO would restore operation.
4) The 120 Ω resistor (R1 in Figure 1) or the ground lead at SW1-8 do not need to be connected for
this demonstration to work. Using the ground is a good idea.
5) Program A does not put out any signal on its own.
6) If the CAN network is intact and running properly; the signals are similar but “fuzzier” indicating
the data is changing and the oscilloscope will not properly lock on to such a dynamic signal.
For more information you may visit these Web sites:
http://www.keil.com/can and www.keil.com/~market for CAN specific material
http://www.keil.com/~market - Technical Marketing page.
http:/www.smi.siemens.com - for the Siemens page.
http:/www.st.com - for the SGS Thomson page