Development Kit For The PIC MCU: Can Bus
Development Kit For The PIC MCU: Can Bus
Development Kit For The PIC MCU: Can Bus
CAN Bus
March 2010
ICS-S40 can also be used in place of ICD-U. Connect it to an available serial port on the PC using the 9
(1)
CCS, Inc.
ICD-U64
1 Storage box
2 Exercise booklet
3 Two Serial PC to Prototyping Board Cables
4 Modular ICD to Prototyping board cable
5 ICD unit for programming and debugging
6 USB (or Serial) PC to Prototyping board cable
7 AC Adaptor (9VDC)
8 CAN Bus Prototyping board
9 CD-ROM of C compiler (optional)
Compiler
Use the drop-down box under Compile to select the compiler. CCS offers different
compilers for each family of Microchip parts. The exercises in this booklet are for the
PIC16F876A and PIC18F4580 chips. Make sure PCM 14 bit is selected in the drop-down
box under the Compiler tab for the PIC16F876A and PCH16 for the PIC18F4580. Other
programs are for the PIC18F4580 part, a 16-bit opcode. Select PIC18 for that part.
The main program compiled is always shown in the bottom of the IDE. If this is not the
file you want to compile, then click on the tab of the file you want to compile. Right click
into editor and select Make file project.
Click Options>Project Options>Output Files… and review the list of directories the
compiler uses to search for included files. The install program should have put two
directories in this list to point to the device: .h files and the device drivers.
Normally the file formats need not be changed and global defines are not used in these
exercises. To review these settings, click Options>Project Options>Output Files and
Options>Project Options>Global Defines.
Click the compile icon to compile. Notice the compilation box shows the files created
and the amount of ROM and RAM used by this program. Press any key to remove the
compilation box.
CCS, Inc.
Viewer
Click Compile>Symbol Map. This file shows how the RAM in the micro-controller
is used. Identifiers that start with @ are compiler generated variables. Notice some
locations are used by more than one item. This is because those variables are not active
at the same time.
Click Compile>C/ASM List. This file shows the original C code and the assembly code
generated for the C. Scroll down to the line:
int_count=INTS_PER_SECOND;
Notice there are two assembly instructions generated. The first loads 4C into the W
register. INTS_PER_SECOND is #defined in the file to 76. 4C hex is 76 decimal. The
second instruction moves W into memory. Switch to the Symbol Map to find the memory
location where int_count is located.
Click View>Data Sheet, then View. This brings up the Microchip data sheet for the
microprocessor being used in the current project.
Click here for the file menu. Files and Projects are created,
Place cursor over each icon
opened, or closed using this menu.
and press F1 for help.
Compiles current selected unit, does NOT link/build
into a HEX file.
Click the help icon for
Compiles all units that have changed since last build, the help menu. The technical
links/builds into a HEX file. support wizard and download
Compiles all units regardless if they have changed since manager are accessed using
last build, links/builds into a HEX file. this menu.
Compile ribbon.
NODE A - PIC18F4580
This node features a Microchip PIC18F4580 chip. This chip has a built-in CAN bus
controller. There is also an I/O block that provides access to spare I/O pins on the PIC.
The pinout is as follows:
+5 B6 B4 B2 B0 D6 D4 D2 D0 C4 C2 C0 A4 A2 E2 E0 G
+5 B7 B5 B3 B1 D7 D5 D3 D1 C5 C3 C1 A5 A3 A1 E1 G
NODE B - PIC16F876A
This node features a Microchip PIC16F876A chip. This chip does NOT have a built-in
CAN bus controller. Instead, an external MCP2515 CAN bus controller is used. This
scheme could be used with any PIC microcontroller.
The following I/O features are also a part of NODE B:
Three LEDs (Red, Yellow, Green)
LED is lit by outputting a LOW to the I/O pin
One push-button
Programs may be downloaded and optionally debugged using the ICD connector.
CCS, Inc.
NODE C - “Dumb” I/O Unit
This node features a Microchip MCP25050 chip. This chip is pre-programmed with
address information and provides CAN bus access to the eight I/O pins. The following
items are connected to the I/O pins:
Three LEDs (Red, Yellow, Green)
LED is lit by outputting a LOW to the I/O pin
Three push-buttons
This chip may be programmed by removing it from the socket and using the Pro Mate II
from Microchip to load in the address information.
This chip is a complete CAN Bus Node that allows eight general input or
output pins, up to four A/D converter inputs and two PWM outputs
This chip can be configured by programming an internal EEPROM with
the addresses and modes of operation.
The chip can also be programmed over the CAN Bus.
#include <18f4580.h>
#device ICD=TRUE
#fuses HS,NOLVP,NOWDT,
#use delay(clock=20000000)
void main () {
while(TRUE) {
output_low(GREEN_LED);
delay_ms(1000);
output_high(GREEN_LED);
delay_ms(1000);
}
}
The first four lines of this program define the basic hardware
environment. The chip being used is the PIC18F4580, running at
20MHz with the ICD debugger.
N OT E S
CCS, Inc.
ICD-U64
Connect the ICD to the Prototyping board using the modular cable, and connect the ICD to
the PC. Power up the Prototyping board.
Click Debug>Enable Debugger and wait for the program to load.
If you are using the ICD-U and the debugger cannot communicate to the ICD unit go to
the debug configure tab and make sure ICD-USB from the list box is selected.
Click the green go icon:
Expect the debugger window status block to turn yellow indicating the program is
running.
The green LED on the Prototyping board should be flashing. One second on and one
second off.
The program can be stopped by clicking on the stop icon:
1 11 1 2 4 0-64 Bits 16 2 3 3
Reserved Data CRC ACK Silence
Address
00
CCS, Inc.
GENERAL RULES:
All nodes on the bus verify the frame. If any node detects an error, that node asserts
a NACK. When any node asserts a NACK for a frame all nodes must ignore the
frame even if the node did not find an error in the frame. The sender re-transmits
NACKed frames.
A node that NACKs a lot of messages or has a lot of messages NACKed is put on
probation (Error Passive state). In this state, the node’s activity is restricted. If the
problem persists, the node must stop all bus transmission and ignore all incoming
packets. This rule is self-enforced by each node keeping local statistics.
A node does not start transmitting unless the bus is quiet for three bit times. If two
nodes start a frame at the same time, one node will bow out while the identifier is
being transmitted. The node to drop out will be the one that first tries to send a one-
bit, when the other send a 0. The 0 is dominant and the sender of the one will realize
there is a collision. This means lower numbered identifiers have a higher priority.
The CAN bus permits an alternate format message with a 29 bit identifier. All the
examples we use will be with an 29 bit identifier. Frames with 11 and 29 bit identifiers
can co-exist on the same bus.
PHYSICAL:
There is no universal standard for the physical CAN Bus. It requires an open drain
type of bus. It could be a single wire, fiber optic or two wire differential bus. The
latter is the physical bus used on the CCS CAN Bus Prototyping board. The Philips
PCA82C251 chips are used to interface the bus to the TTL controllers. This complies
with ISO standard 11898 for use in Automotive and Industrial applications
The bit rate can be as fast as one million bits per second.
The start of frame bit is used by the receiver to determine the exact bit time.
Whenever a transmitter on the bus sends five identical bits, it will send an extra
bit with the reverse polarity. This is referred to as a stuffed bit. The receiver will
ignore the stuffed bits. If a receiver detects six or more bits that are the same, it is
considered an automatic error.
#include <18F4580.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#include <can-18xxx8.c>
void main() {
int i=0;
can_init();
can_putd(0x100,0,0,1,TRUE,FALSE); //send an on-bus message
//to wake up mcp250x0’s
delay_ms(1000); //wait for node c to power-up
while(TRUE) {
write_7_segment(i);
delay_ms(1000);
if( ++i==10)
i=0;
}
}
CCS, Inc.
The include file “can-18xxx8.c” has the functions required to interface
to the PIC18 CAN Bus controller.
The call to can_init() starts the interface.
This program is designed to send data to Node D. The identifier for
Node D is programmed as 0x400. The MCP25050 device accepts a
three byte command.
N OT E S
1 11 1 1 18 1 2 4 0-64 16 2 3 3
Start SRR Data Data ACK Silence
Request
Address Data
Length
CAN Bus Exercise Book
7 DEBUGGING
Open the code for chapter 6, add #device ICD =TRUE, and start the debugger
Debug>Enable Debugger.
Click the reset icon to ensure the target is ready.
Click the step-over icon . This is the step over command. Each click causes a line of
C code to be executed. The highlighted line has not been executed, but the line about to
be executed.
Step over the can _ init; line and notice that one click executed the entire function.
This is the way step over works. Click step over on
delay=ms(1000);. and notice the debugger stops when the function terminates.
Click the Watch tab, then the add icon to add a watch. Enter i or choose i the
variables from list, then click Add Watch. Notice the value shown. Continue to step
over through the loop a few more times and notice the count watch increments.
Step over until the call to write _ 7 _ segment(i); is highlighted. This time, instead of
step over, use the standard step icon several times and notice the debugger is now
stepping into the function.
Click the GO icon to allow the program to run. Click the stop icon to halt
execution. Notice the C source line that the program stopped on.
In the editor, click on write _ 7 _ segment(i); to move the editor cursor to that line.
Then click the Breaks tab and click the add icon to set a breakpoint. The debugger
will now stop every time that line is reached in the code. Click the GO icon. The
debugger should now stop on the breakpoint. Repeat this a couple of times to see how
the breakpoint works.
Click Compile>C/ASM list. Scroll down to the highlighted line. Notice that one assembly
instruction was already executed for the next line. This is another side effect of the ICD-S
debugger. Sometimes breakpoints slip by one ASM instruction.
Click the step over icon a few times and note that when the list file is the selected
window, the debugger has executed one assembly instruction per click instead of one
entire C line.
Close all files and start a new file EX7.C as follows:
CCS, Inc.
#include ,18F4580.h>
#device ICD=TRUE
void main() {
int a,b,c;
a=11;
b=5;
c=a+b;
c=b-a;
while(TRUE);
}
Compile the program and step-over until the c=a+b is executed. Add a watch for c and
the expected value is 16.
Step-over the subtraction and notice the value of c. The int data type by default is
not signed, so c cannot be the expected –6. The modular arithmetic works like a car
odometer when the car is in reverse only in binary. For example, 00000001 minus 1 is
00000000, subtract another 1 and you get 11111111.
Reset and again step up to the c=a+b. Click the Eval tab. This pane allows a one time
expression evaluation. Type in a+b and click Eval to see the debugger and calculate the
result. The complete expression may also be put in the watch pane as well. Now enter
b=10 and click Eval. This expression will actually change the value of B if the “keep side
effects” check box of the evaluation tab is checked. Check it and click Eval again. Step
over the addition line and click the Watch tab to observe the c value was calculated with
the new value of b.
FURTHER STUDY
A Modify the program to include the following C operators to see how they work:
* / % & ^
Then, with b=2 try these operators: >> <<
Finally, try the unary complement operator with: c=~a;
B Design a program to test the results of the relational operators:
< > = = !=
by exercising them with b as 10, 11, and 12.
Then, try the logical operators || and && with the four combinations of a=0,1
and b=0,1.
Finally, try the unary not operator with: c=!a; when a is 0 and 1.
CAN Bus Exercise Book
8 USING THE MCP250XX
FOR OUTPUT
The MCP250xx parts used on Nodes C and D allow for discrete input, output and
analog input. These parts have internal registers that set the device ID, the directions
of the pins, values of the outputs, scheduling information for outgoing frames, and more.
These registers are initialized by programming the part on a Microchip Pro Mate II. The
registers can also be read and modified at run time.
The MCP250xx part for Node D has been programmed with a base ID of 0x400. The low
three bits of the ID specify a function. For example, 0x400 is a write-register command
and 0x404 is a write-configuration comand. Table 4-2 in the data sheet explains the
identifier usage.
The write-register command has three bytes of data namely, a register, mask, and value.
The value is written to the specified register changing only the bits specified in the mask.
For example, in the previous program, a frame was sent with ID 0x400 and data 0x1E,
0x7F, 0x40. 0x1E is the output latch for the GP pins. 0x7F caused GP7 to be unchanged
(connected to decimal point). The value 0x40 puts a low on pins GP0 to GP5 and a high
on GP6. Note that the registers listed in the data sheet table 3-1 use addresses for the
internal EEPROM. The RAM addresses are 0x1C higher.
Example:
Send a frame with ID 0x400 and data 0x1E, 0x80, 0x00 to turn on the DP
0x400 -- Node D
0x1E -- Output Latch register
0x80 -- Only change Bit 7
0x00 -- All zeros (only Bit 7 relevant)
A 0 or low lights the segment
CCS, Inc.
Node C has three LEDs: Red (GP1), Yellow (GP2) and Green (GP3). Add the following
function to ccscana.c:
buffer[0]=0x1E;
buffer[1]=0x02<<led;
if(on)
buffer[2]=0;
else
buffer[2]=0xff;
while(!(can_putd(WRITE_REGISTER_C_ID, buffer, 3, 1, TRUE, FALSE))); }
Add the following logic to the main program loop in EX6.c and save as a new file.
write_c_led(GREEN, i>1);
write_c_led(YELLOW, i>4);
write_c_led(RED, i>7);
The program should display 0-9 on the LED and light the green, yellow and red LEDs on Node C,
if, according to the value, is >1, >4, >7 respectively.
#include <ccscana.c>
void main() {
int buffer[8],rx_len,rxstat;
int32 rx_id;
can_init();
while(TRUE) {
if ( can_kbhit() ) {
if(can_getd(rx_id, &buffer[0], rx_len, rxstat))
if (rx_id == NODE_C_PUSHBUTTON_ID) {
write_c_led(YELLOW, !bit_test(buffer[1],4));
write_c_led(GREEN, !bit_test(buffer[1],5));
write_c_led(RED, !bit_test(buffer[1],6));
}
}
}
}
CCS, Inc.
The write_c_led function calls send a frame to Node C to light a LED. We will now add
a program to Node B to look for this same data and perform the same action at Node B,
name this file EX9A.c.
#include <16F876A.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
void main ( )
{
int32 rx_id;
int rx_len, rxstat,buffer[8];
int1 a,b;
a = b = FALSE;
can_init ( );
while ( TRUE )
{
if ( can_kbhit ( ) )
{
if ( can_getd ( rx_id , &buffer [ 0 ] , rx_len , rxstat ) )
{
if ( rx_id == WRITE_REGISTER_C_ID && buffer [ 0 ] == 0x1e )
{
if ( buffer [ 1 ] & 4 )
a = buffer [ 2 ];
if ( buffer [ 1 ] & 8 )
b = buffer [ 2 ];
output_bit(RED_LED,!(a==b));
output_bit(YELLOW_LED,!(b==TRUE && a==FALSE));
output_bit(GREEN_LED,!(a==TRUE && b==FALSE));
}
}
}
}
}
#include <ccscana.c>
void main() {
int1 waiting;
int buffer[8],rx_len,rxstat;
int32 rx_id;
can_init();
while(TRUE) {
delay_ms(100);
can_putd(WRITE_REGISTER_C_ID, 0, 8, 1, TRUE, TRUE);
waiting=TRUE;
while(waiting) {
if ( can_kbhit() )
if(can_getd(rx_id, &buffer[0], rx_len, rxstat))
if (rx_id == WRITE_REGISTER_C_ID) {
write_7_segment(buffer[2]/26);
waiting=FALSE;
}
}
}
}
CCS, Inc.
The rate the data is updated to the display is determined by the delay_ms line. Try a
delay_ms(1000) to get a feel for how that lag works. Then try a delay_ms(1).
Up to this point, the settings on the MCP25050 have governed what is
pre-programmed into the EEPROM. In this next program, the pre-programmed settings
will be changed. This chip has the capability to send certain messages when specific,
one-time events happen or when events happen on a regular basis. The chip will be
programmed to send out the analog frame roughly 10 times per second.
Add #define NODE_C_SCHEDULED 0x301 to ccscana.c and create a file named
EX10A.c and enter the following code:
#include <ccscana.c>
void main() {
int buffer[8],rx_len,rxstat;
int32 rx_id;
can_init();
buffer[0]=0x2C;
buffer[1]=0xFF;
buffer[2]=0xD7; // Sched ON, For READ ADC, clock *4096 *16 * 7
can_putd(WRITE_REGISTER_C_ID, buffer, 3, 1, TRUE, FALSE);
while(TRUE) {
if ( can_kbhit() ) {
if(can_getd(rx_id, &buffer[0], rx_len, rxstat)) {
if (rx_id == NODE_C_SCHEDULED) {
write_7_segment(buffer[2]/26);
}
}
}
}
}
#include <16F876A.H>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#include <can-mcp2510.c>
void main() {
int32 rx_id;
int i, rx_len, buffer[8];
struct rx_stat rxstat;
can_init();
while(TRUE) {
if ( can_kbhit() ) {
if(can_getd(rx_id, &buffer[0], rx_len, rxstat)) {
printf(“%LX: (%U) “,rx_id,rx_len);
if (!rxstat.rtr) {
for(i=0;i<rx_len;i++)
printf(“%X “,buffer[i]);
}
if (rxstat.rtr) {printf(“ R “);}
if (rxstat.err_ovfl) {printf(“ O “);}
if (rxstat.inv) {printf(“ I “);}
printf(“\r\n”);
}
}
}
}
Enter, compile, and load this program into Node B. Load the EX9.c program into Node A.
Notice the CAN bus activity between Nodes A and C when the push buttons are pressed
and reported over the RS-232 port.
CCS, Inc.
Sample Output:
00000300: (8) R
00000401: (0)
00000301: (0)
00000303: (2) 40 3E
00000300: (3) 1E 04 FF
00000300: (3) 1E 08 FF
00000303: (2) 40 3C
00000300: (3) 1E 04 FF
00000300: (3) 1E 08 FF
CCS, Inc.
Check Last C line executed in addition to the Time and Watch selected already and
close the snapshot window.
Reset and then Step Over until the final printf() is executed.
Use File>Open>Any File to find the file EX12.TXT (by default in the Debugger Profiles
directory) after setting the file type to all files.
Notice the log of what happened with each step over command.
Uncheck the After each single step in the snapshot window.
Click Reset then Go.
When the break is reached click on the Peripherals tab and select Timer 0.
Shown will be the registers associated with timer 0. Although this program does not use
timer 0 the timer is always running so there is a value in the TMR0 register. Write this
value down.
Clear the breakpoints and set a new breakpoint.
Click GO.
Check the TMR0 register again. If the new value is higher than the previous value then
subtract the previous value from the current value. Otherwise, add 256 to the current
value and then subtract the previous value (because the timer flipped over).
The number we now have is the number of clock ticks it took to execute the switch and
addition. A clock tick by default is 0.2ms. Multiply your number of ticks by 0.2 to find the
time in ms. Note that the timers (and all peripherals) are frozen as soon as the program
stops running.
FURTHER STUDY
A The debugger Eval tab can be used to evaluate a C expression. This
includes assignments. Set a break before the switch statement and use the
Eval window to change the operator being used. For example, type a + but
change it to a - before the switch.
B Set a break on the switch statement and when reached, change to the
C/ASM view and single step through the switch statement. Look up the
instructions executed in the PIC16F876A data sheet to see how the switch
statement is implemented. This implementation is dependent on the case
items being close to each other. Change * to ~ and then see how the
implementation changes.
CCS, Inc.
DCAN – Direct CAN
This is a hybrid approach with BCAN-like masks and reference IDs, FCAN-like
individual receive buffers, and a BCAN-like transmit buffer.
TTCAN – Time Triggered CAN
The bus bandwidth is split into time slots. Specific frame IDs are assigned to certain
timeslots. This limits the frequency for the data and helps nodes to know when to be
looking for data.
The following program will set up filtering on the Node B data monitoring program. We
will set the mask and reference ID to only monitor data to Node D. Load EX10.c into
Node A and EX11.c (with the following additions) into Node B after the can_init() line:
can_set_id(RX1MASK,0xFF00,TRUE);
can_set_id(RX1FILTER2,0x400,TRUE);
can_set_id(RX1FILTER3,0x400,TRUE);
can_set_id(RX1FILTER4,0x400,TRUE);
can_set_id(RX1FILTER5,0x400,TRUE);
can_set_mode(CAN_OP_NORMAL);
CCS, Inc.
The following code shows the necessary includes to perform ECAN operations.
Notice that the PIC18F4580 is now included instead of the PIC18F458.
#include <18F4580.h>
#fuses HS,NOLVP,NOWDT,PUT
#use delay(clock=20000000)
#include <can-18F4580.c>
void main()
{
// can must be initialized.
can_init();
can_enable_b_receiver(B0 | B1 | B2 | B3 | B4 | B5);
// enables all buffers
// as receivers
On reset, all of the programmable buffers are set to receive data, therefore the last
example above would only be needed if all buffers had been configured to transmit.
CCS, Inc.
CAN Bus Exercise Book
16 TRANSMITTING AND
RECEIVING DATA IN MODE 1
Transmitting and receiving under functional operating mode one is almost completely
the same as with mode zero. The basic get and put data functions can still be used to
transfer data and logical functions such as can_kbhit() can still be used to test if data
has been received. As noted in the last chapter, in order to use the extra programmable
buffers as transmit buffers, they must be set using the appropriate functions as all of the
programmable buffers default to receive on reset.
First, enter the following code, save as EX16.c, compile it and then load it into node B.
////////////////////////////////////////////////////////////////////////////
demonstration of use of transmit and receive functions using ecan
////////////////////////////////////////////////////////////////////////////
#include <16F876A.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600,xmit=PIN _ C6,rcv=PIN _ C7)
#include <can-mcp2510.c>
void main()
{
int32 rx _ id;
int rx _ len;
struct rx _ stat stat;
int data[8]={7,6,5,4,3,2,1,0};
int receive[8];
can _ init();
while(TRUE)
{
if(can _ kbhit())
{
can _ getd(rx _ id,receive,rx _ len,stat);
printf(“Data has been received\n\r”);
can _ putd(0x600,data,8,3,TRUE,FALSE);
printf(“Data has been sent\n\r”);
}
else
{
printf(“no data found\n\r”);
}
delay _ ms(3000);
}
}
CCS, Inc.
This program is a simple echo program, it enters an infinite loop and then tests to see
if there is data waiting in any of the buffers. If there is, it then loads that data, prints a
statement acknowledging that it has loaded the data and then puts some different data
onto the bus. After acknowledging that the data has been sent, the if statement exits, and
there is a three second delay before the cycle starts again.
Next, we will enter a program that will send the first program some data and listen for
a response using functional mode one. Enter the following code save as EX16A.c,
compiler and load into node A.
////////////////////////////////////////////////////////////////////////////
demonstration of use of transmit and receive functions usine ecan
////////////////////////////////////////////////////////////////////////////
#include <18F4580.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN _ C6,rcv=PIN _ C7)
#include <can-18F4580.c>
void main ( )
{
int32 rx _ id;
int rx _ len,i;
struct rx _ stat stat;
int data[8]={15,14,13,12,11,10,9,8};
(continued...)
int receive[8];
while(TRUE)
{
for(i=0;i<8;i++)
{
printf(“%i “,data[i]);
}
printf(“\n\ris being placed on the bus with id 0x500\n\r\n\r”);
can _ putd(0x500,data,8,3,TRUE,FALSE);
for(i=0;i<8;i++)
{
printf(“%i “,receive[i]);
}
printf(“\n\rwas received with id %Lx\n\r\n\r”,rx _ id);
delay _ ms(3000);
}
}
This program could potentially be run using legacy mode as all of the functions used can
be found in the regular CAN device drivers. In fact, because none of the programmable
buffers were set to transmit, the program is still sort of running in legacy mode. In order
to make use of some of the programmable buffers, the can_enable_b_transfer function
would need to be used.
The following is sample output from node A.
CCS, Inc.
15 14 13 12 11 10 9 8
is being placed on the bus with id 0x500
7 6 5 4 3 2 1 0
was received with id 00000600
There are certain instances in which the program may only need to access the transmit
buffer once every few seconds and therefore will only ever use one transmit buffer.
There are functions included in the ECAN device library that will allow the user to set up
a specific transmit register.
Replace the following line of code and save as a new file named EX16B.c.
can _ putd(0x500,data,8,3,TRUE,FALSE);
with this.
can _ t0 _ putd(0x500,data,8,3,TRUE,FALSE);
This will attempt to place the data into the zeroth transmit register. If the buffer happens
to be transmitting or full, the function will return false otherwise it will return true. Each
buffer has a function associated with it. These functions are as follows.
can _ t0 _ putd
can _ t1 _ putd
can _ t2 _ putd
can _ b0 _ putd
can _ b1 _ putd
can _ b2 _ putd
can _ b3 _ putd
can _ b4 _ putd
can _ b5 _ putd
The first three functions will write to the dedicated transmit buffers while the last six will
write to the programmable buffers. It should be noted again, that in order to use the last
six functions, each associated buffer must be set to transmit mode.
The purpose of these functions is mainly to reduce the amount of program memory
dedicated to placing data on the bus. In the case of the original CAN bus, there were
only three buffers to check, however, now there are nine. If only one transmit register is
needed, it is much more efficient not to test each buffer and simply write to the buffer that
is to be used.
CAN Bus Exercise Book
17 USING FILTERS IN MODE 1
Recall that in mode zero, there were six filters and two masks. Each mask was assigned
to one of the receive buffers and each receive buffer had a certain number of filters. The
mask register was used to determine which bits of the incoming ID the filter should be
applied two. Therefore if the mask had a value of 0x01, only the least significant bit would
have the filter applied to it. The filter was used as a reference to determine which I.D.s
to accept and which to reject. If, for instance, the filter was 0xFF, only numbers with the
value of 0xFF would be accepted unless the mask only applied that filter to certain bits,
in which case, the bits that the filter was applied to would need to be high in order to be
accepted by the filter.
In mode zero, receive register zero had two filters and receive register one had four
filters. In mode one, there are sixteen filters and each one can be dynamically associated
with any of the receive registers including the programmable ones. Each filter can then
be dynamically associated with either of the two masks. For example, mask one could
be associated with filter two which could then be associated with programmable buffer
three. In which case, any data coming in would need to pass through mask one and then
be accepted by filter two in order to be loaded into the third programmable buffer.
It must be noted that many filters can be associated with a single buffer, but multiple
buffers can not be associated with the same filter.
The steps then for setting up filters in mode one are as follows.
1. Load masks and filters with desired Ids using the can_set_id function.
2. Associate each used filter with a mask using the can_associate_filter_to_mask function.
3. Associate each used filter with a buffer using the can_associate_filter_to_buffer function.
Enter the following code and save as EX17.c compile, and load the following source code into node B.
#include <16F876A.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-mcp2510.c>
void main()
{
int data[8]={7,6,5,4,3,2,1,0};
can_init();
(continued...)
CCS, Inc.
(continued...)
while(TRUE){
can_putd(0x600,data,8,3,TRUE,FALSE);
delay_ms(1000);
can_putd(0x700,data,8,3,TRUE,FALSE);
delay_ms(1000);
can_putd(0x800,data,8,3,TRUE,FALSE);
delay_ms(1000);
can_putd(0x900,data,8,3,TRUE,FALSE);
delay_ms(1000);
}
}
This is a simple trasmitter program that sends out data with several I.D.s.
#include <18F4580.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-18F4580.c>
void main ( )
{
int32 rx_id;
int rx_len,i;
struct rx_stat stat;
int receive[8];
can_set_functional_mode(1);
while(TRUE)
{
(continued...)
for(i=0;i<8;i++)
{
printf(“%i “,receive[i]);
}
printf(“\n\rwas received with id %Lx\n\r\n\r”,rx_id);
}
}
}
This program simply listens to the bus and prints out the data and the ID as they are
received. The following is sample output from this code.
76543210
was received with id 00000600
76543210
was received with id 00000700
76543210
was received with id 00000800
76543210
was received with id 00000900
Try to filter out all of the I.D.s except for 0x600. To do this, add the following code right
after can_set_functional_mode(1) and save as a new file named EX17B.c.
can_set_id(RX0MASK,0xFF00,TRUE);
can_set_id(RXFILTER0,0x600,TRUE);
can_set_id(RXFILTER1,0x600,TRUE);
can_set_id(RXFILTER2,0x600,TRUE);
can_set_id(RXFILTER3,0x600,TRUE);
can_set_id(RXFILTER4,0x600,TRUE);
can_set_id(RXFILTER5,0x600,TRUE);
can_set_id(RXFILTER6,0x600,TRUE);
can_set_id(RXFILTER7,0x600,TRUE);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F0BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F1BP);
(continued...)
CCS, Inc.
(continued...)
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F2BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F3BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F4BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F5BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F6BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F7BP);
can_associate_filter_to_buffer(ARXB0, F0BP);
can_associate_filter_to_buffer(ARXB1, F1BP);
can_associate_filter_to_buffer(AB0, F2BP);
can_associate_filter_to_buffer(AB1, F3BP);
can_associate_filter_to_buffer(AB2, F4BP);
can_associate_filter_to_buffer(AB3, F5BP);
can_associate_filter_to_buffer(AB4, F6BP);
can_associate_filter_to_buffer(AB5, F7BP);
The first line sets up the mask ID. In this case, only bytes two and three will have the filter
applied to them. This works well for us because the only addresses that we are dealing
with are 0x600, 0x700, 0x800, and 0x900. If we were expecting an address such as
0x5432, it would probably be best to load the mask with the value 0xFFFF.
The second block sets all of the filter I.D.s to 0x600. This is so that when we associate
each buffer to the filter, it will only be possible for data with an ID value of 0x600 to make
it into the buffer.
The third block associates filters zero through seven with the zeroth acceptance mask.
Under mode one operation, there are four possible masks that can be associated with a
filter. These are as follows.
ACCEPTANCE_MASK_0
ACCEPTANCE_MASK_1
FILTER_15
NO_MASK
The fourth block associates filters zero through seven with the receive buffers zero
through seven. In this way, all receive buffers are associated with a different filter. Each
filter has the same value ID, and each filter is associated with the same acceptance
mask.
After compiling and running the code, the output should look something like this.
76543210
was received with ID 00000600
76543210
was received with ID 00000600
76543210
was received with ID 00000600
As can be seen by the output, only 0x600 IDs are allowed into the receive buffers.
#include <16F876A.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-mcp2510.c>
void main()
{
int data[8]={7,6,5,4,3,2,1,0};
can_init();
(continued...)
CCS, Inc.
(continued...)
while(TRUE){
can_putd(0x100,data,8,3,TRUE,FALSE);
can_putd(0x200,data,8,3,TRUE,FALSE);
can_putd(0x300,data,8,3,TRUE,FALSE);
can_putd(0x400,data,8,3,TRUE,FALSE);
can_putd(0x500,data,8,3,TRUE,FALSE);
can_putd(0x600,data,8,3,TRUE,FALSE);
can_putd(0x700,data,8,3,TRUE,FALSE);
can_putd(0x800,data,8,3,TRUE,FALSE);
delay_ms(3000);
}
}
This program simply sends eight consecutive data frames, and then delays three
seconds before doing repeating. We will use these data frames to demonstrate how the
FIFO system works and how the length of the FIFO can be changed.
Enter the following code and save as EX18A.c, compile, and load this source code into node A.
#include <18F4580.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-18F4580.c>
void main ( )
{
int32 rx_id;
int rx_len,i;
struct rx_stat stat;
int receive[8];
can_set_functional_mode(2);
while(TRUE)
(continued...)
(continued...)
{
if(can_kbhit()) // wait for a response
{
can_fifo_getd(rx_id,receive,rx_len,stat);
for(i=0;i<8;i++)
{
printf(“%i “,receive[i]);
}
printf(“\n\rid = %Lx\n\r”,rx_id);
printf(“buffer = %i\n\r\n\r”,stat.buffer);
}
}
}
This code uses the can_fifo_getd function as apposed to the can_getd function. This new
function uses the pointer described above to retrieve the data from the buffer in stead of
polling each buffer to see if data has been received. This significantly reduces the amount
of program memory used and cuts the amount of time that it takes to execute the function.
Below is a sample of the first three seconds of output.
76543210
id = 00000100
buffer = 0
76543210
id = 00000200
buffer = 1
76543210
id = 00000300
buffer = 2
76543210
id = 00000400
buffer = 3
76543210
id = 00000500
buffer = 4
CCS, Inc.
76543210
ID = 00000600
buffer = 5
76543210
ID = 00000700
buffer = 6
76543210
ID = 00000800
buffer = 7
Notice the output that eight data frames where received and that the FIFO system filled
the receive buffers in order from zero to seven where zero and one are the dedicated
receive buffers and two through seven are the programmable buffers.
Add the following line to the program just after the can_set_functional_mode
function call and save as a new file named EX18B.c.
can_enable_b_transfer(B2);
This not only enables the B2 programmable buffer to be a transmit buffer, it also cuts the
FIFO buffer by half because now the only registers used are the two dedicated buffers
and programmable buffers B0 and B1.
The output for the modified program is as follows.
76543210
id = 00000500
buffer = 0
76543210
id = 00000600
buffer = 1
76543210
id = 00000700
buffer = 2
76543210
id = 00000800
buffer = 3
In order to get the FIFO buffer to its maximum size, any programmable buffers that need
to be configured as transmit buffers should use the higher buffers. For example if two
transmit buffers are needed beyond the three dedicated transmit buffers, they should be
set to programmable buffers B4 and B5 because that way B0 through B3 can be used as
receive buffers for the FIFO buffer.
can_disable_filter(0xffff);
can_enable_filter(0x03);
can_set_id(RX0MASK,0xff00,TRUE);
can_set_id(RXFILTER0,0x400,TRUE);
can_set_id(RXFILTER1,0x800,TRUE);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F0BP);
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F1BP);
CCS, Inc.
Make sure that the node B program from chapter 18 is still running on node B. Compile
and load the node A program into node A. After opening the serial port interface program,
the output should look like the following.
76543210
id = 00000400
buffer = 6]
76543210
id = 00000800
buffer = 7
76543210
id = 00000400
buffer = 0
76543210
id = 00000800
buffer = 1
CCS, Inc.
To demonstrate the auto-RTR functionality, we will first load the following test program
into node B. Enter the following code and save as EX20.c
#include <16F876A.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-mcp2510.c>
void main()
{
int32 rx_id;
int rx_len,i;
struct rx_stat stat;
int data[8]={7,6,5,4,3,2,1,0};
int receive[8];
can_init();
while(TRUE)
{
can_putd(0x500,data,8,3,TRUE,TRUE);
delay_ms(1000);
if(can_kbhit())
{
can_getd(rx_id,receive,rx_len,stat);
printf(“data received!\n\r”);
for(i=0;i<8;i++)
printf(“%i “,receive[i]);
printf(“\n\r%Lx\n\r”,rx_id);
}
else
{
printf(“data not received.\n\r”);
}
}
}
#include <18F4580.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <can-18F4580.c>
void main ( )
{
int data[8]={15,14,13,12,11,10,9,8};
can_set_functional_mode(1);
can_enable_b_transfer(B1);
can_disable_filter(0xffff);
can_enable_filter(0x01);
can_set_id(RX0MASK,0xff00,TRUE);
can_set_id(RXFILTER0,0x500,TRUE);
can_set_id(B1ID,0x500,TRUE);
(continued...)
CCS, Inc.
(continued...)
can_associate_filter_to_mask(ACCEPTANCE_MASK_0,F0BP);
can_associate_filter_to_buffer(AB1,F0BP);
can_load_rtr(B1,data,8);
can_enable_rtr(B1);
while(TRUE)
{
}
}
This program simply follows the seven steps listed above and then enters an infinite loop
which does nothing. All receive and transmit work is done completely in hardware.
Insert the serial cable into the jack on node B and open the serial port interface. As the
program is loaded into node A, the output should look something like this:
data not received.
data not received.
data not received.
data not received.
data received!
15 14 13 12 11 10 9 8
00000500
data received!
15 14 13 12 11 10 9 8
00000500
data received!
15 14 13 12 11 10 9 8
00000500
Fault
Nodes Speed Tolerant
Philips PCA82C251 110 1 meg NO
PCA82C252 15 125k YES
TJA1054 32 125k YES Low EMC
TI SN65LBC031 500k NO
SN65HVD251 120 1 meg NO
SN65HVD232 120 1 meg NO 3.3V
CCS, Inc.
TIMING
All nodes on the bus must have the same target bit time. The fastest time allowed by the
PCA82C251 is 1 million bits per second.
A single-bit time is divided into four segments:
Sync period
Propagation period (allow for delays between nodes)
Phase 1 period
Phase 2 period
The data is sampled for the bit between phase 1 and phase 2.
Each of the four segment times may be programmed in terms of a base time (Time
Quanta or Tq).
The baud rate settings are made in the .h files (like can-18xxx8.h). The following settings
have been made:
Sync period = 1 Tq
Propagation period = 3 Tq
Phase 1 period = 6 Tq
Phase 2 period = 6 Tq
The total bit time is therfor 16 Tq.
Tq is set via the prescaller. The formula is:
Tq = (2 x (prescaller+1))/clock
Use a clock of 20 mhz and have the prescaller set to 4. Therefore:
Tq = (2 x (4+1))/20000000 = 0.5 us
The bit time is 8 us or 125K.
Sample
Point
13 9.4 Timers
On The Web
EMULATORS
The ICD used in this booklet uses two I/O pins on the chip to communicate with a small debug
program in the chip. This is a basic debug tool that takes up some of the chip’s resources (I/O
pins and memory). An emulator replaces the chip with a special connector that connects to a unit
that emulates the chip. The debugging works in a simulator manner except that the chip has all of
its normal resources, the debugger runs faster and there are more debug features. For example
an emulator typically will allow any number of breakpoints. Some of the emulators can break on
an external event like some signal on the target board changing. Some emulators can break on
an external event like some that were executed before a breakpoint was reached. Emulators cost
between $500 and $3000 depending on the chips they cover and the features.
DEVICE PROGRAMMERS
The ICD can be used to program FLASH chips as was done in these exercises. A stand alone
device programmer may be used to program all the chips. These programmers will use the .HEX
file output from the compiler to do the programming. Many standard EEPROM programmers do
know how to program the Microchip parts. There are a large number of Microchip only device
programmers in the $100-$200 price range. Note that some chips can be programmed once
(OTP) and some parts need to be erased under a UV light before they can be re-programmed
(Windowed). CCS offers the Mach X which is a stand-alone programmer and can be used as an
in-circuit debugger.
PROTOTYPING BOARDS
There are a large number of Prototyping boards available from a number of sources. Some
have an ICD interface and others simply have a socket for a chip that is externally programmed.
Some boards have some advanced functionality on the board to help design complex software.
For example, CCS has a Prototyping board with a full 56K modem on board and a TCP/IP stack
chip ready to run internet applications such as an e-mail sending program or a mini web server.
Another Prototyping board from CCS has a USB interface chip, making it easy to start developing
USB application programs.
SIMULATORS
A simulator is a program that runs on the PC and pretends to be a microcontroller chip. A
simulator offers all the normal debug capability such as single stepping and looking at variables,
however there is no interaction with real hardware. This works well if you want to test a math
function but not so good if you want to test an interface to another chip. With the availability of low
cost tools, such as the ICD in this kit, there is less interest in simulators. Microchip offers a free
simulator that can be downloaded from their web site. Some other vendors offer simulators as a
part of their development packages.
CCS Programmer Control Software
The CCSLOAD software will work for all the CCS device programmers and replaces the
older ICD.EXE and MACHX.EXE software. The CCSLOAD software is stand-alone and
does not require any other software on the PC. CCSLOAD supports ICD-Sxx, ICD-Uxx,
Mach X, Load-n-Go, and PRIME8.
Use the Diagnostics tab for troubleshooting or the ccsload.chm help file for additional assistance.
Tue Jun 28 12:55:03 2005
piccan.sch-2 - Tue Jun 28 12:55:06 2005
piccan.sch-3 - Tue Jun 28 12:55:06 2005
iccan.sch-4 - Tue Jun 28 12:55:06 2005
External External
CAN TTL
GP0
MCP GP5
GP6
GP1
Node
25050
GP4 GP2 D
GP3
A1
RS232
PIC16F876A
Node
C6,C7
MCP2515
A2
B
A3
A0
A4
ICD
B1 RS232
PIC18F4580
B4 C6,C7
Node
A
A0
A5
A4
ICD
Connector to attach to an external CANBus network.
7- segment LED
GP0...GP7
H L G Tx Rx G
LED
GP1
GP2
GP3
Pot GP0
Push buttons
GP5
GP6
GP7 LED
RS232
A1
C6,C7
A2
A3
PIC16F876A ICD
Pot AN0
Connector
Push button
A4 LED
B1
B4
A5 RS232
C6,C7
Pot AN0
Push button PIC18F4580
A4
ICD
Connector
Power
9V DC
+5 B6 B4 B2 B0 D6 D4 D2 D0 C4 C2 C0 A4 A2 E2 EO G
+5 B7 B5 B3 B1 D7 D5 D3 D1 C5 C3 C1 A5 A3 A1 E1 G