embeddedsw/XilinxProcessorIPLib/drivers/canps/examples/xcanps_watermark_intr_example.c

801 lines
22 KiB
C
Raw Normal View History

/******************************************************************************
*
* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/****************************************************************************/
/**
*
* @file xcanps_watermark_intr_example.c
*
* This example shows how to use the CAN driver/device in interrupt mode using
* the Rx Watermark Interrupt.
*
*
* @note
* The Baud Rate Prescaler Register (BRPR) and Bit Timing Register (BTR)
* are setup such that CAN baud rate equals 40Kbps, assuming that the
* the CAN clock is 24MHz. The user needs to modify these values based on
* the desired bau rate and the CAN clock frequency. For more information
* see the CAN 2.0A, CAN 2.0B, ISO 11898-1 specifications.
*
* <pre>
*
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ----- -------- -----------------------------------------------
* 1.00a xd/sv 01/12/10 First release
*
*
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xparameters.h"
#include "xcanps.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xil_printf.h"
/************************** Constant Definitions *****************************/
/*
* The following constants map to the XPAR parameters created in the
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
#define CAN_DEVICE_ID XPAR_XCANPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define CAN_INTR_VEC_ID XPAR_XCANPS_0_INTR
/*
* Maximum CAN frame length in words.
*/
#define XCANPS_MAX_FRAME_SIZE_IN_WORDS (XCANPS_MAX_FRAME_SIZE / sizeof(u32))
#define FRAME_DATA_LENGTH 8 /* Frame Data field length */
#define TEST_THRESHOLD 25 /* This is Rx FIFO WaterMark Threshold */
/*
* Message Id Constant.
*/
#define TEST_MESSAGE_ID 2000
/*
* The Baud Rate Prescaler Register (BRPR) and Bit Timing Register (BTR)
* are setup such that CAN baud rate equals 40Kbps, assuming that the
* the CAN clock is 24MHz. The user needs to modify these values based on
* the desired baud rate and the CAN clock frequency. For more information
* see the CAN 2.0A, CAN 2.0B, ISO 11898-1 specifications.
*/
/*
* Timing parameters to be set in the Bit Timing Register (BTR).
* These values are for a 40 Kbps baudrate assuming the CAN input clock
* frequency is 24 MHz.
*/
#define TEST_BTR_SYNCJUMPWIDTH 3
#define TEST_BTR_SECOND_TIMESEGMENT 2
#define TEST_BTR_FIRST_TIMESEGMENT 15
/*
* The Baud rate Prescalar value in the Baud Rate Prescaler Register
* needs to be set based on the input clock frequency to the CAN core and
* the desired CAN baud rate.
* This value is for a 40 Kbps baudrate assuming the CAN input clock frequency
* is 24 MHz.
*/
#define TEST_BRPR_BAUD_PRESCALAR 29
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
int CanPsWatermarkIntrExample(XScuGic *IntcInstPtr,
XCanPs *CanInstPtr,
u16 CanDeviceId,
u16 CanIntrId);
static void Config(XCanPs *InstancePtr);
static void SendFrame(XCanPs *InstancePtr);
static int ReceiveData(XCanPs *InstancePtr);
static void SendHandler(void *CallBackRef);
static void RecvHandler(void *CallBackRef);
static void ErrorHandler(void *CallBackRef, u32 ErrorMask);
static void EventHandler(void *CallBackRef, u32 Mask);
static int SetupInterruptSystem(XScuGic *IntcInstancePtr,
XCanPs *CanInstancePtr,
u16 CanIntrId);
/************************** Variable Definitions *****************************/
static XCanPs CanInstance; /* Instance of the Can driver */
static XScuGic IntcInstance; /* Instance of the Interrupt Controller driver */
/*
* Buffers to hold frames to send and receive. These are declared as global so
* that they are not on the stack.
* These buffers need to be 32-bit aligned
*/
static u32 TxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS];
static u32 RxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS];
/*
* Shared variables used to test the callbacks.
*/
volatile static int LoopbackError; /* Asynchronous error occurred */
volatile static int RecvDone; /* Received a frame */
volatile static int SendDone; /* Frame was sent successfully */
static u8 TestDataOffset; /* Test Data value added to the CAN data */
/****************************************************************************/
/**
*
* This function is the main function of the Can Rx Watermark interrupt example.
*
* @param None.
*
* @return
* - XST_SUCCESS if the example has completed successfully.
* - XST_FAILURE if the example has failed.
*
* @note None
*
*****************************************************************************/
int main()
{
int Status;
xil_printf("CAN Watermark Example Test \r\n");
/*
* Run the Can Rx Watermark interrupt example.
*/
Status = CanPsWatermarkIntrExample(&IntcInstance, &CanInstance,
CAN_DEVICE_ID, CAN_INTR_VEC_ID);
if (Status != XST_SUCCESS) {
xil_printf("CAN Watermark Example Test Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran CAN Watermark Example Test\r\n");
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* The main entry point for showing the usage of XCanPs driver in interrupt
* mode. The example configures the device for internal loop back mode, then
* sends multiple CAN frames and receives the same number of CAN frame's
* using the Rx Watermark Interrupt.
*
* @param IntcInstPtr is a pointer to the instance of the ScuGic driver.
* @param CanInstPtr is a pointer to the instance of the CAN driver which
* is going to be connected to the interrupt controller.
* @param CanDeviceId is the device Id of the CAN device and is typically
* XPAR_<CANPS_instance>_DEVICE_ID value from xparameters.h.
* @param CanIntrId is the interrupt Id and is typically
* XPAR_<CANPS_instance>_INTR value from xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise driver-specific error code.
*
* @note If the device is not working correctly, this function may enter
* an infinite loop and will never return to the caller.
*
******************************************************************************/
int CanPsWatermarkIntrExample(XScuGic *IntcInstPtr, XCanPs *CanInstPtr,
u16 CanDeviceId, u16 CanIntrId)
{
int Status;
XCanPs_Config *ConfigPtr;
u32 Index;
/*
* Initialize the Can device.
*/
ConfigPtr = XCanPs_LookupConfig(CanDeviceId);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Status = XCanPs_CfgInitialize(CanInstPtr,
ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Run self-test on the device, which verifies basic sanity of the
* device and the driver.
*/
Status = XCanPs_SelfTest(CanInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Configure the CAN device.
*/
Config(CanInstPtr);
/*
* Set the interrupt handlers.
*/
XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_SEND,
(void *)SendHandler, (void *)CanInstPtr);
XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_RECV,
(void *)RecvHandler, (void *)CanInstPtr);
XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_ERROR,
(void *)ErrorHandler, (void *)CanInstPtr);
XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_EVENT,
(void *)EventHandler, (void *)CanInstPtr);
/*
* Initialize flags.
*/
SendDone = FALSE;
RecvDone = FALSE;
LoopbackError = FALSE;
/*
* Connect to the interrupt controller.
*/
Status = SetupInterruptSystem(IntcInstPtr,
CanInstPtr,
CanIntrId);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Enable all interrupts in CAN device.
*/
XCanPs_IntrEnable(CanInstPtr, XCANPS_IXR_ALL);
/*
* Disable the Receive FIFO Not Empty Interrupt and the
* New Message Received Interrupt.
*/
XCanPs_IntrDisable(CanInstPtr,
XCANPS_IXR_RXNEMP_MASK |
XCANPS_IXR_RXOK_MASK);
/*
* Enter Loop Back Mode.
*/
XCanPs_EnterMode(CanInstPtr, XCANPS_MODE_LOOPBACK);
while(XCanPs_GetMode(CanInstPtr) != XCANPS_MODE_LOOPBACK);
/*
* Send a number of frames.
*/
TestDataOffset = 1;
for (Index = 0; Index < TEST_THRESHOLD; Index++) {
SendFrame(CanInstPtr); /* Send a frame */
TestDataOffset++;
}
/*
* Wait here until both sending and reception have been completed.
*/
while ((SendDone < TEST_THRESHOLD) || (RecvDone != TRUE));
/*
* Check for errors found in the callbacks.
*/
if (LoopbackError == TRUE) {
return XST_FAILURE;
}
/*
* Read the Received Frames from the FIFO.
*/
TestDataOffset = 1;
Status = ReceiveData(CanInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Check for errors found in the callbacks.
*/
if (LoopbackError == TRUE) {
return XST_LOOPBACK_ERROR;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function configures CAN device. Baud Rate Prescaler Register (BRPR),
* Bit Timing Register (BTR) and RXFIFO Watermark Interrupt Register (RXWIR)
* are set in this function.
*
* @param InstancePtr is a pointer to the driver instance.
*
* @return None.
*
* @note If the CAN device is not working correctly, this function may
* enter an infinite loop and will never return to the caller.
*
******************************************************************************/
static void Config(XCanPs *InstancePtr)
{
/*
* Enter Configuration Mode if the device is not currently in
* Configuration Mode.
*/
XCanPs_EnterMode(InstancePtr, XCANPS_MODE_CONFIG);
while(XCanPs_GetMode(InstancePtr) != XCANPS_MODE_CONFIG);
/*
* Setup Baud Rate Prescaler Register (BRPR) and
* Bit Timing Register (BTR) .
*/
XCanPs_SetBaudRatePrescaler(InstancePtr, TEST_BRPR_BAUD_PRESCALAR);
XCanPs_SetBitTiming(InstancePtr, TEST_BTR_SYNCJUMPWIDTH,
TEST_BTR_SECOND_TIMESEGMENT,
TEST_BTR_FIRST_TIMESEGMENT);
/*
* Set the threshold value for the Rx FIFO Watermark interrupt.
*/
XCanPs_SetRxIntrWatermark(InstancePtr, TEST_THRESHOLD - 1);
}
/*****************************************************************************/
/**
*
* Send a CAN frame.
*
* @param InstancePtr is a pointer to the driver instance.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void SendFrame(XCanPs *InstancePtr)
{
u8 *FramePtr;
int Index;
int Status;
/*
* Create correct values for Identifier and Data Length Code Register.
*/
TxFrame[0] = (u32)XCanPs_CreateIdValue((u32)TEST_MESSAGE_ID, 0, 0, 0, 0);
TxFrame[1] = (u32)XCanPs_CreateDlcValue((u32)FRAME_DATA_LENGTH);
/*
* Now fill in the data field with known values so we can verify them
* on receive.
*/
FramePtr = (u8 *)(&TxFrame[2]);
for (Index = 0; Index < FRAME_DATA_LENGTH; Index++) {
*FramePtr++ = ((u8)Index + TestDataOffset);
}
/*
* Now wait until the TX FIFO is not full and send the frame.
*/
while (XCanPs_IsTxFifoFull(InstancePtr) == TRUE);
Status = XCanPs_Send(InstancePtr, TxFrame);
if (Status != XST_SUCCESS) {
/*
* The frame could not be sent successfully.
*/
LoopbackError = TRUE;
SendDone = TEST_THRESHOLD;
RecvDone = TRUE;
}
}
/*****************************************************************************/
/**
*
* Read the Received CAN frames from the FIFO.
*
* @param InstancePtr is a pointer to the driver instance.
*
* @return
* - XST_SUCCESS if all the CAN Frames are received and the
* data is the same as that was sent.
* - XST_FAILURE if the required number of CAN frames have not
* been received or if the Received Data is not the same as the
* data that was sent.
*
* @note None.
*
******************************************************************************/
static int ReceiveData(XCanPs *InstancePtr)
{
int Status;
int Index;
u8 *FramePtr;
u8 NumRxFrames;
/*
* Initialize the number of received frames to Zero.
*/
NumRxFrames = 0;
/*
* Read the received CAN Frames from the FIFO till the FIFO is Empty.
*/
while (XCanPs_IntrGetStatus(InstancePtr) & XCANPS_IXR_RXNEMP_MASK) {
Status = XCanPs_Recv(InstancePtr, RxFrame);
if (Status != XST_SUCCESS) {
LoopbackError = TRUE;
return XST_FAILURE;
}
/*
* Verify Identifier and Data Length Code.
*/
if (RxFrame[0] !=
(u32)XCanPs_CreateIdValue((u32)TEST_MESSAGE_ID, 0, 0, 0, 0)) {
LoopbackError = TRUE;
return XST_FAILURE;
}
if ((RxFrame[1] & ~XCANPS_DLCR_TIMESTAMP_MASK) != TxFrame[1]) {
LoopbackError = TRUE;
return XST_FAILURE;
}
/*
* Verify Data field contents.
*/
FramePtr = (u8 *)(&RxFrame[2]);
for (Index = 0; Index < FRAME_DATA_LENGTH; Index++) {
if (*FramePtr++ != ((u8)Index + TestDataOffset)) {
LoopbackError = TRUE;
return XST_FAILURE;
}
}
/*
* Increment the number of frames received.
*/
TestDataOffset++;
NumRxFrames++;
}
if (NumRxFrames == TEST_THRESHOLD) {
LoopbackError = FALSE;
return XST_SUCCESS;
}
return XST_FAILURE;
}
/*****************************************************************************/
/**
*
* Callback function (called from interrupt handler) to handle confirmation of
* transmit events when in interrupt mode.
*
* @param CallBackRef is the callback reference passed from the interrupt
* handler, which in our case is a pointer to the driver instance.
*
* @return None.
*
* @note This function is called by the driver within interrupt context.
*
******************************************************************************/
static void SendHandler(void *CallBackRef)
{
/*
* The frame was sent successfully. Notify the task context.
*/
SendDone++;
}
/*****************************************************************************/
/**
*
* Callback function (called from interrupt handler) to handle frames received in
* interrupt mode. This function is called once all the frames are received.
*
* @param CallBackRef is the callback reference passed from the interrupt
* handler, which in our case is a pointer to the device instance.
*
* @return None.
*
* @note This function is called by the driver within interrupt context.
*
******************************************************************************/
static void RecvHandler(void *CallBackRef)
{
XCanPs *CanInstPtr = (XCanPs *)CallBackRef;
/*
* The RX FIFO is Full to the Watermark level specified.
*/
XCanPs_IntrDisable(CanInstPtr, XCANPS_IXR_RXFWMFLL_MASK);
RecvDone = TRUE;
}
/*****************************************************************************/
/**
*
* Callback function (called from interrupt handler) to handle error interrupt.
* Error code read from Error Status register is passed into this function
*
* @param CallBackRef is the callback reference passed from the interrupt
* handler, which in our case is a pointer to the driver instance.
* @param ErrorMask is a bit mask indicating the cause of the error.
* Its value equals 'OR'ing one or more XCANPS_ESR_* defined in
* xcanps_hw.h
*
* @return None.
*
* @note This function is called by the driver within interrupt context.
*
******************************************************************************/
static void ErrorHandler(void *CallBackRef, u32 ErrorMask)
{
if(ErrorMask & XCANPS_ESR_ACKER_MASK) {
/*
* ACK Error handling code should be put here.
*/
}
if(ErrorMask & XCANPS_ESR_BERR_MASK) {
/*
* Bit Error handling code should be put here.
*/
}
if(ErrorMask & XCANPS_ESR_STER_MASK) {
/*
* Stuff Error handling code should be put here.
*/
}
if(ErrorMask & XCANPS_ESR_FMER_MASK) {
/*
* Form Error handling code should be put here.
*/
}
if(ErrorMask & XCANPS_ESR_CRCER_MASK) {
/*
* CRC Error handling code should be put here.
*/
}
/*
* Set the shared variables.
*/
LoopbackError = TRUE;
RecvDone = TRUE;
SendDone = TEST_THRESHOLD;
}
/*****************************************************************************/
/**
*
* Callback function (called from interrupt handler) to handle the following
* interrupts:
* - XCANPS_IXR_BSOFF_MASK: Bus Off Interrupt
* - XCANPS_IXR_RXOFLW_MASK: RX FIFO Overflow Interrupt
* - XCANPS_IXR_RXUFLW_MASK: RX FIFO Underflow Interrupt
* - XCANPS_IXR_TXBFLL_MASK: TX High Priority Buffer Full Interrupt
* - XCANPS_IXR_TXFLL_MASK: TX FIFO Full Interrupt
* - XCANPS_IXR_WKUP_MASK: Wake up Interrupt
* - XCANPS_IXR_SLP_MASK: Sleep Interrupt
* - XCANPS_IXR_ARBLST_MASK: Arbitration Lost Interrupt
*
*
* @param CallBackRef is the callback reference passed from the
* interrupt Handler, which in our case is a pointer to the
* driver instance.
* @param IntrMask is a bit mask indicating pending interrupts.
* Its value equals 'OR'ing one or more of the XCANPS_IXR_*_MASK
* value(s) mentioned above.
*
* @return None.
*
* @note This function is called by the driver within interrupt context.
* This function needs to be changed to meet specific application
* needs.
*
******************************************************************************/
static void EventHandler(void *CallBackRef, u32 IntrMask)
{
XCanPs *CanPtr = (XCanPs *)CallBackRef;
if (IntrMask & XCANPS_IXR_BSOFF_MASK) {
/*
* Entering Bus off status interrupt requires
* the CAN device be reset and reconfigured.
*/
XCanPs_Reset(CanPtr);
Config(CanPtr);
return;
}
if(IntrMask & XCANPS_IXR_RXOFLW_MASK) {
/*
* Code to handle RX FIFO Overflow
* Interrupt should be put here.
*/
}
if(IntrMask & XCANPS_IXR_RXUFLW_MASK) {
/*
* Code to handle RX FIFO Underflow
* Interrupt should be put here.
*/
}
if(IntrMask & XCANPS_IXR_TXBFLL_MASK) {
/*
* Code to handle TX High Priority Buffer Full
* Interrupt should be put here.
*/
}
if(IntrMask & XCANPS_IXR_TXFLL_MASK) {
/*
* Code to handle TX FIFO Full
* Interrupt should be put here.
*/
}
if (IntrMask & XCANPS_IXR_WKUP_MASK) {
/*
* Code to handle Wake up from sleep mode
* Interrupt should be put here.
*/
}
if (IntrMask & XCANPS_IXR_SLP_MASK) {
/*
* Code to handle Enter sleep mode
* Interrupt should be put here.
*/
}
if (IntrMask & XCANPS_IXR_ARBLST_MASK) {
/*
* Code to handle Lost bus arbitration
* Interrupt should be put here.
*/
}
}
/*****************************************************************************/
/**
*
* This function sets up the interrupt system so interrupts can occur for the
* CAN. This function is application-specific since the actual system may or
* may not have an interrupt controller. The CAN could be directly connected
* to a processor without an interrupt controller. The user should modify this
* function to fit the application.
*
* @param IntcInstancePtr is a pointer to the instance of ScuGic driver.
* @param CanInstancePtr contains a pointer to the instance of the CAN
* which is going to be connected to the interrupt controller.
* @param CanIntrId is the interrupt Id and is typically
* XPAR_<CANPS_instance>_INTR value from xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note None.
*
****************************************************************************/
static int SetupInterruptSystem(XScuGic *IntcInstancePtr,
XCanPs *CanInstancePtr,
u16 CanIntrId)
{
int Status;
#ifndef TESTAPP_GEN
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
Xil_ExceptionInit();
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
#endif
/*
* Connect the device driver handler that will be called when an
* interrupt for the device occurs, the handler defined above performs
* the specific interrupt processing for the device.
*/
Status = XScuGic_Connect(IntcInstancePtr, CanIntrId,
(Xil_InterruptHandler)XCanPs_IntrHandler,
(void *)CanInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Enable the interrupt for the CAN device.
*/
XScuGic_Enable(IntcInstancePtr, CanIntrId);
#ifndef TESTAPP_GEN
/*
* Enable interrupts in the Processor.
*/
Xil_ExceptionEnable();
#endif
return XST_SUCCESS;
}