STM32 CAN 单个设备回环通信及三个设备互相通信

发布于:2025-06-28 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、基本流程

基础配置分为三大块:

1.CAN外设的初始化

  •     RCC时钟初始化(GPIO时钟和CAN1的时钟)
  •     GPIO初始化(CAN_TX复用推挽输出模式,CAN_RX上拉输入模式)
  •     整个CAN外设的初始化(结构体配置,模式,波特率,各种其余小功能......)
  •     单独对过滤器初始化(结构体配置,过滤器位宽、模式、R1 R2的值、FIFO关联、使能的参数)
  •    最后若是使用中断再加上ITConfig函数使能中断输出,再加上NVIC和中断函数

2.发送报文

库函数专门封装了一个发送结构体和发送函数,将报文数据写入发送结构体,再调用发送函数就行了

3.接收报文

库函数也有检查接收FIFO状态的函数,若是FIFO里面有报文,就调用接收函数,这样报文数据就会存入到接收的结构体当中。读取结构体就能获取接收到的数据了

二、函数介绍

/*  恢复缺省配置,把CAN配置到默认的复位状态 *****/ 
void CAN_DeInit(CAN_TypeDef* CANx);


/* 初始化和配置函数 *********************************/ 
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);//CAN初始化
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);//过滤器初始化
void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);//赋一个默认值
void CAN_SlaveStartBank(uint8_t CAN_BankNumber); //配置CAN2的起始滤波器号,互联型设备用的
void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState);//调试时的冻结模式
void CAN_TTComModeCmd(CAN_TypeDef* CANx, FunctionalState NewState);//用于使能TTCM模式中的TGT位

/* 发送相关函数 *********************************************************/
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);//发送一个CAN报文,返回值表示报文存入了哪个邮箱
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);//获取发送邮箱的状态
void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox);//取消发送

/* 接收相关函数 **********************************************************/
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);//读取接收FIFO数据
void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber);//单独释放FIFO
uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber);//获取指定FIFO队列里排队的报文数目


/* 工作模式相关函数(了解) **************************************************/
uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode);//工作模式请求
uint8_t CAN_Sleep(CAN_TypeDef* CANx);//直接指定CAN进入睡眠模式
uint8_t CAN_WakeUp(CAN_TypeDef* CANx);//直接指定CAN退出睡眠模式

/* 错误管理相关函数(了解) *************************************************/
uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx);//获取最近一次的错误码
uint8_t CAN_GetReceiveErrorCounter(CAN_TypeDef* CANx);//获取接收错误计数器REC
uint8_t CAN_GetLSBTransmitErrorCounter(CAN_TypeDef* CANx);//获取发送错误计时器的低8位TEC

/* 中断和标志位管理函数 **********************************/
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState);//用于中断输出使能
FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG);//获取标志位状态
void CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG);//清除标志位
ITStatus CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT);//获取中断状态
void CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT);//清除中断挂起位

三、整体代码(主要在于备注)

MyCAN.c:

#include "stm32f10x.h"                  // Device header

void MyCAN_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	CAN_InitTypeDef CAN_InitStructure;
	CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//设置CAN外设的测试模式
	       /*
		#define CAN_Mode_Normal             ((uint8_t)0x00)  正常模式
		#define CAN_Mode_LoopBack           ((uint8_t)0x01)  环回模式
		#define CAN_Mode_Silent             ((uint8_t)0x02)  静默模式
		#define CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  环回静默模式
	*/
	
	//这里若是改成normal,则就是3个设备互相通信代码
	
	CAN_InitStructure.CAN_Prescaler = 48;		//分频系数  
	//波特率 = 36M / 48(分频系数) / (1 + 2(BS1tq数) + 3(BS2tq数)) = 125K
	//高速CAN波特率范围是125k~1M
	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
	CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
	CAN_InitStructure.CAN_NART = DISABLE;//置0,自动重传
	CAN_InitStructure.CAN_TXFP = DISABLE;//置0,优先级由报文标识符来决定
	CAN_InitStructure.CAN_RFLM = DISABLE;//置0,禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖
	CAN_InitStructure.CAN_AWUM = DISABLE;//置0,手动唤醒,软件清零SLEEP,唤醒CAN外设
	CAN_InitStructure.CAN_TTCM = DISABLE;//置0,关闭时间触发通信功能
	                                   //时间触发(每个节点只在一个固定的时间段内发送报文,可以避免优先级仲裁。)
	CAN_InitStructure.CAN_ABOM = DISABLE;//离线自动关闭,1:自动恢复  0:手动恢复
	CAN_Init(CAN1, &CAN_InitStructure);
	
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterNumber = 0;//指定第几个过滤器被初始化(范围0~13)
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
	//16位列表模式,4个参数分别存入一组参数即可
	//16位屏蔽模式,CAN_FilterIdHigh存入第一组ID,
	//             CAN_FilterMaskIdHigh存入对应的屏蔽位
	//             CAN_FilterIdHigh存入第二组ID,
	//             CAN_FilterMaskIdLow存入对应的屏蔽位
	//32位列表模式,IdHign和IdLow组合存到一起,存入第一组32位ID
	//             CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow组合存到一起,存入第一组32位ID
	//32位屏蔽模式,IdHign和IdLow组合存到一起,存入32位ID
	//             CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow组合存到一起,存入对应的屏蔽位
	
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//选择过滤器位宽
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//选择过滤器模式(屏蔽模式和列表模式)
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//配置过滤器关联(进FIFO_0还是FIFO_1)
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//激活过滤器,也就是让过滤器工作
	CAN_FilterInit(&CAN_FilterInitStructure);
}

/* 
   函数作用:发送一个报文
   参数:ID号,数据长度,传入数据内容
   返回值:无
*/

void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{
	CanTxMsg TxMessage;
	TxMessage.StdId = ID;//标准ID
	TxMessage.ExtId = ID;//扩展ID
	TxMessage.IDE = CAN_Id_Standard;//CAN_ID_STD,扩展标志位,
	                              //标准模式(StdId有效,ExtId无效)
                              	//扩展模式(StdId无效,ExtId有效)
	TxMessage.RTR = CAN_RTR_Data;//遥控标志位
	TxMessage.DLC = Length;//数据长度
	for (uint8_t i = 0; i < Length; i ++)
	{
		TxMessage.Data[i] = Data[i];//数据段内容
	}
	
	uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//检查邮箱状态
	
	uint32_t Timeout = 0;
	while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok)
	{
		Timeout ++;
		if (Timeout > 100000)
		{
			break;
		}
	}
}
/* 
   函数作用:判断接收FIFO里是否有报文
   参数:无
   返回值:有报文返回1,没有返回0
*/

uint8_t MyCAN_ReceiveFlag(void)
{
	if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0)//返回FIFO里排队报文的数目
	{
		return 1;
	}
	return 0;
}
/* 
   函数作用:接收报文
   参数:ID号,数据长度,数据内容
   返回值:无
*/
void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{
	CanRxMsg RxMessage;//接收报文的结构体,包含接收报文的ID、DLC、datas和FMI
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
	
	if (RxMessage.IDE == CAN_Id_Standard)//如果读到的报文是标准格式
	{
		*ID = RxMessage.StdId;//把标准ID传出去
	}
	else
	{
		*ID = RxMessage.ExtId;//否则把扩展格式ID传出去
	}
	
	if (RxMessage.RTR == CAN_RTR_Data)//判断是否收到的是数据帧
	{
		*Length = RxMessage.DLC;//将长度数据传出去
		for (uint8_t i = 0; i < *Length; i ++)
		{
			Data[i] = RxMessage.Data[i];//传出内容
		}
	}
	else
	{
		//...如果收到遥控帧
	}
}

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "MyCAN.h"

uint8_t KeyNum;
uint32_t TxID = 0x555;
uint8_t TxLength = 4;
uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44};

uint32_t RxID;
uint8_t RxLength;
uint8_t RxData[8];

int main(void)
{
	OLED_Init();
	Key_Init();
	MyCAN_Init();
	
	OLED_ShowString(1, 1, "TxID:");
	OLED_ShowHexNum(1, 6, TxID, 3);
	OLED_ShowString(2, 1, "RxID:");
	OLED_ShowString(3, 1, "Leng:");
	OLED_ShowString(4, 1, "Data:");
	
	while (1)
	{
		KeyNum = Key_GetNum();
		
		if (KeyNum == 1)
		{
			TxData[0] ++;
			TxData[1] ++;
			TxData[2] ++;
			TxData[3] ++;
			
			MyCAN_Transmit(TxID, TxLength, TxData);
		}
		
		if (MyCAN_ReceiveFlag())
		{
			MyCAN_Receive(&RxID, &RxLength, RxData);
			
			OLED_ShowHexNum(2, 6, RxID, 3);
			OLED_ShowHexNum(3, 6, RxLength, 1);
			OLED_ShowHexNum(4, 6, RxData[0], 2);
			OLED_ShowHexNum(4, 9, RxData[1], 2);
			OLED_ShowHexNum(4, 12, RxData[2], 2);
			OLED_ShowHexNum(4, 15, RxData[3], 2);
		}
	}
}


网站公告

今日签到

点亮在社区的每一天
去签到