STM32 使用CAN进行通信

头文件can.h:1.定义测试ID;2.声明相关函数;

#ifndef __CAN_H
#define __CAN_H

#include "stm32f10x.h"
#include "stm32f10x_can.h"

#define CAN_RX0_INT_ENABLE ENABLE

#define TESTID (0x0CF11AD0 & 0xFFFFFFFF)
#define HAN_TESTID  ((TESTID<<3) | (0x01<<2) | (0x00<<1))

uint8_t Can_Mode_Init(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode);
uint8_t Can_Mode_Init1(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode);

uint8_t Can_Send_Msg(uint8_t* msg,uint8_t len);
uint8_t Can_Send_Msg1(uint8_t* msg,uint8_t len,uint32_t ExId);
uint8_t Can_Recive_Msg(uint8_t* buf);


#endif

源文件can.c:1.CAN1相关初始化(使用库函数及自己写寄存器);2.CAN发送报文函数(使用库函数及自己写寄存器);3.CAN接收函数;

#include "can.h"

/*
 * CAN初始化 成功返回0
 */
uint8_t Can_Mode_Init(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	CAN_InitTypeDef CAN_InitStruct;
	CAN_FilterInitTypeDef CAN_FilterInitStruct;
	
#if CAN_RX0_INT_ENABLE
	NVIC_InitTypeDef NVIC_InitStruct;
	
#endif
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE); // 使能CAN1时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 使能 GPIOA时钟
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; // 引脚12
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11; // 引脚11
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	CAN_InitStruct.CAN_TTCM = DISABLE; // 非时间触发通信模式
	CAN_InitStruct.CAN_ABOM = DISABLE; // 软件自动离线管理
	CAN_InitStruct.CAN_AWUM = DISABLE; // 睡眠模式通过软件唤醒
	CAN_InitStruct.CAN_NART = ENABLE; // 禁止报文自动传送
	CAN_InitStruct.CAN_RFLM = DISABLE; //报文不锁定,新覆盖旧
	CAN_InitStruct.CAN_TXFP = DISABLE; // 优先级由报文标识符决定
	CAN_InitStruct.CAN_Mode = mode; // 模式设置
	CAN_InitStruct.CAN_SJW = tsjw;
	CAN_InitStruct.CAN_BS1 = tbs1;
	CAN_InitStruct.CAN_BS2 = tbs2;
	CAN_InitStruct.CAN_Prescaler = brp;
    CAN_Init(CAN1,&CAN_InitStruct);
	
	CAN_FilterInitStruct.CAN_FilterNumber = 0; // 过滤器0
	CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽位模式
	CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
	CAN_FilterInitStruct.CAN_FilterIdHigh = (HAN_TESTID&0xFFFF0000)>>16; // FxR1 高16位
	CAN_FilterInitStruct.CAN_FilterIdLow = (HAN_TESTID&0x0000FFFF); // // FxR1 低16位
	CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0xFFFF;// 32位MSK 全部关心
	CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0xFFFF; // 
	CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联到FIFO 0
	CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; // 使能过滤器
	CAN_FilterInit(&CAN_FilterInitStruct);
	
#if CAN_RX0_INT_ENABLE
	CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); // FIFO0挂号接收中断

	NVIC_InitStruct.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;

	NVIC_Init(&NVIC_InitStruct);
#endif

	return 0;
}

/*
 * CAN发送数据 官方库函数
 */
uint8_t Can_Send_Msg(uint8_t* msg,uint8_t len)
{
	u8 mbox;
	u16 i = 0;
	CanTxMsg TxMessage;
	TxMessage.StdId = 0x12;
	TxMessage.ExtId = 0x12;
	TxMessage.IDE = CAN_Id_Standard; // 标准帧
	TxMessage.RTR = CAN_RTR_Data; // 数据帧
	TxMessage.DLC = len;
	for(i=0; i<len; i++)
	{
		TxMessage.Data[i] = msg[i];
	}

	mbox = CAN_Transmit(CAN1,&TxMessage);
	i = 0;
	while((CAN_TransmitStatus(CAN1,mbox) != CAN_TxStatus_Ok) && (i<0xfff)) i++;	   // 等待结束
	
	if(i >= 0xfff) 	return 1;     // 如果超时
	return 0;

}

/*
 * CAN接收数据 官方库函数
 */
uint8_t Can_Recive_Msg(uint8_t* buf)
{
	u32 i;
	uint8_t stat = 0;
	CanRxMsg RxMessage;
	
	if(CAN_MessagePending(CAN1,CAN_FIFO0) == 0) return 0; // 没有接收到数据,直接退出
		
	CAN_Receive(CAN1,CAN_FIFO0,&RxMessage); // 读取数据
	
	if(RxMessage.FMI == 0) // 过滤器匹配编号
	{
		for(i=0; i<8; i++) // 复制数据
		{
			buf[i] = RxMessage.Data[i];

		}
		stat = RxMessage.DLC; // 赋值数据长度
		
	}
	
	return stat ; // 返回数据长度
}

/*
 * 初始化CAN1 使用官方基地址
 */
uint8_t Can_Mode_Init1(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode)
{
	uint32_t wait_ack = 0;
	uint8_t InitStatus = 0;

	CAN_FilterInitTypeDef CAN_FilterInitStruct;

	RCC->APB1ENR |= 0x01<<25; // 使能CAN时钟
	RCC->APB2ENR |= 0x01<<2; // 使能GPIOA时钟

	
	GPIOA->CRH &= ~(0x0F<<12); // 复位12-15位
	GPIOA->CRH |= 0x08<<12; // 1000 PA11 上拉/下拉输入
	GPIOA->BSRR = 0x01<<11; // 上拉PA11
	
	GPIOA->CRH &= ~(0x0F<<16); // 复位16-19位
	GPIOA->CRH |= 0x0B<<16; // 1011 PA12 复用推挽输出 50MHz
	
	CAN1->MCR &= ~(0x01<<1); // 退出睡眠模式
	CAN1->MCR |= 0x01<<0; //请求初始化
	
	while (((CAN1->MSR & 0x01) != 0x01) && (wait_ack != 0x0000FFFF)) // 初始化确认 判断是否超时
	{
		wait_ack++;
	}
	if(((CAN1->MSR & 0x01) != 0x01)) // 是否未进入初始化模式
	{
		InitStatus = 0; //初始化失败
		
	}
	
	CAN1->MCR &= ~(0x01<<7); // 静止时间触发通信
	CAN1->MCR &= ~(0x01<<6); // 离线状态的退出是在,软件对CAN_MCR寄存器的INRQ位进行置1随后清0后,一旦硬件检测到128次11位连续的隐性位,就退出离线状态;
	CAN1->MCR &= ~(0x01<<5); // 不开启自动唤醒
	CAN1->MCR &= ~(0x01<<4); // 允许报文自动发送
	CAN1->MCR &= ~(0x01<<3); // 新报文覆盖旧报文
	CAN1->MCR &= ~(0x01<<2); // 优先级由报文的标识符来决定
	CAN1->BTR = (uint32_t)((uint32_t)mode << 30) | \
                ((uint32_t)tsjw << 24) | \
                ((uint32_t)tbs1 << 16) | \
                ((uint32_t)tbs2 << 20) | \
               ((uint32_t)brp - 1);
	CAN1->MCR &= ~(0x01<<0); // 从初始化模式进入正常工作模式
	
	wait_ack = 0;
	while (((CAN1->MSR & 0x01) == 0x01) && (wait_ack != 0x0000FFFF)) //等待退出初始化模式
	{
		wait_ack++;
	}
	if((CAN1->MSR & 0x01) == 0x01) // 是否未进入正常模式
	{
		InitStatus = 0; //初始化失败
	
	}
	
	CAN_FilterInitStruct.CAN_FilterNumber = 0; // 过滤器0
	CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; //
	CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
	CAN_FilterInitStruct.CAN_FilterIdHigh = (HAN_TESTID&0xFFFF0000)>>16; // FxR1 高16位
	CAN_FilterInitStruct.CAN_FilterIdLow = (HAN_TESTID&0x0000FFFF); // // FxR1 低16位
	CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0xFFFF;// 32位MSK 全部关心
	CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0xFFFF; // 
	CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联到FIFO 0
	CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; // 使能过滤器
	CAN_FilterInit(&CAN_FilterInitStruct);
	
	return InitStatus;
	
	
}

/*
 * CAN发送数据 使用官方基地址
 */
