DE1-SoC HPS CAN中断方式通信调试过程--WL
时间:2022-11-23 18:30:01
DE1-SoC HPS CAN通信(中断)调试
实验目的:
利用Altera Cyclone V SoPC HPS集成的CAN总线控制器采用中断法实现裸机下的裸机CAN收发报文数据。
实验环境:
硬件:DE1-SoC开发板,
软件:Quartus II 15.0和DS-5 18.1,Preloader,HPS裸机固件驱动库(18.1)。
实验流程:
1.硬件设计
2.软件设计
a. Preloader部分
b. HPS引导流程
c. CAN裸机中断收发程序流程
1. 主程序框架
2. 关键硬件资源的初始化
3. 实现中断收发服务函数和触发过程
1.硬件设计
HPS 中包含CAN 无需添加总线控制器 IP 核,只需从 HPS 中引出 CAN 的 tx、rx 引脚使其能够通过这两个引脚连接片的外部驱动电路和外部通信。
具体步骤:
在 Qsys 系统组件 HPS 设置参数选项标签页 CAN 的外设引脚
注:CAN pin选择FPGA ,因为 HPS I/O Set 外部无法连接 CAN 通信的引脚
引出双击红色区域 CAN 的引脚
用 verilog 端口声明:
综合分析后, Pin Planner 上述声明将在上述声明中看到。 CAN 端口
双击 Location引脚分配:
从板上分配2个40个引脚pin GPIO随意选择,但需要注意的是,因为 CAN 通信输入差分信号 CAN 所以在总线上选择引脚复用时要选择 DIFFIO_RX DIFFIO_TX
电路连接如下:
需要注意的是,由于需要添加电平转换器,因此需要添加电平转换器 TTL 电平标准和 CAN 的电平标准不同。
最成的最终编译.sof文件经由USB-Blaster下载到开发板。
3.软件设计
硬件设计已经完成,但是HPS 不知道配置信息,软件程序运行环境没有准备好,裸机程序需要加载,所以需要准备一份完成软硬件交互的文件,即preloader部分。
a.Preloader部分
最终需要生成 u-boot-spl 二进制文件初始化相关硬件,引导加载裸机程序
那怎么得到呢?u-boot-spl ?
首先,我们需要知道配置 HPS 外设、管脚重用等信息
对此,可编译上述硬件设计,得到.sof文件(经由USB Blaster下载到板子里)和handoff文件夹,handoff文件夹中包含了这些信息
然后,经由 preloader 也就是生成器bsp(板级支持包) editor 将 handoff 文件夹中的信息转换成源代码, 另外生成 preloader 设置文件、 preloader 调试脚本和编译 preloader 所用的 makefile。
因此,我们得到了源文件,但它与我们最终想要的二进制文件不同,需要帮助 makefile进行编译。
Bsp editor支持多种型号板子的 preloader 生成配置信息
generated文件夹可以看到许多头文件和C语言文件,用于更新uboot系统配置主要涉及相关文件,IO复用,DDR初始化等
Makefile 实施以下步骤:
1.把 generated复制文件夹下的相应文件uboot-socfpga/board/altera/socfpga与下面sdram子目录
2.通过以下变量设置
PRELOADER_UPDATE_DIR := $(PRELOADER_SRC_DIR)/board/altera/socfpga SOCFPGA_BOARD_CONFIG := socfpga_$(DEVICE_FAMILY)_config DEVICE_FAMILY := cyclone5
以及执行make $(board)_config
配置项目,确定具体在目标板上的子目录和头文件。生成配置文件config.h(知道取用U-Boot源代码中的相应文件是什么?
3.配置完成后,需要编译 spl 得到 u-boot-spl.bin
uboot-socfpga顶层文件在目录下Makefile有以下句子
ALL-$(CONFIG_SPL) = $(obj)spl/u-boot-spl.bin
打开spl目录下的文件Makefile
在这里我们可以看到编译 spl 以下是需要使用的各种配置文件、汇编文件和链接器文件的一部分
LIBS-$(CONFIG_SPL_FRAMEWORK) = common/spl/libspl.o LIBS-$(CONFIG_SPL_LIBCOMMON_SUPPORT) = common/libcommon.o LIBS-$(CONFIG_SPL_LIBDISK_SUPPORT) = disk/libdisk.o LIBS-$(CONFIG_SPL_I2C_SUPPORT) = drivers/i2c/libi2c.o LIBS-$(CONFIG_SPL_GPIO_SUPPORT) = drivers/gpio/libgpio.o LIBS-$(CONFIG_SPL_MMC_SUPPORT) = drivers/mmc/libmmc.o LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds
链接文件决定了可执行程序各段的存储(加载)地址和运行(链接)地址。
b.HPS引导流程
上电 reset 后,位于 HPS 中的 ROM 运行包装的代码段Boot ROM
Boot ROM代码的作用是确定所选代码boot重置后的源、初始化HPS,将 preloade 映像从 flash 加载到OCRAM然后跳转到 preloader 。
所选的 Flash 类型 SDMMC 在 bsp-editor spl boot 选项中设置
OCRAM 由地址 0xFFFF0000知
Preloader 在完成Clock Reconfiguration、初始化SDRAM接口、配置HPS I/O引脚及复用、初始化加载下一阶段程序的接口后,将裸机程序引导映像从引导设备复制到SDRAM。并执行
SDRAM 地址0x00100000–0xC0000000
c. CAN裸机中断收发程序流程
1. 主程序框架
初始化:1.通过协议组寄存器模块(如CCTRL寄存器、CBT寄存器等)进行软件初始化(如设置比特率,设置通信模式等)
2.通过IF接口寄存器初始化Message RAM中的接收邮箱和发送邮箱(分别初始化)。
消息对象结构:掩码位、仲裁位、控制位、数据位
通过IF接口寄存器配置消息对象的掩码位、仲裁位、控制位、数据位,存储到Message RAM对应邮箱中请求发送。
Message Handler(状态机)接收到了请求,处理消息对象发送。
邮箱中的消息对象通过IF寄存器传输到CAN core中的移位寄存器,经过封装然后到达CAN 总线接口,进行数据帧的传送。
中断相关:
GIC–中断控制器连接所有能够产生中断的I/O外围设备的IRQ中断信号。CAN0 控制器有4个中断信号连接到全局中断控制器 (GIC),
GIC 由分配器和 CPU 接口组成,分配器从I/O外设接收IRQ中断信号
CPU接口将分配器收到的IRQ请求发送给Cortex-A9处理器。
中断信号可以分为3种
1.SPI(shared peripheral interrupts):该中断由中断控制器可以路由到多个内核的外设生成.中断号32-211
2.PPI(private peripherals interrupts):专用于单个CPU .中断号27-30
3.SGI(software generated interrupts):由软件通过写入专用分配器寄存器生成.中断号0-15
中断过程:
当发生中断时,CAN0 控制器发送一个消息中断信号 ALT_INT_INTERRUPT_CAN0_MO_IRQ 给 GIC ,由分配器接收,然后CPU接口将中断请求发送给Cortex-A9处理器让处理器进行处理,进入到中断模式。
在程序中,跳转到中断向量表 irq 入口,通过跳转指令,跳转到中断服务函数。执行完中断服务函数后,回到原程序继续运行
2.关键硬件资源的初始化代码
进行 CAN 通信,先要对 CAN controller 初始化,其中包括比特率的设置,Message Ram的初始化以及接收邮箱和发送邮箱的初始化等
/* Initialize the can controller*/
alt_can_init( ALT_CAN_CAN0, candev);
/*Initialize the can controller rx mailbox for many received messages */
alt_can_mailbox_init(candev,0x1, 0x16, 0x5,ALT_CAN_TMOD_TX, ALT_CAN_FIFO_MODE_SINGLE_MSG);
alt_can_mailbox_init(candev,0x2, 0x0101, 0x3,ALT_CAN_TMOD_RX, ALT_CAN_FIFO_MODE_SINGLE_MSG);
alt_can_mailbox_init(candev,0x3, 0x0102, 0x3,ALT_CAN_TMOD_RX, ALT_CAN_FIFO_MODE_SINGLE_MSG);
alt_can_mailbox_init(candev,0x4,0x0103,0x3,ALT_CAN_TMOD_RX, ALT_CAN_FIFO_MODE_SINGLE_MSG);
注:当接收多个报文/数据帧时,为避免数据错乱,需设置邮箱的ID 标识符以及掩码位
- 中断收发服务函数实现及触发流程
要以中断方式实现数据收发,需要配置 GIC(包括分配器和 CPU 接口),并配置 CAN 控制器使其能发送中断信号
配置 GIC
ALT_STATUS_CODE status = ALT_E_SUCCESS;
if (status == ALT_E_SUCCESS)
{
status = alt_int_global_init();
}
if (status == ALT_E_SUCCESS)
{
status = alt_int_cpu_init();
}
/* Setup the interrupt specific items */
if (status == ALT_E_SUCCESS)
{
status = alt_int_isr_register(ALT_INT_INTERRUPT_CAN0_MO_IRQ, can_int_callback,NULL);
}
if (status == ALT_E_SUCCESS)
/* Ignore target_set() for non-SPI interrupts. */
{
int target = 0x3; /* 1 = CPU0, 2=CPU1 */
status = alt_int_dist_target_set(ALT_INT_INTERRUPT_CAN0_MO_IRQ, target);
}
if (status == ALT_E_SUCCESS)
{
status = alt_int_dist_trigger_set(ALT_INT_INTERRUPT_CAN0_MO_IRQ,ALT_INT_TRIGGER_LEVEL);
}
/* Enable the distributor, CPU, and global interrupt */
if (status == ALT_E_SUCCESS)
{
status = alt_int_dist_enable(ALT_INT_INTERRUPT_CAN0_MO_IRQ);
}
if (status == ALT_E_SUCCESS)
{
status = alt_int_cpu_enable();
}
if (status == ALT_E_SUCCESS)
{
status = alt_int_global_enable();
}
配置I/O外围设备 CAN 控制器,使其可以发送IRQ中断请求到GIC。
/* Enable can0 interrupt//CCTRL.MIL */
alt_can_int_enable(candev, 0xf);
设置消息对象接收中断。消息对象/邮箱的中断是在消息对象的控制位中设置的。设置成功后,一旦接收到报文,就触发中断
/* Enable rx interrupt */
ALT_CAN_MSG_IFMCTR_t ctrl0 = {
0};
ctrl0.data_len=0x8;
ctrl0.block_end=true;
ctrl2.rx_int=true;
alt_can_if_msg_ctrl_set( candev, ALT_CAN_INTERFACE_WRITE,&ctrl0);
ALT_CAN_MSG_PARAM_t msgparam1 = {
0};
msgparam1.control=true;
alt_can_message_put(candev, ALT_CAN_INTERFACE_WRITE, 0x2,&msgparam1);
触发中断后,跳转指令到中断服务函数
中断服务函数:实现点亮FPGA灯,以及将接收到的数据帧发送出去
void can_int_callback()
{
/* light the FPGA led */
alt_write_word(0xff200000,0x1);}
然后通过读取寄存器 MOIPA 的值判断哪一个消息对象的中断标志位 Inpnd 置1,知道哪个邮箱触发中断
/* send a message object which receive just now */
ALT_CAN_MSG_PARAM_t cmd_params0 = {
0};
cmd_params0.arbitration=true;
cmd_params0.control=true;
cmd_params0.data_A=true;
cmd_params0.data_B=true; // true = transfer Data Bytes 0-3 to IFxDA.
alt_can_message_get(candev,ALT_CAN_INTERFACE_READ, i, &cmd_params0);
uint32_t a = alt_read_word(0xFFC00108);
uint32_t b = alt_read_word(0xFFC00110);
uint32_t c = alt_read_word(0xFFC00114);
uint32_t d = a & 0x1fffffff;
arb_param0.direction=true;
arb_param0.id=d;
arb_param0.extended=false;
arb_param0.valid=true; //MsgVal bit
alt_can_if_arb_set( candev,ALT_CAN_INTERFACE_WRITE, &arb_param0);
ALT_CAN_MSG_IFMCTR_t ctrl0 = {
0};
ctrl0.data_len=0x8;
ctrl0.block_end=true;
alt_can_if_msg_ctrl_set( candev, ALT_CAN_INTERFACE_WRITE,&ctrl0);
/*Sends 8 data bytes to the CAN controller.IF register*/
alt_can_if_data_set( candev, ALT_CAN_INTERFACE_WRITE, b, c);
ALT_CAN_MSG_PARAM_t msgparam = {
0};
msgparam.data_B=true; //true = transfer Data Bytes 0-3 to Message Object.
msgparam.data_A=true;
msgparam.tx_rqst_new_dat=true; // true = set TxRqst and NewDat in Message Object to one
msgparam.control=true; //true = transfer Control Bits to Message Object.
msgparam.arbitration=true;
alt_can_message_put(candev, ALT_CAN_INTERFACE_WRITE, 0x1,&msgparam);
/* clear rx interrupt bit */
ALT_CAN_MSG_IFMCTR_t ctrl2 = {
0};
ctrl2.int_pending=false;
ctrl2.data_len=0x8;
ctrl2.block_end=true;
ctrl2.rx_int=true;
alt_can_if_msg_ctrl_set( candev, ALT_CAN_INTERFACE_WRITE,&ctrl2);
ALT_CAN_MSG_PARAM_t msgparam4 = {
0};
msgparam4.control=true;
alt_can_message_put(candev,ALT_CAN_INTERFACE_WRITE,i,&msgparam4);
为了程序能正常运行而不是一直进入中断,要清除中断位,在对应邮箱/消息对象的 Intpnd 位置0即可。
程序设计完成后,经过编译、汇编、链接最终生成.axf二进制文件
实验结果:
每当USB-CAN Tool 发送一个消息对象,程序触发中断,进入中断服务函数,将接收到的消息对象再返回发送到USB-CAN Tool中,如下图所示。