From 41e76064d6b940f5adb5be8a73a450f450c3e9fd Mon Sep 17 00:00:00 2001 From: florian-s Date: Tue, 3 Dec 2013 17:01:00 +0000 Subject: Added CAN driver for AT91SAM7. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@6536 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/AT91SAM7/can_lld.c | 640 +++++++++++++++++++++++++++++++++ os/hal/platforms/AT91SAM7/can_lld.h | 407 +++++++++++++++++++++ os/hal/platforms/AT91SAM7/platform.dox | 15 + os/hal/platforms/AT91SAM7/platform.mk | 1 + 4 files changed, 1063 insertions(+) create mode 100644 os/hal/platforms/AT91SAM7/can_lld.c create mode 100644 os/hal/platforms/AT91SAM7/can_lld.h (limited to 'os') diff --git a/os/hal/platforms/AT91SAM7/can_lld.c b/os/hal/platforms/AT91SAM7/can_lld.c new file mode 100644 index 000000000..38cc6c166 --- /dev/null +++ b/os/hal/platforms/AT91SAM7/can_lld.c @@ -0,0 +1,640 @@ +/* + ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,2011 Giovanni Di Sirio. + 2012,2013 Martin Schüßler and Florian Sehl, Embedded Software Laboratory, + RWTH Aachen University + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + --- + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes ChibiOS/RT, without being obliged to provide + the source code for any proprietary components. See the file exception.txt + for full details of how and when the exception can be applied. + */ + +/** + * @file AT91SAM7/can_lld.c + * @brief AT91SAM7 CAN Driver subsystem low level driver source. + * + * @pre - Make sure that the Mailbox you are receiving from is holding your + * data. + * - If you have more than one use the rxfull_event provided by the + * Driver. + * - In order to use the Events APIs the CH_USE_EVENTS option must + * be enabled in chconf.h. + * - In order to use the CAN driver the HAL_USE_CAN option must be + * enabled in halconf.h. + * - Mailbox0 is used as a Transmitmailbox. + * - Mailboxes 1-7 are used as receive Mailboxes. + * + * @addtogroup CAN + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_CAN || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief CAN driver identifier. + */ +#if AT91SAM7_CAN_USE_CAN || defined(__DOXYGEN__) +CANDriver CAND; +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/** + * @brief CAN[0] interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(CANIrqHandler) { + + CH_IRQ_PROLOGUE(); + can_lld_serve_interrupt(&CAND); + AT91C_BASE_AIC->AIC_EOICR = 0; + CH_IRQ_EPILOGUE(); +} + + +#if defined(__GNUC__) +__attribute__((noinline)) +#endif +/** + * @brief Handles CAN interrupts. + * + * @param[in] pointer to the driver that received the interrupt + */ + + +void can_lld_serve_interrupt(CANDriver *canp) { + + canstatus_t status; + chSysLockFromIsr(); + + status = canp->base->CAN_SR & canp->base->CAN_IMR; + + /* if AT91C_CAN_WAKEUP is set, the can test was successful + * such that the CAN is now able to Transmit and Receive Data. + */ + if ((status & AT91C_CAN_WAKEUP) == AT91C_CAN_WAKEUP) { + canp->base->CAN_IDR = AT91C_CAN_WAKEUP; + canp->testok = AT91C_TEST_OK; + } + + else { + /* check the mailboxes (MB0-MB7) and broadcast the corresponding event.*/ + + /* configure send mailbox, mailbox 0 */ + #if CAN_USE_MB0 + if(status & AT91C_CAN_MB0 && canp->mb[CAN_TxMB0-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + canp->base->CAN_IDR = AT91C_CAN_MB0; + chSemSignalI(&(canp->txsem)); + + } + #else + #warning You need to acivate Mailbox0 to transmit. + #endif + + #if CAN_USE_MB1 + if (status & AT91C_CAN_MB1 && + canp->mb[CAN_RxMB1-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB1-1)); + canp->base->CAN_IDR = AT91C_CAN_MB1; + } + #endif + + #if CAN_USE_MB2 + if (status & AT91C_CAN_MB2 && + canp->mb[CAN_RxMB2-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB2-1)); + canp->base->CAN_IDR = AT91C_CAN_MB2; + } + #endif + + #if CAN_USE_MB3 + if (status & AT91C_CAN_MB3 && + canp->mb[CAN_RxMB3-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB3-1)); + canp->base->CAN_IDR = AT91C_CAN_MB3; + } + #endif + + #if CAN_USE_MB4 + if (status & AT91C_CAN_MB4 && c + anp->mb[CAN_RxMB4-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB4-1)); + canp->base->CAN_IDR = AT91C_CAN_MB4; + } + #endif + + #if CAN_USE_MB5 + if (status & AT91C_CAN_MB5 && + canp->mb[CAN_RxMB5-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB5-1)); + canp->base->CAN_IDR = AT91C_CAN_MB5; + } + #endif + + #if CAN_USE_MB6 + if (status & AT91C_CAN_MB6 && + canp->mb[CAN_RxMB6-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event, EVENT_MASK (CAN_RxMB6-1)); + canp->base->CAN_IDR = AT91C_CAN_MB6; + } + #endif + + #if CAN_USE_MB7 + if (status & AT91C_CAN_MB7 && + canp->mb[CAN_RxMB7-1]->CAN_MB_MSR & AT91C_CAN_MRDY) { + chSemSignalI(&(canp->rxsem)); + chEvtBroadcastFlagsI(&(canp->rxfull_event), EVENT_MASK (CAN_RxMB7-1)); + canp->base->CAN_IDR = AT91C_CAN_MB7; + } + #endif + } + + /*check if error event is detected*/ + if ((status & 0xFFCF0000) != 0) { + /*The content of the SR register is copied unchanged in the upper + half word of the listener flags mask*/ + chEvtBroadcastFlagsI(&(canp->error_event), (eventmask_t)(status&0xFFFF0000)); + } + + chSysUnlockFromIsr(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level CAN driver initialization. + * + * @notapi + */ +void can_lld_init(void) { + /* Driver initialization.*/ + canObjectInit(&CAND); + + /* + * mailbox vector initialization + */ +#if CAN_USE_MB0 + CAND.mb[CAN_TxMB0-1] = AT91C_BASE_CAN_MB0; +#endif +#if CAN_USE_MB1 + CAND.mb[CAN_RxMB1-1] = AT91C_BASE_CAN_MB1; +#endif +#if CAN_USE_MB2 + CAND.mb[CAN_RxMB2-1] = AT91C_BASE_CAN_MB2; +#endif +#if CAN_USE_MB3 + CAND.mb[CAN_RxMB3-1] = AT91C_BASE_CAN_MB3; +#endif +#if CAN_USE_MB4 + CAND.mb[CAN_RxMB4-1] = AT91C_BASE_CAN_MB4; +#endif +#if CAN_USE_MB5 + CAND.mb[CAN_RxMB5-1] = AT91C_BASE_CAN_MB5; +#endif +#if CAN_USE_MB6 + CAND.mb[CAN_RxMB6-1] = AT91C_BASE_CAN_MB6; +#endif +#if CAN_USE_MB7 + CAND.mb[CAN_RxMB7-1] = AT91C_BASE_CAN_MB7; +#endif + + /* + * PIO + */ + /* Disable interrupts on the pin(s)*/ + AT91C_BASE_PIOA->PIO_IDR = AT91C_PA19_CANRX; + AT91C_BASE_PIOA->PIO_IDR = AT91C_PA20_CANTX; + /* Select peripheral function A*/ + AT91C_BASE_PIOA->PIO_ASR = AT91C_PA19_CANRX; + AT91C_BASE_PIOA->PIO_ASR = AT91C_PA20_CANTX; + /* Disables the PIO from controlling the corresponding pin + * (enables peripheral control of the pin) + */ + AT91C_BASE_PIOA->PIO_PDR = AT91C_PA19_CANRX; + AT91C_BASE_PIOA->PIO_PDR = AT91C_PA20_CANTX; + /* Disable pull up */ + AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA19_CANRX | AT91C_PA20_CANTX; + + /* Configure the AIC for CAN interrupts */ + AIC_ConfigureIT(AT91C_ID_CAN, AT91C_AIC_PRIOR_HIGHEST, CANIrqHandler); + CAND.base = AT91C_BASE_CAN; +} + +/** + * @brief configures and starts the CAN peripheral. + */ +static void can_startsequence(CANDriver *canp) { + canp->state = CAN_STARTING; + canp->testok = AT91C_TEST_NOK; + + /* clock */ + /* Enable clock for PIOA */ + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PIOA); + /* Enable the CAN controller peripheral clock */ + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_CAN); + + /* interrupts */ + + /* disable all interrupts */ + canp->base->CAN_IDR = 0x1FFFFFFF; + /* Enable the interrupt on the interrupt controller*/ + AIC_EnableIT(AT91C_ID_CAN); + + canp->base->CAN_BR = canp->config->br; + + + /* configure send mailbox, mailbox 0 */ + #if CAN_USE_MB0 + canp->mb[CAN_TxMB0-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_TxMB0-1]->CAN_MB_MMR = AT91C_CAN_MOT_TX | AT91C_CAN_PRIOR; + canp->mb[CAN_TxMB0-1]->CAN_MB_MAM = 0x00000000; + canp->mb[CAN_TxMB0-1]->CAN_MB_MID = AT91C_CAN_MIDE; + canp->mb[CAN_TxMB0-1]->CAN_MB_MDL = 0x11223344; + canp->mb[CAN_TxMB0-1]->CAN_MB_MDH = 0x01234567; + canp->mb[CAN_TxMB0-1]->CAN_MB_MCR = (AT91C_CAN_MDLC & (0x8 << 16)); + canp->mb[CAN_TxMB0-1]->CAN_MB_MMR = AT91C_CAN_MOT_TX | AT91C_CAN_PRIOR; + #else + #warning You need to acivate Mailbox0 to transmit. + #endif + + #if CAN_USE_MB1 + canp->mb[CAN_RxMB1-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB1-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB1-1]->CAN_MB_MAM = AT91C_CAN_MIDE + |(AT91C_CAN_MIDvB & canp->config->mb1_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb1_acceptance_mask); + canp->mb[CAN_RxMB1-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb1_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb1_can_id); + canp->mb[CAN_RxMB1-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB1-1]->CAN_MB_MDH = 0x00000000; + + #endif + #if CAN_USE_MB2 + canp->mb[CAN_RxMB2-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB2-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB2-1]->CAN_MB_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb2_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb2_acceptance_mask); + canp->mb[CAN_RxMB2-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb2_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb2_can_id); + canp->mb[CAN_RxMB2-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB2-1]->CAN_MB_MDH = 0x00000000; + + #endif + #if CAN_USE_MB3 + canp->mb[CAN_RxMB3-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB3-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB3-1]->CAN_MB_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb3_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb3_acceptance_mask); + canp->mb[CAN_RxMB3-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb3_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb3_can_id); + canp->mb[CAN_RxMB3-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB3-1]->CAN_MB_MDH = 0x00000000; + + #endif + #if CAN_USE_MB4 + canp->mb[CAN_RxMB4-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB4-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR;_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb4_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb4_acceptance_mask); + canp->mb[CAN_RxMB4-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb4_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb4_can_id); + canp->mb[CAN_RxMB4-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB4-1]->CAN_MB_MDH = 0x00000000; + + #endif + #if CAN_USE_MB5 + canp->mb[CAN_RxMB5-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB4-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB5-1]->CAN_MB_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb5_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb5_acceptance_mask); + canp->mb[CAN_RxMB5-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb5_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb5_can_id); + canp->mb[CAN_RxMB5-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB5-1]->CAN_MB_MDH = 0x00000000; + #endif + #if CAN_USE_MB6 + canp->mb[CAN_RxMB6-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB6-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB6-1]->CAN_MB_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb6_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb6_acceptance_mask); + canp->mb[CAN_RxMB6-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb6_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb6_can_id); + canp->mb[CAN_RxMB6-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB6-1]->CAN_MB_MDH = 0x00000000; + #endif + #if CAN_USE_MB7 + canp->mb[CAN_RxMB7-1]->CAN_MB_MCR = 0x0; + canp->mb[CAN_RxMB7-1]->CAN_MB_MMR = AT91C_CAN_MOT_RX | AT91C_CAN_PRIOR; + canp->mb[CAN_RxMB7-1]->CAN_MB_MAM = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb7_acceptance_mask) + | (AT91C_CAN_MIDvA & canp->config->mb7_acceptance_mask); + canp->mb[CAN_RxMB7-1]->CAN_MB_MID = AT91C_CAN_MIDE + | (AT91C_CAN_MIDvB & canp->config->mb7_can_id) + | (AT91C_CAN_MIDvA & canp->config->mb7_can_id); + canp->mb[CAN_RxMB7-1]->CAN_MB_MDL = 0x00000000; + canp->mb[CAN_RxMB7-1]->CAN_MB_MDH = 0x00000000; + #endif + /* Enable the interrupt with all error cases */ + canp->base->CAN_IER = AT91C_CAN_CERR /* (CAN) CRC Error */ + | AT91C_CAN_SERR /* (CAN) Stuffing Error */ + | AT91C_CAN_BERR /* (CAN) Bit Error */ + | AT91C_CAN_FERR /* (CAN) Form Error */ + | AT91C_CAN_AERR; /* (CAN) Acknowledgment Error */ + + /* Enable CAN and */ + canp->base->CAN_IER = AT91C_CAN_WAKEUP; + + /* CAN Controller Enable */ + canp->base->CAN_MR = AT91C_CAN_CANEN; +} + +/** + * @brief Configures and activates the CAN peripheral. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ + +void can_lld_start(CANDriver *canp) { + + /*start CAN*/ + can_startsequence(canp); + /* wait until wakeup. If no wakeup after 1 sec restart. */ + chThdSleepS(1); + while( (canp->testok) == AT91C_TEST_NOK) { + /* stop CAN */ + /* Disable all interrupts */ + canp->base->CAN_IDR = 0x1FFFFFFF; + /* Disable the interrupt on the interrupt controller */ + AIC_DisableIT(AT91C_ID_CAN); + /* Disable the CAN controller peripheral clock */ + AT91C_BASE_PMC->PMC_PCER = (0 << AT91C_ID_CAN); + /* restart CAN */ + can_startsequence(canp); + + chThdSleepS(1); + } + + /*enable Mailboxes */ +#if CAN_USE_MB1 + canp->base->CAN_IER = (0x1 << 1); +#endif +#if CAN_USE_MB2 + canp->base->CAN_IER = (0x1 << 2); +#endif +#if CAN_USE_MB3 + canp->base->CAN_IER = (0x1 << 3); +#endif +#if CAN_USE_MB4 + canp->base->CAN_IER = (0x1 << 4); +#endif +#if CAN_USE_MB5 + canp->base->CAN_IER = (0x1 << 5); +#endif +#if CAN_USE_MB6 + canp->base->CAN_IER = (0x1 << 6); +#endif +#if CAN_USE_MB7 + canp->base->CAN_IER = (0x1 << 7); +#endif + + +} + +/** + * @brief Deactivates the CAN peripheral. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_stop(CANDriver *canp) { + + /* If in ready state then disables the CAN peripheral.*/ + if (canp->state == CAN_READY) { + + /* Disable all interrupts */ + canp->base->CAN_IDR = 0x1FFFFFFF; + + /* Disable the interrupt on the interrupt controller */ + AIC_DisableIT(AT91C_ID_CAN); + + /* Disable the CAN controller peripheral clock */ + AT91C_BASE_PMC->PMC_PCER = (0 << AT91C_ID_CAN); + } +} + +/** + * @brief Determines whether a frame can be transmitted. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * @return The queue space availability. + * @retval FALSE no space in the transmit queue. + * @retval TRUE transmit slot available. + * + * @notapi + */ +bool_t can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox) { + + return ((canp->mb[CAN_TxMB0-1]->CAN_MB_MSR & AT91C_CAN_MRDY) != 0); +} + +/** + * @brief Send specified frame. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * @param[in] ctfp pointer to the CAN frame to be transmitted + * + * @notapi + */ +void can_lld_transmit(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp) { + while (!( canp->mb[CAN_TxMB0-1]->CAN_MB_MSR & AT91C_CAN_MRDY)){ + } +#if CAN_IDE_EXT + canp->mb[CAN_TxMB0-1]->CAN_MB_MID = + AT91C_CAN_MIDE | (AT91C_CAN_MIDvB & ctfp->EID) + | (AT91C_CAN_MIDvA & (ctfp->EID)); //configure the id +#else + canp->mb[CAN_TxMB0-1]->CAN_MB_MID = AT91C_CAN_MIDE | + (AT91C_CAN_MIDvB & 0x0) + | (AT91C_CAN_MIDvA & ctfp->SID); //configure the id + +#endif + + /* set length of data */ + canp->mb[CAN_TxMB0-1]->CAN_MB_MCR = (AT91C_CAN_MDLC & (ctfp->DLC << 16)); + + /* fill low register if 0DLC>0) + canp->mb[CAN_TxMB0-1]->CAN_MB_MDL = ctfp->data32[0]; + /* fill high register 4DLC>4) + canp->mb[CAN_TxMB0-1]->CAN_MB_MDH = ctfp->data32[1]; + canp->base->CAN_IER = 1 << CAN_TxMB0-1; + canp->base->CAN_TCR = 1 << 0; //send msg +} + +/** + * @brief check if some data is available mailbox. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * @return The queue space availability. + * @retval FALSE mailbox is empty + * @retval TRUE some data in the mailbox + * + * @notapi + */ +bool_t can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox) { + + if(mailbox == CAN_ANY_MAILBOX){ + /* get status of all receive mailboxes (Mailbox0 is transmit mailbox) */ + if((canp->base->CAN_SR & 0xFE) != 0){ + return TRUE; + } + } else{ + /* get status of specified mailbox */ + if((canp->base->CAN_SR & (1<<(mailbox-1))) != 0){ + /* data available */ + return TRUE; + } else{ + /* no data */ + return FALSE; + } + } +} + +/** + * @brief Receives a frame from the specified mailbox. + * + * @pre Make sure that the Mailbox you are receiving from is holding + * your data. If you have more than one use the rxfull_event + * provided by the Driver. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * @param[out] crfp pointer to the buffer where the CAN frame is copied + * + * @notapi + */ +void can_lld_receive(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp) { + if(mailbox != CAN_ANY_MAILBOX){ + /* get length of data */ + crfp->DLC = (canp->mb[mailbox-1]->CAN_MB_MSR & + AT91C_CAN_MDLC) >> 16; + /* store data */ + if(crfp->DLC>0){ + crfp->data32[0] = canp->mb[mailbox-1]->CAN_MB_MDL; + } + if(crfp->DLC>=4){ + crfp->data32[1] = canp->mb[mailbox-1]->CAN_MB_MDH; + } + } + else{ + /* TODO implement can_lld_receive for CAN_ANY_MAILBOX */ + } + /* clear register */ + canp->base->CAN_TCR = (0x1 << (mailbox-1)); + /* reenable interrupt */ + canp->base->CAN_IER = (0x1 << (mailbox-1)); +} + +#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__) +/** + * @brief Enters the sleep mode. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_sleep(CANDriver *canp) { + /* Wait till Tx Mailbox is ready */ + while(!((canp->mb[CAN_TxMB0-1]->CAN_MB_MSR & AT91C_CAN_MRDY) == AT91C_CAN_MRDY)){ + } + /* enable low power mode */ + canp->base->CAN_MR |= AT91C_CAN_LPM; + /* wait till CAN is in low power mode */ + while(!((canp->base->CAN_SR & AT91C_CAN_SLEEP) == AT91C_CAN_SLEEP)){ + } + /* disable Clock */ + AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_CAN); + +} + +/** + * @brief Enforces leaving the sleep mode. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_wakeup(CANDriver *canp) { + /* enable clock */ + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_CAN); + /* disable low power mode */ + canp->base->CAN_MR &= ~(AT91C_CAN_SLEEP); + while(!((canp->base->CAN_SR & AT91C_CAN_WAKEUP)==AT91C_CAN_WAKEUP)){ + } + +} +#endif /* CAN_USE_SLEEP_MODE */ + +#endif /* HAL_USE_CAN */ + +/** @} */ diff --git a/os/hal/platforms/AT91SAM7/can_lld.h b/os/hal/platforms/AT91SAM7/can_lld.h new file mode 100644 index 000000000..f9ea7f6c2 --- /dev/null +++ b/os/hal/platforms/AT91SAM7/can_lld.h @@ -0,0 +1,407 @@ +/* + ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,2011 Giovanni Di Sirio. + 2012,2013 Martin Schüßler and Florian Sehl, Embedded Software Laboratory, + RWTH Aachen University + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + --- + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes ChibiOS/RT, without being obliged to provide + the source code for any proprietary components. See the file exception.txt + for full details of how and when the exception can be applied. + */ + +/** + * @file AT91SAM7/can_lld.h + * @brief AT91SAM7 CAN Driver subsystem low level driver header. + * + * @pre - Make sure that the Mailbox you are receiving from is holding your + * data. + * - If you have more than one use the rxfull_event provided by the + * Driver. + * - In order to use the Events APIs the CH_USE_EVENTS option must + * be enabled in chconf.h. + * - In order to use the CAN driver the HAL_USE_CAN option must be + * enabled in halconf.h. + * - Mailbox0 is used as a Transmitmailbox. + * - Mailboxes 1-7 are used as receive Mailboxes. + * + * @addtogroup CAN + * @{ + */ + +#ifndef _CAN_LLD_H_ +#define _CAN_LLD_H_ + +#if HAL_USE_CAN || defined(__DOXYGEN__) + + + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief This switch defines whether the driver implementation supports + * a low power switch mode with automatic an wakeup feature. + */ +#define CAN_SUPPORTS_SLEEP TRUE + +/** + * @brief This implementation supports one transmit mailboxes. + */ +#define CAN_TX_MAILBOXES 1 + +/** + * @brief This implementation supports two receive mailboxes. + */ +#define CAN_RX_MAILBOXES 7 + +/** + * @brief CAN mailboxes (Mailbox0 should always enabled to transmit) + */ +#ifndef CAN_USE_MB0 +#define CAN_USE_MB0 TRUE +#endif +#ifndef CAN_USE_MB1 +#define CAN_USE_MB1 TRUE +#endif +#ifndef CAN_USE_MB2 +#define CAN_USE_MB2 TRUE +#endif +#ifndef CAN_USE_MB3 +#define CAN_USE_MB3 TRUE +#endif +#ifndef CAN_USE_MB4 +#define CAN_USE_MB4 FALSE +#endif +#ifndef CAN_USE_MB5 +#define CAN_USE_MB5 FALSE +#endif +#ifndef CAN_USE_MB6 +#define CAN_USE_MB6 FALSE +#endif +#ifndef CAN_USE_MB7 +#define CAN_USE_MB7 FALSE +#endif + + +#define CAN_IDE_STD FALSE /**< @brief Standard id. */ +#define CAN_IDE_EXT TRUE /**< @brief Extended id. */ + +#define CAN_RTR_DATA FALSE /**< @brief Data frame. */ +#define CAN_RTR_REMOTE TRUE /**< @brief Remote frame. */ + +/** + * @brief timeout until can_lld_start stops waiting + * on CAN and ends up in error state + */ +#define AT91C_CAN_TIMEOUT 100 + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief CAN driver enable switch. + * @details If set to @p TRUE the support for CAN0 is included. + * @note The default is @p TRUE. + */ +#if !defined(AT91SAM7_CAN_USE_CAN) || defined(__DOXYGEN__) +#define AT91SAM7_CAN_USE_CAN TRUE +#endif + +/** + * @brief CAN interrupt priority level setting. + */ +#if !defined(AT91SAM7_CAN_CAN_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define AT91SAM7_CAN_CAN_IRQ_PRIORITY 11 +#endif + +/** + * @brief Sleep mode related APIs inclusion switch. + * @note This switch is enforced to @p FALSE if the driver implementation + * does not support the sleep mode. + */ +#if CAN_SUPPORTS_SLEEP || defined(__DOXYGEN__) +#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) +#define CAN_USE_SLEEP_MODE TRUE +#endif +#else /* !CAN_SUPPORTS_SLEEP */ +#define CAN_USE_SLEEP_MODE FALSE +#endif /* !CAN_SUPPORTS_SLEEP */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if CAN_USE_SLEEP_MODE && !CAN_SUPPORTS_SLEEP +#error "CAN sleep mode not supported in this architecture" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a transmission mailbox index + */ +typedef uint32_t canmbx_t; + +/** + * wakeup status possible states + */ +typedef enum { + AT91C_TEST_NOK = 0, /**< waiting for wakeup event */ + AT91C_TEST_OK = 1 /**< wakeup event occured */ +} canteststate_t; + +/** + * @brief possible receive mailboxes. + * These Mailboxes get an offset to ensure that the Mailbox are not uninitialized. + * + */ +typedef enum { + CAN_TxMB0 = 1, /**< Transmit Mailbox. */ + CAN_RxMB1 = 2, /**< Receive Mailbox 1. */ + CAN_RxMB2 = 3, /**< Receive Mailbox 2. */ + CAN_RxMB3 = 4, /**< Receive Mailbox 3. */ + CAN_RxMB4 = 5, /**< Receive Mailbox 4. */ + CAN_RxMB5 = 6, /**< Receive Mailbox 5. */ + CAN_RxMB6 = 7, /**< Receive Mailbox 6. */ + CAN_RxMB7 = 8 /**< Receive Mailbox 7. */ +} canmailbox_t; + +/** + * @brief CAN status flags. + */ +typedef uint32_t canstatus_t; + +/** + * @brief CAN transmission frame. + * @note Accessing the frame data as word16 or word32 is not portable + * because machine data endianness, it can be still useful for a + * quick filling. + */ +typedef struct { + struct { + uint8_t DLC:4; /**< @brief Data length. */ + uint8_t RTR:1; /**< @brief Frame type. */ + uint8_t IDE:1; /**< @brief Identifier type. */ + }; + union { + struct { + uint32_t SID:11; /**< @brief Standard identifier.*/ + }; + struct { + uint32_t EID:29; /**< @brief Extended identifier.*/ + }; + }; + union { + uint8_t data8[8]; /**< @brief Frame data. */ + uint16_t data16[4]; /**< @brief Frame data. */ + uint32_t data32[2]; /**< @brief Frame data. */ + }; +}CANTxFrame; + +/** + * @brief CAN received frame. + * @note Accessing the frame data as word16 or word32 is not portable + * because machine data endianness, it can be still useful for a + * quick filling. + */ +typedef struct { + struct { + uint8_t DLC:4; /**< @brief Data length. */ + uint8_t RTR:1; /**< @brief Frame type. */ + uint8_t IDE:1; /**< @brief Identifier type. */ + }; + union { + struct { + uint32_t SID:11; /**< @brief Standard identifier.*/ + }; + struct { + uint32_t EID:29; /**< @brief Extended identifier.*/ + }; + }; + union { + uint8_t data8[8]; /**< @brief Frame data. */ + uint16_t data16[4]; /**< @brief Frame data. */ + uint32_t data32[2]; /**< @brief Frame data. */ + }; + canmailbox_t mbid; /**< @brief mailbox to read from */ +}CANRxFrame; + +/** + * @brief CAN filter. + * @note NOT used on this platform + */ +typedef struct { +}CANFilter; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + +#if CAN_USE_MB1 + uint32_t mb1_can_id; /**< @brief mailbox 1 can id */ + uint32_t mb1_acceptance_mask; /**< @brief mailbox 1 acceptance mask */ +#endif +#if CAN_USE_MB2 + uint32_t mb2_can_id; /**< @brief mailbox 2 can id */ + uint32_t mb2_acceptance_mask; /**< @brief mailbox 2 acceptance mask */ +#endif +#if CAN_USE_MB3 + uint32_t mb3_can_id; /**< @brief mailbox 3 can id */ + uint32_t mb3_acceptance_mask; /**< @brief mailbox 3 acceptance mask */ +#endif +#if CAN_USE_MB4 + uint32_t mb4_can_id; /**< @brief mailbox 4 can id */ + uint32_t mb4_acceptance_mask; /**< @brief mailbox 4 acceptance mask */ +#endif +#if CAN_USE_MB5 + uint32_t mb5_can_id; /**< @brief mailbox 5 can id */ + uint32_t mb5_acceptance_mask; /**< @brief mailbox 5 acceptance mask */ +#endif +#if CAN_USE_MB6 + uint32_t mb6_can_id; /**< @brief mailbox 6 can id */ + uint32_t mb6_acceptance_mask; /**< @brief mailbox 6 acceptance mask */ +#endif +#if CAN_USE_MB7 + uint32_t mb7_can_id; /**< @brief mailbox 7 can id */ + uint32_t mb7_acceptance_mask; /**< @brief mailbox 7 acceptance mask */ +#endif + /** + * @brief value of the BR register + * @note use 0x00050301 for 1MBit/s! + * + */ + uint32_t br; + +}CANConfig; + + +/** + * @brief Structure representing an CAN driver. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + /** + * @brief Driver state. + */ + canstate_t state; + /** + * @brief Current configuration data. + */ + const CANConfig *config; + /** + * @brief Transmission queue semaphore. + */ + Semaphore txsem; + /** + * @brief Receive queue semaphore. + */ + Semaphore rxsem; + /** + * @brief One frame for one specified mailbox become available. + * @note The number of the mailbox will be broadcasted to all listening threads. + * It is responsibility of the application(thread) to empty the specified mailbox + * by invoking @p chReceive() when listening to this event. + */ + EventSource rxfull_event; + /** + * @brief transmission slot become available. + */ + EventSource txempty_event; + /** + * @brief A CAN bus error happened. + */ + EventSource error_event; +#if CAN_USE_SLEEP_MODE || defined (__DOXYGEN__) + /** + * @brief Entering sleep state event. + */ + EventSource sleep_event; + /** + * @brief Exiting sleep state event. + */ + EventSource wakeup_event; +#endif /* CAN_USE_SLEEP_MODE */ + /* End of the mandatory fields.*/ + /** + * @brief Driver test OK (wakeup event occurred). + */ + volatile canteststate_t testok; + /** + * @brief Pointer to the Mailboxes registers block. + */ + AT91PS_CAN_MB mb[8]; + /** + * @brief Pointer to the CAN registers. + */ + AT91PS_CAN base; +}CANDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + /** + * @brief create event mask using enum of the corresponding mailbox. + */ +#define CAN_EVENT_MASK(mailbox) EVENT_MASK(mailbox-1) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if AT91SAM7_CAN_USE_CAN && !defined(__DOXYGEN__) +extern CANDriver CAND; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void can_lld_init(void); + void can_lld_serve_interrupt(CANDriver *canp); + void can_lld_start(CANDriver *canp); + void can_lld_stop(CANDriver *canp); + bool_t can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox); + void can_lld_transmit(CANDriver *canp, + canmbx_t mailbox, + const CANTxFrame *crfp); + bool_t can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox); + void can_lld_receive(CANDriver *canp, + canmbx_t mailbox, + CANRxFrame *ctfp); +#if CAN_USE_SLEEP_MODE + void can_lld_sleep(CANDriver *canp); + void can_lld_wakeup(CANDriver *canp); + +#endif /* CAN_USE_SLEEP_MODE */ +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_CAN */ + +#endif /* _CAN_LLD_H_ */ + +/** @} */ diff --git a/os/hal/platforms/AT91SAM7/platform.dox b/os/hal/platforms/AT91SAM7/platform.dox index b423bb461..ec810c211 100644 --- a/os/hal/platforms/AT91SAM7/platform.dox +++ b/os/hal/platforms/AT91SAM7/platform.dox @@ -134,3 +134,18 @@ * . * @ingroup AT91SAM7 */ + +/** + * @defgroup AT91SAM7_CAN AT91SAM7 CAN Support + * @details The CAN driver supports the AT91SAM7 CAN peripherals. + * + * @section at91sam7_can_1 Supported HW resources + * - CAN. + * . + * @section at91sam7_can_1 AT91SAM7 CAN driver implementation features + * - one send mailbox and seven receive mailboxes. + * - wakeup event on receive data. + * - deactivate unused mailbox to decrease memory usage and CPU usage + * . + * @ingroup AT91SAM7 + */ diff --git a/os/hal/platforms/AT91SAM7/platform.mk b/os/hal/platforms/AT91SAM7/platform.mk index 40a71afc9..6b8438c6d 100644 --- a/os/hal/platforms/AT91SAM7/platform.mk +++ b/os/hal/platforms/AT91SAM7/platform.mk @@ -8,6 +8,7 @@ PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/AT91SAM7/hal_lld.c \ ${CHIBIOS}/os/hal/platforms/AT91SAM7/spi_lld.c \ ${CHIBIOS}/os/hal/platforms/AT91SAM7/mac_lld.c \ ${CHIBIOS}/os/hal/platforms/AT91SAM7/pwm_lld.c \ + ${CHIBIOS}/os/hal/platforms/AT91SAM7/can_lld.c \ ${CHIBIOS}/os/hal/platforms/AT91SAM7/at91sam7_mii.c \ ${CHIBIOS}/os/hal/platforms/AT91SAM7/at91lib/aic.c -- cgit v1.2.3