在调试基于F103单片机的MCP2515时,怎么调试都不正常,用的还是以前调试过的代码,隔了一段时间后再重新运行就不行了;
于是单步运行调试,没想到,单步运行调试竟然成功了;于是想到是延时的问题,在初始化那里的reset函数里加了个延时,再下载进去板子里果然就正常了。
void mcp2515_reset(void)
{
static uint8_t status;
uint8_t cmd = MCP2515_CMD_RESET;
mcp2515_cs_enable();//置MCP2515的CS为低电平
status = HAL_SPI_Transmit(&hspi1,&cmd,1,100);//发送写命令//发送寄存器复位命令
delay_ms(100); //没有这个延时,下载到板子里就不能正行发送数据,单步调试过去这里后就能正常运行,所以断定是没有这个延时的原因。
mcp2515_cs_disable(); //置MCP2515的CS为高电平
}
看了MCP2515的datasheet后,SPI接口描述如下图,MCP2515第一次发送数据时,需要先拉高CS引脚,再拉低CS引脚进行写入数据;于是按照下面更改mcp2515_reset函数也是可以正常运行的:
void mcp2515_reset(void)
{
static uint8_t status;
uint8_t cmd = MCP2515_CMD_RESET;
mcp2515_cs_enable();//置MCP2515的CS为低电平
status = HAL_SPI_Transmit(&hspi1,&cmd,1,100);//发送写命令//发送寄存器复位命令
// delay_ms(100); //去掉这里
mcp2515_cs_disable(); //置MCP2515的CS为高电平
}
uint8_t mcp2515_init(uint16_t Bps)
{
uint8_t temp[4] = { 0, 0, 0, 0 };
MX_SPI1_Init();
/* Deselect: Chip Select high */
mcp2515_cs_disable();//取消片选 //加上这里
// MCP2515 启动前进行软件复位
mcp2515_reset();
。。。
}
由于SPI初始化完成后,直接将CS拉低就发送命令,mcp2515是收不到命令的,看到下面图中红色框框中的描述就知道原因了,所以真正的原因不是上面描述的加延时的问题,而是CS需要先上拉的问题。
MCP2515 SPI接口的描述:

;另外,CAN采样点标准如下,设置波特率配置时可以作为参考:
/*
*CAN采样点:baud>800k时,75%;
* baud>500时,80%;
* baud<=500时,87.5%
*/
源代码:
can2_dev.c
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_spi.h"
#include "can2_dev.h"
#include "w25qxx.h"
#include <rtthread.h>
#include <rtdef.h>
#include "spi.h"
#include "delay.h"
extern SPI_HandleTypeDef hspi1;
void mcp2515_cs_enable(void)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET);//片选引脚
}
void mcp2515_cs_disable(void)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET);//片选引脚
}
/**********************************************************
*函数说明:MCP2515写控制寄存器程序
*输入: 寄存器地址,写入数据
*输出: 无
*调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
//说明:本函数是用来设置寄存器的,写一字节数据
uint8_t mcp2515_write_register(unsigned char addr,unsigned char dat)
{
uint8_t w_buf[1];
uint8_t status;
mcp2515_cs_enable();//置MCP2515的CS为低电平
w_buf[0] = MCP2515_CMD_WRITE;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送写命令
w_buf[0] = addr;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100); //发送地址
w_buf[0] = dat;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100); //写入数据
mcp2515_cs_disable();//置MCP2515的CS为高电平
return status;
}
/**********************************************************
*函数说明:对MCP2515连续寄存器进行连续写操作
*输入: 连续寄存器起始地址,数据指针,数据长度
*输出: 无
*调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
void mcp2515_write_register_p( uint8_t addr, uint8_t *data, uint8_t len )
{
uint8_t w_buf[1];
// CS low ,MCP2515 enable
mcp2515_cs_enable();
w_buf[0] = MCP2515_CMD_WRITE;
HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送写命令
w_buf[0] = addr;
HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送起始寄存器地址
HAL_SPI_Transmit(&hspi1,data,len,100); //发送数据
//CS high ,MCP2515 disable
mcp2515_cs_disable();
}
/**********************************************************
* 函数说明:MCP2515读控制寄存器程序
* 输入: 寄存器地址,
* 输出: 寄存器数据
* 调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
uint8_t mcp2515_read_register(unsigned char addr)
{
uint8_t w_buf;
mcp2515_cs_enable();//置MCP2515的CS为低电平
w_buf = MCP2515_CMD_READ;
HAL_SPI_Transmit(&hspi1,&w_buf,1,100);//发送写命令
w_buf = addr;
HAL_SPI_Transmit(&hspi1,&w_buf,1,100); //发送地址
HAL_SPI_Receive(&hspi1,&w_buf,1,100); //写入数据
mcp2515_cs_disable();//置MCP2515的CS为高电平
return w_buf;
}
/**********************************************************
* 函数说明:MCP2515连续读控制寄存器程序
* 输入: 寄存器地址,
* 输出: 寄存器数据
* 调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
uint8_t mcp2515_read_data(unsigned char addr,uint8_t *readbyte,uint16_t len)
{
uint8_t w_buf[1];
uint8_t status = HAL_OK;
mcp2515_cs_enable();//置MCP2515的CS为低电平
w_buf[0] = MCP2515_CMD_READ;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送写命令
w_buf[0] = addr;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100); //发送地址
for(int i=0; i<len ;i++){
status = HAL_SPI_Receive(&hspi1,w_buf,1,100); //接收数据
*readbyte++ = w_buf[0];
}
mcp2515_cs_disable();//置MCP2515的CS为高电平
return status;
}
/**********************************************************
* 函数说明:对MCP2515连续寄存器进行连续读操作
* 输入: 连续寄存器起始地址,数据指针,数据长度
* 输出: 无
* 调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
void mcp2515_read_register_p( uint8_t addr, uint8_t *data, uint8_t len )
{
uint8_t w_buf[1];
// CS low ,MCP2515 enable
mcp2515_cs_enable();
w_buf[0] = MCP2515_CMD_READ;
HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送写命令
w_buf[0] = addr;
HAL_SPI_Transmit(&hspi1,w_buf,1,100); //发送地址 //发送起始寄存器地址
HAL_SPI_Receive(&hspi1,data,len,100); //接收数据 //数据保存
// HAL_SPI_TransmitReceive(&hspi1,data,data,len,100);
//CS high ,MCP2515 disable
mcp2515_cs_disable();
}
/**********************************************************
* 函数说明:读MCP2515接收缓冲器程序
* 输入: 缓冲器地址,
* 输出: 缓冲器数据
* 调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
uint8_t mcp2515_read_rx_buffer(uint8_t addr)
{
uint8_t data;
uint8_t w_buf[1];
// 判断adress是否有效,除了1,2位,其余都应为0
if (addr & 0xF9)
return 0;
// CS low ,MCP2515 enable
mcp2515_cs_enable();
w_buf[0] = MCP2515_CMD_READ_RXB0SIDH | addr;
HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送写命令 //发送读取控制字
HAL_SPI_Receive(&hspi1,&data,1,100); //读回数据
//CS high ,MCP2515 disable
mcp2515_cs_disable();
return data;
}
uint8_t mcp2515_read_status(uint8_t *sta){
uint8_t w_buf[1];
int status;
mcp2515_cs_enable();
w_buf[0] = MCP2515_CMD_READ_STATUS;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);
status = HAL_SPI_Receive(&hspi1,w_buf,1,100);
*sta = w_buf[0];
mcp2515_cs_disable();
return status;
}
void mcp2515_reset(void)
{
static uint8_t status;
uint8_t cmd = MCP2515_CMD_RESET;
mcp2515_cs_enable();//置MCP2515的CS为低电平
status = HAL_SPI_Transmit(&hspi1,&cmd,1,100);//发送写命令//发送寄存器复位命令
delay_ms(100);
mcp2515_cs_disable(); //置MCP2515的CS为高电平
}
/*******************************************************************
* 函数说明:MCP2515控制寄存器位修改程序
* 输入: 寄存器地址,修改位,修改数据
* 输出: 无
* 调用函数:SPI发送程序SPI1_ReadWriteByte
**********************************************************/
uint8_t mcp2515_bit_modify(uint8_t address,uint8_t mask,uint8_t dat)
{
uint8_t w_buf[1];
uint8_t status;
mcp2515_cs_disable();
// Delay(2); //CS禁止时间,大于50nS
mcp2515_cs_enable();
w_buf[0] = MCP2515_CMD_BIT_MODIFY;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送modify命令
w_buf[0] = address;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送寄存器地址命令
w_buf[0] = mask;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送屏蔽字节
w_buf[0] = dat;
status = HAL_SPI_Transmit(&hspi1,w_buf,1,100);//发送数据字节
mcp2515_cs_disable();
return status;
}
//功能:设置2515的工作模式,
//参数:newmode: MCP2515的五种模式之一
//返回:实际的工作模式,返回之后需要判断返回值来确定是否设置成功
uint8_t mcp2515_set_can_mode(uint8_t mode){
uint8_t i;
mcp2515_bit_modify(CANCTRL,MODE_MASK,mode);
i = mcp2515_read_register(CANCTRL);
i&= MODE_MASK;
if(i == mode)
return MCP2515_OK; //OK 为 0
else
return MCP2515_FAIL; //FAIL 为 1
}
/*************************************************************
* 函数说明:CAN BUS发送消息程序
* 通过对标志位判断,来判断报文格式和报文用途
* 完成报文ID,数据的发送功能
* 输入:uint32_t id , 节点标示位信息
* uint8_t *data , 待发数据
* uint8_t length, 数据长度
* uint8_t flags), 标志位,
* 标志位包含两个信息:1.数据报文or远程数据请求
* 2.标准格式 or 扩展格式
* 输出: 无
* 调用函数: mcp2515_write_register_p()
* SPI1_ReadWriteByte()
* mcp2515_write_register()
*************************************************************/
uint8_t mcp2515_can_send_msg( uint32_t id, uint8_t *data, uint8_t length, uint8_t flags)
{
uint8_t temp[4],error=0,w_buf;
uint32_t i=0;
if (length > 8)
length = 0;
//设置发送缓冲器0 发送优先级,TXP[1:0] = 11 , 为最高发送优先级
mcp2515_bit_modify( TXB0CTRL, (1<<TXP1)|(1<<TXP0), (1<<TXP1)|(1<<TXP0) );
// 根据标志位,确定其ID格式,标准格式或扩展格式,同时发送其ID
if (2 == flags) //扩展格式,11+18位ID
{
temp[0] = (uint8_t) (id>>21); //取出基本ID的高8位
//取出基本ID的低三位,加上扩展ID的高2位,同时设置扩展标示符EXIDE使能
temp[1] = (((uint8_t) (id>>16)) & 0x03) | (((uint8_t) (id>>13)) & 0xE0) | (1<<EXIDE);
temp[2] = (uint8_t) (id>>8); //取扩展ID
temp[3] = (uint8_t) id;
}
else //标准格式,11位ID
{
temp[0] = (uint8_t) (id>>3); //取出基本ID的高8位
temp[1] = (uint8_t) (id<<5); //取出基本ID的低3位 , 同时设置扩展标示符EXIDE为0,即禁用
temp[2] = 0;
temp[3] = 0;
}
mcp2515_write_register_p( TXB0SIDH, temp, 4 ); //发送ID
// 根据标志位,设定报文是否为远程发送请求,同时设定数据字节数
if (4 ==flags)
mcp2515_write_register( TXB0DLC, length | (1<<RTR) ); //设定发送的报文为远程发送请求,并定义报文数据字节数
else
mcp2515_write_register( TXB0DLC, length ); // 设定报文的数据字节数
//写入发送数据
mcp2515_write_register_p( TXB0D0, data, length );
// 请求发送报文,发送缓冲器为TXB0
mcp2515_cs_enable();
w_buf = MCP2515_CMD_RTS_TXB0;
HAL_SPI_Transmit(&hspi1,&w_buf,1,100);
mcp2515_cs_disable();
i=0;
while(((mcp2515_read_register(TXB0CTRL)& (1<<TXREQ))== (1<<TXREQ))&&((i++) < 360)); //等待发送结束
if(i>=360)//超时
{
error = 1;//有错误
}
return error;
}
/*************************************************************
*函数说明:CAN BUS接收消息程序
*输入: u32 *id , 接收到的节点标示位信息指针
* uint8_t *data , 接收到的数据
* uint8_t *length, 数据长度
* uint8_t *flags, 标志位信息,
* 标志位包含信息:1.接收到的是哪个接收缓冲器
*
* 输出: 无
* 调用函数: mcp2515_read_register_p()
* mcp2515_read_register()
*************************************************************/
uint8_t mcp2515_can_read_msg( uint32_t *ID, uint8_t *data, uint8_t *length, uint8_t *flags) //此函数目前有问题,会进入hard_fault中断
{
uint8_t Intf_temp,len;
uint8_t ID_temp[4] = {0,0,0,0};
length = 0;
uint8_t ret = HAL_ERROR;
Intf_temp = mcp2515_read_register( CANINTF); //读取mcp2515中断标志位
Intf_temp = (Intf_temp & ((1<<RX1IF) |(1<<RX0IF))); //取出CANINTF中的RX1IF,RX0IF判断是否有缓冲器满
if(Intf_temp == (1<<RX0IF)) //判断是否为RX0中断
{
*flags = 0xA0 ; //表示接收到的是RX0缓冲器
//RXB_CTRL_temp = mcp2515_read_register( RXB0CTRL ); //读取验收滤波器编号
mcp2515_read_register_p( RXB0SIDH, ID_temp, 4); //读取4个字节的ID信息
len = mcp2515_read_register(RXB0DLC); //读取数据字节长度
mcp2515_read_register_p( RXB0D0, data, len); //读取数据
mcp2515_bit_modify( CANINTF, (1<<RX0IF), 0 ); //清除缓冲器0满标志
if(ID_temp[1]&0x08)//扩展帧?
{
*ID = (((uint32_t) ID_temp[0]<<21)|((uint32_t) (ID_temp[1]&0xE0)<<13)|((uint32_t) (ID_temp[1]&0x03)<<16)|((uint32_t) ID_temp[2]<<8)|ID_temp[3]); //将4个8位数据还原为32位//扩展帧
}
else
{
*ID = (((uint32_t) ID_temp[0]<<3)|(ID_temp[1]>>5)); //将4个8位数据还原为32位//标准帧
}
*length = len;
ret = HAL_OK;
}
else if(Intf_temp == (1<<RX1IF)) //判断是否为RX1中断
{
*flags = 0xA1 ; //表示接收到的是RX1缓冲器
mcp2515_read_register_p( RXB1SIDH , ID_temp , 4); //读取4个字节的ID信息
len = mcp2515_read_register( RXB1DLC ); //读取数据字节长度
mcp2515_read_register_p( RXB1D0 , data , len); //读取数据
mcp2515_bit_modify( CANINTF, (1<<RX1IF), 0 ); //清除缓冲器1满标志
if(ID_temp[1]&0x08)//扩展帧?
{
*ID = (((uint32_t) ID_temp[0]<<21)|((uint32_t) (ID_temp[1]&0xE0)<<13)|((uint32_t) (ID_temp[1]&0x03)<<16)|((uint32_t) ID_temp[2]<<8)|ID_temp[3]); //将4个8位数据还原为32位//扩展帧
}
else
{
*ID = (((uint32_t) ID_temp[0]<<3)|(ID_temp[1]>>5)); //将4个8位数据还原为32位//标准帧
}
*length = len;
ret = HAL_OK;
}
return ret;
// CanRecMessage(2,*ID,data);
}
uint8_t can_read_message(struct can_msg *msg)
{
uint8_t Intf_temp;
uint8_t ID_temp[4] = {0,0,0,0};
uint8_t ret = HAL_ERROR;
Intf_temp = mcp2515_read_register(CANINTF); //读取mcp2515中断标志位
Intf_temp = (Intf_temp&(MCP_STAT_RX1IF|MCP_STAT_RX0IF)); //取出CANINTF中的RX1IF,RX0IF判断是否有缓冲器满
if(Intf_temp == MCP_STAT_RX0IF) //判断是否为RX0中断
{
msg->flag= 0xA0 ; //表示接收到的是RX0缓冲器
//RXB_CTRL_temp = mcp2515_read_register( RXB0CTRL ); //读取验收滤波器编号
mcp2515_read_register_p(MCP_RXB0SIDH,ID_temp,4);//读取4个字节的ID信息
msg->len= mcp2515_read_register(MCP_RXB0DLC); //读取数据字节长度
mcp2515_read_register_p(MCP_RXB0D0,msg->Dat,msg->len); //读取数据
mcp2515_bit_modify(CANINTF,MCP_STAT_RX0IF,0); //清除缓冲器0满标志
if(ID_temp[1]&0x08)//扩展帧?
{
msg->id = (((uint32_t) ID_temp[0]<<21)|((uint32_t) (ID_temp[1]&0xE0)<<13)|((uint32_t) (ID_temp[1]&0x03)<<16)|((uint32_t) ID_temp[2]<<8)|ID_temp[3]); //将4个8位数据还原为32位//扩展帧
}
else
{
msg->id = (((uint32_t) ID_temp[0]<<3)|(ID_temp[1]>>5)); //将4个8位数据还原为32位//标准帧
}
ret = HAL_OK;
}
else if(Intf_temp==MCP_STAT_RX1IF) //判断是否为RX1中断
{
msg->flag= 0xA1 ; //表示接收到的是RX1缓冲器
mcp2515_read_register_p(MCP_RXB1SIDH ,ID_temp,4); //读取4个字节的ID信息
msg->len= mcp2515_read_register(MCP_RXB1DLC); //读取数据字节长度
mcp2515_read_register_p(MCP_RXB1D0,msg->Dat,msg->len); //读取数据
mcp2515_bit_modify(CANINTF,MCP_STAT_RX0IF,0);//清除缓冲器1满标志
//msg->id=((uint32)(ID_temp[0]<<3))|((uint32)(ID_temp[1]>>5));
if(ID_temp[1]&0x08)//扩展帧?
{
msg->id = (((uint32_t) ID_temp[0]<<21)|((uint32_t) (ID_temp[1]&0xE0)<<13)|((uint32_t) (ID_temp[1]&0x03)<<16)|((uint32_t) ID_temp[2]<<8)|ID_temp[3]); //将4个8位数据还原为32位//扩展帧
}
else
{
msg->id = (((uint32_t) ID_temp[0]<<3)|(ID_temp[1]>>5)); //将4个8位数据还原为32位//标准帧
}
ret = HAL_OK;
}
return ret;
}
/***********************************************************
晶振16MHz
波特率(bps) CNF1(hex) CNF2(hex) CNF3(hex)
1M 0x00 0x82 0x02
800K 0x00 0x92 0x02
500K 0x00 0x9E 0x03
250K 0x01 0x1E 0x03
125K 0x03 0x9E 0x03
100K 0x04 0x9E 0x03
50K 0x09 0xA4 0x04
20K 0x18 0xA4 0x04
10K 0x31 0xA4 0x04
5K 0x3F 0xBF 0x07
晶振8MHz
波特率(bps) CNF1(hex) CNF2(hex) CNF3(hex)
500K 0x00 0x82 0x02
250K 0x00 0x9E 0x03
125K 0x01 0x1E 0x03
50K 0x04 0x9E 0x03
10K 0x18 0xA4 0x04
5K 0x31 0xA4 0x04
***********************************************************/
//功能: 设置通信的速率
//参数: canSpeed:20k或125k
//返回: 设置成功(0)与否(1)
uint8_t mcp2515_cfg_baudrate(uint32_t canSpeed)
{
unsigned char cfg1, cfg2, cfg3;
unsigned char cfg1_r, cfg2_r, cfg3_r;
uint8_t res = MCP2515_OK;
uint8_t mode = 0;
mcp2515_bit_modify( CANCTRL, 0xE0, (1<<REQOP2) );
switch (canSpeed)
{
case 5: //5K
cfg1 =0x31;
cfg2 =0xa4;
cfg3 =0x04;
break;
case 10: //10K
cfg1 =0x18;
cfg2 =0xa4;
cfg3 =0x04;
break;
case 50: //50K
cfg1 =0x04;
cfg2 =0x9e;
cfg3 =0x03;
break;
case 125: //125K
cfg1 =0x01; //采样点87.5%的配置,01,1e,03采样点没找出来,不过这个配置也可以
cfg2 =0xB5;
cfg3 =0x01;
break;
case 250: //250K
cfg1 =0x00;
cfg2 =0x9e;
cfg3 =0x03;
break;
case 500: //500K
cfg1 =0x00;
cfg2 =0x82;
cfg3 =0x02;
break;
// case 1000: //1000K //8M晶振配置到不了1000波特率
// cfg1 =0x00;
// cfg2 =0x82;
// cfg3 =0x02;
break;
default: //250K
cfg1 =0x00;
cfg2 =0x9e;
cfg3 =0x03;
break;
}
mcp2515_write_register(CNF1,cfg1);
mcp2515_write_register(CNF2,cfg2);
mcp2515_write_register(CNF3,cfg3);
mcp2515_bit_modify( CANCTRL, 0xE0, 0);
//add1 by zj start
cfg1_r = mcp2515_read_register(CNF1);
cfg2_r = mcp2515_read_register(CNF2);
cfg3_r = mcp2515_read_register(CNF3);
if(cfg1 != cfg1_r || cfg2 != cfg2_r || cfg3 != cfg3_r)
{
res = MCP2515_FAIL;
}
mode = mcp2515_read_register(CANCTRL);
if ((mode >>5) != MCP2515_NORMAL_WORK_MODE )
{
res = MCP2515_FAIL;
}
//add1 by zj end
return res;
}
//**********************************************************//
// 函数说明:MCP2515初始化程序 //
// 输入: bps //
// 输出: 无 //
// 调用函数:SPI1_ReadWriteByte //
//**********************************************************//
uint8_t mcp2515_init(uint16_t Bps)
{
uint8_t temp[4] = { 0, 0, 0, 0 };
/* Deselect: Chip Select high */
mcp2515_cs_disable();//取消片选
MX_SPI1_Init();
// MCP2515 启动前进行软件复位
mcp2515_reset();
//使用位修改指令将MCP2515设置为配置模式
//也就是将CANCTRL寄存器的REQOP[2:0]设置为100
mcp2515_bit_modify( CANCTRL, 0xE0, (1<<REQOP2) );
rt_kprintf("RTC mode1 CANCTRL:%02x\r\n",mcp2515_read_register(CANCTRL));
//举例:
// 时钟频率:Fosc = 8MHz
// 分频控制器 CNF1.BRP[5:0] = 3
// 最小时间份额 TQ = 2 * ( BRP + 1 ) / Fosc = 2*(3+1)/8M = 1uS
// 同步段 Sync Seg = 1TQ
// 传播段 Prop Seg = ( PRSEG + 1 ) * TQ = 1 TQ
// 相位缓冲段 Phase Seg1 = ( PHSEG1 + 1 ) * TQ = 3 TQ
// 相位缓冲段 Phase Seg2 = ( PHSEG2 + 1 ) * TQ = 3 TQ
// 同步跳转长度设置为 CNF1.SJW[1:0] = 00, 即 1TQ
// 总线波特率 NBR = Fbit = 1/(sync seg + Prop seg + PS1 + PS2 )
// = 1/(8TQ) = 1/8uS = 125kHz
switch(Bps)//设置波特率
{
case 125:
{
//设置为250kbps ,TQ = 1us
//分配0,同步段 1TQ
mcp2515_write_register( CNF1, 0x03);
// 设置传播段 Prop Seg 为00,即1TQ,相位缓冲段 Phase Seg1的长度3TQ
mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
// 设置 相位缓冲段 Phase Seg2为 3TQ , 禁用唤醒滤波器
mcp2515_write_register( CNF3, (1<<PHSEG21) );
mcp2515_write_register( CNF1, 0x03);
break;
}
case 250:
{
//设置为250kbps ,TQ = 1/2us
//分配0,同步段 1TQ
mcp2515_write_register( CNF1, 0x01);
// 设置传播段 Prop Seg 为00,即1TQ,相位缓冲段 Phase Seg1的长度3TQ
mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
// 设置 相位缓冲段 Phase Seg2为 3TQ , 禁用唤醒滤波器
mcp2515_write_register( CNF3, (1<<PHSEG21) );
mcp2515_write_register( CNF1, 0x01);
break;
}
case 500:
{
//设置为500kbps ,TQ = 1/4us
//分配0,同步段 1TQ
mcp2515_write_register( CNF1, (0<<SJW0) );
// 设置传播段 Prop Seg 为00,即1TQ,相位缓冲段 Phase Seg1的长度3TQ
mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
// 设置 相位缓冲段 Phase Seg2为 3TQ , 禁用唤醒滤波器
mcp2515_write_register( CNF3, (1<<PHSEG21) );
break;
}
case 1000:
{
//设置为1000kbps ,TQ = 1/4us
//分配0,同步段 1TQ
mcp2515_write_register( CNF1, (0<<SJW0) );
// 设置传播段 Prop Seg 为00,即1TQ,相位缓冲段 Phase Seg1的长度1TQ
mcp2515_write_register( CNF2, (1<<BTLMODE)|(0<<PHSEG11) );
// 设置 相位缓冲段 Phase Seg2为 1TQ , 禁用唤醒滤波器
mcp2515_write_register( CNF3, (0<<PHSEG21) );
break;
}
default:
{
//设置为250kbps ,TQ = 1/2us
//分配0,同步段 1TQ
mcp2515_write_register( CNF1, 0x01);
// 设置传播段 Prop Seg 为00,即1TQ,相位缓冲段 Phase Seg1的长度3TQ
mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
// 设置 相位缓冲段 Phase Seg2为 3TQ , 禁用唤醒滤波器
mcp2515_write_register( CNF3, (1<<PHSEG21) );
mcp2515_write_register( CNF1, 0x01);
break;
}
}
#if MCP2515_LOG_EN > 0
rt_kprintf("RTC Set Bps:%d\r\n",Bps);
rt_kprintf("RTC Set CANCTRL:%02x\r\n",mcp2515_read_register(CANCTRL));
rt_kprintf("RTC Set CNF1:%02x\r\n",mcp2515_read_register(CNF1));
rt_kprintf("RTC Set CNF2:%02x\r\n",mcp2515_read_register(CNF2));
rt_kprintf("RTC Set CNF3:%02x\r\n",mcp2515_read_register(CNF3));
#endif
// 设置MCP2515中断使能寄存器,禁用所有中断
// mcp2515_write_register( CANINTE, /*(1<<RX1IE)|(1<<RX0IE)*/ 0 );
// 设置MCP2515中断使能寄存器,使能接收缓冲器中断
mcp2515_write_register( CANINTE, (1<<RX1IE)|(1<<RX0IE) );
//设置数据接收相关寄存器
// 设置RXM[1:0]=11,关闭接收缓冲器0屏蔽/滤波功能,接收所有报文;禁止滚存功能
mcp2515_write_register( RXB0CTRL, (1<<RXM1)|(1<<RXM0) );
// 设置RXM[1:0]=11,关闭接收缓冲器1屏蔽/滤波功能,接收所有报文;
mcp2515_write_register( RXB1CTRL, (1<<RXM1)|(1<<RXM0) );
//设置6个验收滤波寄存器为0,
mcp2515_write_register_p( RXF0SIDH, temp, 4 );
mcp2515_write_register_p( RXF1SIDH, temp, 4 );
mcp2515_write_register_p( RXF2SIDH, temp, 4 );
mcp2515_write_register_p( RXF3SIDH, temp, 4 );
mcp2515_write_register_p( RXF4SIDH, temp, 4 );
mcp2515_write_register_p( RXF5SIDH, temp, 4 );
//设置2个验收滤波寄存器为0,
mcp2515_write_register_p( RXM0SIDH, temp, 4 );
mcp2515_write_register_p( RXM1SIDH, temp, 4 );
//配置引脚
//设置接收相关引脚控制寄存器,配置它们禁用第二功能
mcp2515_write_register( BFPCTRL, 0 );
//调试使用,设置BFPCTRL使RX0BF,RX1BF设置为数字输出。
//mcp2515_bit_modify( BFPCTRL, (1<<B1BFE)|(1<<B0BFE)|(1<<B1BFM)|(1<<B0BFM), (1<<B1BFE)|(1<<B0BFE) );
//设置发送相关引脚控制寄存器,配置它们禁用第二功能
mcp2515_write_register( TXRTSCTRL, 0 );
//MCP2515进入环回模式,进行功能测试
//mcp2515_bit_modify( CANCTRL, 0XE0, (1<<REQOP1) );
//MCP2515进入正常模式
mcp2515_bit_modify( CANCTRL, 0xE0, 0);
rt_kprintf("RTC mode CANCTRL:%02x\r\n",mcp2515_read_register(CANCTRL));
return 0;
}
void mcp2515_Deinit(void)//SPI去初始化
{
HAL_SPI_DeInit(&hspi1);
}
/*******************************************************************
* 以下开始设备注册的代码(RTT)
*********************************************************************/
#define DEVICE_NAME "can2"
static struct rt_device can2_dev;
static rt_err_t drv_mcp2515_init(rt_device_t dev)
{
mcp2515_init(250);//默认250kbps
return RT_EOK;
}
static rt_err_t drv_mcp2515_open(rt_device_t dev, rt_uint16_t oflag)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET); /*给led上电,给w25q64上电,给mcp2515上电*/
delay_ms(20);
return RT_EOK;
}
static rt_err_t drv_mcp2515_close(rt_device_t dev)//注意这里关闭电源PB15 IF3V3,有其他设备电源也会关掉
{
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET); /*给mcp2515断电*/
return RT_EOK;
}
/*
输入参数:cmd:命令控制字;
args:根据控制命令字确定输入参数的类型;
*/
static rt_err_t drv_mcp2515_control(rt_device_t dev, int cmd, void *args)
{
uint16_t baud;
uint8_t ret = RT_EOK;
switch(cmd){
case MCP2515_BAUD_CFG:
baud = *((uint16_t*)args);
ret = mcp2515_cfg_baudrate(baud);
break;
default:
break;
}
return ret;
}
/*
输入参数:pos和size都没有用,所以输入可以设定pos=0,size=0;
输出数据:
读取的id,data,len,flag都通过buf传递出来。
buf[0-3] = id;
buf[4]=len;
buf[5]=flag;
buf[6-...]=data;
flag,代表从哪个接收缓冲器
*/
static rt_size_t drv_mcp2515_read(rt_device_t dev, rt_off_t pos, void *buf, rt_size_t size)
{
uint8_t ret,i;
struct can_msg canMsg;
canMsg.len = 0;
ret = can_read_message(&canMsg);
if(ret == HAL_OK && canMsg.len != 0){
((uint8_t*)buf)[0] = (canMsg.id >>24) & 0xff;
((uint8_t*)buf)[1] = (canMsg.id >>16) & 0xff;
((uint8_t*)buf)[2] = (canMsg.id >>8) & 0xff;
((uint8_t*)buf)[3] = canMsg.id &0xff;
((uint8_t*)buf)[4] = canMsg.len;
((uint8_t*)buf)[5] = canMsg.flag;
for(i=0;i<canMsg.len;i++){
((uint8_t*)buf)[6+i]=canMsg.Dat[i];
}
}
return ret;
}
/*
输入参数:pos和size都没有用,所以输入可以设定pos=0,size=0;
读取的id,data,len,flag都通过buf传递出来。
buf[0-3] = id;
buf[4]=len;
buf[5]=flag;
buf[6-...]=data;
flag,代表从扩展格式,还是标准格式
返回值:成功0,失败1.
*/
static rt_size_t drv_mcp2515_write(rt_device_t dev, rt_off_t pos, const void *buf, rt_size_t size)
{
uint8_t ret = RT_EOK;
uint32_t canid;
uint8_t len = ((uint8_t*)buf)[4];
uint8_t flags = ((uint8_t*)buf)[5];
canid = ((uint8_t*)buf)[0]<<24 | ((uint8_t*)buf)[1]<<16 | ((uint8_t*)buf)[2]<<8 | ((uint8_t*)buf)[3];
ret = mcp2515_can_send_msg(canid,((uint8_t*)buf)+6,len,flags);
return ret;
}
int drv_can2_init(void)
{
can2_dev.init = drv_mcp2515_init;
can2_dev.open = drv_mcp2515_open;
can2_dev.close = drv_mcp2515_close;
can2_dev.control= drv_mcp2515_control;
can2_dev.read = drv_mcp2515_read;
can2_dev.write = drv_mcp2515_write;
can2_dev.type = RT_Device_Class_SPIDevice;
rt_device_register(&can2_dev, DEVICE_NAME, RT_DEVICE_FLAG_RDWR);
return 0;
}
INIT_BOARD_EXPORT(drv_can2_init);
can2_dev.h
#ifndef __can2_dev_H
#define __can2_dev_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f1xx_hal.h"
//函数返回值宏定义
#define MCP2515_OK 0
#define MCP2515_FAIL 1
//CANSTAT——CAN 状态寄存器 (地址: XEh)
#define MCP2515_CFG_MODE 0x04 //100
#define MCP2515_NORMAL_WORK_MODE 0x0 //000
#define MCP2515_LOG_EN 1
#define MCP2515_BAUD_CFG 1
#define MCP2515_INT_PORT GPIOD /* GPIOD */
#define MCP2515_INT_PIN GPIO_PIN_2 /* PD.2 mcp2515中断输入引脚*/
//常用命令控制字节
#define MCP2515_CMD_RESET 0xC0
#define MCP2515_CMD_READ 0x03
#define MCP2515_CMD_READ_RXB0SIDH 0x90
#define MCP2515_CMD_READ_RXB0D0 0x92
#define MCP2515_CMD_READ_RXB1SIDH 0x94
#define MCP2515_CMD_READ_RXB1D0 0x96
#define MCP2515_CMD_WRITE 0x02
#define MCP2515_CMD_LOAD_TX_B0SIDH 0x40
#define MCP2515_CMD_LOAD_TX_B0D0 0x41
#define MCP2515_CMD_LOAD_TX_B1SIDH 0x42
#define MCP2515_CMD_LOAD_TX_B1D0 0x43
#define MCP2515_CMD_LOAD_TX_B2SIDH 0x44
#define MCP2515_CMD_LOAD_TX_B2D0 0x45
#define MCP2515_CMD_RTS_TXB0 0x81 // 请求发送报文,发送缓冲器为TXB0
#define MCP2515_CMD_RTS_TXB1 0x82 // 请求发送报文,发送缓冲器为TXB1
#define MCP2515_CMD_RTS_TXB2 0x84 // 请求发送报文,发送缓冲器为TXB2
#define MCP2515_CMD_BIT_MODIFY 0x05
#define MCP2515_CMD_READ_STATUS 0xA0
#define MCP2515_CMD_RX_STATUS 0xB0
#define MCP_STAT_RX0IF (1<<0)
#define MCP_STAT_RX1IF (1<<1)
#define MCP_RXB0SIDH 0x61
#define MCP_RXB0DLC 0x65
#define MCP_RXB0D0 0x66
#define MCP_RXB1SIDH 0x71
#define MCP_RXB1DLC 0x75
#define MCP_RXB1D0 0x76
#define MCP_RXB1D1 0x77
struct can_msg
{
uint8_t Dat[8]; //m0不支持非对齐访问,m3支持非对齐访问
uint8_t rtr;
uint8_t len;
uint8_t prty;
uint8_t flag;
uint32_t id;
};
// 定义寄存器地址
#define RXF0SIDH 0x00
#define RXF0SIDL 0x01
#define RXF0EID8 0x02
#define RXF0EID0 0x03
#define RXF1SIDH 0x04
#define RXF1SIDL 0x05
#define RXF1EID8 0x06
#define RXF1EID0 0x07
#define RXF2SIDH 0x08
#define RXF2SIDL 0x09
#define RXF2EID8 0x0A
#define RXF2EID0 0x0B
#define BFPCTRL 0x0C
#define TXRTSCTRL 0x0D
#define CANSTAT 0x0E
#define CANCTRL 0x0F
#define RXF3SIDH 0x10
#define RXF3SIDL 0x11
#define RXF3EID8 0x12
#define RXF3EID0 0x13
#define RXF4SIDH 0x14
#define RXF4SIDL 0x15
#define RXF4EID8 0x16
#define RXF4EID0 0x17
#define RXF5SIDH 0x18
#define RXF5SIDL 0x19
#define RXF5EID8 0x1A
#define RXF5EID0 0x1B
#define TEC 0x1C
#define REC 0x1D
#define RXM0SIDH 0x20
#define RXM0SIDL 0x21
#define RXM0EID8 0x22
#define RXM0EID0 0x23
#define RXM1SIDH 0x24
#define RXM1SIDL 0x25
#define RXM1EID8 0x26
#define RXM1EID0 0x27
#define CNF3 0x28
#define CNF2 0x29
#define CNF1 0x2A
#define CANINTE 0x2B
#define CANINTF 0x2C
#define EFLG 0x2D
#define TXB0CTRL 0x30
#define TXB0SIDH 0x31
#define TXB0SIDL 0x32
#define TXB0EID8 0x33
#define TXB0EID0 0x34
#define TXB0DLC 0x35
#define TXB0D0 0x36
#define TXB0D1 0x37
#define TXB0D2 0x38
#define TXB0D3 0x39
#define TXB0D4 0x3A
#define TXB0D5 0x3B
#define TXB0D6 0x3C
#define TXB0D7 0x3D
#define TXB1CTRL 0x40
#define TXB1SIDH 0x41
#define TXB1SIDL 0x42
#define TXB1EID8 0x43
#define TXB1EID0 0x44
#define TXB1DLC 0x45
#define TXB1D0 0x46
#define TXB1D1 0x47
#define TXB1D2 0x48
#define TXB1D3 0x49
#define TXB1D4 0x4A
#define TXB1D5 0x4B
#define TXB1D6 0x4C
#define TXB1D7 0x4D
#define TXB2CTRL 0x50
#define TXB2SIDH 0x51
#define TXB2SIDL 0x52
#define TXB2EID8 0x53
#define TXB2EID0 0x54
#define TXB2DLC 0x55
#define TXB2D0 0x56
#define TXB2D1 0x57
#define TXB2D2 0x58
#define TXB2D3 0x59
#define TXB2D4 0x5A
#define TXB2D5 0x5B
#define TXB2D6 0x5C
#define TXB2D7 0x5D
#define RXB0CTRL 0x60
#define RXB0SIDH 0x61
#define RXB0SIDL 0x62
#define RXB0EID8 0x63
#define RXB0EID0 0x64
#define RXB0DLC 0x65
#define RXB0D0 0x66
#define RXB0D1 0x67
#define RXB0D2 0x68
#define RXB0D3 0x69
#define RXB0D4 0x6A
#define RXB0D5 0x6B
#define RXB0D6 0x6C
#define RXB0D7 0x6D
#define RXB1CTRL 0x70
#define RXB1SIDH 0x71
#define RXB1SIDL 0x72
#define RXB1EID8 0x73
#define RXB1EID0 0x74
#define RXB1DLC 0x75
#define RXB1D0 0x76
#define RXB1D1 0x77
#define RXB1D2 0x78
#define RXB1D3 0x79
#define RXB1D4 0x7A
#define RXB1D5 0x7B
#define RXB1D6 0x7C
#define RXB1D7 0x7D
//定义寄存器BFPCTRL位信息
#define B1BFS 5
#define B0BFS 4
#define B1BFE 3
#define B0BFE 2
#define B1BFM 1
#define B0BFM 0
//定义寄存器TXRTSCTRL位信息
#define B2RTS 5
#define B1RTS 4
#define B0RTS 3
#define B2RTSM 2
#define B1RTSM 1
#define B0RTSM 0
//定义寄存器CANSTAT位信息
#define OPMOD2 7
#define OPMOD1 6
#define OPMOD0 5
#define ICOD2 3
#define ICOD1 2
#define ICOD0 1
//定义寄存器CANCTRL位信息
#define REQOP2 7
#define REQOP1 6
#define REQOP0 5
#define ABAT 4
#define CLKEN 2
#define CLKPRE1 1
#define CLKPRE0 0
//定义寄存器CNF3位信息
#define WAKFIL 6
#define PHSEG22 2
#define PHSEG21 1
#define PHSEG20 0
//定义寄存器CNF2位信息
#define BTLMODE 7
#define SAM 6
#define PHSEG12 5
#define PHSEG11 4
#define PHSEG10 3
#define PHSEG2 2
#define PHSEG1 1
#define PHSEG0 0
//定义寄存器CNF1位信息
#define SJW1 7
#define SJW0 6
#define BRP5 5
#define BRP4 4
#define BRP3 3
#define BRP2 2
#define BRP1 1
#define BRP0 0
//定义寄存器CANINTE位信息
#define MERRE 7
#define WAKIE 6
#define ERRIE 5
#define TX2IE 4
#define TX1IE 3
#define TX0IE 2
#define RX1IE 1
#define RX0IE 0
//定义寄存器CANINTF位信息
#define MERRF 7
#define WAKIF 6
#define ERRIF 5
#define TX2IF 4
#define TX1IF 3
#define TX0IF 2
#define RX1IF 1
#define RX0IF 0
//定义寄存器EFLG位信息
#define RX1OVR 7
#define RX0OVR 6
#define TXB0 5
#define TXEP 4
#define RXEP 3
#define TXWAR 2
#define RXWAR 1
#define EWARN 0
//定义寄存器TXBnCTRL ( n = 0, 1, 2 )位信息
#define ABTF 6
#define MLOA 5
#define TXERR 4
#define TXREQ 3
#define TXP1 1
#define TXP0 0
//定义寄存器RXB0CTRL位信息
#define RXM1 6
#define RXM0 5
#define RXRTR 3
#define BUKT 2
#define BUKT1 1
#define FILHIT0 0
//定义发送缓冲寄存器 TXBnSIDL ( n = 0, 1 )的位信息
#define EXIDE 3
//定义接受缓冲器1控制寄存器的位信息
#define FILHIT2 2
#define FILHIT1 1
/**
* 定义接收缓冲器n标准标示符低位 RXBnSIDL ( n = 0, 1 )的位信息
*/
#define SRR 4
#define IDE 3
// 定义接收缓冲器n数据长度码 RXBnDLC ( n = 0, 1 )的位信息
#define RTR 6
#define DLC3 3
#define DLC2 2
#define DLC1 1
#define DLC0 0
//#define NORMAL_MODE
#define MODE_MASK 0xE0
// Header:
// File Name:
// Author:
// Date:
uint8_t mcp2515_read_status(uint8_t *sta);
uint8_t mcp2515_init(uint16_t Bps);
uint8_t mcp2515_can_send_msg( uint32_t id, uint8_t *data, uint8_t length, uint8_t flags);
uint8_t can2_read_message(struct can_msg *msg);
uint8_t mcp2515_can_read_message(struct can_msg *msg);
uint8_t mcp2515_baud_cfg(uint16_t Bps);
#ifdef __cplusplus
}
#endif
#endif
调用方法:
uint32_t canid = 0x18ff5a53;
can_buf[0]=canid>>24;
can_buf[1]=canid>>16;
can_buf[2]=canid>>8;
can_buf[3]=canid;
can_buf[4]=0x82;
can_buf[5]='t';
can_buf[6]='e';
can_buf[7]='s';
can_buf[8]='t';
can_buf[9]='-';
can_buf[10]='c';
can_buf[11]='a';
can_buf[12]='n';
//
// /*can2 test*/
rt_device_t can2_dev;
can2_dev = n_device_find("can2");
n_device_open(can2_dev,RT_DEVICE_FLAG_RDWR);
n_can2_test();
uint32_t baud = 500;
ret = n_device_control(can2_dev,MCP2515_BAUD_CFG,&baud);
if(ret != N_OK)
{
rt_kprintf("can2 cfg baud:%d failed.\r\n",baud);
}
while(1)
{
n_device_write(can2_dev,0,can_buf,13);
ret = n_device_read(can2_dev,0,can_read_data,8); //此方法可以正常接收
if(ret == RT_EOK){
can_read_id = can_read_data[0]<<24|can_read_data[1]<<16|can_read_data[2]<<8|can_read_data[3];
canlen = can_read_data[4];
rt_kprintf("mcp2515 recv data:canid = 0x%08X, len=%d,flags=0x%02X,data:",can_read_id,can_read_data[4],can_read_data[5]);
for(int i=0;i<canlen;i++){
rt_kprintf("0x%02X ",can_read_data[6+i]);
}
rt_kprintf("\r\n");
}
rt_thread_delay(1000);
}

本文记录了在使用F103单片机调试MCP2515时遇到的问题及解决办法。在初始化过程中,通过增加延时和正确操作CS引脚状态,解决了数据无法正常发送的问题。调整后的代码确保了MCP2515能正确接收SPI命令,避免了不必要的延迟导致的通信失败。
2089

被折叠的 条评论
为什么被折叠?



