CAN通信实验

本实验通过 KEY_UP 按键选择 CAN 的工作模式(正常模式/环回模式),然后通过 KEY0 控制数据发送,并通过查询的办法,将接收到的数据通过串口打印出来。如果是环回模式,用一个开发板即可测试。如果是正常模式,就需要 2 个探索者 STM32F4 开发板,并且 将他们的 CAN 接口对接起来,然后一个开发板发送数据,另外一个开发板将接收到的数据通过串口打印出来。

 CAN 的初始化配置步骤:

1)配置相关引脚的复用功能(AF9), 使能CAN 时钟。

//使能相关时钟
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 PORTA 时钟
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
//初始化 GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 PA11,PA12
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);//PA11 复用为 CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1);//PA12 复用为 CAN1

 2)设置 CAN 工作模式及波特率等。

CAN_Init()用来初始化 CAN 的工作模式以及波特率,CAN_Init() 函数体中,在初始化之前,会设置 CAN_MCR 寄存器的 INRQ 为 1 让其进入初始化模式,然后初始化 CAN_MCR 寄存器和 CRN_BTR 寄存器之后,会设置 CAN_MCR 寄存器的 INRQ 为 0 让其退出初始化模式。所以我们在调用这个函数的前后不需要再进行初始化模式设置。CAN_Init()函数的定义:

uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);

第一个参数就是 CAN 标号,这里我们的芯片只有一个 CAN,所以就是 CAN1。

第二个参数是 CAN 初始化结构体指针,结构体类型是 CAN_InitTypeDef,结构体的定义:

 typedef struct

{

uint16_t CAN_Prescaler;

uint8_t CAN_Mode;

uint8_t CAN_SJW;

uint8_t CAN_BS1;

uint8_t CAN_BS2;

FunctionalState CAN_TTCM;

FunctionalState CAN_ABOM;

FunctionalState CAN_AWUM;

FunctionalState CAN_NART;

FunctionalState CAN_RFLM;

FunctionalState CAN_TXFP;

} CAN_InitTypeDef;

初始化实例为: 

CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack; //模式设置 1,回环模式;
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度为个时间单位 
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq; //时间段 1 占用 8 个时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//时间段 2 占用 7 个时间单位
CAN_InitStructure.CAN_Prescaler=5; //分频系数(Fdiv)
CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN1

3)设置滤波器。

函数 CAN_FilterInit ()用来初始化 CAN 的滤波器相关参数,CAN_Init() 函数体中,在初始化之前,会设置 CAN_FMR 寄存器的 INRQ 为 INIT 让其进入初始化模式, 然后初始化 CAN 滤波器相关的寄存器之后,会设置 CAN_FMR 寄存器的 FINIT 为 0 让其退出初始化模式。所以我们在调用这个函数的前后不需要再进行初始化模式设置。CAN_FilterInit ()函数的定义:

void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);

 CAN_FilterInitTypeDef类型定义:

typedef struct

{

uint16_t CAN_FilterIdHigh;

uint16_t CAN_FilterIdLow;

uint16_t CAN_FilterMaskIdHigh;

uint16_t CAN_FilterMaskIdLow;

uint16_t CAN_FilterFIFOAssignment;

uint8_t CAN_FilterNumber;

uint8_t CAN_FilterMode;

uint8_t CAN_FilterScale;

FunctionalState CAN_FilterActivation;

} CAN_FilterInitTypeDef;

第 1 个至第 4 个是用来设置过滤器的 32 位 id 以及 32 位 mask id, 分别通过 2 个 16 位来组合的。

第 5 个成员变量 CAN_FilterFIFOAssignment 用来设置 FIFO 和过滤器的关联关系,我们的实验 是关联的过滤器 0 到 FIFO0,值为 CAN_Filter_FIFO0。

第 6 个成员变量 CAN_FilterNumber 用来设置初始化的过滤器组,取值范围为 0~13。

第 7 个成员变量 FilterMode 用来设置过滤器组的模式,取值为标识符列表模式 CAN_FilterMode_IdList 和标识符屏蔽位模式 CAN_FilterMode_IdMask。

第 8 个成员变量 FilterScale 用来设置过滤器的位宽为 2 个 16 位 CAN_FilterScale_16bit 还是 1 个 32 位 CAN_FilterScale_32bit。

第 9 个成员变量 CAN_FilterActivation 就很明了了,用来激活该过滤器。

CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器 0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32 位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;32 位 ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32 位 MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;// FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化

4)发送接受消息

发送消息的函数是:

uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);

第一个参数是 CAN 标号,我们使用 CAN1。第二个参数是相关消息结构体 CanTxMsg 指针类型,CanTxMsg 结构体的成员变量用来设置标准标识符,扩展标识符,消息类型和消息帧长度等信息。 接受消息的函数是:

void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);

