MicroBlaze最小系统+UART/CAN/GPIO(PS端 Hello World)

1. 前言

MicroBlaze PS的开发是基于PL的,所以在设计设计师要了解底层的设计时什么样的,不然程序就是空中楼阁根本跑不起来。
本篇设计时基于我上一篇博客的设计而来的MicroBlaze最小系统+UART/CAN/GPIO

为保证质量,本系列文章通过四篇文章四个工程来讲解PS开发的过程:(想写多少写多少)

  • hello world
  • can tx rxtest
  • bram read write
  • 中断

平台是XCKU040,Vivado版本2019.1。

本人是之前参与过STM32单片机的开发,水平有限,共同进步。

2. Hello World工程搭建

2.1 打开SDK

首先要知道如何从Vivado界面打开SDK。这里需要注意的是随着Vivado版本的变化,从2019.2版本以后PS开发平台变成了Vitis。打开方式和开发方法差不多。

点击Launch SDK就可以在Vivado 界面打开SDK
在这里插入图片描述

随着电脑CPU性能越好,打开速度越快,可能要等待一阵时间。打开SDK后的界面是这样的:
在这里插入图片描述

2.2 创建APP工程

点击Flie – > new --> Application project创建工程
输入工程名hello
在这里插入图片描述
点击Next还可以选择工程类型。这是Xilinx官方准备的部分Demo,结合应用可以在这几个工程中选取何时的进行开发,我们之间保持默认选择Hello World。从Hello World开发中,我们就能掌握开发的全貌
在这里插入图片描述

生成成功后可以看到多了一个名为hello的工程,这里面是我们需要开发的C文件夹,hello_bsp是板级系统支持包的意思,里面有一些支持库
在这里插入图片描述

2.3 设置BSP

当没有准备实体串口的时候,可以点开Modify this BSP‘s Settings 设置stdin和stdout为mdm。这样有些信息就可以通过mdm在控制台上打印出来,这是为什么我强烈建议大家使用debug & UART的原因。
在这里插入图片描述

2.4 代码分析

主文件代码:

/*
 * helloworld.c: simple test application
 *
 * This application configures UART 16550 to baud rate 9600.
 * PS7 UART (Zynq) is not initialized by this application, since
 * bootrom/bsp configures it to baud rate 115200
 *
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"


int main()
{
    init_platform();

    print("Hello World\n\r");

    cleanup_platform();
    return 0;
}

可以看到代码十分简单,架构也十分的清晰,按住CTRL+鼠标左键可以查看函数定义的地方,我们进入init_platfrom();主要就是一个初始化cache和串口的过程,这里使用的是Uart Lite IP所以,STDOUT_IS_16550未定义。

init_uart()
{
#ifdef STDOUT_IS_16550
    XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ, UART_BAUD);
    XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
#endif
    /* Bootrom/BSP configures PS7/PSU UART to 115200 bps */
}

void
init_platform()
{
    /*
     * If you want to run this example outside of SDK,
     * uncomment one of the following two lines and also #include "ps7_init.h"
     * or #include "ps7_init.h" at the top, depending on the target.
     * Make sure that the ps7/psu_init.c and ps7/psu_init.h files are included
     * along with this example source files for compilation.
     */
    /* ps7_init();*/
    /* psu_init();*/
    enable_caches();
    init_uart();
}

这里我们关心的其实是这个文件下的头文件

#include “xparameters.h”

进入这个头文件,我们可以看到,它定义了我们所有用到的外设,并都指定了一个名和基地址。

UART

/* Definitions for peripheral AXI_UARTLITE_0 */
#define XPAR_AXI_UARTLITE_0_BASEADDR 0x40600000
#define XPAR_AXI_UARTLITE_0_HIGHADDR 0x4060FFFF
#define XPAR_AXI_UARTLITE_0_DEVICE_ID 0
#define XPAR_AXI_UARTLITE_0_BAUDRATE 115200
#define XPAR_AXI_UARTLITE_0_USE_PARITY 0
#define XPAR_AXI_UARTLITE_0_ODD_PARITY 0
#define XPAR_AXI_UARTLITE_0_DATA_BITS 8

