STM32F4 - Std Periphals CAN BUS With CAN Interrupts

STM32F4 - Std Periphals CAN BUS With CAN Interrupts

Hi everyone, we are gonna talk about how to communicate with CAN BUS protocol with CAN INTERRUPTS on STM32F407 Discovery board and Standart Periphals Libraries.

Now lets get started. Firstly I want to talk about what CAN BUS is?
CAN is a communication protocol that connects all devices can communicate with each other. CAN stands for Controller Area Network. This technology developed by Bosch Automotive company to reduce cabling loads and make systems seperates from each other. Because if there is a problem in any sensor, this problem will harm all system. In can bus system all systems working individual and just communicate via CAN BUS. Debugging in CAN BUS is very easy, because every device has a unique id code and devices use this id number to communicate each other. CAN BUS can communicate with very high distances fast and secure.

Now SHOW TIME. Lets get started to code...

First we need to create some variables to Receive and send datas with CANBUS.

//CAN BUS device identity number
uint16_t canbus_device_id = 0xaf;

// Can bus Struct to Transmit data
CanTxMsg TxMessage;

// Can bus Struct to Receive data 
CanRxMsg RxMessage;

//we are going to use these variables to use CAN BUS data
char canbusData_id[2];
char canbusData_0 [2];
char canbusData_1 [2];
char canbusData_2 [2];
char canbusData_3 [2];
char canbusData_4 [2];
char canbusData_5 [2];
char canbusData_6 [2];
char canbusData_7 [2];

Now lets create CAN BUS data sender method. We will use the TxMessage Variable in here. We will set message id number, DLC and data packets. After setting these configurations we are going to transmit data to CAN bus.

Lets write this code:


void Can1WriteData( uint16_t canbus_device_id, 	uint16_t d0,	uint16_t d1,	uint16_t d2,	uint16_t d3,	uint16_t d4,	uint16_t d5,	uint16_t d6,	uint16_t d7){
	// MAX STD CAN BUS id value is 0x07ff (2047)   

	//Here we set the can bus device id
	TxMessage.StdId = canbus_device_id;
	//here we set the IDE and RTR data
	TxMessage.RTR 	= CAN_RTR_DATA;
	TxMessage.IDE 	= CAN_ID_STD;
	//Here we set the Content Count
	//This means how many content will be sent.
	TxMessage.DLC 	= 8;

	//Here we set the transmit packages
	TxMessage.Data[0] = d0;    
	TxMessage.Data[1] = d1;    
	TxMessage.Data[2] = d2;    
	TxMessage.Data[3] = d3;    
	TxMessage.Data[4] = d4;    
	TxMessage.Data[5] = d5;    
	TxMessage.Data[6] = d6;    
	TxMessage.Data[7] = d7;    
	
	//Here we are sending the TxMessage Struct.
	//When we sent this struct STM32F407 processor will create a package that includes all specifications above. 
	//And then load the can bus data bus line.
	CAN_Transmit(CAN1,&TxMessage);
}

We have created the sender method.

As I said begining of the article we will use the interrupts to receive CAN data. So now let me create method to get CAN bus data.and load it on the RxMessage struct. There is a __weak method named CAN1_RX0_IRQHandler in Std Periphal libraries. We will declare this function in our main.c file and set this as IRQ_Handler. First create method and load datas into struct. In main function we will create an NVIC and set this handler as interrupt function.

ATTENTION : NEVER CHANGE THE NAME OF FUNCTION. HEADER LIBRARY KNOW THIS INTERRUPT WITH THIS NAME.


void CAN1_RX0_IRQHandler(void)
{
   //THIS INTERRUPT LOCATED UNDER DIRECTLY ASSEMBLY
 //IT IS GOING TO WORK AFTER SET WITH NVIC HARDWARE
 //WHEN A DATA RECEIVED FROM CAN BUS DATA LINE THIS FUNCTION WILL RUN AUTOMATICALLY 

    //When data is ready to get firstly we need to read data
    //below function reads the data from canbus data line
    //and load fetched data inside RxMessage struct.
    CAN_Receive( CAN1, 0, &RxMessage);
 
 //now we can use all data from RxMessage
 //I have equalized the data from RxMessage to my variables.
 canbusData_id[0]   = RxMessage.StdId;
 canbusData_0[0]    = RxMessage.Data[0];
 canbusData_1[0] = RxMessage.Data[1];
 canbusData_2[0] = RxMessage.Data[2];
 canbusData_3[0] = RxMessage.Data[3];
 canbusData_4[0] = RxMessage.Data[4];
 canbusData_5[0] = RxMessage.Data[5];
 canbusData_6[0] = RxMessage.Data[6];
 canbusData_7[0] = RxMessage.Data[7]; 

//now we can use these datas in our main function.
//I have just selected the message data bu if you want to use other values
//you can find them inside RxMessage struct and fetch them from RxMessage Struct
 
}

Now just we need to prepare a NVIC to use CAN BUS interrupt. We will set the NVIC and CAN BUS in main function. Now lets get started to configure these configuration.

Here is the my main function. I will share the code first then explain it part by part.


int main()
{
 //Here we have enable the CAN1 RCC bus
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
 //here we enable the GPIO bus for pins
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
 
 //Hee we have configurad the pins to communicate via CAN BUS
 GPIO_InitTypeDef GPIO_InitStructure;
//important to configure pin mode as GPIO_Mode_AF
//so we can use the pins for Alternate Functions
 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
 GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
 GPIO_Init(GPIOD, &GPIO_InitStructure);
 
//Here we are setting the pins for CAN BUS alternate function
//this means now we can use the pins for can bus
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_CAN1);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_CAN1); 
 
 //Here we declerated our CAN bus variables
 CAN_InitTypeDef CAN_InitStructure;
 CAN_FilterInitTypeDef CAN_FilterInitStructure;
 
 
    CAN_DeInit(CAN1);
    CAN_StructInit(&CAN_InitStructure);

    /* CAN cell init */
    CAN_InitStructure.CAN_TTCM = DISABLE;
    CAN_InitStructure.CAN_ABOM = DISABLE;
    CAN_InitStructure.CAN_AWUM = DISABLE;
    CAN_InitStructure.CAN_NART = DISABLE;
    CAN_InitStructure.CAN_RFLM = DISABLE;
    CAN_InitStructure.CAN_TXFP = DISABLE;
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    // 42 / 2* ( 12 + 8 + 1 ) = 1MBaud 
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq;
    CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;
    CAN_InitStructure.CAN_Prescaler = 2; 
 
    CAN_Init(CAN1, &CAN_InitStructure);

    /* CAN filter init */
    CAN_FilterInitStructure.CAN_FilterNumber = 0;
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
    CAN_FilterInit(&CAN_FilterInitStructure);

    /* Enable FIFO 0 message pending Interrupt */
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
 
    NVIC_InitTypeDef  NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);


    while(1)
    {
       //write can bus data to the line
       Can1WriteData(0x01, 0x12,0x31,0x1A,0x00,0x00,0x00, 0x56 ,0x25 );
       //just wait for a moment. 
       while(65000--); 
    }
 
}

Lets get started to explain part by part...


 /* CAN cell init */
    CAN_InitStructure.CAN_TTCM = DISABLE;
    CAN_InitStructure.CAN_ABOM = DISABLE;
    CAN_InitStructure.CAN_AWUM = DISABLE;
    CAN_InitStructure.CAN_NART = DISABLE;
    CAN_InitStructure.CAN_RFLM = DISABLE;
    CAN_InitStructure.CAN_TXFP = DISABLE;

    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    // 42 / 2* ( 12 + 8 + 1 ) = 1MBaud 
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq;
    CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;
    CAN_InitStructure.CAN_Prescaler = 2; 
 
    CAN_Init(CAN1, &CAN_InitStructure);

In here we configured the can bus communication frequency as 1MBaud. APB1 bus has 42MHz. We are using to configure frequency we are using the SJW, BS1, BS2 and prescaler values.

Here is the equasition :
BusFrequency / Prescaler * ( BS1 + BS2 + SJW ) .

You can set the filter consigurations as you want. So I will not explain again the filter settings. Now I will explain the NVIC settings.


/* Enable FIFO 0 message pending Interrupt */
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
 
    NVIC_InitTypeDef  NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

First we activvated the FIFO for CAN BUS. FIFO stands for First In First Out. So the datas comes first will out first. We can do something the datas as serialized as received.

Now you are good to use the CAN BUS with STM32F4 in Standart Periphals. This is the end of the article.

Keep following my articles.

Have a nice coding.

Burak Hamdi TUFAN…


Tags


Share this Post

Send with Whatsapp

Post a Comment

Success! Your comment sent to post. It will be showed after confirmation.
Error! There was an error sending your comment. Check your inputs!

Comments

  • Thecodeprogram User
    Andrii

    I really appresiate your work. Thank you for such a great article!

    2022/03/25 11:26:12