uint8_t Can_Send_Msg1(uint8_t* msg,uint8_t len,uint32_t ExId)
{
	u8 transmit_mailbox;
	u16 i = 0;
	
	/* 选择发送邮箱 */
	if((CAN1->TSR & (0x01<<26)) == (0x01<<26)) // 邮箱0空
	{
		transmit_mailbox = 0;

	}
	else if((CAN1->TSR & (0x01<<27)) == (0x01<<27)) // 邮箱1空
	{
		transmit_mailbox = 1;

	}
	else if((CAN1->TSR & (0x01<<28)) == (0x01<<28)) // 邮箱2空
	{
		transmit_mailbox = 2;
	
	}
	else
	{
		transmit_mailbox = 4; // 无邮箱空
	
	}

	if(transmit_mailbox != 0x04) // 寻找到空闲邮箱
	{
		CAN1->sTxMailBox[transmit_mailbox].TIR &= 0x01; // 复位除了TXRQ以外的位
		CAN1->sTxMailBox[transmit_mailbox].TIR |= (0x00<<1) | (0x01<<2) | (ExId&0xffffffff)<<3 ; // 设置扩展标识符
		
		CAN1->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0; // 复位数据长度位
		CAN1->sTxMailBox[transmit_mailbox].TDTR |= len&0x0f; // 设置数据长度
		
		CAN1->sTxMailBox[transmit_mailbox].TDLR = ((u32)msg[0]) | ((u32)msg[1]<<8) | ((u32)msg[2]<<16) |((u32)msg[3]<<24); // 数据填充
		CAN1->sTxMailBox[transmit_mailbox].TDHR = ((u32)msg[4]) | ((u32)msg[5]<<8) | ((u32)msg[6]<<16) |((u32)msg[7]<<24);
		
		CAN1->sTxMailBox[transmit_mailbox].TIR |=0x01; // 请求发送
	
	}	
	
    i = 0;
	while((CAN_TransmitStatus(CAN1,transmit_mailbox) != CAN_TxStatus_Ok) && (i<0xfff)) i++;	   // 等待结束
	if(i >= 0xfff) 	return 1;     // 如果超时
	return 0;	
	
}

中断服务函数,

/*
 * CAN1中断服务函数 
 * 可用
 */
uint8_t buf[8] = {0,1,2,3,4,5,6,7};
uint8_t stat = 0;
void USB_LP_CAN1_RX0_IRQHandler(void)
{
	uint8_t i;
	CanRxMsg RxMessage;
	stat = 0;
	CAN_Receive(CAN1,CAN_FIFO0,&RxMessage); // 读取数据
    if(RxMessage.FMI == 0) // 关联滤波器号 == 0?
	{
		for(i=0; i<8; i++) // 复制数据
		{
			buf[i] = RxMessage.Data[i]; 

		}
		stat = RxMessage.DLC; // 状态赋值
		
	}
	ToggleLed(GPIOB,GPIO_Pin_15); // 翻转PB15

}

/*
 * CAN1中断方式读取报文信息
 * 读取成功返回:1 失败返回:0
 */
uint8_t ReadRxDat(uint8_t* read_buff)  
{
	uint8_t i;
	uint8_t rx_ok =0;
	if(stat != 0)
	{
		for(i = 0; i<8; i++) // 复制数据
		{
			read_buff[i] = buf[i];

		}
		stat = 0; // 给状态标志位清零
		rx_ok = 1; // 读取状态标志位
	}
	return rx_ok; // 返回标志位 成功:1 失败:0
	
}

主函数main.c:1.调用初始化函数;2.若收到报文则向上位机返回原报文;

#include "stm32f10x.h"  
#include "stm32f10x_it.h"
#include "led.h"
#include "timer.h"
#include "systick.h"
#include "can.h"

/*
 * 主函数
 */
int main(void)
{
	uint8_t can_buf[8] = {0,1,2,3,4,5,6,7};
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组 2位优先级 2位响应优先级
	LedInit1(); // LED初始化
	SysTick_Init(); // 滴答定时器初始化
	Can_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq ,CAN_BS1_9tq ,8,CAN_Mode_Normal);
	while(1)
	{
		if(ReadRxDat(can_buf) != 0) // if 中断接收到指定ID报文
		{
			Can_Send_Msg1(can_buf,8,TESTID); // 发送收到的报文
			ToggleLed(GPIOC,GPIO_Pin_6); // 翻转LED
		}

		Delay_ms(500); // 延时500ms
	}
}
亲测可用,无论是库函数还是自己写的寄存器都能使用!!!
参考资料:正点原子代码及STM32中文参考手册!!!




  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值