/* Canonical definitions for peripheral AXI_UARTLITE_0 */
#define XPAR_UARTLITE_0_DEVICE_ID XPAR_AXI_UARTLITE_0_DEVICE_ID
#define XPAR_UARTLITE_0_BASEADDR 0x40600000
#define XPAR_UARTLITE_0_HIGHADDR 0x4060FFFF
#define XPAR_UARTLITE_0_BAUDRATE 115200
#define XPAR_UARTLITE_0_USE_PARITY 0
#define XPAR_UARTLITE_0_ODD_PARITY 0
#define XPAR_UARTLITE_0_DATA_BITS 8

CAN

/* Definitions for driver CAN */
#define XPAR_XCAN_NUM_INSTANCES 1

/* Definitions for peripheral CAN_0 */
#define XPAR_CAN_0_DEVICE_ID 0
#define XPAR_CAN_0_BASEADDR 0x44A00000
#define XPAR_CAN_0_HIGHADDR 0x44A0FFFF
#define XPAR_CAN_0_CAN_RX_DPTH 2
#define XPAR_CAN_0_CAN_TX_DPTH 2
#define XPAR_CAN_0_CAN_NUM_ACF 0

GPIO

/* Definitions for driver GPIO */
#define XPAR_XGPIO_NUM_INSTANCES 1

/* Definitions for peripheral AXI_GPIO_0 */
#define XPAR_AXI_GPIO_0_BASEADDR 0x40000000
#define XPAR_AXI_GPIO_0_HIGHADDR 0x4000FFFF
#define XPAR_AXI_GPIO_0_DEVICE_ID 0
#define XPAR_AXI_GPIO_0_INTERRUPT_PRESENT 1
#define XPAR_AXI_GPIO_0_IS_DUAL 1


/******************************************************************/

/* Canonical definitions for peripheral AXI_GPIO_0 */
#define XPAR_GPIO_0_BASEADDR 0x40000000
#define XPAR_GPIO_0_HIGHADDR 0x4000FFFF
#define XPAR_GPIO_0_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define XPAR_GPIO_0_INTERRUPT_PRESENT 1
#define XPAR_GPIO_0_IS_DUAL 1


/******************************************************************/

中断控制器

/* Definitions for peripheral MICROBLAZE_0_AXI_INTC */
#define XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID 0
#define XPAR_MICROBLAZE_0_AXI_INTC_BASEADDR 0x41200000
#define XPAR_MICROBLAZE_0_AXI_INTC_HIGHADDR 0x4120FFFF
#define XPAR_MICROBLAZE_0_AXI_INTC_KIND_OF_INTR 0xFFFFFFFF
#define XPAR_MICROBLAZE_0_AXI_INTC_HAS_FAST 1
#define XPAR_MICROBLAZE_0_AXI_INTC_IVAR_RESET_VALUE 0x0000000000000010
#define XPAR_MICROBLAZE_0_AXI_INTC_NUM_INTR_INPUTS 3
#define XPAR_MICROBLAZE_0_AXI_INTC_ADDR_WIDTH 32


/******************************************************************/

#define XPAR_INTC_SINGLE_BASEADDR 0x41200000
#define XPAR_INTC_SINGLE_HIGHADDR 0x4120FFFF
#define XPAR_INTC_SINGLE_DEVICE_ID XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID
#define XPAR_MICROBLAZE_0_AXI_INTC_TYPE 0U
#define XPAR_AXI_UARTLITE_0_INTERRUPT_MASK 0X000001U
#define XPAR_MICROBLAZE_0_AXI_INTC_AXI_UARTLITE_0_INTERRUPT_INTR 0U
#define XPAR_AXI_GPIO_0_IP2INTC_IRPT_MASK 0X000002U
#define XPAR_MICROBLAZE_0_AXI_INTC_AXI_GPIO_0_IP2INTC_IRPT_INTR 1U
#define XPAR_CAN_0_IP2BUS_INTREVENT_MASK 0X000004U
#define XPAR_MICROBLAZE_0_AXI_INTC_CAN_0_IP2BUS_INTREVENT_INTR 2U

