S32K144 CAN使用

        S32K144是恩智浦半导体推出的一款高性能微控制器(MCU),主要针对汽车和高可靠性的工业应用。在汽车工业应用中使用CAN总线必不可少。

需要注意的是在ISO 11898(High Speed,速度在5kbps-1Mbps)中,隐形电平电压差在0附件。显性电平电压差在2V左右

ISO 11519(Low Speed,速度在5kbsp-125kbps)中,隐形电平电压差小于0V,显性电平电压差大于0V。

CAN总线与RS485的比较:

1,速度与距离:CAN与RS485以1Mbit/S的高速率传输的距离都不超过100M,可谓高速上的距离差不多。但是在低速时CAN以5Kbit/S时,距离可达10KM,而485再低的速率也只能到1219米左右(都无中继)。可见CAN在长距离的传输上拥有绝对的优势

2,总线利用率:RS485是单主从结构,就是一个总线上只能有一台主机,通讯都由它发起的,它没有下命令,下面的节点不能发送,而且要发完即答,受到答复后,主机才向下一个节点询问,这样是为了防止多个节点向总线发送数据,而造成数据错乱。而CAN-bus是多主从结构,每个节点都有CAN控制器,多个节点发送时,以发送的ID号自动进行仲裁,这样就可以实现总线数据不错乱,而且一个节点发完,另一个节点可以探测到总线空闲,而马上发送,这样省去了主机的询问,提高了总线利用率,增强了快速性。所以在汽车等实性要求高的系统,都是用CAN总线,或者其他类似的总线。

3,错误检测机制,RS485只规定了物理层,而没有数据链路层,所以它对错误是无法识别的,除非一些短路等物理错误。这样容易造成一个节点破坏了,拼命向总线发数据(一直发1),这样造成整个总线瘫痪。所以RS485一旦坏一个节点,这个总线网络都挂。而CAN总线有CAN控制器,可以对总线任何错误进行检测,如果自身错误超过128个,就自动闭锁。保护总线。如果检测到其他节点错误或者自身错误,都会向总线发送错误帧,来提示其他节点,这个数据是错误的。大家小心。这样CAN总线一旦有一个节点CPU程序跑飞了,它的控制器自动闭锁。保护总线。所以在安全性要求高的网路,CAN是很强的。

4,价格与培训成本:CAN器件的价格大约是485的2倍这样,485的通讯从软件上是很方便的,只要懂串行通讯,就可以编程,而CAN需要底层工程师了解CAN复杂的层,编写上位机软件也要了解CAN的协议。可谓培训成本较高

5,CAN总线通过CAN控制器接口芯片82C250的两个输出端CANH和CANL与物理总线相连,而CANH端的状态只能是高电平或悬浮状态,CANL端只能是低电平或悬浮状态。这就保证不会出现象在RS-485网络中,当系统有错误,出现多节点同时向总线发送数据时,导致总线呈现短路,从而损坏某些节点的现象。而且CAN节点在错误严重的情况下具有自动关闭输出功能,以使总线上其他节点的操作不受影响,从而保证不会出现象在网络中,因个别节点出现问题,使得总线处于“死锁”状态。

6,CAN具有完善的通信协议,可由CAN控制器芯片及其接口芯片来实现,从而大大降低了系统的开发难度,缩短了开发周期,这些是只仅仅有电气协议的RS-485所无法比拟的

        本次示例使用的芯片为S32K14,环境为S32 Design Stduio for ARM,使用的CAN端口号为CAN1,测试方法为回环测试

目录

1.创建一个基本工程

2.初始化用到的引脚

3.创建一个delay.c/h

4.设置串口输出

4.设置CAN输入&输出

初始化

 接收

发送

整体main函数

 整体CAN函数

现象 


1.创建一个基本工程

这里就不细说

2.初始化用到的引脚

用到的引脚有:

UART1:TX->PTC7  RX->PTC6

CAN1:    TX->PTA13 RX->PTA12

构建项目

配置串口1 波特率115200

配置CAN为CAN1,波特率为500K,回环模式

设置完成后构建项目

3.创建一个delay.c/h

delay.c

  static uint32_t fac_us;

  /*延迟函数致敬原子哥*/
  void SysTick_Init(void)  //systick 走 core_clk 48mhz  重装载寄存器 FFFFFF 为16 777 215hz 16.77mhz/48mhz 约等于333毫秒
  {
  	S32_SysTick->RVR = 0xFFFFFFul; //重装载寄存器
  	S32_SysTick->CVR = 0ul; //当前计数
  	S32_SysTick->CSR = 0u; //控制寄存器
  }

  /* Enable SysTick counter and interrupt */
  void SysTick_Enable(void)
  {
  	S32_SysTick->CSR = S32_SysTick_CSR_TICKINT(1u) | S32_SysTick_CSR_ENABLE(1);
  }

  /* Disable SysTick */
  void SysTick_Disable(void)
  {
  	S32_SysTick->CSR = 0ul;
  }


  static uint32_t fac_us;

  void delay_us(uint32_t nus)
  {
  	uint32_t ticks;
  	uint32_t told,tnow,tcnt=0;
  	uint32_t reload=S32_SysTick->RVR;				//LOAD的值
  	ticks=nus*fac_us; 						//需要的节拍数
  	told= S32_SysTick->CVR ;      				//刚进入时的计数器值
  	while(1)
  	{
  		tnow= S32_SysTick->CVR ;
  		if(tnow!=told)
  		{
  			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
  			else tcnt+=reload-tnow+told;
  			told=tnow;
  			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
  		}
  	};
  }

  void delay_ms(uint32_t nms)
  {
  	uint32_t i;
  	for(i=0;i<nms;i++) delay_us(1000);
  }

  int delay_init(void)
  {
  	uint32_t frequency;
  	CLOCK_SYS_GetFreq(CORE_CLOCK, &frequency);
  	fac_us = frequency / 1000000;// 得到的频率是mhz 如果想知道1us多少个节拍,需要除 1 000 000
  	SysTick_Init(); //systick 走 core_clk 48mhz  重装载寄存器 FFFFFF 为16 777 215hz 16.77mhz/48mhz 0.333秒  约等于333毫秒
  	SysTick_Enable();
  	return fac_us;
  }

delay.h


#ifndef DELAY_H_
#define DELAY_H_

void delay_ms(uint32_t nms);
void delay_us(uint32_t nus);
int delay_init(void);

#endif /* DELAY_H_ */

4.设置串口输出

#include <stdio.h>
#include <string.h>
#include "stdarg.h"
#include <stdint.h>
#include <stdbool.h>


 char USART1_TX_BUF[200];

 void u1_printf(char* fmt,...)
 {
   uint32_t bytesRemaining;
	va_list ap;
	va_start(ap,fmt);
	vsprintf((char*)USART1_TX_BUF,fmt,ap);
	va_end(ap);
	LPUART_DRV_SendData(INST_LPUART1, (uint8_t *)USART1_TX_BUF, strlen(USART1_TX_BUF)); //发送
    while (LPUART_DRV_GetTransmitStatus(INST_LPUART1, &bytesRemaining)!= STATUS_SUCCESS)  {}
 }

接下来就可以使用u1_printf来输出了

int main(void)
{
  /* Write your local variable definition here */

  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
  #ifdef PEX_RTOS_INIT
    PEX_RTOS_INIT();                   /* Initialization of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of Processor Expert internal initialization.                    ***/
    CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
    CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);
    delay_init();//初始化delay函数
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);
    LPUART_DRV_Init(INST_LPUART1, &lpuart1_State, &lpuart1_InitConfig0);
  /* Write your code here */
  /* For example: for(;;) { } */
    u1_printf("S32 begin\r\n");
	while(1){

	}
  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;) {
    if(exit_code != 0) {
      break;
    }
  }
  return exit_code;
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

无误

4.设置CAN输入&输出

初始化

 
#define CAN_MSG_MASK_ALL  0x07FF //全bit匹配 只接收接收邮箱里有的ID
#define CAN_MSG_MASK_NULL 0x0    //不匹配   接收所有消息

can_message_t recvMsg_CAN1; //接收邮箱结构体
can_message_t trasiMsg_CAN1 ; //发送邮箱结构体
uint32_t IRQ_CAN1_RX; //CAN1收到信息标志位

#define CAN1_TX_MAILBOX1  0x1     //CAN1发送邮箱标识
#define CAN1_RX_MAILBOX1  0x2	  //CAN1接收邮箱标识 最大为32
#define CAN1_RX_MAILBOX1_ID 0x55  //CAN1识别的ID
#define CAN1_TX_MAILBOX1_ID 0x55  //CAN1发送的ID

 void CAN1_Init(void){
	 CAN_Init(&can_pal1_instance, &can_pal1_Config0);
	  can_buff_config_t Rx_buffCfg =  {
		  .enableFD = false,//表示是否启用灵活数据速率CAN FD
		  .enableBRS = false,//表示是否在CAN FD帧内启用比特率切换
		  .fdPadding = 0U,//表示当数据长度码指定的负载大小大于实际数据长度时,用于填充的值。这有助于保持数据帧的一致性
		  .idType = CAN_MSG_ID_STD,//标准帧
		  .isRemote = false,//表示该帧是否为远程请求帧
	  };
	  can_buff_config_t Tx_buffCfg =  {
		  .enableFD = false,
		  .enableBRS = false,
		  .fdPadding = 0U,
		  .idType = CAN_MSG_ID_STD,
		  .isRemote = false
	  };
	  CAN_ConfigRxBuff(&can_pal1_instance, CAN1_RX_MAILBOX1, &Rx_buffCfg, CAN1_RX_MAILBOX1_ID);//注册一个接收的邮箱 接收的ID 0x55
	  CAN_ConfigTxBuff(&can_pal1_instance, CAN1_TX_MAILBOX1, &Tx_buffCfg);  //发送邮箱配置载入
	  CAN_SetRxFilter(&can_pal1_instance,CAN_MSG_ID_STD,CAN1_RX_MAILBOX1,CAN_MSG_MASK_ALL);//标准帧 全bit匹配
	  CAN_InstallEventCallback(&can_pal1_instance,&CAN1_Callback_Func,(void*)0); //注册CAN1回调函数
	  CAN_Receive(&can_pal1_instance, CAN1_RX_MAILBOX1, &recvMsg_CAN1); //CAN1接收邮箱CAN1_RX_MAILBOX1开启接收
}
 

初始化CAN1,主要设置了CAN1的收发邮箱以及开启了中断

 接收

刚刚在初始化里开启了中断,现在接收只需要在中断里置位标志位后再main函数处理即可,刚刚注册的回调函数为CAN1_Callback_Func()

//CAN1回调函数
void CAN1_Callback_Func (uint32_t instance,can_event_t event,uint32_t buffIdx,void *flexcanState)
{
	(void)flexcanState;
	(void)instance;
	(void)buffIdx;
	if(event == CAN_EVENT_RX_COMPLETE){//接收完成 事件
		if(recvMsg_CAN1.id == CAN1_RX_MAILBOX1_ID) {
			IRQ_CAN1_RX =CAN1_RX_MAILBOX1_ID;
			CAN_Receive(&can_pal1_instance, CAN1_RX_MAILBOX1, &recvMsg_CAN1);//接收报文并重新注册回调函数
		}
	}
}

在main函数里 处理数据

//CAN1处理消息
void CAN1_Read(void){
	u1_printf("CAN1 邮箱消息: ID:0x%x \r\n",recvMsg_CAN1.id);
	for(uint8_t i=0;i<recvMsg_CAN1.length;i++){
		u1_printf("0x%x ",recvMsg_CAN1.data[i]);
	}
	u1_printf("\r\n");//依次打印出标准帧的数据
}

main里: 

if(IRQ_CAN1_RX == CAN1_RX_MAILBOX1_ID){
	CAN1_Read();
}

发送

//CAN发送数据数组 ID
void CAN1_Send(uint8_t *dat,uint32_t ID){
	can_message_t trasiMsg_CAN1;
	trasiMsg_CAN1.cs = 0U;
	trasiMsg_CAN1.id = ID;
	for(uint8_t i=0;i<8;i++){
		trasiMsg_CAN1.data[i]=dat[i];
	}
	trasiMsg_CAN1.length = 8;
	CAN_Send(&can_pal1_instance, CAN1_TX_MAILBOX1, &trasiMsg_CAN1);
	 u1_printf("CAN1 Send  ID:0x%x \r\n",trasiMsg_CAN1.id);
	 for(int i=0; i<trasiMsg_CAN1.length;i++)
	 {
		 u1_printf("0x%x ",trasiMsg_CAN1.data[i]);
	 }
	 u1_printf("\r\n");
}

在main函数中使用一个按键触发,也可以用别的条件触发

if((PINS_DRV_ReadPins(PTC)>>12)&0x01){
	while((PINS_DRV_ReadPins(PTC)>>12)&0x01);
	CAN1_Send(candat,CAN1_TX_MAILBOX1_ID);//使用CAN1_TX_MAILBOX1_ID发送数组candat
}

 在main再加一句 使得在接收到数据之后可以处理

if(IRQ_CAN1_RX==CAN1_RX_MAILBOX1_ID){
		CAN1_Read();
		IRQ_CAN1_RX=0;
}

整体main函数

int main(void)
{

  /* Write your local variable definition here */
  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
  #ifdef PEX_RTOS_INIT
    PEX_RTOS_INIT();                   /* Initialization of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of Processor Expert internal initialization.                    ***/
    CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
    CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);
    delay_init();//初始化delay函数
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);
    LPUART_DRV_Init(INST_LPUART1, &lpuart1_State, &lpuart1_InitConfig0);
    CAN1_Init();
  /* Write your code here */
  /* For example: for(;;) { } */
    u1_printf("S32 begin\r\n");
	while(1){
		if(IRQ_CAN1_RX==CAN1_RX_MAILBOX1_ID){
			CAN1_Read();
			IRQ_CAN1_RX=0;
		}
		if((PINS_DRV_ReadPins(PTC)>>12)&0x01){
			CAN1_Send(candat,CAN1_TX_MAILBOX1_ID);
		    while((PINS_DRV_ReadPins(PTC)>>12)&0x01);
		}
		delay_ms(100);
	}
  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;) {
    if(exit_code != 0) {
      break;
    }
  }
  return exit_code;
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

 整体CAN函数

#define CAN_MSG_MASK_ALL  0x07FF //全bit匹配 只接收接收邮箱里有的ID
#define CAN_MSG_MASK_NULL 0x0    //不匹配   接收所有消息

can_message_t recvMsg_CAN1; //接收邮箱结构体

uint32_t IRQ_CAN1_RX; //CAN1收到信息标志位

#define CAN1_TX_MAILBOX1  0x0     //CAN1发送邮箱标识
#define CAN1_RX_MAILBOX1  0x1	  //CAN1接收邮箱标识 最大为32
#define CAN1_RX_MAILBOX1_ID 0x55  //CAN1识别的ID 为11位标识
#define CAN1_TX_MAILBOX1_ID 0x55  //CAN1发送的ID 为11位标识

//CAN1回调函数
void CAN1_Callback_Func (uint32_t instance,can_event_t event,uint32_t buffIdx,void *flexcanState)
{
	(void)flexcanState;
	(void)instance;
	(void)buffIdx;
	if(event == CAN_EVENT_RX_COMPLETE){//接收完成 事件
		if(recvMsg_CAN1.id == CAN1_RX_MAILBOX1_ID) {
			IRQ_CAN1_RX =CAN1_RX_MAILBOX1_ID;
			CAN_Receive(&can_pal1_instance, CAN1_RX_MAILBOX1, &recvMsg_CAN1);//接收报文并重新注册回调函数
		}
	}
}

 void CAN1_Init(void){
	 CAN_Init(&can_pal1_instance, &can_pal1_Config0);
	  can_buff_config_t Rx_buffCfg =  {
		  .enableFD = false,//表示是否启用灵活数据速率CAN FD
		  .enableBRS = false,//表示是否在CAN FD帧内启用比特率切换
		  .fdPadding = 0U,//表示当数据长度码指定的负载大小大于实际数据长度时,用于填充的值。这有助于保持数据帧的一致性
		  .idType = CAN_MSG_ID_STD,//标准帧
		  .isRemote = false,//表示该帧是否为远程请求帧
	  };
	  can_buff_config_t Tx_buffCfg =  {
		  .enableFD = false,
		  .enableBRS = false,
		  .fdPadding = 0U,
		  .idType = CAN_MSG_ID_STD,
		  .isRemote = false
	  };
	  CAN_ConfigRxBuff(&can_pal1_instance, CAN1_RX_MAILBOX1, &Rx_buffCfg, CAN1_RX_MAILBOX1_ID);//注册一个接收的邮箱 接收的ID 0x55
	  CAN_ConfigTxBuff(&can_pal1_instance, CAN1_TX_MAILBOX1, &Tx_buffCfg);  //发送邮箱配置载入
	  CAN_SetRxFilter(&can_pal1_instance,CAN_MSG_ID_STD,CAN1_RX_MAILBOX1,CAN_MSG_MASK_ALL);//标准帧 全bit匹配
	  CAN_InstallEventCallback(&can_pal1_instance,&CAN1_Callback_Func,(void*)0); //注册CAN1回调函数
	  CAN_Receive(&can_pal1_instance, CAN1_RX_MAILBOX1, &recvMsg_CAN1); //CAN1接收邮箱CAN1_RX_MAILBOX1开启接收
 }


//CAN1处理消息
void CAN1_Read(void){
	u1_printf("CAN1 邮箱消息: ID:0x%x \r\n",recvMsg_CAN1.id);
	for(uint8_t i=0;i<recvMsg_CAN1.length;i++){
		u1_printf("0x%x ",recvMsg_CAN1.data[i]);
	}
	u1_printf("\r\n");//依次打印出标准帧的数据
}

uint8_t candat[8]={1,2,3,4,5,6,7,8};
void CAN1_Send(uint8_t *dat,uint32_t ID){
	can_message_t trasiMsg_CAN1;
	trasiMsg_CAN1.cs = 0U;
	trasiMsg_CAN1.id = ID;
	for(uint8_t i=0;i<8;i++){
		trasiMsg_CAN1.data[i]=dat[i];
	}
	trasiMsg_CAN1.length = 8;
	CAN_Send(&can_pal1_instance, CAN1_TX_MAILBOX1, &trasiMsg_CAN1);
	 u1_printf("CAN1 Send  ID:0x%x \r\n",trasiMsg_CAN1.id);
	 for(int i=0; i<trasiMsg_CAN1.length;i++)
	 {
		 u1_printf("0x%x ",trasiMsg_CAN1.data[i]);
	 }
	 u1_printf("\r\n");
}

现象 

开机为

按下按键,松开后

至此CAN回环测试成功

参考文章:

一文读懂CAN总线协议 (超详细配34张高清图)_can总线通信协议-CSDN博客

CAN总线的仲裁机制简述_can仲裁的原则和原理-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值