DM6467没有自带的CAN总线控制器,不过有SPI控制器,所以要使用CAN总线的话可以用MCP2510芯片实现SPI总线到CAN总线的转换。总线转换电路如下所示。
对于CAN总线模块的测试需要先测试SPI模块,然后测试MCP2510部分。DM6467的SPI模块自带回环模式(loop-backmode),可以自发自收,对于测试比较方便。并且MCP2510也自带回环模式,也可以测试发送和接收。所以我们的测试过程是:
1, 初始化SPI模块,置为回环模式,测试收发数据。
2, 设置SPI模块为正常工作模式,配置MCP2510为回环模式,测试数据收发。
3, 用SPI配置MCP2510为正常工作模式,测试CAN总线上的波形。
对于SPI总线协议这里不做介绍,只讲DM6467中SPI模块初始化的步骤。
1, 置寄存器CPIGCR0=0以复位SPI模块,然后置SPIGCR0=1使能SPI模块,开始配置。
2, 配置SPIGCR1为loop-back模式,DM6467为SPI主设备,使用内部时钟(SYSCLK3)。
3, 配置SPIPC0选择我们使用了的四根SPI线:MISO,MOSI,CS0和CLK。
4, 配置SPIFMT,选择传输字长为1个字节,至于时钟相序的搭配需根据MCP2510的实际情况来配置,将在下一部分介绍,在回环模式时可以使用任意格式。
5, 配置SPIDAT1选择CS0。
6, 置SPIGCR1的第24位为1,使SPI处于工作状态。
具体的配置代码如下所示:
void SPI_init(void) { SPIGCR0 = 0; _waitmsec(100); SPIGCR0 = 1; _waitmsec(100); SPIGCR1 = 0 | (1 << 16) | (1 << 1) | (1 << 0); // loop-back; master; internal clock SPIPC0 = 0 | (1 << 11) | (1 << 10) | (1 << 9) | 1; // SOMI, SIMO, CLK, CS0 SPIFMT0 = 0 | (0x3f << 24) | (1 << 17) | (0xff << 8) | 8; //word length: 8-bit SPIDAT1 = 0 | (2 << 16); // CS0 selected SPIGCR1 = SPIGCR1 | (1 << 24); // activate spi } |
完成初始化配置后,需要接收和发送数据。接收和发送数据都比较简单,主要方法就是根据中断寄存器来判断数据是否发送完毕或者接收缓冲区是否接到数据。
实现SPI接收数据的代码如下:
while(SPIBUF & (1 << 31)); // exit when RXEMPTY=0 temp = 0xff & SPIBUF; |
实现SPI发送数据的代码如下:
SPIDAT1 = 0 | (2 << 16) | (data & 0xff); while(SPIBUF & (1 << 29)); // TXFULL=0 |
接下来就可以测试SPI模块自发自收数据的过程了,使用以下代码实现:
SPI_TX(0xa9); _waitmsec(100); reg = SPI_RX(); |
运行后显示reg为0xa9,表示SPI模块能够正常工作。
开始测试MCP2510部分之前需要将SPI模块设置为正常工作模式,这只需要将SPIGCR1的第16位置为0即可。
然后需要配置SPI模块的时序。在MCP2510中对发送和接收数据的相序有明确要求,MCP2510发送数据的时序和接收数据的时序分别为:
根据MCP2510的芯片手册,我们需要配置DM6467SPI模块的SPIFMT寄存器为:polarity = 1;phase = 0。这种配置模式下SPI模块的时序如下所示:
在配置好之后就可以测试SPI模块的输出波形了,验证是否符合MCP2510的要求。使用示波器捕捉到SPI发数据时CLK引脚与MOSI引脚的波形如下所示:
发送数据时CLK引脚和CS0引脚的波形如下所示:
通过以上两张图片显示的波形我们可以得知SPI模块的输出波形是符合MCP2510要求的,所以能够与MCP2510进行正常通讯。
接下来需要配置MCP2510的寄存器,配置的步骤包括:
1, 向MCP2510发送reset命令,复位该芯片。
2, 配置CANCTRL寄存器,REQOP[2:0]=100b,即配置模式。
3, 配置CNF1、CNF2和CNF3,设定波特率。
4, 配置发送缓冲器,设置ID、优先级、数据长度等参数。
5, 配置接收缓冲器,设置ID、接收模式等,可以选择配置接收滤波器。
6, 配置CANCTRL,REQOP[2:0]=010b,即回环模式。
MCP2510的初始化代码如下所示:
void MCP2510_init(void) { MCP2510_reset(); // reset mcp2510 MCP2510_WriteOneByte(CANCTRL, 0x80); // REQOP[2:0] = 100b: configuration mode MCP2510_WriteOneByte(CNF1, 0x01); // set baudrate MCP2510_WriteOneByte(CNF2, 0xae); MCP2510_WriteOneByte(CNF3, 0x05);
MCP2510_WriteOneByte(TXB0CTRL, 0x00); // lowest priority MCP2510_WriteOneByte(TXB0SIDH, 0x00); // identifier bits: 0000 0000 110 b MCP2510_WriteOneByte(TXB0SIDL, 0xc0); // standard identifier MCP2510_WriteOneByte(TXB0EID8, 0x00); MCP2510_WriteOneByte(TXB0EID0, 0x00); MCP2510_WriteOneByte(TXB0DLC, 0x01); // data frame; data field length: one byte
MCP2510_WriteOneByte(RXB0CTRL, 0x60); // receive all messages; rollover disabled; RXF0 MCP2510_WriteOneByte(RXF0SIDH, 0x00); // identifier bits: 0000 0000 110b MCP2510_WriteOneByte(RXF0SIDL, 0xc0); MCP2510_WriteOneByte(RXF0EID8, 0x00); MCP2510_WriteOneByte(RXF0EID0, 0x00);
//MCP2510_WriteOneByte(CANCTRL, 0x47); // REQOP[2:0] = 010b: loopback mode; clock out enable; 1/8 Fclkout _waitmsec(10); }
void MCP2510_WriteOneByte(Uint8 addr, Uint8 data) { SPI_TX(MCP2510CMD_WRITE); SPI_TX(addr); SPI_TX(data); SPIPC2 = SPIPC2 | 0x3; _waitmsec(1); } |
接下来编写发送CAN报文的函数。暂时我们设置的报文数据域长度为1,即只发送一个字节。发送之前需要将数据写到发送缓冲寄存器,然后置TXB0CTRL.TXREQ位为1即可,代码如下所示:
void CAN_TXOneByte(Uint8 data) { MCP2510_WriteOneByte(0x36, data); MCP2510_WriteOneByte(TXB0CTRL, 0x0d); // request to send data; highest priority } |
最后,在主程序中通过以下代码来实现回环测试。
CAN_TXOneByte(0xa9); // data: 0xa9 reg = MCP2510_ReadOneByte(RXB0D0); printf("reg = 0x%2.2x\n", reg); |
运行代码,输出reg=0xa9,表示MCP2510回环测试成功。
要使MCP2510正常工作,只需在原来的初始化代码基础上修改CANCTRL的REQOP[2:0]为000b。然后运行程序,在总线上测试CAN报文的发送情况。
由于现在没有从设备,所以每次的发送都是失败,CAN控制器会一直发送报文,这也方便了测试。使用示波器测得CAN总线上CANH和CANL两根线上的波形如下所示: