基于STM32F4的CAN通信例程(库函数版)

发布时间:2023年12月17日

一、介绍

? 实现STM32的CAN通信的时候,我们无需关注本身复杂的CAN通信协议许多细枝末节的东西已经被厂商封装起来,我们可以先让程序跑起来,再通过调整各个参数来熟悉STM32中的CAN通信的原理。

? 实现CAN通信与实现SPI、I2C、串口等通信并无二致,都是先初始化引脚、片上外设、中断处理。然后在主函数中调用初始化,接着就可以当作通信使用,任何通信本质上都是接收receive)和发送(transmit).

? STM32中的CAN接收发送原型库函数。

uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
{
  uint8_t transmit_mailbox = 0;
  /* Check the parameters */
  assert_param(IS_CAN_ALL_PERIPH(CANx));
  assert_param(IS_CAN_IDTYPE(TxMessage->IDE));
  assert_param(IS_CAN_RTR(TxMessage->RTR));
  assert_param(IS_CAN_DLC(TxMessage->DLC));

  /* Select one empty transmit mailbox */
  if ((CANx->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
  {
    transmit_mailbox = 0;
  }
  else if ((CANx->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
  {
    transmit_mailbox = 1;
  }
  else if ((CANx->TSR&CAN_TSR_TME2) == CAN_TSR_TME2)
  {
    transmit_mailbox = 2;
  }
  else
  {
    transmit_mailbox = CAN_TxStatus_NoMailBox;
  }

  if (transmit_mailbox != CAN_TxStatus_NoMailBox)
  {
    /* Set up the Id */
    CANx->sTxMailBox[transmit_mailbox].TIR &= TMIDxR_TXRQ;
    if (TxMessage->IDE == CAN_Id_Standard)
    {
      assert_param(IS_CAN_STDID(TxMessage->StdId));  
      CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->StdId << 21) | \
                                                  TxMessage->RTR);
    }
    else
    {
      assert_param(IS_CAN_EXTID(TxMessage->ExtId));
      CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->ExtId << 3) | \
                                                  TxMessage->IDE | \
                                                  TxMessage->RTR);
    }
    
    /* Set up the DLC */
    TxMessage->DLC &= (uint8_t)0x0000000F;
    CANx->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0;
    CANx->sTxMailBox[transmit_mailbox].TDTR |= TxMessage->DLC;

    /* Set up the data field */
    CANx->sTxMailBox[transmit_mailbox].TDLR = (((uint32_t)TxMessage->Data[3] << 24) | 
                                             ((uint32_t)TxMessage->Data[2] << 16) |
                                             ((uint32_t)TxMessage->Data[1] << 8) | 
                                             ((uint32_t)TxMessage->Data[0]));
    CANx->sTxMailBox[transmit_mailbox].TDHR = (((uint32_t)TxMessage->Data[7] << 24) | 
                                             ((uint32_t)TxMessage->Data[6] << 16) |
                                             ((uint32_t)TxMessage->Data[5] << 8) |
                                             ((uint32_t)TxMessage->Data[4]));
    /* Request transmission */
    CANx->sTxMailBox[transmit_mailbox].TIR |= TMIDxR_TXRQ;
  }
  return transmit_mailbox;
}
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage)
{
  /* Check the parameters */
  assert_param(IS_CAN_ALL_PERIPH(CANx));
  assert_param(IS_CAN_FIFO(FIFONumber));
  /* Get the Id */
  RxMessage->IDE = (uint8_t)0x04 & CANx->sFIFOMailBox[FIFONumber].RIR;
  if (RxMessage->IDE == CAN_Id_Standard)
  {
    RxMessage->StdId = (uint32_t)0x000007FF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 21);
  }
  else
  {
    RxMessage->ExtId = (uint32_t)0x1FFFFFFF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 3);
  }
  
  RxMessage->RTR = (uint8_t)0x02 & CANx->sFIFOMailBox[FIFONumber].RIR;
  /* Get the DLC */
  RxMessage->DLC = (uint8_t)0x0F & CANx->sFIFOMailBox[FIFONumber].RDTR;
  /* Get the FMI */
  RxMessage->FMI = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDTR >> 8);
  /* Get the data field */
  RxMessage->Data[0] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDLR;
  RxMessage->Data[1] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 8);
  RxMessage->Data[2] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 16);
  RxMessage->Data[3] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 24);
  RxMessage->Data[4] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDHR;
  RxMessage->Data[5] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 8);
  RxMessage->Data[6] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 16);
  RxMessage->Data[7] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 24);
  /* Release the FIFO */
  /* Release FIFO0 */
  if (FIFONumber == CAN_FIFO0)
  {
    CANx->RF0R |= CAN_RF0R_RFOM0;
  }
  /* Release FIFO1 */
  else /* FIFONumber == CAN_FIFO1 */
  {
    CANx->RF1R |= CAN_RF1R_RFOM1;
  }
}

