0. Pin Map
On the STM32F446RET6 Nucleo board, the SPI1 is connected as below
1. Configure STM32
First, set the configuration from the Template Project .
Assuming system clock is configured to be 160 MHz .
In the left sidebar, select Connectivity -> CAN1 .
Enable Activated checkbox.
Set Bit Timings Parameters -> Prescaler to 32. Time Quantum should be 1000 ns.
Set Bit Timings Parameters -> Time Quanta in Bit Segment 1 to 3.
Set Bit Timings Parameters -> Time Quanta in Bit Segment 2 to 1.
This way, we get a 250 kbps CAN bus with a sampling point at 75% position.
We can also change the prescaler value to 16 to get a 500 kbps bus, or 8 to get a 1 Mbps bus.
The optimal sampling point is 87.5% , and we are pretty close to it.
This is a useful website for CAN bit-timing .
To debug the CAN bus, set Advanced Parameters -> Operating Mode to "Loopback".
Errata: When using loopback mode, the RX pin should still be pulled-up externally. We can do this simply by configuring the PA11 GPIO to enable the internal pull-up resistor, but please remember to do this. Otherwise, the HAL_CAN_Start()
method will timeout. Detailed information can be found here .
2. Code
First, add the code from the Template Project .
In main.c
, add the following code
Copy /* USER CODE BEGIN 2 */
uint8_t counter = 0 ;
uint32_t filter_id = 0 ;
uint32_t filter_mask = 0x 0 ;
CAN_FilterTypeDef filter_config;
filter_config.FilterBank = 0 ;
filter_config.FilterMode = CAN_FILTERMODE_IDMASK;
filter_config.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter_config.FilterIdHigh = filter_id << 5 ;
filter_config.FilterIdLow = 0 ;
filter_config.FilterMaskIdHigh = filter_mask << 5 ;
filter_config.FilterMaskIdLow = 0 ;
filter_config.FilterScale = CAN_FILTERSCALE_32BIT;
filter_config.FilterActivation = CAN_FILTER_ENABLE;
filter_config.SlaveStartFilterBank = 14 ;
HAL_CAN_ConfigFilter ( & hcan1 , & filter_config);
if ( HAL_CAN_Start ( & hcan1) != HAL_OK) {
while ( 1 )
HAL_UART_Transmit( & huart2 , ( uint8_t * ) "CAN init Error\r\n" , strlen( "CAN init Error\r\n" ) , 100 ) ;
}
HAL_Delay ( 2000 );
/* USER CODE END 2 */
Copy /* USER CODE BEGIN 3 */
// CAN TX
uint32_t tx_mailbox;
CAN_TxHeaderTypeDef tx_header;
tx_header.DLC = 4 ;
tx_header.IDE = CAN_ID_STD;
tx_header.RTR = CAN_RTR_DATA;
tx_header.StdId = 0x 00A ;
tx_header.TransmitGlobalTime = DISABLE;
uint8_t tx_data[ 8 ];
tx_data[ 0 ] = counter;
tx_data[ 1 ] = 0x 07 ;
tx_data[ 2 ] = 0x 08 ;
tx_data[ 3 ] = 0x 09 ;
if ( HAL_CAN_AddTxMessage ( & hcan1 , & tx_header , tx_data , & tx_mailbox) != HAL_OK) {
HAL_UART_Transmit( & huart2 , ( uint8_t * ) "CAN TX Error\r\n" , strlen( "CAN TX Error\r\n" ) , 100 ) ;
}
HAL_Delay ( 1 );
// CAN RX
uint32_t rx_fifo_level = HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) || HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO1);
char rx_level_str[ 50 ];
sprintf (rx_level_str , "level: %d \r\n" , rx_fifo_level);
HAL_UART_Transmit ( & huart2 , ( uint8_t * )rx_level_str , strlen (rx_level_str) , 100 );
if (rx_fifo_level > 0 ) {
HAL_UART_Transmit( & huart2 , ( uint8_t * ) "CAN msg pending\r\n" , strlen( "CAN msg pending\r\n" ) , 100 ) ;
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[ 8 ];
HAL_CAN_GetRxMessage( & hcan1 , CAN_RX_FIFO0 , & rx_header , rx_data) ;
char rx_data_str[ 32 ];
sprintf(rx_data_str , "receive data: %d \r\n" , rx_data[ 0 ]) ;
HAL_UART_Transmit( & huart2 , ( uint8_t * )rx_data_str , strlen(rx_data_str) , 100 ) ;
}
counter += 1 ;
HAL_Delay ( 100 );
}
/* USER CODE END 3 */
After saving, upload the code.
Detailed information on how CAN filter works can be found here .
3. Result
4. Using both CAN1 and CAN2
5. Interrupt
Copy HAL_CAN_ConfigFilter ( & hcan1 , & filter_config);
HAL_CAN_Start ( & hcan1);
HAL_CAN_ActivateNotification ( & hcan1 , CAN_IT_RX_FIFO0_MSG_PENDING);