6.CAN波特率异常
CAN控制器采用的是XL2515,通过SPI接口控制,该芯片可以Pin2Pin替换MCP2515,这是应用很广泛的一颗CAN控制器。测试过程中发现输出波形频率只有400多Hz,最开始测试代码我是从一款开发板的示例copy过来的,按理说一样的芯片不应该出问题。
仔细对比两款设计后发现我用的是8MHz晶振,而开发板用的16MHz晶振。
【原理图】
【问题排查】
分析XL2515芯片的Datasheet,与波特率有关的是CNF1、CNF2和CNF3这几个寄存器。这几个寄存器的值计算起来挺繁琐的,于是乎网上找了个配置软件,根据自己需要的波特率可以生成所需的寄存器值,很方便。这我也贴上来,不过软件在win10下运行有点麻烦,需要参照下面链接操作,不然会报错。
波特率计算软件:下载链接
***********************************************************************
注册OCX或DLL文件时。这种错误通常是由于权限不足或系统缺少必要的组件导致的。
示例
regsvr32 C:\Windows\SysWOW64\mscomm32.ocx
执行上述命令时,可能会出现错误代码0x8002801c。
解决方法
1. 复制必要的组件
首先,将所需的OCX组件复制到系统目录中。例如,将msinet.ocx或MSCOMCTL.OCX复制到C:\Windows\SysWOW64目录下。
2. 以管理员身份运行命令提示符
在C:\Windows\SysWOW64目录下找到cmd.exe文件,右键单击并选择“以管理员身份运行”。
3. 注册组件
在管理员命令提示符中输入以下命令来注册组件:
regsvr32 %windir%\SysWOW64\msinet.ocx
************************************************************************************
最终修改了波特率相关的这个数据,就可以跟CAN调试器正常通讯收发数据了。
【测试源代码】
from machine import Pin, SPI, PWM, I2C
import time
XL2515_SPI_PORT = 0
XL2515_SCLK_PIN = 18
XL2515_MOSI_PIN = 19
XL2515_MISO_PIN = 16
XL2515_CS_PIN = 17
XL2515_INT_PIN = 20
class RP2350_CAN:
def __init__(self, rate_kbps = "100KBPS", spi_cs = XL2515_CS_PIN, irq = XL2515_INT_PIN, spi_port = XL2515_SPI_PORT, spi_clk = XL2515_SCLK_PIN,spi_mosi = XL2515_MOSI_PIN, spi_miso = XL2515_MISO_PIN, spi_freq=10_000_000):
self.can_rate_arr = {
"5KBPS" : [0x27, 0XB4, 0x06],
"10KBPS" : [0x13, 0XB4, 0X06],
"20KBPS" : [0x09, 0XB4, 0x06],
"50KBPS" : [0x03, 0XB4, 0x06],
"100KBPS" : [0x01, 0xB4, 0x06],
"125KBPS" : [0x01, 0xB9, 0x04],
"250KBPS" : [0x01, 0x90, 0x02],
"500KBPS" : [0x00, 0x90, 0x02],
}
self.spi = SPI(spi_port, spi_freq, polarity = 0, phase = 0, bits = 8, sck = Pin(spi_clk), mosi = Pin(spi_mosi), miso = Pin(spi_miso))
self.cs = Pin(spi_cs, Pin.OUT)
self.cs(1)
self.int = Pin(irq, Pin.IN, Pin.PULL_UP)
self.int.irq(handler = self.int_callback, trigger = Pin.IRQ_FALLING)
self.recv_flag = False
self.reset()
time.sleep(0.1)
self.config(rate_kbps)
def config(self, rate_kbps):
CNF3 = 0x28
CNF2 = 0x29
CNF1 = 0x2A
TXB0SIDH = 0x31
TXB0SIDL = 0x32
TXB0DLC = 0x35
RXB0SIDH = 0x61
RXB0SIDL = 0x62
RXB0CTRL = 0x60
RXB0DLC = 0x65
RXF0SIDH = 0x00
RXF0SIDL = 0x01
RXM0SIDH = 0x20
RXM0SIDL = 0x21
CANINTF = 0x2C
CANINTE = 0x2B
CANCTRL = 0x0F
REQOP_NORMAL = 0x00
CLKOUT_ENABLED = 0x07
CANSTAT = 0x0E
OPMODE_NORMAL = 0x00
self.write_byte(CNF1, self.can_rate_arr[rate_kbps][0])
self.write_byte(CNF2, self.can_rate_arr[rate_kbps][1])
self.write_byte(CNF3, self.can_rate_arr[rate_kbps][2])
self.write_byte(TXB0SIDH, 0xFF);
self.write_byte(TXB0SIDL, 0xE0);
self.write_byte(TXB0DLC, 0x40 | 0x08);
# Set RX
self.write_byte(RXB0SIDH, 0x00);
self.write_byte(RXB0SIDL, 0x60);
self.write_byte(RXB0CTRL, 0x60);
self.write_byte(RXB0DLC, 0x08);
self.write_byte(RXF0SIDH, 0xFF);
self.write_byte(RXF0SIDL, 0xE0);
self.write_byte(RXM0SIDH, 0xFF);
self.write_byte(RXM0SIDL, 0xE0);
# can int
self.write_byte(CANINTF, 0x00); # clean interrupt flag
self.write_byte(CANINTE, 0x01); # Receive Buffer 0 Full Interrupt Enable Bit
self.write_byte(CANCTRL, REQOP_NORMAL | CLKOUT_ENABLED)
dummy = self.read_byte(CANSTAT)
if ((dummy & 0xe0) != OPMODE_NORMAL):
print("OPMODE_NORMAL")
self.write_byte(CANCTRL, REQOP_NORMAL | CLKOUT_ENABLED) #set normal mode
def send(self, can_id, data):
TXB0CTRL = 0x30
TXB0SIDH = 0x31
TXB0SIDL = 0x32
TXB0EID8 = 0x33
TXB0EID0 = 0x34
TXB0DLC = 0x35
TXB0D0 = 0x36
dly = 0
while ((self.read_byte(TXB0CTRL) & 0x08) and (dly < 50)):
dly += 1
time.sleep_ms(1)
self.write_byte(TXB0SIDH, (can_id >> 3) & 0XFF)
self.write_byte(TXB0SIDL, (can_id & 0x07) << 5)
self.write_byte(TXB0EID8, 0)
self.write_byte(TXB0EID0, 0)
self.write_byte(TXB0DLC, len(data))
for i in range(len(data)):
self.write_byte(TXB0D0 + i, data[i])
self.write_byte(TXB0CTRL, 0x08)
def recv(self, can_id):
RXB0SIDH = 0x61
RXB0SIDL = 0x62
CANINTF = 0x2C
CANINTE = 0x2B
RXB0DLC = 0x65
RXB0D0 = 0x66
if self.recv_flag == False:
return None
self.recv_flag = False
self.write_byte(RXB0SIDH, (can_id >> 3) & 0XFF)
self.write_byte(RXB0SIDL, (can_id & 0x07) << 5)
while True:
if (self.read_byte(CANINTF) & 0x01):
len = self.read_byte(RXB0DLC)
buf = bytearray(len)
for i in range(len):
buf[i] = self.read_byte(RXB0D0 + i)
self.write_byte(CANINTF, 0);
self.write_byte(CANINTE, 0x01) # enable
self.write_byte(RXB0SIDH, 0x00) # clean
self.write_byte(RXB0SIDL, 0x60)
return buf
def int_callback(self, pin):
self.recv_flag = True
def reset(self):
CAN_RESET = 0xC0
self.cs(0)
self.spi.write(bytearray([CAN_RESET]))
self.cs(1)
def read_byte(self, reg):
CAN_READ = 0x03
self.cs(0)
self.spi.write(bytearray([CAN_READ, reg]))
data = self.spi.read(1)
self.cs(1)
return data[0]
def write_byte(self, reg, data):
CAN_WRITE = 0x02
self.cs(0)
self.spi.write(bytearray([CAN_WRITE, reg, data]))
self.cs(1)
if __name__ == "__main__":
can = RP2350_CAN()
while True:
can.send(0x123, [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88])
recv_data = can.recv(0x123)
if recv_data != None:
print("recv:", [hex(i) for i in recv_data])
time.sleep(1)
【测试结果】
发送:软件里发送了ID为0x123,数据为0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88的报文,在CAN调试工具里可以正常接收。
接收:在CAN调试工具里手动发送数据,在micropython的REL窗口里可以看到对应数据