使用它们时就是初始化参数,赋值参数然后调用函数。将以上步骤封装为函数。

二、完整代码

can.h

#ifndef __STM32_CAN_H_
#define __STM32_CAN_H_

#include "stm32f4xx.h"


#define CAN_RX_INT_CONFIG 1 //配置CAN接收中断结构体

void CanTransPort_Init(void);

uint8_t sendMsgToCan(CanTxMsg *msg,uint8_t *data);//发送报文
uint16_t CanRecMsg(uint8_t *msg); 	//接收报文



#endif

?can.c

#include "stm32_can.h"
#include "usart.h"

CanRxMsg rxMessage;
CanTxMsg txMessage;

void CanTransPort_Init(void)
{
	GPIO_InitTypeDef 			 GPIO_InitStructure;		//GPIO物理接口
	CAN_InitTypeDef        CAN_InitStructure;	//CAN外设初始结构体	规定CAN通信速率 策略
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;	//CAN过滤器结构体 用于控制收发报文
	
#if CAN_RX_INT_CONFIG
	NVIC_InitTypeDef NVIC_InitStructure;
#endif
	//PD0 RX PD1 TX
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
	
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_CAN1);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_CAN1);
	
	//Configure CAN1 TX
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed=GPIO_Fast_Speed;
	
	GPIO_Init(GPIOD,&GPIO_InitStructure);
	
	//Configure CAN1 RX
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
	GPIO_Init(GPIOD,&GPIO_InitStructure);
	
	//CAN外设初始化
	CAN_DeInit(CAN1);
	CAN_StructInit(&CAN_InitStructure);
	
	CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;
	CAN_InitStructure.CAN_ABOM=ENABLE; //自动离线管理
	CAN_InitStructure.CAN_AWUM=ENABLE;	//自动唤醒
	//42MHZ / (1+4+2)/ 12 = 500kbps
	CAN_InitStructure.CAN_BS1=CAN_BS1_4tq;
	CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;
	CAN_InitStructure.CAN_SJW=CAN_SJW_2tq;
	CAN_InitStructure.CAN_Prescaler=12;
	CAN_InitStructure.CAN_RFLM=DISABLE; //锁定模式
	CAN_InitStructure.CAN_NART=DISABLE; //自动重传
	CAN_InitStructure.CAN_TTCM=DISABLE; //时间触发模式
	CAN_InitStructure.CAN_TXFP=DISABLE; //报文发送优先级判定方法
	
	CAN_Init(CAN1,&CAN_InitStructure);
	
	//CAN过滤器初始化
	CAN_FilterInitStructure.CAN_FilterNumber=0;
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FilterFIFO0; //安装过滤器
	CAN_FilterInitStructure.CAN_FilterIdHigh=0x00000000;
	CAN_FilterInitStructure.CAN_FilterIdLow=0x0000000;
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;	//过滤器使能
	
	CAN_FilterInit(&CAN_FilterInitStructure);
	
