blob: 1804b1ca0c19ac5f21105d5da1f51d7b5413d632 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2011, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/**
* \file
*
* Implementation of the CAN low level functions.
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "chip.h"
/*----------------------------------------------------------------------------
* Definitions
*----------------------------------------------------------------------------*/
/* CAN state */
#define CAN_DISABLED 0
#define CAN_HALTED 1
#define CAN_IDLE 2
#define CAN_SENDING 3
#define CAN_RECEIVING 4
/* MOT: Mailbox Object Type */
#define CAN_MOT_DISABLE 0 /* Mailbox is disabled */
#define CAN_MOT_RECEPT 1 /* Reception Mailbox */
#define CAN_MOT_RECEPT_OW 2 /* Reception mailbox with overwrite */
#define CAN_MOT_TRANSMIT 3 /* Transmit mailbox */
#define CAN_MOT_CONSUMER 4 /* Consumer mailbox */
#define CAN_MOT_PRODUCER 5 /* Producer mailbox */
/* CAN synchronisation state */
#define CAN_SYNC_NOK 0
#define CAN_SYNC_OK 1
/* CAN timeout for sync */
#define CAN_TIMEOUT 100000
/*----------------------------------------------------------------------------
* Local functions
*----------------------------------------------------------------------------*/
/**
* CAN Error Detection
*
* \param pCan Can peripheral
* \param dwStatus Status register content
*/
static void CAN_ErrorHandling( Can* pCan, uint32_t dwStatus )
{
if ( (dwStatus & CAN_SR_ERRA) == CAN_SR_ERRA )
{
TRACE_ERROR( "(CAN) CAN is in Error Active mode\n\r" ) ;
}
else
{
if ( (dwStatus & CAN_SR_ERRP) == CAN_SR_ERRP )
{
TRACE_ERROR( "(CAN) CAN is in Error Passive mode\n\r" ) ;
}
else
{
if ( (dwStatus & CAN_SR_BOFF) == CAN_SR_BOFF )
{
TRACE_ERROR( "(CAN) CAN is in Buff Off mode\n\r" ) ;
/* CAN reset */
TRACE_ERROR( "(CAN) CAN reset\n\r" ) ;
/* CAN Controller Disable */
pCan->CAN_MR &= ~CAN_MR_CANEN ;
/* CAN Controller Enable */
pCan->CAN_MR |= CAN_MR_CANEN ;
}
}
}
/* Error for Frame dataframe */
/* CRC error */
if ( (dwStatus & CAN_SR_CERR) == CAN_SR_CERR )
{
TRACE_ERROR( "(CAN) CRC Error\n\r" ) ;
}
else
{
/* Bit-stuffing error */
if ( (dwStatus & CAN_SR_SERR) == CAN_SR_SERR )
{
TRACE_ERROR( "(CAN) Stuffing Error\n\r" ) ;
}
else
{
/* Bit error */
if ( (dwStatus & CAN_SR_BERR) == CAN_SR_BERR )
{
TRACE_ERROR( "(CAN) Bit Error\n\r" ) ;
}
else
{
/* Form error */
if ( (dwStatus & CAN_SR_FERR) == CAN_SR_FERR )
{
TRACE_ERROR( "(CAN) Form Error\n\r" ) ;
}
else
{
/* Acknowledgment error */
if ( (dwStatus & CAN_SR_AERR) == CAN_SR_AERR )
{
TRACE_ERROR( "(CAN) Acknowledgment Error\n\r" ) ;
}
}
}
}
}
/* Error interrupt handler
* Represent the current status of the CAN bus and are not latched.
* See CAN, par. Error Interrupt Handler
* CAN_SR_WARN
* CAN_SR_ERRA
*/
}
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
/**
* Generic CAN Interrupt handler
*
* \param pCan CAN peripheral
* \param pTransfer Xfer parameters
*/
extern void CAN_Handler( Can* pCan, SCanTransfer* pTransfer )
{
uint32_t dwStatus ;
uint32_t dwMSR ;
uint32_t dwMid ;
uint32_t dwMessageMode ;
uint32_t dwMailbox ;
uint8_t ucState = CAN_DISABLED ;
ucState = pTransfer->state ;
dwStatus = (pCan->CAN_SR) ;
dwStatus &= (pCan->CAN_IMR) ;
pCan->CAN_IDR = dwStatus ;
TRACE_DEBUG( "CAN status=0x%X\n\r", status ) ;
if ( dwStatus & CAN_IDR_WAKEUP )
{
pTransfer->sync_can = CAN_SYNC_OK ;
pTransfer->state = CAN_IDLE ;
}
/* Mailbox event ? */
else
{
if ( (dwStatus&0x0000FFFF) != 0 )
{
TRACE_DEBUG( "Mailbox event\n\r" ) ;
/* Handle Mailbox interrupts */
for ( dwMailbox = 0 ; dwMailbox < CAN_MB_NUMBER ; dwMailbox++ )
{
dwMSR = pCan->CAN_MB[dwMailbox].CAN_MSR ;
if ( (dwMSR & CAN_MSR_MRDY) == CAN_MSR_MRDY )
{
/* Mailbox object type */
dwMessageMode = (pCan->CAN_MB[dwMailbox].CAN_MMR & CAN_MMR_MOT_Msk) >> CAN_MMR_MOT_Pos ;
TRACE_DEBUG( "message_mode 0x%X\n\r", dwMessageMode ) ;
TRACE_DEBUG( "dwMailbox 0x%X\n\r", dwMailbox ) ;
if ( dwMessageMode == 0 )
{
TRACE_ERROR( "Error in MOT\n\r" ) ;
}
else
{
if ( ( dwMessageMode == CAN_MOT_RECEPT ) || ( dwMessageMode == CAN_MOT_RECEPT_OW ) || ( dwMessageMode == CAN_MOT_PRODUCER ) )
{
TRACE_DEBUG( "Mailbox is in RECEPTION\n\r" ) ;
TRACE_DEBUG( "Length 0x%X\n\r", (can_msr>>16)&0xF ) ;
TRACE_DEBUG( "CAN Mailbox ID 0x%X\n\r", (pCan->CAN_MB[dwMailbox].dwMid & CAN_MID_MIDvA_Msk) >> CAN_MID_MIDvA_Pos ) ;
/* Read CAN data */
pTransfer->data_low_reg = pCan->CAN_MB[dwMailbox].CAN_MDL ;
pTransfer->data_high_reg = pCan->CAN_MB[dwMailbox].CAN_MDH ;
pTransfer->size = (pCan->CAN_MB[dwMailbox].CAN_MSR&CAN_MSR_MDLC_Msk)>>CAN_MSR_MDLC_Pos ;
pTransfer->mailbox_number = dwMailbox ;
dwMid = pCan->CAN_MB[dwMailbox].CAN_MID ;
if( (dwMid & CAN_MID_MIDE) == CAN_MID_MIDE )
{
pTransfer->identifier = dwMid & CAN_MID_MIDvA_Msk & CAN_MID_MIDvB_Msk;
}
else
{
pTransfer->identifier = (dwMid & CAN_MID_MIDvA_Msk) >> 18 ;
}
ucState = CAN_IDLE ;
/* Message Data has been received */
pCan->CAN_MB[dwMailbox].CAN_MCR = CAN_MCR_MTCR ;
}
else
{
TRACE_DEBUG( "Mailbox is in TRANSMIT\n\r" ) ;
TRACE_DEBUG( "Length 0x%X\n\r", (can_msr>>16)&0xF ) ;
ucState = CAN_IDLE ;
}
}
}
}
pTransfer->state = ucState ;
}
}
if ( (dwStatus & 0xFFCF0000) != 0 )
{
CAN_ErrorHandling( pCan, dwStatus ) ;
}
}
/**
* Configure the corresponding mailbox
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*/
extern void CAN_InitMailboxRegisters( Can* pCan, SCanTransfer* pTransfer )
{
CanMb* pCAN_Mailbox ;
uint32_t dwTemp;
pCAN_Mailbox = pCan->CAN_MB + pTransfer->mailbox_number ;
dwTemp = 1 << (pTransfer->mailbox_number) ;
pTransfer->mailbox_in_use |= dwTemp;
/* MailBox Control Register */
pCAN_Mailbox->CAN_MCR = 0x0 ;
/* MailBox Mode Register */
pCAN_Mailbox->CAN_MMR = 0x00 ;
/* CAN Message Acceptance Mask Register */
pCAN_Mailbox->CAN_MAM = pTransfer->acceptance_mask_reg ;
/* MailBox ID Register */
/* Disable the mailbox before writing to CAN_MIDx registers */
if( (pTransfer->identifier & CAN_MAM_MIDE) == CAN_MAM_MIDE )
{
/* Extended */
pCAN_Mailbox->CAN_MAM |= CAN_MAM_MIDE ;
}
else
{
pCAN_Mailbox->CAN_MAM &= ~CAN_MAM_MIDE ;
}
pCAN_Mailbox->CAN_MID = pTransfer->identifier;
/* MailBox Mode Register */
pCAN_Mailbox->CAN_MMR = pTransfer->mode_reg ;
/* MailBox Data Low Register */
pCAN_Mailbox->CAN_MDL = pTransfer->data_low_reg ;
/* MailBox Data High Register */
pCAN_Mailbox->CAN_MDH = pTransfer->data_high_reg ;
/* MailBox Control Register */
pCAN_Mailbox->CAN_MCR = pTransfer->control_reg ;
}
/**
* Reset the MBx
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*/
extern void CAN_ResetAllMailbox( Can* pCan, SCanTransfer* pTransfer)
{
uint32_t dw ;
if ( pTransfer != NULL )
{
CAN_TransferReset( pCan, pTransfer ) ;
for ( dw = 0 ; dw < CAN_MB_NUMBER ; dw++ )
{
pTransfer->mailbox_number = dw ;
pTransfer->mode_reg = CAN_MMR_MOT_MB_DISABLED ;
pTransfer->acceptance_mask_reg = 0x00000000 ;
pTransfer->identifier = 0x00000000 ;
pTransfer->data_low_reg = 0x00000000 ;
pTransfer->data_high_reg = 0x00000000 ;
pTransfer->control_reg = 0x00000000 ;
CAN_InitMailboxRegisters( pCan, pTransfer ) ;
}
}
}
/**
* CAN reset Transfer descriptor
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*/
extern void CAN_TransferReset( Can* pCan, SCanTransfer* pTransfer )
{
pCan = pCan; /* stop warning */
pTransfer->state = CAN_IDLE ;
pTransfer->mailbox_number = 0 ;
pTransfer->sync_can = 0 ;
pTransfer->mode_reg = 0 ;
pTransfer->acceptance_mask_reg = 0 ;
pTransfer->identifier = 0 ;
pTransfer->data_low_reg = 0x00000000 ;
pTransfer->data_high_reg = 0x00000000 ;
pTransfer->control_reg = 0x00000000 ;
pTransfer->mailbox_in_use = 0 ;
pTransfer->size = 0 ;
}
/**
* Wait for CAN synchronisation
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*
* \return 1 for good initialisation, otherwise 0
*/
static uint32_t CAN_Synchronisation( Can* pCan, SCanTransfer *pTransfer )
{
uint32_t dwTick = 0 ;
TRACE_INFO( "CAN_Synchronisation\n\r" ) ;
pTransfer->sync_can = CAN_SYNC_NOK ;
/* Enable CAN and Wait for WakeUp Interrupt */
pCan->CAN_IER = CAN_IER_WAKEUP ;
/* CAN Controller Enable */
pCan->CAN_MR = CAN_MR_CANEN ;
/* Enable Autobaud/Listen mode */
/* dangerous, CAN not answer in this mode */
while( (pTransfer->sync_can != CAN_SYNC_OK) && (dwTick < CAN_TIMEOUT) )
{
dwTick++ ;
}
if ( dwTick == CAN_TIMEOUT )
{
TRACE_ERROR( "CAN Initialisations FAILED\n\r" ) ;
return 0 ;
}
else
{
TRACE_INFO( "CAN Initialisations Completed\n\r" ) ;
}
return 1 ;
}
/**
* Write a CAN transfer
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*
* \return return CAN_STATUS_SUCCESS if command passed, otherwise
* return CAN_STATUS_LOCKED
*/
extern uint32_t CAN_Write( Can* pCan, SCanTransfer *pTransfer )
{
if ( pTransfer->state == CAN_RECEIVING )
{
pTransfer->state = CAN_IDLE ;
}
if ( pTransfer->state != CAN_IDLE )
{
return CAN_STATUS_LOCKED ;
}
TRACE_DEBUG( "CAN_Write\n\r" ) ;
pTransfer->state = CAN_SENDING ;
pCan->CAN_TCR = pTransfer->mailbox_in_use ;
pCan->CAN_IER = pTransfer->mailbox_in_use ;
return CAN_STATUS_SUCCESS ;
}
/**
* Read a CAN transfer
* \param pCan CAN peripheral
* \param pTransfer can transfer structure
*
* \return return CAN_STATUS_SUCCESS if command passed, otherwise
* return CAN_STATUS_LOCKED
*/
extern uint32_t CAN_Read( Can* pCan, SCanTransfer *pTransfer )
{
if ( pTransfer->state != CAN_IDLE )
{
return CAN_STATUS_LOCKED ;
}
TRACE_DEBUG( "CAN_Read\n\r" ) ;
pTransfer->state = CAN_RECEIVING ;
/* Enable interrupt */
pCan->CAN_IER = pTransfer->mailbox_in_use ;
return CAN_STATUS_SUCCESS ;
}
/**
* Test if CAN is in IDLE state
*
* \param pTransfer can transfer structure
*
* \return return 0 if CAN is in IDLE, otherwise return 1
*/
extern uint32_t CAN_IsInIdle( Can* pCan, SCanTransfer* pTransfer )
{
pCan = pCan; /* stop warning */
return ( pTransfer->state != CAN_IDLE ) ;
}
/**
* Disable CAN and enter in low power
*/
extern void CAN_Disable( Can* pCan )
{
/* Disable the interrupt on the interrupt controller */
if ( pCan == CAN0 )
{
NVIC_DisableIRQ( CAN0_IRQn ) ;
}
else
{
NVIC_DisableIRQ( CAN1_IRQn ) ;
}
/* Disable all IT */
pCan->CAN_IDR = 0x1FFFFFFF ;
/* Enable Low Power mode */
pCan->CAN_MR |= CAN_MR_LPM ;
/* Disable the CAN controller peripheral clock */
if ( pCan == CAN0 )
{
PMC_DisablePeripheral( ID_CAN0 ) ;
}
else
{
PMC_DisablePeripheral( ID_CAN1 ) ;
}
}
/**
* Baudrate processing
*
* \param pCan CAN base address
* \param baudrate Baudrate value (kB/s)
* allowed values: 1000, 800, 500, 250, 125, 50, 25, 10
*
* \return return 1 in success, otherwise return 0
*/
static uint32_t CAN_BaudRateCalculate( Can* pCan, uint32_t dwMCK, uint32_t dwBaudrate )
{
uint32_t BRP ;
uint32_t PROPAG ;
uint32_t PHASE1;
uint32_t PHASE2 ;
uint32_t SJW ;
uint32_t t1t2 ;
uint32_t dwTimeQuanta ;
pCan->CAN_BR = 0 ;
if ( dwBaudrate == 1000 )
{
dwTimeQuanta = 8 ;
}
else
{
dwTimeQuanta = 16 ;
}
BRP = (dwMCK / (dwBaudrate*1000*dwTimeQuanta))-1 ;
/*TRACE_DEBUG("BRP = 0x%X\n\r", BRP); */
/* timing Delay:
* Delay Bus Driver: 50 ns
* Delay Receiver: 30 ns
* Delay Bus Line (20m): 110 ns
*/
if ( (dwTimeQuanta*dwBaudrate*2*(50+30+110)/1000000) >= 1 )
{
PROPAG = (dwTimeQuanta*dwBaudrate*2*(50+30+110)/1000000)-1 ;
}
else
{
PROPAG = 0 ;
}
/*TRACE_DEBUG("PROPAG = 0x%X\n\r", PROPAG); */
t1t2 = dwTimeQuanta-1-(PROPAG+1) ;
/*TRACE_DEBUG("t1t2 = 0x%X\n\r", t1t2); */
if ( (t1t2 & 0x01) == 0x01 )
{
/* ODD */
/*TRACE_DEBUG("ODD\n\r"); */
PHASE1 = ((t1t2-1)/2)-1 ;
PHASE2 = PHASE1+1 ;
}
else
{
/* EVEN */
/*TRACE_DEBUG("EVEN\n\r"); */
PHASE1 = (t1t2/2)-1 ;
PHASE2 = PHASE1 ;
}
/*TRACE_DEBUG("PHASE1 = 0x%X\n\r", PHASE1); */
/*TRACE_DEBUG("PHASE2 = 0x%X\n\r", PHASE2); */
if ( 1 > (4/(PHASE1+1)) )
{
/*TRACE_DEBUG("4*Tcsc\n\r"); */
SJW = 3 ;
}
else
{
/*TRACE_DEBUG("Tphs1\n\r"); */
SJW = PHASE1 ;
}
/*TRACE_DEBUG("SJW = 0x%X\n\r", SJW); */
/* Verif */
if ( BRP == 0 )
{
TRACE_DEBUG( "BRP = 0 is not authorized\n\r" ) ;
return 0 ;
}
if ( (PROPAG + PHASE1 + PHASE2) != (dwTimeQuanta-4) )
{
TRACE_DEBUG( "Pb (PROPAG + PHASE1 + PHASE2) = %d\n\r", PROPAG + PHASE1 + PHASE2 ) ;
TRACE_DEBUG( "with TimeQuanta-4 = %d\n\r", dwTimeQuanta-4 ) ;
return 0 ;
}
pCan->CAN_BR = CAN_BR_PHASE2( PHASE2 ) | CAN_BR_PHASE1( PHASE1 ) | CAN_BR_PROPAG( PROPAG ) | CAN_BR_SJW( SJW ) | CAN_BR_BRP( BRP ) ;
return 1 ;
}
/**
* Init of the CAN peripheral
* \param baudrate Baudrate value (kB/s)
* allowed values: 1000, 800, 500, 250, 125, 50, 25, 10
* \param canTransfer0 CAN0 structure transfer
* \param canTransfer1 CAN1 structure transfer
* \return return 1 if CAN has good baudrate and CAN is synchronized,
* otherwise return 0
*/
extern uint32_t CAN_Init( Can* pCan, uint32_t dwMCK, uint32_t dwBaudrate, SCanTransfer *pTransfer)
{
/* Enable the CAN controller peripheral clock */
if ( pCan == CAN0 )
{
PMC_EnablePeripheral( ID_CAN0 ) ;
}
else
{
PMC_EnablePeripheral( ID_CAN1 ) ;
}
/* disable all IT */
pCan->CAN_IDR = 0x1FFFFFFF ;
/* Enable the interrupt on the interrupt controller */
NVIC_EnableIRQ( (pCan == CAN0) ? CAN0_IRQn : CAN1_IRQn ) ;
if ( CAN_BaudRateCalculate( pCan, dwMCK, dwBaudrate ) == 0 )
{
/* Baudrate problem */
TRACE_DEBUG( "Baudrate problem\r\n" ) ;
return 0 ;
}
CAN_ResetAllMailbox( pCan, pTransfer);
/* Enable the interrupt with all error cases */
pCan->CAN_IER = CAN_IER_CERR | /* (CAN) CRC Error */
CAN_IER_SERR | /* (CAN) Stuffing Error */
CAN_IER_BERR | /* (CAN) Bit Error */
CAN_IER_FERR | /* (CAN) Form Error */
CAN_IER_AERR ; /* (CAN) Acknowledgment Error */
/* Wait for CAN synchronisation */
if ( CAN_Synchronisation( pCan, pTransfer ) == 1 )
{
return 1u ;
}
else
{
return 0u ;
}
}