树莓派利用MCP2515实现SPI转CAN通信(C)
1、主要硬件
树莓派3b、ubuntu16.04、RS485_CAN_HAT、CAN分析仪
2、主要实现方法
之前使用PYTHON库工具直接实现了CAN的通信,但项目上层使用C++,也因为不知道如何去得到PYTHON接收到的数据,套接字的方法还是了解的少,故重新使用C来实现基于BCM2835库的CAN通信协议。
1
3、主要函数
协议首先需要封装基于BCM2835的SPI的读和写,一次操作一个字节的数据。基于这两个读写,再封装读写命令。
MCP2515的读写之前,都需要向SPI总线发送CAN_READ或者CAN_WRITE命令,再提供地址、数据来完成读写。MCP2515的初始化,首先调用BCM2835来初始化SPI,树莓派3B的spi接口固定,片选引脚可选,按照原理图的设计来选择是CE0还是CE1。
发送缓冲器标准标志符的高低位为16位,前11位对应于CAN的ID,在初始化MCP2515时不对其进行限制。初始化验收屏蔽寄存器为0,可以不启用验收滤波器,接收所有ID的CAN报文。验收屏蔽寄存器与验收滤波器位对应,屏蔽寄存器对应位为1,为启用该位的滤波,滤波位相同接收,详细见MCP2515手册。
发送函数需要提供数据的长度、数据地址、报文ID。接收函数在存储数组的最后一位存放该报文的ID。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
#include <stdlib.h> #include <stdio.h> #include <bcm2835.h> #include "mcp2515_new.h" #include "MCP2515.h" #define MCP2515_CS 8 unsigned char SPI_ReadByte(void) { return bcm2835_spi_transfer(0xFF); } void SPI_SendByte(unsigned char dt) { bcm2835_spi_transfer(dt); } void MCP2515_WriteByte(unsigned char addr,unsigned char dat) { bcm2835_gpio_write(MCP2515_CS,LOW);//置MCP2515的CS为低电平 SPI_SendByte(CAN_WRITE); //发送写命令 SPI_SendByte(addr); //发送地址 SPI_SendByte(dat); //写入数据 bcm2835_gpio_write(MCP2515_CS,HIGH);//置MCP2515的CS为高电平 } unsigned char MCP2515_ReadByte(unsigned char addr) { unsigned char rByte; bcm2835_gpio_write(MCP2515_CS,LOW); //置MCP2515的CS为低电平 SPI_SendByte(CAN_READ); //发送读命令 SPI_SendByte(addr); //发送地址 rByte=SPI_ReadByte(); //读取数据 bcm2835_gpio_write(MCP2515_CS,HIGH); //置MCP2515的CS为高电平 return rByte; //返回读到的一个字节数据 } void MCP2515_Reset(void) { bcm2835_gpio_write(MCP2515_CS,LOW); //置MCP2515的CS为低电平 SPI_SendByte(CAN_RESET); //发送寄存器复位命令 bcm2835_gpio_write(MCP2515_CS,HIGH); //置MCP2515的CS为高电平 } int MCP2515_Init(void) { unsigned char temp=0; if (!bcm2835_init()) { printf("bcm2835_init failed. Are you running as root??\n"); return 1; } if (!bcm2835_spi_begin()) { printf("bcm2835_spi_begin failedg. Are you running as root??\n"); return 1; } bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default 上跳沿或下跳沿的选择 bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default 时钟速率 bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default 片选信号 bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default bcm2835_gpio_fsel(MCP2515_CS, BCM2835_GPIO_FSEL_OUTP); MCP2515_Reset(); //发送复位指令软件复位MCP2515 bcm2835_delay(5); //设置波特率为125Kbps //set CNF1,SJW=00,长度为1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us MCP2515_WriteByte(CNF1,CAN_500Kbps); //set CNF2,SAM=0,在采样点对总线进行一次采样,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ); //set CNF3,PHSEG2=(2+1)TQ=3TQ,同时当CANCTRL.CLKEN=1时设定CLKOUT引脚为时间输出使能位 MCP2515_WriteByte(CNF3,PHSEG2_3TQ); // MCP2515_WriteByte(TXB0SIDH,0x09);//发送缓冲器0标准标识符高位 // MCP2515_WriteByte(TXB0SIDL,0x20);//发送缓冲器0标准标识符低位 MCP2515_WriteByte(RXB0SIDH,0x00);//清空接收缓冲器0的标准标识符高位 MCP2515_WriteByte(RXB0SIDL,0x00);//清空接收缓冲器0的标准标识符低位 MCP2515_WriteByte(RXB0CTRL,0x20);//仅仅接收标准标识符的有效信息 MCP2515_WriteByte(RXB0DLC,DLC_8);//设置接收数据的长度为8个字节 MCP2515_WriteByte(RXF0SIDH,0x00);//配置验收滤波寄存器n标准标识符高位 MCP2515_WriteByte(RXF0SIDL,0x00);//配置验收滤波寄存器n标准标识符低位 MCP2515_WriteByte(RXM0SIDH,0x00);//配置验收屏蔽寄存器n标准标识符高位 MCP2515_WriteByte(RXM0SIDL,0x00);//配置验收屏蔽寄存器n标准标识符低位 MCP2515_WriteByte(CANINTF,0x00);//清空CAN中断标志寄存器的所有位(必须由MCU清空) MCP2515_WriteByte(CANINTE,0x01);//配置CAN中断使能寄存器的接收缓冲器0满中断使能,其它位禁止中断 MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//将MCP2515设置为环回模式REQOP_LOOPBACK,退出配置模式;normal module:REQOP_NORMAL temp=MCP2515_ReadByte(CANSTAT);//读取CAN状态寄存器的值 if(OPMODE_NORMAL!=(temp&&0xE0))//判断MCP2515是否已经进入环回模式 { MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//再次将MCP2515设置为正常模式,退出配置模式 } return 0; } void MCP2515_End(void) { bcm2835_spi_end(); bcm2835_close(); } void CAN_Send_Buffer(unsigned char *CAN_TX_Buf,unsigned char len, unsigned char msgID) { unsigned char j,dly,count; count=0; MCP2515_WriteByte(TXB0SIDH, (msgID>>3)&0x1F); MCP2515_WriteByte(TXB0SIDL, (msgID<<5)&0xE0); while(count<len) { dly=0; while((MCP2515_ReadByte(TXB0CTRL)&0x08) && (dly<50))//快速读某些状态指令,等待TXREQ标志清零 { bcm2835_delay(1);//通过软件延时约nms(不准确) dly++; } for(j=0;j<8;) { MCP2515_WriteByte(TXB0D0+j,CAN_TX_Buf[count++]);//将待发送的数据写入发送缓冲寄存器 j++; if(count>=len) break; } MCP2515_WriteByte(TXB0DLC,j);//将本帧待发送的数据长度写入发送缓冲器0的发送长度寄存器 bcm2835_gpio_write(MCP2515_CS,LOW); MCP2515_WriteByte(TXB0CTRL,0x08);//请求发送报文 bcm2835_gpio_write(MCP2515_CS,HIGH); } } unsigned char CAN_Receive_Buffer(unsigned char *CAN_RX_Buf) { unsigned char i=0,len=0,temp=0; temp = MCP2515_ReadByte(CANINTF); if(temp & 0x01) { len=MCP2515_ReadByte(RXB0DLC);//读取接收缓冲器0接收到的数据长度(0~8个字节) while(i<len) { CAN_RX_Buf[i]=MCP2515_ReadByte(RXB0D0+i);//把CAN接收到的数据放入指定缓冲区 i++; } CAN_RX_Buf[i]=(((MCP2515_ReadByte(RXB0SIDH))<<3) | ((MCP2515_ReadByte(RXB0SIDL))>>5)); } MCP2515_WriteByte(CANINTF,0);//清除中断标志位(中断标志寄存器必须由MCU清零) return len; } --------------------- 作者:Yellor__ 来源:CSDN 原文:https://blog.csdn.net/LLiu_M__/article/details/81782986 版权声明:本文为博主原创文章,转载请附上博文链接! |
4、总结
树莓派基于库的操作相对都是简单的。
1
5、参考
https://blog.csdn.net/eric_lmy/article/details/51946156
http://www.waveshare.net/wiki/RS485_CAN_HAT
MCP2515数据手册http://www.usr.cn/Uploads/Attach/201012/4cf65dd9de775.pdf
https://blog.csdn.net/tylr2005/article/details/52468433
http://www.geek-workshop.com/thread-33355-1-1.html
———————
作者:Yellor__
来源:CSDN
原文:https://blog.csdn.net/LLiu_M__/article/details/81782986
版权声明:本文为博主原创文章,转载请附上博文链接!