📄 can_ifc.c
字号:
/******************************************************************************
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;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -