ZYNQ 裸机和petalinux扩展CAN接口
一,CAN總線原理詳解:
1,簡介:
CAN是控制器局域網(wǎng)絡(luò)(Controller Area Network, CAN)的簡稱,是一種多主通信模式,即只要總線空閑,總線上任何節(jié)點(diǎn)都可以發(fā)送報(bào)文。CAN總線使用兩根線來連接各個(gè)單元:CAN_H和CAN_L,CAN 控制器通過判斷這兩根線上的電位差來得到總線電平,CAN 總線電平分為顯性電平和隱性電平兩種。顯性電平表示邏輯“0”,此時(shí) CAN_H 電平比 CAN_L 高,分別為 3.5V 和 1.5V,電位差為 2V。隱形電平表示邏輯“1”,此時(shí) CAN_H 和 CAN_L 電壓都為 2.5V 左右,電位差為 0V。CAN 總線就通過顯性和隱形電平的變化來將具體的數(shù)據(jù)發(fā)送出去。
2,格式
CAN總線有2種幀格式(標(biāo)準(zhǔn)格式、擴(kuò)展格式),5種幀類型(數(shù)據(jù)幀、遙控幀、錯(cuò)誤幀、過載幀、間隔幀)。
標(biāo)準(zhǔn)格式和擴(kuò)展格式的主要區(qū)別是仲裁段中幀ID的長度,標(biāo)準(zhǔn)幀的幀ID長度是11位,擴(kuò)展幀的幀ID長度是29位。
數(shù)據(jù)幀根據(jù)仲裁段長度不同分為標(biāo)準(zhǔn)幀(2.0A)和擴(kuò)展幀(2.0B)
(1)CAN2.0A
?(2)CAN2.0B
(1)幀起始:由一個(gè)顯性位(低電平)表示,發(fā)送節(jié)點(diǎn)發(fā)送幀起始。
(2)仲裁段:如果有兩個(gè)或兩個(gè)以上的節(jié)點(diǎn)開始傳送報(bào)文,那么就會(huì)存在總線訪問沖突的可能。但是CAN使用了標(biāo)識(shí)符的逐位仲裁方法可以解決這個(gè)問題。
RTR(Remote Transmission Request)用于區(qū)分報(bào)文是數(shù)據(jù)幀(RTR 位為顯性狀態(tài))還是遠(yuǎn)程幀 (RTR 位為隱性狀態(tài))。
SRR(Substitute Remote Request)擴(kuò)展幀中替代遠(yuǎn)程請(qǐng)求位,定義為隱性狀態(tài)。
(3)控制段:控制段共6位,標(biāo)準(zhǔn)幀的控制段由擴(kuò)展幀標(biāo)志位IDE、保留位r0和數(shù)據(jù)長度代碼DLC組成;擴(kuò)展幀控制段則由IDE、r1、r0和DLC組成。
IDE(Identifier Extension)該位顯性狀態(tài)表示標(biāo)準(zhǔn)幀,隱性狀態(tài)表示擴(kuò)展幀。
DLC(Data Length Code)用來指定報(bào)文中包含的數(shù)據(jù)字節(jié)數(shù) (0 ~ 8 字節(jié))。
(4)數(shù)據(jù)段:一個(gè)數(shù)據(jù)幀傳輸?shù)臄?shù)據(jù)長度為0~8個(gè)字節(jié),這種短幀結(jié)構(gòu)使得CAN總線實(shí)時(shí)性很高,非常適合汽車和工控應(yīng)用場(chǎng)合。
3,CAN收發(fā)器:CAN收發(fā)器的作用是負(fù)責(zé)邏輯電平和信號(hào)電平之間的轉(zhuǎn)換。
4,CAN 總線上沒有節(jié)點(diǎn)傳輸數(shù)據(jù)的時(shí)候一直處于隱性狀態(tài),也就是說總線空閑狀態(tài)的時(shí)候一
直處于隱性。CAN 網(wǎng)絡(luò)中的所有單元都通過 CAN_H 和 CAN_L 這兩根線連接在一起。
5,ZYNQ中CAN的定義
u32 frame[4];
Frame[0]對(duì)應(yīng)仲裁段,frame[1]對(duì)應(yīng)控制段,frame[2]和frame[3]對(duì)應(yīng)數(shù)據(jù)段。
Frame[0],就是圖片中的CAN ID(hex),比如0x02cb5000。
Frame[1]中高四位[31:28]表示長度,長度范圍寫0-8,Can每次都只能最多8字節(jié)進(jìn)行發(fā)送。
Frame[2]是8字節(jié)數(shù)據(jù)的前四個(gè),frame[3]是8字節(jié)數(shù)據(jù)的后四個(gè),如果長度不足8的不用管后面的數(shù)據(jù),不用填0進(jìn)去;如果不發(fā)數(shù)據(jù),frame[2]和frame[3]不用填。
?二,ZYNQ中vivado搭建
1,vivado工程,勾選EMIO或者M(jìn)IO擴(kuò)展CAN0,電壓一般1.8V
2,勾選CAN CLK時(shí)鐘配置,這里的時(shí)鐘用于后面計(jì)算波特率
3,添加CAN總線的約束
set_property PACKAGE_PIN xx [get_ports can_rx]
set_property IOSTANDARD LVCMOS33 [get_ports can_rx]
set_property PACKAGE_PIN yy [get_ports can_tx]
set_property IOSTANDARD LVCMOS33 [get_ports can_tx]
4,導(dǎo)入sdk,新建empty空文件,加入相關(guān)代碼
三,ZYNQ CAN中代碼解析
1,CAN驅(qū)動(dòng)代碼,同時(shí)支持CAN2.0A和CAN2.0B
(1)can_zynq.h
#ifndef _CAN_ZYNQ_H_ #define _CAN_ZYNQ_H_ #include "xparameters.h" #include "xcanps.h" #include "xscugic.h" #define XCANPS_MAX_FRAME_SIZE_IN_WORDS (XCANPS_MAX_FRAME_SIZE / sizeof(u32)) #define CAN_DEVICE_ID XPAR_XCANPS_0_DEVICE_ID #define CAN_INTR_VEC_ID XPAR_XCANPS_0_INTR #define TEST_BTR_SYNCJUMPWIDTH 3 #define TEST_BTR_SECOND_TIMESEGMENT 7 #define TEST_BTR_FIRST_TIMESEGMENT 15 #define TEST_BRPR_BAUD_PRESCALAR 4 //100M/(4+1) = 20M; 20/(1+1+2+1+15)=1Mbps //#define TEST_BRPR_BAUD_PRESCALAR 9 //100M/(9+1) = 10M; 10/(1+1+2+1+15)=500Kbps XCanPs Can; u32 RxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS]; enum can_baud_rate {rate_1000kbps,rate_800kbps,rate_500kbps,rate_250kbps,rate_125kbps,rate_100kbps,rate_50kbps,rate_20kbps }; typedef struct {u16 id; //frame's ID u8 idr_ide;u8 rtr; //remote transmission request. (0 if not rtr frame, 1 if rtr frame)u8 len; //frame's length (0 to 8) u8 data[8]; } can_frame; typedef struct {u8 BaudRate;u8 BaudRatePrescaler;u8 TimeSegment1;u8 TimeSegment2;u8 SyncJumpWidth; } can_bit_timing; int can_init(XCanPs *CanInstPtr, u16 DeviceId, u8 BaudRate); int Can_send(XCanPs *InstancePtr, can_frame *msg); int CanSetupIntrSystem(XScuGic *IntcInstancePtr, XCanPs *CanInstancePtr, u16 CanIntrId); void SendHandler(void *CallBackRef); void RecvHandler(void *CallBackRef); void ErrorHandler(void *CallBackRef, u32 ErrorMask); void EventHandler(void *CallBackRef, u32 Mask); #endif(2)can_zynq.c
#include "can_zynq.h" #include "sys_intr.h" can_bit_timing can_bit_timing_config[]= {{rate_1000kbps, 1, 15, 2, 2},{rate_800kbps, 4, 6, 1, 1},{rate_500kbps, 4, 12, 1, 0},{rate_250kbps, 9, 12, 1, 0},{rate_125kbps, 19, 12, 1, 0},{rate_100kbps, 24, 12, 1, 0},{rate_50kbps, 49, 12, 1, 0},{rate_20kbps, 124, 12, 1, 0} }; static int Config(XCanPs *InstancePtr ,u8 BaudRate) {int i;XCanPs_EnterMode(InstancePtr, XCANPS_MODE_CONFIG);while(XCanPs_GetMode(InstancePtr) != XCANPS_MODE_CONFIG);for(i=0; i<(sizeof(can_bit_timing_config)/sizeof(can_bit_timing)); i++){if(can_bit_timing_config[i].BaudRate == BaudRate){XCanPs_SetBaudRatePrescaler(InstancePtr, can_bit_timing_config[i].BaudRatePrescaler);XCanPs_SetBitTiming(InstancePtr, can_bit_timing_config[i].SyncJumpWidth,can_bit_timing_config[i].TimeSegment2,can_bit_timing_config[i].TimeSegment1);break;}if(i == ((sizeof(can_bit_timing_config)/sizeof(can_bit_timing)) - 1)){xil_printf("Baud rate not found!\r\n");return XST_FAILURE;}}} int can_init(XCanPs *CanInstPtr, u16 DeviceId, u8 BaudRate) {XCanPs_Config *ConfigPtr;int Status;int i;ConfigPtr = XCanPs_LookupConfig(DeviceId);if (CanInstPtr == NULL){return XST_FAILURE;}Status = XCanPs_CfgInitialize(CanInstPtr,ConfigPtr,ConfigPtr->BaseAddr);if (Status != XST_SUCCESS){return XST_FAILURE;}Status = XCanPs_SelfTest(CanInstPtr);if (Status != XST_SUCCESS){return XST_FAILURE;}Config(CanInstPtr,BaudRate);XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_SEND,(void *)SendHandler, (void *)CanInstPtr);XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_RECV,(void *)RecvHandler, (void *)CanInstPtr);XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_ERROR,(void *)ErrorHandler, (void *)CanInstPtr);XCanPs_SetHandler(CanInstPtr, XCANPS_HANDLER_EVENT,(void *)EventHandler, (void *)CanInstPtr);Status = CanSetupIntrSystem(&Intc, CanInstPtr, CAN_INTR_VEC_ID);if (Status != XST_SUCCESS){return XST_FAILURE;}XCanPs_EnterMode(CanInstPtr, XCANPS_MODE_NORMAL);while(XCanPs_GetMode(CanInstPtr) != XCANPS_MODE_NORMAL);return XST_SUCCESS; }int Can_send(XCanPs *InstancePtr, can_frame *msg) {int Status;u32 TxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS] = {0};//TxFrame[0] =(u32)( (msg->id&XCANPS_IDR_ID1_SHIFT)|0x00100000|0x00080000|(0x7FFFE&msg->id));//TxFrame[0] =(u32)XCanPs_CreateIdValue((u32)msg->id, (u32)msg->rtr, 1,(u32)msg->id, 1);//TxFrame[0] =(u32)XCanPs_CreateIdValue((u32)msg->id, 0, 1,(u32)msg->id,0);if(msg->idr_ide){TxFrame[0] =(u32)XCanPs_CreateIdValue((u32)msg->id>>18, (u32)msg->rtr, (u32)msg->idr_ide,(u32)msg->id,0);}else{TxFrame[0] =(u32)XCanPs_CreateIdValue((u32)msg->id, (u32)msg->rtr, 0,0,0);}TxFrame[1] = (u32)XCanPs_CreateDlcValue((u32)msg->len);TxFrame[2] = (u32)(((u32)msg->data[3] << 24) | ((u32)msg->data[2] << 16) | ((u32)msg->data[1] << 8) | ((u32)msg->data[0]));TxFrame[3] = (u32)(((u32)msg->data[7] << 24) | ((u32)msg->data[6] << 16) | ((u32)msg->data[5] << 8) | ((u32)msg->data[4]));//printf("frame[0]=%08x\n",TxFrame[0]);/** Wait until TX FIFO has room.*/while (XCanPs_IsTxFifoFull(InstancePtr) == TRUE);Status = XCanPs_Send(InstancePtr, TxFrame);if (Status != XST_SUCCESS){xil_printf("Can send failed\r\n");}return Status; }int CanSetupIntrSystem(XScuGic *IntcInstancePtr,XCanPs *CanInstancePtr,u16 CanIntrId) {int Status;Status = XScuGic_Connect(IntcInstancePtr, CanIntrId, (Xil_InterruptHandler)XCanPs_IntrHandler,(void *)CanInstancePtr);if (Status != XST_SUCCESS){return Status;}XScuGic_Enable(IntcInstancePtr, CanIntrId);XCanPs_IntrEnable(CanInstancePtr, XCANPS_IXR_ALL);return XST_SUCCESS; } void SendHandler(void *CallBackRef) {xil_printf("Can send done\r\n"); } void RecvHandler(void *CallBackRef) {XCanPs *CanPtr = (XCanPs *)CallBackRef;int Status;int i;can_frame msg = {0};u8 *FramePtr;Status = XCanPs_Recv(CanPtr, RxFrame);if (Status != XST_SUCCESS){xil_printf("Can receive failed\r\n");return;}msg.idr_ide=(u8)((RxFrame[0]& XCANPS_IDR_IDE_MASK)>>XCANPS_IDR_IDE_SHIFT);if(msg.idr_ide==1){msg.id =(u32)((RxFrame[0] & XCANPS_IDR_ID2_MASK )>> XCANPS_IDR_ID2_SHIFT);msg.id |=(u32)((RxFrame[0] & XCANPS_IDR_ID1_MASK )>> (XCANPS_IDR_ID1_SHIFT-18) );msg.rtr = (u8)(RxFrame[0] & XCANPS_IDR_RTR_MASK);}else{msg.id = (u16)((RxFrame[0] & XCANPS_IDR_ID1_MASK )>> XCANPS_IDR_ID1_SHIFT);msg.rtr = (u8)((RxFrame[0] & XCANPS_IDR_SRR_MASK) >> XCANPS_IDR_SRR_SHIFT);}msg.len = (u8)((RxFrame[1] & XCANPS_DLCR_DLC_MASK) >> XCANPS_DLCR_DLC_SHIFT);FramePtr = (u8 *)(&RxFrame[2]);for(i=0; i<msg.len; i++){msg.data[i] = *FramePtr++;if((!msg.rtr)xil_printf("data %d is: 0x%02x\r\n", i+1, msg.data[i]);} } void ErrorHandler(void *CallBackRef, u32 ErrorMask) {xil_printf("Can error happen\r\n"); } void EventHandler(void *CallBackRef, u32 IntrMask) {XCanPs *CanPtr = (XCanPs *)CallBackRef;xil_printf("Can event happen\r\n");if (IntrMask & XCANPS_IXR_BSOFF_MASK){XCanPs_Reset(CanPtr);Config(CanPtr,rate_500kbps);return;} }3,主函數(shù)main.c
#include "can_zynq.h" #include "sys_intr.h" #include "sleep.h"void send_back_book() {msg.id = 0x0c285000;msg.rtr=1;msg.len = 8;msg.data[0]=0xab;msg.data[1]=0xab;msg.data[2]=0xcd;msg.data[3]=0xcd;msg.data[4]=0xcd;msg.data[5]=0xcd;msg.data[6]=0xef;msg.data[7]=0xef;Can_send(&Can, &msg);usleep(200);msg.id = 0x0c2c5001;msg.len = 5;msg.data[0]=0x04;msg.data[1]=0xb0;msg.data[2]=0x00;msg.data[3]=0xaa;msg.data[4]=0xbb;msg.data[5]=0xcc;msg.data[6]=0xdd;msg.data[7]=0xee;Can_send(&Can, &msg); } int main(void) {can_frame msg;int i;xil_printf("Can start\r\n");Init_Intr_System(&Intc);can_init(&Can, CAN_DEVICE_ID, rate_500kbps);Setup_Intr_Exception(&Intc);while(1){send_back_book();sleep(2);} }四,CAN測(cè)試:
1,CAN模塊自收自發(fā)測(cè)試
在代碼中將NORMAL模式改為LOOPBACK模式可以自收自發(fā)進(jìn)行數(shù)據(jù)打印,測(cè)試vivado配置CAN總線是否出錯(cuò)。
2,在電腦上面安裝CAN驅(qū)動(dòng),外接如下模塊和can分析儀下載運(yùn)行,打開canpro查看數(shù)據(jù):先測(cè)試一下CAN通信是否正常。按sdk時(shí)候的連接,測(cè)試can收發(fā)數(shù)據(jù)
3,用ZYNQ的waterMark中斷例程環(huán)回模式改寫的NORMAL模式CAN通信不進(jìn)接收中斷且接收數(shù)組沒有數(shù)據(jù),經(jīng)過這段時(shí)間的調(diào)試,終于有了結(jié)論,原因如下:
(1)例程中時(shí)鐘是20M,這里需要根據(jù)實(shí)際使用配置自己的波特率
(2)發(fā)送幀格式有問題進(jìn)入了EVENT中斷-缺少總線仲裁,屏蔽不用的中斷
(3)LOOPBACK模式里禁止了接收中斷
4,將*.hdf硬件復(fù)制到虛擬機(jī)
petalinux-create --type project --template zynq --name can_test
petalinux-config --get-hw-description ../linux_base.sdk
5,在devicetree中需要增加can的配置信息,如下:
can@e0008000 {compatible= "xlnx,zynq-can-1.0";status= "okay";clocks= <0x1 0x13 0x1 0x24>;clock-names= "ref_clk", "aper_clk";reg = <0xe00080000x1000>;interrupts= <0x0 0x1c 0x4>;interrupt-parent= <0x3>;tx-fifo-depth= <0x40>;rx-fifo-depth= <0x40>; };6,添加CAN總線驅(qū)動(dòng),在kernel中需要增加can驅(qū)動(dòng)的支持,然后petalinux-config -c kernel編譯
?7,ifconfig? -a可以查看到如下信息:
?設(shè)置can0的波特率,這里設(shè)置的是100k:ip link set can0 type can bitrate100000
啟用can0:ip link set can0 up
顯示can0狀態(tài)信息:ip -d -s link show can0
8,linux下的can總線應(yīng)用程序示范:test_can.c
#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <linux/sockios.h> #include <linux/if.h> #include <pthread.h> #include "can.h" #define PF_CAN 29 #define AF_CAN PF_CAN #define SIOCSCANBAUDRATE (SIOCDEVPRIVATE+0) #define SIOCGCANBAUDRATE (SIOCDEVPRIVATE+1) #define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW) #define CAN_RAW_FILTER 1 #define CAN_RAW_RECV_OWN_MSGS 0x4 typedef __u32 can_baudrate_t; struct ifreq ifr; int init_can(char* can) {int sock;struct sockaddr_can addr;sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if(sock < 0){printf("error\n");return -1;}addr.can_family = AF_CAN;strcpy(ifr.ifr_name, can );int ret;ret = ioctl(sock, SIOCGIFINDEX, &ifr); //get indexif(ret && ifr.ifr_ifindex == 0){printf("Can't get interface index for can0, code= %d, can0 ifr_ifindex value: %d, name: %s\n", ret, ifr.ifr_ifindex, ifr.ifr_name);close(sock);return -1;}printf("%s can_ifindex = %x\n",ifr.ifr_name,ifr.ifr_ifindex);addr.can_ifindex = ifr.ifr_ifindex; int recv_own_msgs = 0;//set loop back: 1 enable 0 disablesetsockopt(sock, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,&recv_own_msgs, sizeof(recv_own_msgs));if (bind(sock,(struct sockaddr*)&addr,sizeof(addr))<0){printf("bind error\n");close(sock);return -1;}return sock; }void Can_Read_thread(void* psock) {int sock = *(int *)psock;int i = 0;struct can_frame frame;while(1){memset(&frame,0,sizeof(struct can_frame));read(sock,&frame,sizeof(struct can_frame));if(frame.can_dlc){printf("\n%s DLC:%d Data:", ifr.ifr_name, frame.can_dlc); for(i = 0; i < frame.can_dlc; i++){printf("%#x ",frame.data[i]);}printf("\n");}} }int Write_Can_Data(int sock,char* str,int len) {struct can_frame frame;int i;int nbytes = 0;frame.can_id = 0x0; //can device idwhile(len){if(len > sizeof(frame.data)){memset(&frame.data,0,sizeof(frame.data));memcpy(&frame.data,str,sizeof(frame.data));//printf("%d,(%s)\r\n",sizeof(frame.data),frame.data);frame.can_dlc = sizeof(frame.data);str += sizeof(frame.data);len -= sizeof(frame.data);}else {memset(&frame.data,0,sizeof(frame.data));memcpy(&frame.data,str,len);//printf("%d,(%s)\r\n",len,frame.data);frame.can_dlc = len;str = NULL;len = 0;}write(sock, &frame, sizeof(struct can_frame));usleep(100);} }int main(int argc ,char** argv) {int sock;int nbytes;pthread_t canthreadid;int ret;char str[4096];if(argc < 2){printf("Usage: %s <device>(can0/can1)\n", argv[0]);return 0;}sock = init_can(argv[1]);if(sock < 0){return 0;}ret=pthread_create(&canthreadid,NULL,(void*)Can_Read_thread,&sock);while(1){printf("Please Input string to send to %s\n:",argv[1]);scanf("%s", str);if(strlen(str)>0)Write_Can_Data(sock,str,strlen(str));} // struct can_frame frame; // char buf[20]; // frame.can_id = 0x0; //can device id // int i; // for(i=0;i<20;i++) // {//nbytes = sendto(sock,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); // memset(&buf,0,sizeof(buf)); // sprintf(buf,"%s%d","hello",i); // strcpy((char *)frame.data,buf); // frame.can_dlc = strlen(frame.data); // nbytes = write(sock, &frame, sizeof(struct can_frame)); // printf("%d. %ld bytes has been sent\n", i, nbytes); // sleep(1); // }close(sock);printf("ok\n"); return 0; }總結(jié)
以上是生活随笔為你收集整理的ZYNQ 裸机和petalinux扩展CAN接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: solidworks正版多少钱_SOLI
- 下一篇: 《勒索软件防护体系建设指南》发布|美创深