强大的AI8051U没有CAN?NO,只要有SPI就有CAN.
强大的AI8051U,看哪哪都好,全兼容唯一就是没有CAN做项目很受限。这都不是事。
大家来看过来,MCP2515完美解决。
看看怎么用吧:
MCP2515 编程核心是配置寄存器实现 CAN 通信模式,再通过数据寄存器收发数据,关键在于理解寄存器功能和初始化流程。以下内容提炼数据手册中编程相关的核心信息,按 “基础认知→编程步骤→关键寄存器” 结构整理,可直接用于开发参考。
[*]一、MCP2515 编程基础认知在开始编程前,需明确两个核心前提,避免后续配置出错:
[*]通信接口:MCP2515 通过SPI 接口与 MCU(如 STM32、Arduino)通信,编程时需先确保 MCU 的 SPI 外设正常工作(配置 SPI 时钟、极性、相位,需与 MCP2515 一致)。
[*]工作模式:MCP2515 有 3 种核心工作模式,编程时需通过 “模式配置寄存器(CANCTRL)” 设置,不同模式对应不同功能:
[*]配置模式(Configuration Mode):仅用于修改寄存器(如波特率、滤波规则),此时不参与 CAN 通信。
[*]正常模式(Normal Mode):正常收发 CAN 数据,支持标准帧(11 位 ID)和扩展帧(29 位 ID)。
[*]监听模式(Listen-Only Mode):仅接收 CAN 数据,不发送,也不参与总线仲裁,常用于调试或被动接收场景。
二、MCP2515 核心编程步骤(以 “正常收发” 为例)所有操作需遵循 “先进入配置模式→修改寄存器→切换到目标模式” 的逻辑,具体步骤如下:
1. 初始化 SPI 通信(MCU 侧)这是与 MCP2515 通信的前提,以 STM32 为例,关键配置项:
[*]SPI 模式:模式 0(CPOL=0,CPHA=0),MCP2515 默认支持此模式。
[*]时钟频率:MCP2515 最大支持 10MHz SPI 时钟,实际需根据 MCU 性能和通信稳定性调整(推荐 1-5MHz)。
[*]数据位:8 位数据帧,MSB(高位)先发送。
2. 进入配置模式(MCP2515 侧)MCP2515 上电默认进入 “休眠模式”,需主动切换到配置模式才能修改寄存器:
[*]向 MCP2515 发送 “写寄存器命令(0x02)”。
[*]发送目标寄存器地址:CANCTRL 寄存器(地址 0x0F)。
[*]发送配置值:0x80(将 CANCTRL 的最高位(REQOP2)设为 1,REQOP1/REQOP0 设为 0,即请求进入配置模式)。
[*]延时 1-2ms 后,读取 “CANSTAT 寄存器(地址 0x0E)”,确认 bit7-bit5 为100,即已成功进入配置模式。
3. 配置 CAN 波特率(核心!)CAN 波特率需与总线中其他设备一致(如 250kbps、500kbps、1Mbps),通过 “波特率配置寄存器(CNF1、CNF2、CNF3)” 设置,核心是计算 “位时序”(每个 CAN 位分为同步段、传播段、相位缓冲段 1/2)。
以500kbps 波特率(假设 MCP2515 外部晶振为 16MHz) 为例,配置值参考:
[*]CNF1 寄存器(地址 0x2A):0x00(SJW=1TQ,BRP=0,即波特率预分频系数 = 1)。
[*]CNF2 寄存器(地址 0x29):0x90(BTLMODE=1,SAM=1,PHSEG1=4TQ)。
[*]CNF3 寄存器(地址 0x28):0x02(PHSEG2=3TQ)。
[*]位时序总长度:1(同步段)+ 1(传播段)+ 4(PHSEG1)+ 3(PHSEG2)= 9TQ,波特率 = 16MHz/(2BRP总 TQ)=16MHz/(219)≈888kbps?实际需根据晶振和需求调整,可参考数据手册 “波特率计算表”。
4. 配置滤波与掩码(可选,按需设置)MCP2515 有 2 个接收缓冲器(RXB0、RXB1),可通过 “滤波寄存器(RXFnSID、RXFnEID)” 和 “掩码寄存器(RXMnSID、RXMnEID)” 设置接收规则,只接收符合 ID 规则的数据(减少无效数据中断)。
[*]若不需要滤波:直接将掩码寄存器设为0x00(接收所有 ID 的帧)。
[*]若需要接收指定标准帧(如 ID=0x123):
[*]配置 RXF0SID 寄存器(地址 0x00):0x12(ID 的高 8 位)。
[*]配置 RXF0SIDH 寄存器(地址 0x01):0x60(ID 的低 3 位,结合 bit6-bit5 的 IDE 位(0 = 标准帧))。
[*]配置 RXM0SID 寄存器(地址 0x02):0xFF(掩码高 8 位,全 1 表示严格匹配)。
[*]配置 RXM0SIDH 寄存器(地址 0x03):0xE0(掩码低 3 位,全 1 表示严格匹配)。
5. 配置中断(可选,推荐)若需通过中断通知 MCU “数据接收完成” 或 “数据发送完成”,需配置 “中断使能寄存器(CANINTE)”:
[*]接收中断:将 CANINTE 的 RX0IE 位(bit0)设为 1,当 RXB0 接收到数据时,MCP2515 的 INT 引脚会拉低,触发 MCU 中断。
[*]发送中断:将 CANINTE 的 TX0IE 位(bit3)设为 1,当 TXB0 数据发送完成时,触发中断。
6. 切换到正常模式完成所有配置后,需切换到正常模式才能收发数据:
[*]向 CANCTRL 寄存器(0x0F)发送配置值0x00(REQOP2-REQOP0=000,请求进入正常模式)。
[*]读取 CANSTAT 寄存器,确认 bit7-bit5 为000,即已进入正常模式。
7. 数据发送流程
[*]检查 “发送缓冲器状态寄存器(TXB0CTRL)” 的 TXREQ 位(bit3),若为 0,表示 TXB0 空闲,可写入数据。
[*]向 MCP2515 发送 “写 TXB0 数据命令(0x31)”(或先发送写寄存器命令,再写 TXB0D0-TXB0D7 地址)。
[*]依次发送 CAN 数据(最多 8 字节,如 0x01、0x02、0x03)。
[*]向 TXB0CTRL 寄存器(地址 0x30)写入0x08(将 TXREQ 位设为 1,请求发送数据)。
[*](若开启发送中断)等待中断触发,或轮询 TXB0CTRL 的 TXREQ 位,变为 0 表示发送完成。
8. 数据接收流程
[*](若开启接收中断)当 INT 引脚拉低时,进入中断服务函数;或轮询 “接收缓冲器状态寄存器(RXB0CTRL)” 的 RX0IF 位(bit0),为 1 表示有数据。
[*]向 MCP2515 发送 “读 RXB0 数据命令(0x90)”(或先发送读寄存器命令,再读 RXB0D0-TXB0D7 地址)。
[*]依次读取数据(最多 8 字节),存储到 MCU 缓存中。
[*]读取 “接收状态寄存器(RXB0SIDH/RXB0SIDL)”,确认接收帧的 ID(标准帧 / 扩展帧、具体 ID 值)。
[*]清除接收中断标志:向 “中断标志寄存器(CANINTF)” 的 RX0IF 位(bit0)写入 0。
三、核心编程寄存器汇总(必看)以下是编程中高频使用的寄存器,地址和功能需熟记,避免混淆:
寄存器名称地址核心功能关键位 / 配置值示例
CANCTRL(模式控制)0x0F设置 MCP2515 工作模式配置模式:0x80;正常模式:0x00
CANSTAT(状态查询)0x0E读取当前工作模式配置模式:bit7-bit5=100;正常模式:000
CNF1(波特率 1)0x2A配置同步跳转宽度(SJW)和波特率预分频(BRP)BRP=0(分频 1):0x00
CNF2(波特率 2)0x29配置采样点(SAM)和相位缓冲段 1(PHSEG1)采样 1 次 + PHSEG1=4TQ:0x90
CNF3(波特率 3)0x28配置相位缓冲段 2(PHSEG2)PHSEG2=3TQ:0x02
CANINTE(中断使能)0x2B使能接收 / 发送中断使能 RX0 中断:0x01;使能 TX0 中断:0x08
CANINTF(中断标志)0x2C读取 / 清除中断标志RX0 中断标志:bit0;TX0 中断标志:bit3
TXB0CTRL(发送控制)0x30控制 TXB0 发送请求请求发送:0x08;发送完成:TXREQ=0
RXB0CTRL(接收控制)0x60查看 RXB0 接收状态有数据:RX0IF=1
四、编程常见问题与避坑
[*]SPI 通信失败:检查 SPI 模式是否为 “模式 0”,时钟频率是否超过 10MHz,MCP2515 的 CS 引脚(片选)是否在通信时拉低(仅通信时拉低,结束后拉高)。
[*]无法进入配置模式:确认 CANCTRL 寄存器写入值是否为0x80,若多次失败,可能是 SPI 接线错误(如 SCK、MOSI、MISO 接反)。
[*]发送数据失败:先检查 TXB0 是否空闲(TXREQ=0),再确认 CAN 总线是否有其他设备(总线需接 120Ω 终端电阻,否则信号衰减)。
[*]接收不到数据:检查滤波和掩码配置是否正确(若不需要滤波,掩码需设为全 0),CAN 波特率是否与发送端一致,INT 引脚中断是否使能。
MCP2515.C
#include "mcp2515.h"
// 延时函数(约10us@11.0592MHz)
void Delay10us(unsigned int t)
{
while(t--)
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
}
// SPI发送一个字节
unsigned char SPI_SendByte(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
MCP2515_SCK = 0; // 时钟线拉低
if(dat & 0x80) MCP2515_MOSI = 1;// 发送高位
else MCP2515_MOSI = 0;
dat <<= 1; // 移位
MCP2515_SCK = 1; // 时钟线拉高,从机采样
}
return 0;
}
// SPI接收一个字节
unsigned char SPI_ReceiveByte(void)
{
unsigned char i, dat=0;
for(i=0;i<8;i++)
{
MCP2515_SCK = 0; // 时钟线拉低
dat <<= 1; // 移位
MCP2515_SCK = 1; // 时钟线拉高
if(MCP2515_MISO) dat |= 0x01;// 接收数据
}
return dat;
}
// 写MCP2515寄存器
void MCP2515_WriteReg(unsigned char addr, unsigned char dat)
{
MCP2515_CS = 0; // 片选拉低
SPI_SendByte(CMD_WRITE); // 发送写命令
SPI_SendByte(addr); // 发送寄存器地址
SPI_SendByte(dat); // 发送数据
MCP2515_CS = 1; // 片选拉高
Delay10us(1);
}
// 读MCP2515寄存器
unsigned char MCP2515_ReadReg(unsigned char addr)
{
unsigned char dat;
MCP2515_CS = 0; // 片选拉低
SPI_SendByte(CMD_READ); // 发送读命令
SPI_SendByte(addr); // 发送寄存器地址
dat = SPI_ReceiveByte(); // 接收数据
MCP2515_CS = 1; // 片选拉高
Delay10us(1);
return dat;
}
// 软件复位MCP2515
void MCP2515_Reset(void)
{
MCP2515_CS = 0;
SPI_SendByte(CMD_RESET);
MCP2515_CS = 1;
Delay10us(100);// 等待复位完成
}
// 设置MCP2515工作模式
unsigned char MCP2515_SetMode(unsigned char mode)
{
unsigned char i;
MCP2515_WriteReg(REG_CANCTRL, mode);// 写入模式配置
Delay10us(10); // 等待模式切换
// 检查模式是否设置成功
for(i=0;i<10;i++)
{
if((MCP2515_ReadReg(REG_CANSTAT) & 0xE0) == (mode & 0xE0))
{
return 0;// 成功
}
Delay10us(10);
}
return 1;// 失败
}
// 初始化MCP2515(500kbps,16MHz晶振)
void MCP2515_Init(void)
{
// 初始化SPI引脚
MCP2515_CS = 1;
MCP2515_SCK = 1;
MCP2515_MOSI = 0;
MCP2515_Reset();// 软件复位
// 进入配置模式
while(MCP2515_SetMode(MODE_CONFIG));
// 配置波特率(500kbps @16MHz)
MCP2515_WriteReg(REG_CNF1, 0x01);// CNF1: BRP=1 (分频2)
MCP2515_WriteReg(REG_CNF2, 0xB6);// CNF2: 采样点=87.5%
MCP2515_WriteReg(REG_CNF3, 0x05);// CNF3: PHSEG2=6TQ
// 关闭滤波,接收所有标准帧
MCP2515_WriteReg(0x02, 0x00);// RXM0SIDH
MCP2515_WriteReg(0x03, 0x00);// RXM0SIDL
// 使能接收中断
MCP2515_WriteReg(REG_CANINTE, 0x01);// 使能RX0中断
// 进入正常工作模式
MCP2515_SetMode(MODE_NORMAL);
// 初始化外部中断0(接收中断)
IT0 = 1;// 下降沿触发
EX0 = 1;// 使能外部中断0
EA = 1; // 使能总中断
}
// 发送数据(标准帧,ID=0x123)
unsigned char MCP2515_Send(unsigned char *data, unsigned char len)
{
unsigned char i;
// 检查发送缓冲区是否空闲
if(MCP2515_ReadReg(REG_TXB0CTRL) & 0x08)
{
return 1;// 发送缓冲区忙
}
// 限制数据长度(最大8字节)
if(len > 8) len = 8;
// 写入数据到TXB0缓冲区
MCP2515_CS = 0;
SPI_SendByte(CMD_WRITE);
SPI_SendByte(0x31);// TXB0D0地址
// 写入数据
for(i=0;i<len;i++)
{
SPI_SendByte(data);
}
MCP2515_CS = 1;
// 写入标准帧ID(0x123)
MCP2515_WriteReg(0x32, 0x12);// TXB0SIDH
MCP2515_WriteReg(0x33, 0x60);// TXB0SIDL (ID低3位+IDE=0)
MCP2515_WriteReg(0x35, len); // 数据长度
// 请求发送
MCP2515_CS = 0;
SPI_SendByte(CMD_RTS_TX0);
MCP2515_CS = 1;
return 0;// 发送成功
}
// 检查是否有接收数据
unsigned char MCP2515_CheckReceive(void)
{
return (MCP2515_ReadReg(REG_CANINTF) & 0x01);// 检查RX0中断标志
}
// 接收数据
unsigned char MCP2515_Receive(unsigned char *data, unsigned char *len)
{
unsigned char i;
// 检查是否有数据
if(!(MCP2515_ReadReg(REG_CANINTF) & 0x01))
{
return 1;// 无数据
}
// 读取数据长度
MCP2515_CS = 0;
SPI_SendByte(CMD_READ_RX0);
SPI_ReceiveByte();// 跳过SIDH
SPI_ReceiveByte();// 跳过SIDL
SPI_ReceiveByte();// 跳过EID8
SPI_ReceiveByte();// 跳过EID0
*len = SPI_ReceiveByte() & 0x0F;// 读取数据长度
// 限制最大长度
if(*len > 8) *len = 8;
// 读取数据
for(i=0;i<*len;i++)
{
data = SPI_ReceiveByte();
}
MCP2515_CS = 1;
// 清除中断标志
MCP2515_WriteReg(REG_CANINTF, 0x00);
return 0;// 接收成功
}
// 外部中断0服务函数(接收中断)
void EX0_ISR(void) interrupt 0
{
// 可在这里添加接收提示代码
// 例如:设置接收标志位
}
MCP2515.H
#ifndef __MCP2515_H__
#define __MCP2515_H__
#include <reg52.h>
#include <intrins.h>
// 引脚定义(可根据实际接线修改)
sbit MCP2515_CS= P3^4;// 片选引脚
sbit MCP2515_INT = P3^2;// 中断引脚(外部中断0)
sbit MCP2515_SCK = P3^6;// SPI时钟
sbit MCP2515_MOSI= P3^5;// 主机发送,从机接收
sbit MCP2515_MISO= P3^7;// 主机接收,从机发送
// MCP2515命令
#define CMD_WRITE 0x02// 写寄存器
#define CMD_READ 0x03// 读寄存器
#define CMD_RTS_TX0 0x20// 请求发送TXB0
#define CMD_READ_RX0 0x90// 读RXB0缓冲区
#define CMD_RESET 0xC0// 软件复位
// 寄存器地址
#define REG_CANCTRL 0x0F// 模式控制寄存器
#define REG_CANSTAT 0x0E// 状态寄存器
#define REG_CNF1 0x2A// 波特率配置1
#define REG_CNF2 0x29// 波特率配置2
#define REG_CNF3 0x28// 波特率配置3
#define REG_CANINTE 0x2B// 中断使能寄存器
#define REG_CANINTF 0x2C// 中断标志寄存器
#define REG_TXB0CTRL 0x30// TXB0控制寄存器
#define REG_RXB0CTRL 0x60// RXB0控制寄存器
// 工作模式
#define MODE_CONFIG 0x80// 配置模式
#define MODE_NORMAL 0x00// 正常模式
#define MODE_LISTEN 0x20// 监听模式
// 函数声明
void MCP2515_Init(void); // 初始化MCP2515
unsigned char MCP2515_SetMode(unsigned char mode);// 设置工作模式
unsigned char MCP2515_Send(unsigned char *data, unsigned char len);// 发送数据
unsigned char MCP2515_Receive(unsigned char *data, unsigned char *len);// 接收数据
unsigned char MCP2515_CheckReceive(void);// 检查是否有接收数据
#endif MAIN.C
#include <reg52.h>
#include "mcp2515.h"
unsigned char send_buf = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
unsigned char recv_buf;
unsigned char recv_len;
// 毫秒级延时函数
void DelayMs(unsigned int ms)
{
unsigned int i, j;
for(i=0;i<ms;i++)
for(j=0;j<112;j++);
}
void main(void)
{
MCP2515_Init();// 初始化MCP2515
while(1)
{
// 发送数据
MCP2515_Send(send_buf, 8);
DelayMs(1000);// 每秒发送一次
// 检查接收
if(MCP2515_CheckReceive())
{
MCP2515_Receive(recv_buf, &recv_len);
// 接收到的数据在recv_buf中,长度为recv_len
// 可在这里添加数据处理代码
}
}
} 要CAN, 用 STC32G12K128, STC32G12K64, STC32G8K64
要CAN-FD, 用最新 STC32G144K246 MCP2515比带CAN的单片机贵多了,不经济
页:
[1]