/******************************************************************/

2.5 重点!

我之前一直强调:
对于这些外设的开发就是针对地址空间的读写。
我们来深究这一点。

    print("Hello World\n\r");

在helloworld文件中,有个print函数,我们可以知道就是将数据通过串口打印出去的函数。

#include "xil_printf.h"

void print(const char8 *ptr)
{
#if HYP_GUEST && EL1_NONSECURE && XEN_USE_PV_CONSOLE
	XPVXenConsole_Write(ptr);
#else
#ifdef STDOUT_BASEADDRESS
  while (*ptr != (char8)0) {
    outbyte (*ptr);
	ptr++;
  }
#else
(void)ptr;
#endif
#endif
}

按住CTRL+鼠标左键(或者 右键–> open declaration)可以查看他的定义。
可以看到这里明显是使用outbyte函数输出的。对

outbyte (*ptr);

进行溯源!

void outbyte(char c) {
	 XUartLite_SendByte(STDOUT_BASEADDRESS, c);
}

发现就是把XuartLite_SendByte()封装了一层。直接将要传输的变量给了XuartLite_SendByte。

注意:这里我们有个参数也被传了进去STDOUT_BASEADDRESS,这个是什么呢,这个就是我们当时指定调试的输出串口的基地址,因为我们在bsp的设置中改成了mdm,所以这个基地址是与mdm_1的一致。
在这里插入图片描述


/******************************************************************/

#define STDIN_BASEADDRESS 0x41400000
#define STDOUT_BASEADDRESS 0x41400000

/* Definitions for peripheral MDM_1 */
#define XPAR_MDM_1_BASEADDR 0x41400000
#define XPAR_MDM_1_HIGHADDR 0x41400FFF
#define XPAR_MDM_1_DEVICE_ID 1
#define XPAR_MDM_1_BAUDRATE 0
#define XPAR_MDM_1_USE_PARITY 0
#define XPAR_MDM_1_ODD_PARITY 0
#define XPAR_MDM_1_DATA_BITS 0

/******************************************************************/

继续对

XUartLite_SendByte(STDOUT_BASEADDRESS, c);

溯源!

******************************************************************************/
void XUartLite_SendByte(UINTPTR BaseAddress, u8 Data)
{
	while (XUartLite_IsTransmitFull(BaseAddress));

	XUartLite_WriteReg(BaseAddress, XUL_TX_FIFO_OFFSET, Data);
}


/****************************************************************************/
/**
*
* This functions receives a single byte using the UART. It is blocking in that
* it waits for the receiver to become non-empty before it reads from the
* receive register.
*
* @param	BaseAddress is the base address of the device
*
* @return	The byte of data received.
*
* @note		None.
*
******************************************************************************/
u8 XUartLite_RecvByte(UINTPTR BaseAddress)
{
	while (XUartLite_IsReceiveEmpty(BaseAddress));

	return (u8)XUartLite_ReadReg(BaseAddress, XUL_RX_FIFO_OFFSET);
}

/** @} */

发现到了XUartLite收数和发数的函数定义文件中,我们在使用uart lite时,完成初始化后,也可以直接用这两个函数。
写数据的时候不过就是写寄存器,我们对

XUartLite_WriteReg(BaseAddress, XUL_TX_FIFO_OFFSET, Data);

继续溯源!
注意:这里的传参是:基地址,偏移地址,数据

****************************************************************************/
#define XUartLite_WriteReg(BaseAddress, RegOffset, Data) \
	XUartLite_Out32((BaseAddress) + (RegOffset), (u32)(Data))

/****************************************************************************/
/**
*
* Read a value from a UartLite register. A 32 bit read is performed.
*
* @param	BaseAddress is the base address of the UartLite device.
* @param	RegOffset is the register offset from the base to read from.
*
* @return	Data read from the register.
*
* @note		C-style signature:
*		u32 XUartLite_ReadReg(u32 BaseAddress, u32 RegOffset)
*
****************************************************************************/
#define XUartLite_ReadReg(BaseAddress, RegOffset) \
	XUartLite_In32((BaseAddress) + (RegOffset))


/****************************************************************************/

到了更简单的两个函数,
注意:这里的传参变成了地址和数据。

XUartLite_Out32((BaseAddress) + (RegOffset), (u32)(Data))

进行溯源!

#define XUartLite_In32  Xil_In32
#define XUartLite_Out32 Xil_Out32

继续!

******************************************************************************/
static INLINE void Xil_Out32(UINTPTR Addr, u32 Value)
{
#ifndef ENABLE_SAFETY
	volatile u32 *LocalAddr = (volatile u32 *)Addr;
	*LocalAddr = Value;
#else
	XStl_RegUpdate(Addr, Value);
#endif
}

这里终于可以看出,对于任何外设的读写,最后都变成了对指定地址的读写。因为已经转换成了一个与外设类型无关的读写函数。

void Xil_Out32(UINTPTR Addr, u32 Value)

同理 读也差不多。有兴趣可以自己看一下,读比写更加复杂。

2.6 debug

  1. 右键选中hello文件夹
  2. 选中debug as
  3. 选中debug configurations
  4. 双击system debugger
  5. 勾选Reset entire system和Program FPGA

在这里插入图片描述
到这里Hello world的开发都完成了,但是由于本人手上现在没有FPGA开发板,暂时无法测试工程。

3. 后言

  • 7
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MicroBlaze是Xilinx公司推出的一种基于FPGA的软核处理器,它可以通过UART接收数据。下面是MicroBlaze+UART接收数据的简要介绍: 1. 首先需要在Vivado中创建一个MicroBlaze处理器系统,并将UART IP核添加到该系统中。 2. 在MicroBlaze处理器中,需要使用Xilinx提供的驱动程序来控制UART IP核。可以使用Xilinx提供的XUartLite驱动程序,该驱动程序提供了一组API函数,可以方便地实现UART接收数据的功能。 3. 在应用程序中,需要调用XUartLite_Recv函数来接收UART数据。该函数的原型如下: ``` u32 XUartLite_Recv(XUartLite *InstancePtr, u8 *BufferPtr, u32 NumBytes); ``` 其中,InstancePtr是指向XUartLite实例的指针,BufferPtr是指向接收缓冲区的指针,NumBytes是要接收的字节数。 4. 在接收数据之前,需要先初始化UART IP核。可以使用XUartLite_CfgInitialize函数来初始化UART IP核。该函数的原型如下: ``` int XUartLite_CfgInitialize(XUartLite *InstancePtr, XUartLite_Config *Config, u32 EffectiveAddr); ``` 其中,InstancePtr是指向XUartLite实例的指针,Config是指向UART配置结构体的指针,EffectiveAddr是UART IP核的物理地址。 5. 接收数据时,需要先判断是否有数据可读。可以使用XUartLite_IsReceiveEmpty函数来判断是否有数据可读。该函数的原型如下: ``` int XUartLite_IsReceiveEmpty(XUartLite *InstancePtr); ``` 如果返回值为0,则表示有数据可读;如果返回值为1,则表示没有数据可读。 6. 接收数据时,需要注意接收缓冲区的大小。如果接收缓冲区已满,则需要停止接收数据,直到应用程序处理完接收缓冲区中的数据后再继续接收数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bigbeea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值