📄 can_at91sam7.c
字号:
#ifndef CYGNUM_DEVS_CAN_AT91SAM7_CAN0_EXT_MBOXES
#define CYGNUM_DEVS_CAN_AT91SAM7_CAN0_EXT_MBOXES 0
#endif
#else // #if CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS == 1
#define CAN_PID(_extra_) ((_extra_)->isrvec)
#define CAN_ISRVEC(_extra_) ((_extra_)->isrvec)
#define CAN_ISRPRIO(_extra_) ((_extra_)->isrprio)
#define CAN_BASE(_extra_) ((_extra_)->base)
#define CAN_DECLARE_INFO(_chan_) at91sam7_can_info_t *info = (at91sam7_can_info_t *)chan->dev_priv;
#define CAN_MBOX_TX(_extra_) 7 // normally it is always the last mailbox
#define CAN_MBOX_STD_CNT(_extra_) ((_extra_)->mboxes_std_cnt)
#define CAN_MBOX_EXT_CNT(_extra_) ((_extra_)->mboxes_ext_cnt)
#define CAN_MBOX_RX_ALL_CNT(_extra) ((_extra_)->mboxes_rx_all_cnt)
#endif // #if CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS == 1
//===========================================================================
// DATA TYPES
//===========================================================================
typedef struct at91sam7_can_info_t
{
cyg_interrupt interrupt;
cyg_handle_t interrupt_handle;
cyg_uint32 stat; // buffers status register value between ISR and DSR
cyg_uint8 free_mboxes; // number of free message boxes for msg filters and rtr buffers
bool rx_all; // true if reception of call can messages is active
cyg_can_state state; // state of CAN controller
#if CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS > 1
cyg_uint32 base; // Per-bus h/w details
cyg_uint8 isrpri; // ISR priority
cyg_uint8 isrvec; // ISR vector (peripheral id)
cyg_uint8 mboxes_std_cnt; // contains number of standard message boxes available
cyg_uint8 mboxes_ext_cnt; // number of message boxes with ext id
cyg_uint8 mboxes_rx_all_cnt;// number of all available mboxes
#endif
} at91sam7_can_info_t;
//
// at91sam7 info initialisation
//
#if CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS > 1
#define AT91SAM7_CAN_INFO(_l, _base, _isrpri, _isrvec, _std_mboxes, _ext_mboxes) \
at91sam7_can_info_t _l { \
state : CYGNUM_CAN_STATE_STOPPED, \
base : (_base), \
isrpri : (_isrpri), \
isrvec : (_isrvec), \
mboxes_std_cnt : (_std_mboxes), \
mboxes_ext_cnt : (_ext_mboxes), \
mboxes_rx_all_cnt : ((_std_mboxes) + (_ext_mboxes)), \
};
#else
#define AT91SAM7_CAN_INFO(_l) \
at91sam7_can_info_t _l = { \
state : CYGNUM_CAN_STATE_STOPPED, \
};
#endif
//===========================================================================
// GLOBAL DATA
//===========================================================================
#if CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS > 1
//
// ToDo - Initialisation of individual CAN channels if more than one channel
// is supported
//
#else // CYGINT_DEVS_CAN_AT91SAM7_CAN_CHANNELS == 1
//
// Only one single CAN channel supported by SAM7 chip
//
AT91SAM7_CAN_INFO(at91sam7_can0_info);
#endif
//===========================================================================
// LOCAL DATA
//===========================================================================
//
// Macro for creation of CAN_BR value for baudrate tbl
//
#define CAN_BR_TBL_ENTRY(_brp_, _propag_, _phase1_, _phase2_, _sjw_) \
((_brp_ << 16) | (_propag_ << 8) | (_phase2_) | (_phase1_ << 4) | (_sjw_ << 12))
//
// Table with register values for baudrates at main clock of 48 MHz
//
static const cyg_uint32 at91sam7_br_tbl[] =
{
CAN_BR_TBL_ENTRY(0xef, 0x07, 0x07, 0x02, 0), // 10 kbaud
CAN_BR_TBL_ENTRY(0x95, 0x04, 0x07, 0x01, 0), // 20 kbaud
CAN_BR_TBL_ENTRY(0x3b, 0x04, 0x07, 0x01, 0), // 50 kbaud
CAN_BR_TBL_ENTRY(0x1d, 0x04, 0x07, 0x01, 0), // 100 kbaud
CAN_BR_TBL_ENTRY(0x17, 0x04, 0x07, 0x01, 0), // 125 kbaud
CAN_BR_TBL_ENTRY(0x0b, 0x04, 0x07, 0x01, 0), // 250 kbaud
CAN_BR_TBL_ENTRY(0x05, 0x04, 0x07, 0x01, 0), // 500 kbaud
CAN_BR_TBL_ENTRY(0x03, 0x03, 0x07, 0x01, 0), // 800 kbaud
CAN_BR_TBL_ENTRY(0x02, 0x04, 0x07, 0x01, 0), // 1000 kbaud
CAN_BR_TBL_ENTRY(0x00, 0x00, 0x00, 0x00, 0), // Autobaud
};
//
// Macro fills baudrate register value depending on selected baudrate
// For a standard AT91 clock speed of 48 MHz we provide a pre calculated
// baudrate table. If the board uses another clock speed, then the platform
// HAL needs to provide an own HAL_AT91SAM7_GET_CAN_BR() macro that returns
// valid baudrate register values
//
#ifdef CYGNUM_HAL_ARM_AT91_CLOCK_SPEED_48000000
#define HAL_AT91SAM7_GET_CAN_BR(_baudrate_, _br_) \
CYG_MACRO_START \
_br_ = at91sam7_br_tbl[(_baudrate_) - CYGNUM_CAN_KBAUD_10]; \
CYG_MACRO_END
#endif
//===========================================================================
// PROTOTYPES
//===========================================================================
//--------------------------------------------------------------------------
// Device driver interface functions
//
static bool at91sam7_can_init(struct cyg_devtab_entry* devtab_entry);
static Cyg_ErrNo at91sam7_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name);
static Cyg_ErrNo at91sam7_can_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static Cyg_ErrNo at91sam7_can_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static bool at91sam7_can_putmsg(can_channel *priv, CYG_CAN_MSG_T *pmsg, void *pdata);
static bool at91sam7_can_getevent(can_channel *priv, CYG_CAN_EVENT_T *pevent, void *pdata);
static void at91sam7_can_start_xmit(can_channel* chan);
static void at91sam7_can_stop_xmit(can_channel* chan);
//--------------------------------------------------------------------------
// ISRs and DSRs
//
static cyg_uint32 at91sam7_can_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void at91sam7_can_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
//--------------------------------------------------------------------------
// Private utility functions
//
static bool at91sam7_can_config_channel(can_channel* chan, cyg_can_info_t* config, cyg_bool init);
static bool at91sam7_can_set_baud(can_channel *chan, cyg_can_baud_rate_t *baudrate);
static void at91sam7_can_mbox_config_rx_all(can_channel *chan);
static void at91sam7_can_setup_mbox(can_channel *chan, // channel
cyg_uint8 mbox, // message box number (0 -7)
cyg_uint32 mid, // message identifier
cyg_uint32 mam, // acceptance mask for this message box
cyg_uint32 rxtype); // RX or RX with overwrite are valid values
static void at91sam7_enter_lowpower_mode(can_channel *chan);
static void at91sam7_start_module(can_channel *chan);
static cyg_can_state at91sam7_get_state(at91sam7_can_info_t *info);
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
static void at91sam7_can_config_rx_none(can_channel *chan);
static Cyg_ErrNo at91sam7_can_set_config_msgbuf(can_channel *chan, cyg_can_msgbuf_cfg *buf);
#endif
//===========================================================================
// GENERIC CAN IO DATA INITIALISATION
//===========================================================================
CAN_LOWLEVEL_FUNS(at91sam7_can_lowlevel_funs,
at91sam7_can_putmsg,
at91sam7_can_getevent,
at91sam7_can_get_config,
at91sam7_can_set_config,
at91sam7_can_start_xmit,
at91sam7_can_stop_xmit
);
CYG_CAN_EVENT_T at91sam7_can0_rxbuf[CYGNUM_DEVS_CAN_AT91SAM7_CAN0_QUEUESIZE_RX]; // buffer for RX can events
CYG_CAN_MSG_T at91sam7_can0_txbuf[CYGNUM_DEVS_CAN_AT91SAM7_CAN0_QUEUESIZE_TX]; // buffer for TX can messages
CAN_CHANNEL_USING_INTERRUPTS(at91sam7_can0_chan,
at91sam7_can_lowlevel_funs,
at91sam7_can0_info,
CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_AT91SAM7_CAN0_KBAUD),
at91sam7_can0_txbuf, CYGNUM_DEVS_CAN_AT91SAM7_CAN0_QUEUESIZE_TX,
at91sam7_can0_rxbuf, CYGNUM_DEVS_CAN_AT91SAM7_CAN0_QUEUESIZE_RX
);
DEVTAB_ENTRY(at91sam7_can_devtab,
CYGPKG_DEVS_CAN_AT91SAM7_CAN0_NAME,
0, // Does not depend on a lower level interface
&cyg_io_can_devio,
at91sam7_can_init,
at91sam7_can_lookup, // CAN driver may need initializing
&at91sam7_can0_chan
);
//===========================================================================
// IMPLEMENTATION
//===========================================================================
//===========================================================================
/// First initialisation and reset of CAN modul.
//===========================================================================
static bool at91sam7_can_init(struct cyg_devtab_entry* devtab_entry)
{
can_channel *chan = (can_channel*)devtab_entry->priv;
at91sam7_can_info_t *info = (at91sam7_can_info_t *)chan->dev_priv;
#ifdef CYGDBG_IO_INIT
diag_printf("AT91 CAN init\n");
#endif
cyg_drv_interrupt_create(CAN_ISRVEC(info),
CAN_ISRPRIO(info), // Priority
(cyg_addrword_t)chan, // Data item passed to interrupt handler
at91sam7_can_ISR,
at91sam7_can_DSR,
&info->interrupt_handle,
&info->interrupt);
cyg_drv_interrupt_attach(info->interrupt_handle);
cyg_drv_interrupt_unmask(CAN_ISRVEC(info));
return at91sam7_can_config_channel(chan, &chan->config, true);
}
//===========================================================================
// Lookup the device and return its handle
//===========================================================================
static Cyg_ErrNo at91sam7_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name)
{
can_channel* chan = (can_channel*) (*tab)->priv;
CAN_DECLARE_INFO(chan);
chan->callbacks->can_init(chan);
HAL_WRITE_UINT32(CAN_IER(info), INT_DEFAULT); // enable wakeup and error interrupts
HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, 1 << CAN_PID(info)); // Enable the peripheral clock to the device
//
// It is important to setup the message buffer configuration after enabling the
// peripheral clock. This is nowhere documented in the at91sam7 hardware manual.
// If the message buffer configuration is set before the peripheral clock is
// enabled, then message buffers that receive extended frames might not work
// properly
//
at91sam7_can_mbox_config_rx_all(chan);
return ENOERR;
}
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Setup AT91SAM7 CAN module in a state where all message boxes are disabled
// After this callit is possible to add single message buffers and filters
//===========================================================================
static void at91sam7_can_config_rx_none(can_channel *chan)
{
at91sam7_can_info_t *info = (at91sam7_can_info_t *)chan->dev_priv;
cyg_uint8 i;
//
// setup all RX messages moxes into a disabled state and disable all
// interrupts - maybe we have to abort pending transfers before $$$$
//
HAL_WRITE_UINT32(CAN_IDR(info), INT_MB_RX);
for (i = 0; i < CAN_MBOX_RX_CNT; ++i)
{
HAL_WRITE_UINT32(CAN_MB_MMR(info, i), MMR_MB_TYPE_DISABLED); // first disable message box
}
info->free_mboxes = CAN_MBOX_RX_CNT;
info->rx_all = false;
}
//===========================================================================
// Add single message filter - setupm message box and enable interrupt
//===========================================================================
static void at91sam7_can_add_rx_filter(can_channel *chan, cyg_uint8 mbox, cyg_can_message *msg)
{
CAN_DECLARE_INFO(chan);
if (msg->ext)
{
at91sam7_can_setup_mbox(chan, mbox, MID_SET_EXT(msg->id), MAM_SET_EXT, MMR_MB_TYPE_RX);
}
else
{
at91sam7_can_setup_mbox(chan, mbox, MID_SET_STD(msg->id), MAM_SET_STD, MMR_MB_TYPE_RX);
}
HAL_WRITE_UINT32(CAN_IER(info), 0x01 << mbox);
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Allocate message box
// Try to find a free message box and return its ID
//===========================================================================
static cyg_int8 at91sam7_can_alloc_mbox(at91sam7_can_info_t *info)
{
cyg_uint8 i;
cyg_int8 res = CYGNUM_CAN_MSGBUF_NA;
if (info->free_mboxes)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -