📄 can.c
字号:
/* ------------------------------------------------------------------------
File : can.c
Descr : Functions for serial operation of an 81C91 CAN-controller.
History: 19JAN.00; Henk B&B; Definition.
31JAN.00; Henk B&B; Beware!: start reading/writing message
with most-significant byte (7) and end
with data byte 0.
21JUL.00; Henk B&B; Enable the use of all 16 message objects
(up to now just the first 8 were considered).
22FEB.01; Henk B&B; Keep some variables in EEPROM (optionally).
17JUL.01; Henk B&B; Go to sleep for a while at bus-off, before
reinitialising.
17OCT.01; Henk B&B; Implement CAN descriptor register refresh.
27FEB.03; Henk B&B; Add a CAN configuration parameters datablock
(in EEPROM);
add possibility to disable Remote Frames,
through the Object Dictionary, but keep
Nodeguarding possible (through automatic replies).
MAY.03; Henk B&B; Added receive message buffering under interrupt.
02JUN.03; Henk B&B; Add to CAN configuration parameters datablock:
- add CANopenOpStateInit bool, to automatically
go to state Operational at startup
- add Bus-off max counter value
26SEP.03; Henk B&B; Added can_recv_descriptor_refresh();
call it in can_read() as well as can_write(),
to refresh descriptors of all receiving buffers.
23OCT.03; Henk B&B; Enable the 81c91's Transmit Check feature.
25FEB.04; Henk B&B; Added toggle bit to Emergency message.
31MAR.04; Henk B&B; remove can_recv_descriptor_refresh() from
can_write(): message loss is possible.
--------------------------------------------------------------------------- */
#include "general.h"
#include "can.h"
#include "guarding.h"
#include "jumpers.h"
#include "pdo.h"
#include "spi.h"
#include "store.h"
#include "timer103.h"
#ifdef __VARS_IN_EEPROM__
#include "eeprom.h"
#endif
/* CANopen state of this node (declared in ELMBmain.c) */
extern BYTE NodeState;
/* ------------------------------------------------------------------------ */
/* CAN message buffering in RAM */
/* Number of message buffers (here must be a power of 2!) */
#define CAN_BUFS 64
#define CAN_BUFS_MASK CAN_BUFS-1
/* Size of message buffer */
#define CAN_BUF_SIZE 11
/* Indices into the individual buffers */
#define MSG_DATA_I 0
#define MSG_DLC_I 8
#define MSG_OBJECT_I 9
#define MSG_VALID_I 10
/* Buffer values:
'message-present' byte in location MSG_VALID_I:
0x00 means 'message present', 0xFF means 'no message present'.
To tolerate up to 2 bitflips:
bits-in-byte <= 2: message
bits-in-byte >= 6: no message */
#define BUF_EMPTY 0xFF
#define BUF_NOT_EMPTY 0x00
/* Array of CAN message buffers */
static BYTE CanMsgBuf[CAN_BUFS][CAN_BUF_SIZE];
static BOOL CanBufFull;
/* Parameters which are being used according to a majority voting mechanism:
MsgOutIndex_ : index of the first-to-be-handled CAN-message buffer,
MsgCounter_: number of unhandled CAN-messages in buffers */
static BYTE MsgOutIndex1, MsgOutIndex2, MsgOutIndex3;
static BYTE MsgCounter1, MsgCounter2, MsgCounter3;
/* NB: make sure to disable the CAN INT interrupt before writing or reading
anything to/from the CAN-controller in the main program loop !
(and also when updating the buffer counter in the main loop) */
/* ------------------------------------------------------------------------ */
/* CAN-controller message object descriptors */
/* Description Registers settings for NMT, SDO-tx/SDO-rx, Emergency,
Bootup/Nodeguard, SYNC, PDOs, etc.
(according to the CANopen Predefined Connection Set);
in the following array is space for all available message buffers;
NB: to simplify refreshing the descriptor registers the CAN_DESCRIPTOR
array contains the values in order of message buffer number to
write to; make absolutely sure this list matches the message/buffer
numbering in can.h !! */
const BYTE CAN_DESCRIPTOR[C91_MSG_BUFFERS][2] =
{
/* Buffer 0 must be used if we want to filter out RTRs for this node
(and to let them be handled by the microcontroller!) */
{ 0xFF, 0xE0 },
/* NMT */
{ NMT_OBJ, C91_NMT_LEN },
/* SYNC */
{ SYNC_OBJ, C91_SYNC_LEN },
/* EMERGENCY */
{ EMERGENCY_OBJ, C91_EMERGENCY_LEN },
/* SDO-Transmit */
{ SDOTX_OBJ, C91_SDOTX_LEN },
/* SDO-Receive */
{ SDORX_OBJ, C91_SDORX_LEN },
/* Bootup/Nodeguard */
{ NODEGUARD_OBJ, C91_NODEGUARD_LEN },
/* Transmit-PDO2 */
{ TPDO2_OBJ, C91_TPDO2_LEN },
/* Transmit-PDO1 */
{ TPDO1_OBJ, C91_TPDO1_LEN },
/* Receive-PDO1 */
{ RPDO1_OBJ, C91_RPDO1_LEN },
/* Receive-PDO2 */
{ RPDO2_OBJ, C91_RPDO2_LEN },
/* Transmit-PDO3 */
{ TPDO3_OBJ, C91_TPDO3_LEN },
/* ---- object available ---- */
{ 0xFF, 0xE0 },
/* ---- object available ---- */
{ 0xFF, 0xE0 },
/* ---- object available ---- */
{ 0xFF, 0xE0 },
/* ---- object available ---- */
{ 0xFF, 0xE0 }
};
/* This array of BOOLs indicates which CAN-controller buffers
are receiving buffers; their descriptors are refreshed
(if __CAN_REFRESH__ is defined) in can_read() and can_write()
Make sure this array matches CAN_DESCRIPTOR[] !
Do not refresh RTR buffer like this: RTR may get lost */
const BOOL CANBUF_IS_RECV[C91_MSG_BUFFERS] =
{
FALSE, TRUE, TRUE, FALSE,
FALSE, TRUE, FALSE, FALSE,
FALSE, TRUE, TRUE, FALSE,
FALSE, FALSE, FALSE, FALSE
};
/* Index for CANBUF_IS_RECV[] */
static BYTE CanRefreshIndex;
/* CAN-controller register settings for baudrate configuration */
const BYTE CAN_BAUDRATE_CONFIGS[4][3] =
{
{ C91_BRP_50K, C91_BL1_50K, C91_BL2_50K },
{ C91_BRP_500K, C91_BL1_500K, C91_BL2_500K },
{ C91_BRP_250K, C91_BL1_250K, C91_BL2_250K },
{ C91_BRP_125K, C91_BL1_125K, C91_BL2_125K }
};
/* ------------------------------------------------------------------------ */
/* Globals */
BYTE NodeID; /* (stored in EEPROM) */
BYTE CANopenErrorReg;
/* Counter for errors in the CAN communication */
static BYTE CanErrorCntr;
BOOL RtrDisabled; /* (stored in EEPROM) */
/* Boolean to enable 'autostart' (automatically goto Operational mode) */
static BOOL CANopenOpStateInit; /* (copy in EEPROM) */
/* Max counter value for bus access retries after Bus-off events */
static BYTE CanBusOffMaxCnt; /* (copy in EEPROM) */
/* Bus-off events counter */
BYTE CanBusOffCnt = 0;
/* Help variables for RTR reception */
static BYTE RtrIdHi; /* (stored in EEPROM) */
static BYTE RtrIdLo; /* (stored in EEPROM) */
/* Help variables for CAN message reception */
static BYTE ObjectMask1;
static BYTE ObjectMask2;
/* Toggle bit for the Emergency CAN-message */
static BYTE CanEmgToggle = 0x80;
/* ------------------------------------------------------------------------ */
/* Local prototypes */
static BYTE can_check_for_msgs( void );
static BYTE get_buf_index ( void );
static BYTE get_buf_cntr ( void );
static BYTE bits_in_byte ( BYTE val );
static void can_load_config ( void );
#ifdef __CAN_REFRESH__
static void can_descriptor_refresh( BYTE object_no );
static void can_recv_descriptor_refresh( void );
#endif /* __CAN_REFRESH__ */
/* ------------------------------------------------------------------------ */
BYTE can_read_reg( BYTE regaddr )
{
BYTE byt;
/* Select CAN-controller and read-mode */
CAN_SELECT();
CAN_READ_ENABLE();
spi_write( regaddr );
byt = spi_read();
/* Deselect CAN-controller */
CAN_DESELECT();
return byt;
}
/* ------------------------------------------------------------------------ */
void can_write_reg( BYTE regaddr, BYTE byt )
{
/* Select CAN-controller and write-mode */
CAN_SELECT();
CAN_WRITE_ENABLE();
spi_write( regaddr );
spi_write( byt );
/* Deselect CAN-controller */
CAN_DESELECT();
}
/* ------------------------------------------------------------------------ */
void can_init( BOOL init_msg_buffer )
{
BYTE baudrate;
BYTE i, canaddr;
BYTE id_hi, id_lo;
BYTE bufno;
/* Disable interrupt */
CAN_INT_DISABLE();
/* Initialise configuration parameters */
can_load_config();
/* Reset error counter */
if( init_msg_buffer ) CanErrorCntr = 0;
/* Initialize CANopen Error Register Object (Object 0x1001) */
CANopenErrorReg = 0x00;
/* Prepare PORTB for jumper read-out */
DDRB = PORTB_DDR_FOR_JUMPERS;
PORTB = PORTB_DATA_FOR_JUMPERS;
/* Read the jumper settings */
NodeID = read_nodeid();
baudrate = read_baudrate();
/* Set PORTB back to 'operational' setting */
DDRB = PORTB_DDR_OPERATIONAL;
PORTB = PORTB_DATA_OPERATIONAL;
/* Set CAN-controller in configuration mode */
can_write_reg( C91_MODE_STATUS_I, C91_RES | C91_IM );
/* Initialise registers 0 to 0x0A to zero */
for( i=0; i<0x0B; ++i ) can_write_reg( i, 0x00 );
/* Enable Monitor Mode to enable reception of RTRs (by CPU!) ?
###BEWARE: all messages not received by any of the other
buffers are now accepted in buffer 0...
could be a bit much if there are many nodes
(many messages) on the bus... */
can_write_reg( C91_CONTROL_I, 0x00 ); /* Do not enable Monitor Mode */
/* Enable the 81C91 controller's Transmit Check feature */
can_write_reg( C91_CONTROL_I, can_read_reg(C91_CONTROL_I) |
C91_TRANSMIT_CHECK_ENABLE );
/* Reset bits in Interrupt Register */
can_write_reg( C91_INTERRUPT_I, 0x00 );
/* Output Control */
can_write_reg( C91_OUTPUTCONTROL_I, 0x18 );
/* Clock Control */
can_write_reg( C91_CLOCKCONTROL_I, 0x80 );
can_write_reg( C91_CLOCKCONTROL_I, 0x01 );
/* Write the settings for the required CAN-bus baudrate */
can_write_reg( C91_BRP_I, CAN_BAUDRATE_CONFIGS[baudrate][0] );
can_write_reg( C91_BL1_I, CAN_BAUDRATE_CONFIGS[baudrate][1] );
can_write_reg( C91_BL2_I, CAN_BAUDRATE_CONFIGS[baudrate][2] );
/* Node-ID for Description Register is split over 2 bytes */
id_hi = NodeID >> 3;
id_lo = NodeID << 5;
/* Initialise the Object Descriptor Registers */
canaddr = C91_DR00_I;
for( bufno=0; bufno<C91_MSG_BUFFERS; ++bufno )
{
BYTE desc_hi, desc_lo;
/* Use the corresponding descriptor bytes */
desc_hi = CAN_DESCRIPTOR[bufno][0];
desc_lo = CAN_DESCRIPTOR[bufno][1];
/* NMT and SYNC are broadcast messages: Node-ID is not in */
if( bufno != C91_NMT && bufno != C91_SYNC )
{
/* Node-ID is included in COB-ID */
desc_hi |= id_hi;
desc_lo |= id_lo;
}
/* Write the CAN-controller's Descriptor Registers */
can_write_reg( canaddr, desc_hi );
++canaddr;
can_write_reg( canaddr, desc_lo );
++canaddr;
}
/* Receive-Interrupt Mask Registers
(in combination with the Interrupt Mask setting below) */
can_write_reg( C91_RECV_INTERRUPT_MASK1_I, 0xFF );
can_write_reg( C91_RECV_INTERRUPT_MASK2_I, 0xFF );
/* Enable INT pin interrupt for received messages only */
can_write_reg( C91_INTERRUPT_MASK_I, C91_RECV_INT );
/* If Remote Frames are not required adjust
the CAN-controller's configuration: disable Monitor Mode for buffer 0
and enable automatic RTR for NODEGUARD messages */
can_rtr_enable( pdo_rtr_required() );
/* Set CAN-controller to operational mode */
can_write_reg( C91_MODE_STATUS_I, 0x00 );
/* Bit masks for objects received */
ObjectMask1 = 0;
ObjectMask2 = 0;
/* Help variables for RTR reception */
RtrIdHi = id_hi;
RtrIdLo = id_lo | C91_DR_RTR_MASK;
#ifdef __VARS_IN_EEPROM__
/* Create working copies of configuration globals in EEPROM */
if( eeprom_read( EE_NODEID ) != NodeID )
eeprom_write( EE_NODEID, NodeID );
if( eeprom_read( EE_RTRIDHI ) != RtrIdHi )
eeprom_write( EE_RTRIDHI, RtrIdHi );
if( eeprom_read( EE_RTRIDLO ) != RtrIdLo )
eeprom_write( EE_RTRIDLO, RtrIdLo );
#endif /* __VARS_IN_EEPROM__ */
if( init_msg_buffer )
{
/* Initialize the CAN-message buffer and management stuff */
for( bufno=0; bufno<CAN_BUFS; ++bufno )
CanMsgBuf[bufno][MSG_VALID_I] = BUF_EMPTY;
CanBufFull = FALSE;
MsgOutIndex1 = 0; MsgOutIndex2 = 0; MsgOutIndex3 = 0;
MsgCounter1 = 0; MsgCounter2 = 0; MsgCounter3 = 0;
}
/* Enable interrupt */
#ifndef __ELMB103__
/* Low level of INT1 generates an interrupt
(= default and unchangeable on ATmega103) */
EICRA &= ~(BIT(ISC11) | BIT(ISC10));
#endif
CAN_INT_ENABLE(); /* Enable interrupt from CAN-controller */
CanRefreshIndex = C91_MSG_BUFFERS-1;
}
/* ------------------------------------------------------------------------ */
BOOL can_msg_available( void )
{
BOOL available;
/* Need undisturbed access to buffer management variables !
(because even the get_buf_xxx() functions may alter variables,
due to the (self-correcting) majority voting mechanism) */
CAN_INT_DISABLE();
/* CAN-message available in buffer ? */
available = (get_buf_cntr() > 0);
CAN_INT_ENABLE();
#ifdef __VARS_IN_EEPROM__
/* EEPROM access removed from CAN interrupt handling, do it here instead ! */
if( !available )
{
NodeID = eeprom_read( EE_NODEID );
RtrIdLo = eeprom_read( EE_RTRIDLO );
RtrIdHi = eeprom_read( EE_RTRIDHI );
}
#endif /* __VARS_IN_EEPROM__ */
return available;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -