大家好,欢迎来到IT知识分享网。
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.初始化用到的引脚
用到的引脚有:
UART1:TX->PTC7 RX->PTC6
CAN1: TX->PTA13 RX->PTA12
构建项目
配置串口1 波特率


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 / ;// 得到的频率是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博客
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/114137.html








