本人某高校研究生,刚刚研一,接触了一些小项目,想单纯记录一下项目进度以及成果。水平不足,仅供参考,希望能够对大家有所帮助。
板卡介绍:板卡上共4片SJA1000-T芯片,具体芯片功能不进行赘述了,布线方面,四片SJA1000的AD0~AD7拉成了一套数据地址总线,RD#、WR#、RST均是总线形式,ALE、INT#、CS#则是每片使用。
以下是SJA1000的具体原理图,采用了两片晶振分别为16MHZ和12MHZ,通过拨码开关进行选择。
接下来进行SJA1000初始化的具体操作:
Vivado部分:
首先,在Vivado中加入system.bd以及AXI GPIO IP核,此程序选择dual channel 模式:
Auto Connect以后生成以下design。其中GPIO_0是所有的控制引脚,GPIO2_0则为AD0~AD7(需要注意的是,这里的信号反向全部使用IO,在SDK中我们可以通过XGpio_SetDirection函数进行输入输出方向的设置,其内部原理就是控制IOBUF中的IO_t引脚高低电平)
之后Create HDL——Genrate Output——xdc管脚约束——generate bitstream——export hardware——launch sdk
SDK部分:
1.首先我们要根据Datasheet中给出的寄存器偏移地址写出SJA1000.h文件方便之后寄存器调用
#ifndef __SJA1000_H__
#define __SJA1000_H__
#include "string.h"
#define SJA_BaseAdr 0X00
#define REG_CONTROL SJA_BaseAdr+0x00 //内部控制寄存器
#define REG_COMMAND SJA_BaseAdr+0x01 //命令寄存器 只写
#define REG_STATUS SJA_BaseAdr+0x02 //状态寄存器 只读
#define REG_INTERRUPT SJA_BaseAdr+0x03 //中断寄存器 只读
#define REG_INTENABLE SJA_BaseAdr+0x04 //中断使能寄存器 可读可写
#define REG_RESVER0 SJA_BaseAdr+0x05 //保留0
#define REG_BTR0 SJA_BaseAdr+0x06 //总线定时寄存器0 复位模式读写
//定义了波特率预设值BRP 和同步跳转宽度SJW 的值
#define REG_BTR1 SJA_BaseAdr+0x07 //总线定时寄存器1 复位模式读写
//总线定时寄存器1 定义了每个位周期的长度采样点的位置和在每个采样点的采样数目
#define REG_OCR SJA_BaseAdr+0x08 //输出控制寄存器 复位模式读写
//输出控制寄存器实现了由软件控制不同输出驱动配置的建立
#define REG_TEST SJA_BaseAdr+0x09 //测试寄存器
#define REG_RESVER1 SJA_BaseAdr+0x0A //保留1
#define REG_ARBITRATE SJA_BaseAdr+0x0B //仲裁丢失捕捉 只读
#define REG_ERRCATCH SJA_BaseAdr+0x0C //错误代码捕捉 只读
#define REG_ERRLIMIT SJA_BaseAdr+0x0D //错误报警限额 工作模式只读 复位模式可读写
#define REG_RXERR SJA_BaseAdr+0x0E //接收错误计数器工作模式只读 复位模式可读写
#define REG_TXERR SJA_BaseAdr+0x0F //发送错误计数器工作模式只读 复位模式可读写
#define REG_ACR0 SJA_BaseAdr+0x10 //验收代码寄存器
#define REG_ACR1 SJA_BaseAdr+0x11 //验收代码寄存器
#define REG_ACR2 SJA_BaseAdr+0x12 //验收代码寄存器
#define REG_ACR3 SJA_BaseAdr+0x13 //验收代码寄存器
#define REG_AMR0 SJA_BaseAdr+0x14 //验收屏蔽寄存器
#define REG_AMR1 SJA_BaseAdr+0x15 //验收屏蔽寄存器
#define REG_AMR2 SJA_BaseAdr+0x16 //验收屏蔽寄存器
#define REG_AMR3 SJA_BaseAdr+0x17 //验收屏蔽寄存器
// 发送缓冲区寄存器 (发送缓冲区长13字节,在CAN地址是16-28即0x10-0x1c)
#define REG_TXBuffer1 SJA_BaseAdr+0x10 //发送缓冲区1
#define REG_TXBuffer2 SJA_BaseAdr+0x11 //发送缓冲区2
#define REG_TXBuffer3 SJA_BaseAdr+0x12 //发送缓冲区3
#define REG_TXBuffer4 SJA_BaseAdr+0x13 //发送缓冲区4
#define REG_TXBuffer5 SJA_BaseAdr+0x14 //发送缓冲区5
#define REG_TXBuffer6 SJA_BaseAdr+0x15 //发送缓冲区6
#define REG_TXBuffer7 SJA_BaseAdr+0x16 //发送缓冲区7
#define REG_TXBuffer8 SJA_BaseAdr+0x17 //发送缓冲区8
#define REG_TXBuffer9 SJA_BaseAdr+0x18 //发送缓冲区9
#define REG_TXBuffer10 SJA_BaseAdr+0x19 //发送缓冲区10
#define REG_TXBuffer11 SJA_BaseAdr+0x1A //发送缓冲区11
#define REG_TXBuffer12 SJA_BaseAdr+0x1B //发送缓冲区12
#define REG_TXBuffer13 SJA_BaseAdr+0x1C //发送缓冲区13
// 接收缓冲区寄存器 (接收缓冲区长13字节,在CAN地址是16-28即0x10-0x1c)
#define REG_RXBuffer1 SJA_BaseAdr+0x10 //接收缓冲区1
#define REG_RXBuffer2 SJA_BaseAdr+0x11 //接收缓冲区2
#define REG_RXBuffer3 SJA_BaseAdr+0x12 //接收缓冲区3
#define REG_RXBuffer4 SJA_BaseAdr+0x13 //接收缓冲区4
#define REG_RXBuffer5 SJA_BaseAdr+0x14 //接收缓冲区5
#define REG_RXBuffer6 SJA_BaseAdr+0x15 //接收缓冲区6
#define REG_RXBuffer7 SJA_BaseAdr+0x16 //接收缓冲区7
#define REG_RXBuffer8 SJA_BaseAdr+0x17 //接收缓冲区8
#define REG_RXBuffer9 SJA_BaseAdr+0x18 //接收缓冲区9
#define REG_RXBuffer10 SJA_BaseAdr+0x19 //接收缓冲区10
#define REG_RXBuffer11 SJA_BaseAdr+0x1A //接收缓冲区11
#define REG_RXBuffer12 SJA_BaseAdr+0x1B //接收缓冲区12
#define REG_RXBuffer13 SJA_BaseAdr+0x1C //接收缓冲区13
#define REG_RXCOUNT SJA_BaseAdr+0x1D //RX报文计数器 只读 RX信息计数器(RMC)反应RXFIFO中可用的信息数目
#define REG_RBSA SJA_BaseAdr+0x1E //RX缓冲器起始地址寄存器(RBSA)可读写 复位模式只写
//反映了当前可用来存储位于接收缓冲器窗口中的信息的内部RAM地址
#define REG_CDR SJA_BaseAdr+0x1F //时钟分频寄存器
//时钟分频寄存器为微控制器控制CLKOUT 的频率以及屏蔽CLKOUT 引脚而且它还控制着TX1上
//的专用接收中断脉冲接收比较通道和BasicCAN 模式与PeliCAN 模式的选择
/*
功能说明: CAN控制器SJA1000通讯波特率.SJA1000的晶振为必须为16MHZ*/
#define BTR0_Rate_20k 0x53 //20KBPS的预设值
#define BTR1_Rate_20k 0x2F //20KBPS的预设值
#define BTR0_Rate_40k 0x87 //40KBPS的预设值
#define BTR1_Rate_40k 0xFF //40KBPS的预设值
#define BTR0_Rate_50k 0x47 //50KBPS的预设值
#define BTR1_Rate_50k 0x2F //50KBPS的预设值
#define BTR0_Rate_80k 0x83 //80KBPS的预设值
#define BTR1_Rate_80k 0xFF //80KBPS的预设值
#define BTR0_Rate_100k 0x43 //100KBPS的预设值
#define BTR1_Rate_100k 0x2f //100KBPS的预设值
#define BTR0_Rate_125k 0x03 //125KBPS的预设值
#define BTR1_Rate_125k 0x1c //125KBPS的预设值
#define BTR0_Rate_200k 0x81 //200KBPS的预设值
#define BTR1_Rate_200k 0xFA //200KBPS的预设值
#define BTR0_Rate_250k 0x01 //250KBPS的预设值
#define BTR1_Rate_250k 0x1c //250KBPS的预设值
#define BTR0_Rate_400k 0x43 //400KBPS的预设值
#define BTR1_Rate_400k 0x11 //400KBPS的预设值
#define BTR0_Rate_500k 0x81 //500KBPS的预设值
#define BTR1_Rate_500k 0x23 //500KBPS的预设值
#define BTR0_Rate_666k 0x41 //666KBPS的预设值
#define BTR1_Rate_666k 0x12 //666KBPS的预设值
#define BTR0_Rate_800k 0x41 //800KBPS的预设值
#define BTR1_Rate_800k 0x11 //800KBPS的预设值
#define BTR0_Rate_1000k 0x40 //1000KBPS的预设值
#define BTR1_Rate_1000k 0x23 //1000KBPS的预设值
//BPS
//功能说明: CAN控制器SJA1000通讯波特率.SJA1000的晶振为必须为24MHZ*/
#define BTR0_Rate_10k 0xEF //20KBPS的预设值
#define BTR1_Rate_10k 0xFF //20KBPS的预设值
#define ByteRate_10k 10
#define ByteRate_20k 20
#define ByteRate_40k 40
#define ByteRate_50k 50
#define ByteRate_80k 80
#define ByteRate_100k 100
#define ByteRate_125k 125
#define ByteRate_200k 200
#define ByteRate_250k 250
#define ByteRate_400k 400
#define ByteRate_500k 500
#define ByteRate_800k 800
#define ByteRate_1000k 1000
//命令字
#define TR_CMD 0X01 //CMR.0发送请求位
#define AT_CMD 0X02 //CMR.1中止发送位
#define RRB_CMD 0X04 //CMR.2释放接收缓冲器
#define COS_CMD 0X08 //CMR.3清除数据溢出
#define SRR_CMD 0X10 //CMR.4自接收模式
#define GTS_CMD 0X10 //????CMR.5.CMR7保留位
//错误字
#define CAN_INTERFACE_OK 0 //CAN总线接口OK
#define CAN_BUS_OK 0 //CAN总线OK
#define CAN_INTERFACE_ERR 0XFF //CAN总线接口错误
#define CAN_ENTERSET_ERR 0XFE //CAN总线初始化错误
#define CAN_QUITSET_ERR 0XFD //CAN总线退出复位模式错误
#define CAN_INITOBJECT_ERR 0XFC //CAN总线初始化对象错误
#define CAN_INITBTR_ERR 0XFB //?
#define CAN_INITOUTCTL_ERR 0XFA //??
#define CAN_INTCLKDIV_ERR 0XF9 //??
#define CAN_BUS_ERR 0XF8 //CAN总线错误
#define ID28_21 0X0A;
#define ID20_13 0X4A;
#define ID12_5 0X6B;
#define ID4_0 0XE8; //低三位不影响设为0
//定义扩展模式数据帧ID
//Basic CAN模式标准帧格式 :帧信息,TX识别码1-2,TX数据字节1-8
//Pelican模式扩展帧格式 :帧信息,TX识别码1-4,TX数据字节1-8
#endif
2.根据时序图进行寄存器读写操作,因为我采用的是Intel模式,这里只给出Intel的时序图。
贴入部分代码,以下SJA1000_WR_Time_1是通过控制CS,ALE,RD,WR根据SJA1000的datasheet给出的时序图进行地址锁存以及数据读写,这将应用在之后的SJA1000初始化时各寄存器的读写。
SJA1000_Read_1则是单纯的寄存器数据读。
(注:这两个程序只是SJA1000中1片的读写,因为各片CS,ALE连接至PL端的引脚不同,所以每个都需要重新编写)
int SJA1000_WR_Time_1(unsigned int Address,unsigned int Data)
{
/* Set the direction for all signals as inputs except the LED output */
//WRITE
int s;
XGpio_SetDataDirection(&Gpio1, CAN_CHANNEL1, 0x8E01);//all output except CAN_INT-input
XGpio_SetDataDirection(&Gpio2, CAN_CHANNEL2, 0x00);//all output AD0~AD7
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFF);//Test_Reg,ALE-1,WR-1,RD-1,CS-1,INT-1
XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, Address);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,ALE-0,WR-1,RD-1,CS-1,INT-1,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,ALE-0,WR-1,RD-1,CS-0,INT-1,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FD5);//Test_Reg,ALE-0,WR-0,RD-1,CS-0,ADDRESS-FREE
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FD5);//Test_Reg,SET-DATA,ALE-0,WR-0,RD-1,CS-0
XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, Data);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,SET-DATA,ALE-0,WR-1,RD-1,CS-0
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x99);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,SET-DATA,ALE-0,WR-1,RD-1,CS-1
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x99);
Delay_ms(1000);
//READ
//CHANNEL1:0:CAN1_INT,1:CAN1_ALE,2:CAN_RST,3:CAN_WR,4:CAN_RD,5:CAN1_CS,6:CAN2_CS,7:CAN3_CS,8:CAN4_CS
//CHANNEL2:0~7:AD0~AD7
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFF);//Test_Reg,ALE-1,WR-1,RD-1,CS-1,SET-ADDRESS
XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, Address);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,ALE-0,WR-1,RD-1,CS-1,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,ALE-0,WR-1,RD-1,CS-0,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FCD);//Test_Reg,ALE-0,WR-1,RD-0,CS-0,ADDRESS-FREE
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_SetDataDirection(&Gpio2, CAN_CHANNEL2, 0xFF);
Delay_ms(1000);
s = XGpio_DiscreteRead(&Gpio2, CAN_CHANNEL2);//Test_Reg,READ_DATA,ALE-0,WR-1,RD-0,CS-0
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,ALE-0,WR-1,RD-1,CS-0?
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,ALE-0,WR-1,RD-1,CS-1
if(s == Data) return 1;
else return 0;
}
///
int SJA1000_Read_1(unsigned int Address)
{
int s;
XGpio_SetDataDirection(&Gpio1, CAN_CHANNEL1, 0x8E01);//all output except CAN_INT-input
XGpio_SetDataDirection(&Gpio2, CAN_CHANNEL2, 0x00);//all output AD0~AD7
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFF);//Test_Reg,ALE-1,WR-1,RD-1,CS-1,SET-ADDRESS
XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, Address);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,ALE-0,WR-1,RD-1,CS-1,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,ALE-0,WR-1,RD-1,CS-0,KEEP-ADDRESS
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FCD);//Test_Reg,ALE-0,WR-1,RD-0,CS-0,ADDRESS-FREE
// XGpio_DiscreteWrite(&Gpio2, CAN_CHANNEL2, 0x09);
Delay_ms(1000);
XGpio_SetDataDirection(&Gpio2, CAN_CHANNEL2, 0xFF);
Delay_ms(1000);
s = XGpio_DiscreteRead(&Gpio2, CAN_CHANNEL2);//Test_Reg,READ_DATA,ALE-0,WR-1,RD-0,CS-0
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FDD);//Test_Reg,ALE-0,WR-1,RD-1,CS-0?
Delay_ms(1000);
XGpio_DiscreteWrite(&Gpio1, CAN_CHANNEL1, 0x0FFD);//Test_Reg,ALE-0,WR-1,RD-1,CS-1
return s;
}
3.SJA1000的初始化:这里给出的是一片SJA1000的初始化操作(这些是包含在主函数内部的,AXI GPIO的初始化等操作不再赘述了,看SDK内部例程即可),具体各寄存器内部配置请参考SJA1000的datasheet,大致就是要先进行测试寄存器读写判断通道是否正常,时序是否有问题,之后进入复位模式,配置波特率,ACR,AMR,工作模式等,最后退出复位模式。
while(c==0){
if(SJA1000_WR_Time_4(REG_TEST,0XAA)==1)//sja_interface_test();
{
printf("Interface is OK!\r\n");
a = 1;
}
else
{
printf("Interface error!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_CONTROL,0x09)==1 && a==1) //sja_init();
{
printf("Enter RST Mode is OK!\r\n");
b = SJA1000_Read_4(REG_CONTROL);//0X09
}
else
{
printf("RST Mode fail\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_TEST,0xAA)==1 && a==1)
{
printf("Test PASS!\r\n");
}
else
{
printf("Test Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_CDR,0xC8)==1 && a==1)
{
printf("Clock Set is ok!\r\n");
}
else
{
printf("Clock Set Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_ACR0,0x0A)==1 && a==1)
{
printf("ACR0 SET 0X0A!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_ACR1,0x4A)==1 && a==1)
{
printf("ACR1 SET 0X4A!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_ACR2,0x6B)==1 && a==1)
{
printf("ACR2 SET 0X6B!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_ACR3,0x78)==1 && a==1)
{
printf("ACR3 SET 0X78!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_AMR0,0x00)==1 && a==1)
{
printf("AMR0 SET MODE 0X00!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_AMR1,0x00)==1 && a==1)
{
printf("AMR1 SET MODE 0X4A!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_AMR2,0x00)==1 && a==1)
{
printf("AMR2 SET MODE 0X6B!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_AMR3,0x03)==1 && a==1)
{
printf("AMR3 SET MODE 0X78!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_BTR0,BTR0_Rate_1000k)==1 && a==1)
{
printf("BANDRATE1 SET IS OK!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_BTR1,BTR1_Rate_1000k)==1 && a==1)
{
printf("BANDRATE2 SET IS OK!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_INTENABLE,0xFF)==1 && a==1)
{
printf("Interrupt SET IS OK!\r\n");
}
else
{
printf("Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_OCR,0x1A)==1 && a==1)
{
printf("OUTPUT MODE SET!\r\n");
}
else
{
printf("OUTPUT MODE Fail!\r\n");
a = 0;
}
if(SJA1000_WR_Time_4(REG_CONTROL,0x08) == 1 && a==1)//?
{
printf("RST MODE IS QUIT!\r\n");
}
else
{
printf("RST MODE QUIT Fail!\r\n");
a = 0;
}
if(a==1)
{
printf("SJA1000_4 Init is OK\r\n");
c = 1;
}
}
4.通过中断信号进行数据读取:
这里只是简单的数据读取,当SJA1000内部RXBuffer接收到数据时,会产生INT引脚拉低的情况,我的初步思考是通过读取引脚电平,进行相应的数据读取。
之前也考虑过中断触发的方式,但看了例程和网上PL-PS点灯的程序后,感觉也是进行中断使能读取的操作,不知道有没有大佬给讲讲为什么要采用中断方式,我这种直接读取电平方式会有什么问题。
while(b)
{//4093 2045 1111 1111 1101 0111 1111 1101
Which_SJA1000 = XGpio_DiscreteRead(&Gpio1, CAN_CHANNEL1);
if((Which_SJA1000 & 0x0800) == 0)
{
data1 = SJA1000_Read_4(REG_RXBuffer1);
}
else if((Which_SJA1000 & 0x0400) == 0)
{
data2 = SJA1000_Read_3(REG_RXBuffer1);
}
else if((Which_SJA1000 & 0x0200) == 0)
{
data3 = SJA1000_Read_2(REG_RXBuffer1);
}
else if((Which_SJA1000 & 0x0001) == 0)
{
data4 = SJA1000_Read_1(REG_RXBuffer1);
}
}
}
以上就是SJA1000的调试过程,历经两个星期调到了现在这个程度,感觉自己还是有点云里雾里的,有一个重点就是在起初的管脚约束时,引脚一定要分配正确!!!我就是因为xdc问题,导致开始无法正确读写寄存器数据,搞了很久最后发现是XDC的问题。大家引以为戒,如果有任何建议和问题,请告诉我,我们共同进步。