#if CAN_RX_INT_CONFIG
	NVIC_InitStructure.NVIC_IRQChannel=CAN1_RX0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x00;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);
#endif

}
uint8_t Can_ReceiveMsg(uint8_t *MsgBuffer)
{
	uint32_t i;
	CanRxMsg rxMsg;
	if(CAN_MessagePending(CAN1,CAN_FIFO0) == 0 ) return 0;
	CAN_Receive(CAN1,CAN_FIFO0,&rxMsg);
	for(i=0;i<8;i++)
	MsgBuffer[i] = rxMsg.Data[i];
	return rxMsg.DLC;
	
}

#if CAN_RX_INT_CONFIG

void CAN1_RX0_IRQHandler(void)
{	
	printf("enter CAN1_RX0_IRQHandler \r\n");
//	if(CAN_GetFlagStatus(CAN1,CAN_IT_FMP0) != RESET)		//添加中断会发送程序会死循环在中断里
//	{
			CAN_Receive(CAN1,CAN_FIFO0,&rxMessage);
			printf("Data:%s\r\n",rxMessage.Data);
			printf("ExtId:%d\r\n",rxMessage.ExtId);
//		CAN_ClearITPendingBit(CAN1,CAN_IT_FMP0);
//	}
	printf("exti CAN1_RX0_IRQHandler \r\n");
}

#endif

uint8_t sendMsgToCan(CanTxMsg *msg,uint8_t *data)
{
	uint16_t i = 0;
	uint8_t mailBox;
	msg->ExtId=0x1e2c;
	msg->IDE=CAN_Id_Extended;
	msg->RTR=CAN_RTR_Data;
	msg->DLC=8;
	
	for(i = 0;i<8;i++)
	{
		msg->Data[i] = data[i];
	}
	i=0;
	mailBox = CAN_Transmit(CAN1,msg);
	while(CAN_TransmitStatus(CAN1,mailBox) !=CAN_TxStatus_Ok && i <= 2000)
	{
		i++;
	}
	if(i >= 2000)
	{
		return 1;
	}
	else 
		return 0;
}


在主函数中使用CAN通信

main.c

#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "stm32_can.h"

extern CanRxMsg rxMessage;
extern CanTxMsg txMessage;

int main(void)
{	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uint8_t txBuf[8];
	CanTransPort_Init();
	usart2_init(115200);
	delay_init(168);
	uint8_t i = 0;
	uint8_t cnt = 0;
	txBuf[0]=0x0e;
	txBuf[1]=0x1c;
	txBuf[2]=0xee;
	txBuf[3]=0xb0;
	txBuf[4]=0xe7;
	txBuf[5]=0xa1;
	txBuf[6]=0x99;
	txBuf[7]=0x35;
	
	USART2_printf("传输开始!\r\n");
	
	while(1)
	{
//		CanSendMsg(txBuf,8,0x00000000);
		sendMsgToCan(&txMessage,txBuf);
		txBuf[i]++;
		i++;
		if(i%8 == 0)
		{
			i = 0;
			cnt++;
		}
		if(cnt%4 == 0)
		{
			cnt = 0;
			for(i=0;i<8;i++)
			{
				txBuf[i]-=0x1F;
			}
			i=0;
		}
		delay_ms(2000);
		
	}
}



三、实验设备:

usart 转 CAN USB模块

STM32 F407

can硬件电路

四、实验现象

五、发送特定报文

查阅Ascii码表?

发送:?48 65 6C 6C 6F 43 61 6E?

对应: H? e? ? ?l? ? l? ? ?o? ?C? ?a? n

实验现象:

上位机

串口接收

六、工程代码地址

ZzlYang/STM32F4_CAN_Example: STM32F407 can example with usart_can model (github.com)

文章来源:https://blog.csdn.net/qq_41044925/article/details/132753385
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。