前面两个参数,CAN 标号和 FIFO 号。第二个参数 RxMessage 是用来存放接受到的消息信息。结构体 CanRxMsg 和结构体 CanTxMsg 比较接近,分别用来定义发送消息和描述接受消息。

5)CAN 状态获取 对于 CAN 发送消息的状态,挂起消息数目等等之类的传输状态信息,库函数提供了一些 列的函数,包括 CAN_TransmitStatus()函数,CAN_MessagePending()函数,CAN_GetFlagStatus() 函数等等,可以根据需要来调用。

源码讲解:

打开 can.c 文件,此部分代码总共 3 个函数,首先是:CAN_Mode_Init 函数。该函数用于 CAN 的初始化, 该函数带有 5 个参数,可以设置 CAN 通信的波特率和工作模式等。

第二个函数,Can_Send_Msg 函数。该函数用于 CAN 报文的发送,主要是设置标识符 ID 等信息,写入数据长度和数据,并请求发送,实现一次报文的发送。

//can 发送一组数据(固定格式:ID 为 0X12,标准帧,数据帧)
//len:数据长度(最大为 8) msg:数据指针,最大为 8 个字节.
//返回值:0,成功; 其他,失败;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{
 u8 mbox;
 u16 i=0;
 CanTxMsg TxMessage;
 TxMessage.StdId=0x12; // 标准标识符为 0
 TxMessage.ExtId=0x12; // 设置扩展标示符(29 位)
 TxMessage.IDE=0; // 使用扩展标识符
 TxMessage.RTR=0; // 消息类型为数据帧,一帧 8 位
 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_Failed)&&(i<0XFFF))i++;
 if(i>=0XFFF)return 1;
 return 0;
}

第三个函数,Can_Receive_Msg 函数。用来接受数据并且将接受到的数据存放到 buf 中。

//can 口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到; 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{ u32 i;
CanRxMsg RxMessage;
 if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//没有接收到数据,直接退出
 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
 for(i=0;i<RxMessage.DLC;i++)
 buf[i]=RxMessage.Data[i]; 
return RxMessage.DLC; //返回接受到的数据长度
}

can.c 里面,还包含了中断接收的配置,通过 can.h 的 CAN1_RX0_INT_ENABLE 宏定义, 来配置是否使能中断接收,本章我们不开启中断接收的。

can.h 头文件中,CAN1_RX0_INT_ENABLE 用于设置是否使能中断接收,本章我们不用中 断接收,故设置为 0。

main函数

int main(void)
{ 
	u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 canbuf[8];
	u8 res;
	u8 mode=1;//CAN工作模式;0,普通模式;1,环回模式
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);    //初始化延时函数
	uart_init(115200);	//初始化串口波特率为115200
	LED_Init();					//初始化LED  
	KEY_Init(); 				//按键初始化  
	CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_Mode_LoopBack);//CAN初始化环回模式,波特率500Kbps    
 									  
while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY0_PRES)//KEY0按下,发送一次数据
		{
			for(i=0;i<8;i++)
			{
				canbuf[i]=cnt+i;//填充发送缓冲区
 			}
			printf("发送的数据:\r\n");
			printf("%d	%d	%d	%d\r\n",canbuf[0],canbuf[1],canbuf[2],canbuf[3]);	  //显示数据
			printf("%d	%d	%d	%d\r\n",canbuf[4],canbuf[5],canbuf[6],canbuf[7]);	  //显示数据
			res=CAN1_Send_Msg(canbuf,8);//发送8个字节 
			if(res)printf("Failed\r\n");		//提示发送失败
			else printf("OK    \r\n");	 		//提示发送成功								   
		}else if(key==WKUP_PRES)//WK_UP按下,改变CAN的工作模式
		{	   
			mode=!mode;
			CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,mode);	//CAN普通模式初始化,普通模式,波特率500Kbps
			if(mode==0)//普通模式,需要2个开发板
			{
				printf("Nnormal Mode \r\n");	    
			}else //回环模式,一个开发板就可以测试了.
			{
 				printf("LoopBack Mode\r\n");
			}
		}		 
		key=CAN1_Receive_Msg(canbuf);
		if(key)//接收到有数据
		{	
			printf("接受的数据:\r\n");			
 			printf("%d	%d	%d	%d\r\n",canbuf[0],canbuf[1],canbuf[2],canbuf[3]);	  //显示数据
			printf("%d	%d	%d	%d\r\n",canbuf[4],canbuf[5],canbuf[6],canbuf[7]);	  //显示数据
		}
		t++; 
		delay_ms(10);
		if(t==20)
		{
			LED0=!LED0;//提示系统正在运行	
			t=0;
			cnt++;
			//printf("%d\r\n",cnt);	//显示数据
		}		   
	} 
	
}

运行视频

CAN通讯实验

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值