文章目录
- UART串口中断实验
- 定时器中断实验
- PS-XADC实验
- QSPI Flash读写测试
- SD卡读写文本文档
UART串口中断实验
UART控制器是一个全双工异步收发器,支持可编程的波特率和IO信号格式,具有独立的TX和RX数据路径,每个路径有一个64字节的FIFO,FIFO的中断状态支持轮询或者中断模式.
UART的四种工作模式如下,一般使用的都是正常模式。
黑金开发板原理图中的UART如下图所示。
其在Bank501上的连接位置如下图所示。
本次实验的任务是使用UART控制器,完成串口中断数据环回功能。硬件配置和ZYNQ之嵌入式开发01——HelloWorld实验一致,然后导出到硬件,打开SDK进行编程。
导入一个UART中断示例,然后参考该示例代码进行编写。
本实验中的C代码如下。
#include "stdio.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"
#include "xil_exception.h"#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_1_INTR
XUartPs Uart;
XScuGic Intc;void uart_init(XUartPs *UartInstPtr)
{XUartPs_Config *Config;//根据器件ID查找配置信息Config = XUartPs_LookupConfig(UART_DEVICE_ID);//根据配置信息对UART进行初始化XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);//设置波特率XUartPs_SetBaudRate(UartInstPtr, 115200);//设置RxFIFO触发阈值XUartPs_SetFifoThreshold(UartInstPtr, 1);//设置操作模式XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);
}void uart_InterruptHandler(void *call_back_ref)
{XUartPs *uart_instance_ptr = (XUartPs *)call_back_ref;u32 rec_data = 0;u32 interrupt_status;//读取中断寄存器interrupt_status = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_IMR_OFFSET);interrupt_status &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET);if(interrupt_status & (u32)XUARTPS_IXR_RXOVR){rec_data = XUartPs_RecvByte(XPAR_XUARTPS_0_BASEADDR); //接收数据XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET,XUARTPS_IXR_RXOVR);}XUartPs_SendByte(XPAR_XUARTPS_0_BASEADDR, rec_data); //发送数据
}void uart_interrupt_init(XScuGic *IntcInstPtr, XUartPs *UartInstPtr)
{XScuGic_Config *IntcConfig;IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(IntcInstPtr, IntcConfig, IntcConfig->CpuBaseAddress);Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, IntcInstPtr);XScuGic_Connect(IntcInstPtr, UART_INT_IRQ_ID, (Xil_ExceptionHandler) uart_InterruptHandler, (void *) UartInstPtr);//设置触发类型XUartPs_SetInterruptMask(UartInstPtr, XUARTPS_IXR_RXOVR);XScuGic_Enable(IntcInstPtr, UART_INT_IRQ_ID);Xil_ExceptionEnable();
}int main()
{//UART控制器的初始化uart_init(&Uart);//中断初始化uart_interrupt_init(&Intc, &Uart);//printf("Uart interrupt test!\n"); //stdio.hxil_printf("Uart interrupt test!\n"); //xil_printf.hwhile(1);return 0;
}
开发板连接JTAG和UART线后上电,测试的结果如下图所示。
定时器中断实验
通过定时器的中断,每200ms控制一次PS LED灯的亮灭。
本实验和前面LED实验采用的硬件部分一致,需要勾选GPIO MIO,如下图所示。
ZYNQ IP核中的CPU时钟频率为666.666666MHz,定时器的频率是CPU时钟频率的一半。
导出硬件文件,不需要包含比特流,因为本实验不使用到PL部分,然后启动SDK,可以参考中断的模板编写代码。
LED实验中,采用的是sleep()函数来延时的,这个函数使用简单,但是会占用CPU资源,本实验中将使用定时器中断来替代sleep()进行延时,该实验的代码如下。
#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xil_exception.h"#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
#define TIMER_LOAD_VALUE 0x3F94067 //定时200ms CPU——>333.333MHz——>周期约是3ns
XGpioPs_Config *ConfigPtr; //结构体类型,成员有设备ID和寄存器地址
XGpioPs Gpio; //结构体类型,GPIO操作实例
static u32 PS_LED1 = 0; //PS端LED1连接的输出引脚是0号
static u32 PS_LED2 = 13; //PS端LED2连接的输出引脚是13号
XScuTimer TimerInstance; //私有定时器的实例
XScuGic IntcInstance; //中断控制器实例void gpio_init()
{//查找器件的配置信息ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);//初始化GPIO的驱动XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);//设置GPIO的方向为输出XGpioPs_SetDirectionPin(&Gpio, PS_LED1, 1); //0-input,1-outputXGpioPs_SetDirectionPin(&Gpio, PS_LED2, 1);//设置输出使能XGpioPs_SetOutputEnablePin(&Gpio, PS_LED1, 1); //0-disable,1-enableXGpioPs_SetOutputEnablePin(&Gpio, PS_LED2, 1);
}void TimerIntrHandler(void *CallBackRef)
{static int led_status = 0;XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;if (XScuTimer_IsExpired(TimerInstancePtr)){//清除中断XScuTimer_ClearInterruptStatus(TimerInstancePtr);XGpioPs_WritePin(&Gpio, PS_LED1, led_status);led_status = ~led_status;XGpioPs_WritePin(&Gpio, PS_LED2, led_status); //让PS LED2和PS LED1的状态相反}
}void timer_interrupt_init(XScuGic *IntcInstancePtr, XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{XScuGic_Config *IntcConfig;IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);//中断异常处理Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr);Xil_ExceptionEnable();//设置定时器的中断处理函数XScuGic_Connect(IntcInstancePtr, TimerIntrId, (Xil_ExceptionHandler)TimerIntrHandler, (void *)TimerInstancePtr);XScuGic_Enable(IntcInstancePtr, TimerIntrId);XScuTimer_EnableInterrupt(TimerInstancePtr);
}void timer_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr, u16 TimerDeviceId, u16 TimerIntrId)
{//初始化私有定时器XScuTimer_Config *ConfigPtr;ConfigPtr = XScuTimer_LookupConfig(TimerDeviceId);XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr, ConfigPtr->BaseAddr);timer_interrupt_init(IntcInstancePtr, TimerInstancePtr, TimerIntrId);//使能自动加载模式XScuTimer_EnableAutoReload(TimerInstancePtr);//配置计数器的初始值XScuTimer_LoadTimer(TimerInstancePtr, TIMER_LOAD_VALUE);XScuTimer_Start(TimerInstancePtr);
}int main()
{printf("Timer interrupt test!\n");gpio_init();timer_init(&IntcInstance,&TimerInstance,TIMER_DEVICE_ID,TIMER_IRPT_INTR);while(1);return 0;
}
开发板上验证的动图如下所示,代码中设计的是每200ms翻转一次LED的状态,并且PS端LED1和LED2的状态相反。
SDK中的调试方式是在工程上右键,选择Debug As——>Launch on Hardware,在弹出的界面中就可以设置断点,然后按相应的按钮进行代码调试了。
PS-XADC实验
XADC内部是两个1Mbps的ADC,可以采集模拟信号转为数字信号。
PS端XADC接口框图如下图所示。
本实验将同规格PS-XADC接口,读取XADC测量的芯片温度、供电电压等信息,然后通过串口进行打印。
不用写程序,通过JTAG连接开发板就可以读取到芯片的温度、电压等信息,给开发板连接JTAG线,然后在Vivado中点击Open Target,双击XADC后就可以添加自己想要观测的指标。
本实验使用Helloworld实验的框图即可,里面的XADC是灰色的,说明其不需要进行配置就可以使用。
在SDK中导入PS XADC的模板代码,仿照模板写代码。
本实验按照样例编写的代码如下。
#include "xparameters.h"
#include "xadcps.h"
#include "xstatus.h"
#include "stdio.h"
#include "sleep.h"#define XADC_DEVICE_ID XPAR_XADCPS_0_DEVICE_ID
static XAdcPs XAdcInst;int XAdcFractionToInt(float FloatNum)
{float Temp;Temp = FloatNum;if (FloatNum < 0)Temp = -(FloatNum);return( ((int)((Temp -(float)((int)Temp)) * (1000.0f))));
}int main()
{XAdcPs_Config *ConfigPtr;u32 TempRawData;u32 VccPintRawData;float TempData;float VccPintData;printf("PS XADC Test!\n");//Initialize the XAdc driver.ConfigPtr = XAdcPs_LookupConfig(XADC_DEVICE_ID);XAdcPs_CfgInitialize(&XAdcInst, ConfigPtr, ConfigPtr->BaseAddress);//Disable the Channel Sequencer before configuring the Sequence registers.XAdcPs_SetSequencerMode(&XAdcInst, XADCPS_SEQ_MODE_SAFE);while(1){TempRawData = XAdcPs_GetAdcData(&XAdcInst, XADCPS_CH_TEMP);TempData = XAdcPs_RawToTemperature(TempRawData);VccPintRawData = XAdcPs_GetAdcData(&XAdcInst, XADCPS_CH_VCCPINT);VccPintData = XAdcPs_RawToVoltage(VccPintRawData);printf("Raw temperature : %lu, Real temperature : %fC\n",TempRawData,TempData);printf("Raw VccPint : %lu, Real VccPint : %fV\n",VccPintRawData,VccPintData);sleep(3);printf("\n");}return 0;
}
终端中打印的信息如下图所示。
QSPI Flash读写测试
Flash存储器又称闪存,是一种非易失性存储器,具有操作方便、读写速度快等优点,一般用于存储操作系统和程序代码。
Flash的存储单元组织为块阵列,块是擦除操作的最小单位,擦除操作将块内的所有位置为1,页是读和写操作的基本单位。在对页进行写操作之前,需要判断该页内所有的位是否为1,如果全部为1,可以进行写操作,否则需要对整个块进行擦除操作,Flash的特性是只能从1翻转到0,从0翻转到1的操作就是擦除 。
Flash从内部结构分为Nor Flash和NAND Flash。Nor Flash写入和擦除的速度较低,结构复杂成本高,存储容量小,一般用于存储Bootloader以及操作系统或者程序代码,可以在芯片内部直接运行代码。NAND Flash写入和擦除速度快,结构简单成本低,存储容量较大,一般用于存储资料。
Flash从外部接口上分为CFI Flash和SPI Flash。CFI Flash读写速度快,需要的硬件引脚多,且不同容量上的硬件不兼容。SPI Flash读写速度慢,需要硬件引脚少,且不同容量上硬件兼容。
SPI(Serial Peripheral Interface)是串行外设接口的缩写,是摩托罗拉公司推出的一种同步串行接口技术,是一种高速的、全双工、同步的通信总线,其优点是支持全双工通信、通信简单、传输速率快,缺点是没有应答机制确认是否接收到数据,跟IIC总线协议比较在数据可靠性上有一定的缺陷。
SPI的简单示意图如下。
QSPI Flash的简单示意图如下。
QSPI有四根双向的数据线,因此在读写速度上要比SPI快。
本实验使用QSPI Flash控制器,先后对开发板上的Flash进行写和读操作,通过对比读出的数据是否等于写入的数据,从而验证读写操作是否正确。
在Helloworld实验工程的基础上,勾选SPI Flash选项。
在SDK中导入代码示例。
直接使用示例提供的代码,在开发板上运行后的结果如下图所示。
SD卡读写文本文档
SD(Secure Digital Card)卡称安全数字卡,也叫安全数码卡,特点是体积小、容量大、传输速度快、支持热插拔。SD卡是在MMC(MultiMedia Card)的基础上发展而来,增加了更高的安全性和更快的读写速度。
SD卡在7020开发板中的原理图如下。
其连接在BANK501上面,对应的接口是40-45,SD_CD连接在47口。
文件系统是负责管理和存储文件信息的软件机构,是在磁盘上组织文件的方法。常用的有FAT、FATFS、NTFS、CDFS、exFAT。FATFS是一个完全免费开源的FAT模块,专门为小型的嵌入式系统而设计,用C语言编写,具有良好的硬件平台独立性,可支持多个存储媒介,是可裁剪的文件系统。关于FATFS及其函数的介绍可以在其官网中查看:点此跳转。
本实验通过Xilinx SDK自带的FATFS库,完成对TF卡中TXT文本读写功能,并将读写测试结果通过串口打印。
IP核配置这里按照原理图勾选相应的SD。
在MIO配置中,CD的引脚也按照原理图进行配置。
打开SDK新建工程,在_bsp上右击打开板级支持包设置,勾选xilffs,如下图。
在xilffs的配置界面中将下面的默认选项改为true。
在目录树下查看,该库文件就添加到相应的板级支持包文件夹中了。
本实验中使用的代码如下,可以参考FATFS网站进行编写。
#include "stdio.h"
#include "string.h"
#include "xparameters.h"
#include "ff.h"#define FILE_NAME "sd_message.txt"
char wr_data[20] = "abcdefghi&123456789";
FATFS fs;void sd_mount()
{FRESULT ret;ret = f_mount(&fs, "", 1); //注册FATFS文件系统对象,挂载if(ret != FR_OK){printf("Volume is not FAT format!\n");f_mkfs("", 0, 0); //格式化SD卡f_mount(&fs, "", 1);}
}void write_data_to_sd(char *str,u32 len)
{FIL fil;UINT bw;//打开或创建文件f_open(&fil, FILE_NAME, FA_CREATE_ALWAYS | FA_WRITE | FA_READ);f_lseek(&fil, 0);f_write(&fil, str, len, &bw);f_close(&fil);
}void read_data_from_sd(char *str,u32 len)
{FIL fil;UINT br;f_open(&fil, FILE_NAME, FA_READ);f_lseek(&fil, 0);f_read(&fil, str, len, &br);f_close(&fil);
}int main()
{char rd_data[20] = "";sd_mount();write_data_to_sd(wr_data,strlen(wr_data));read_data_from_sd(rd_data,strlen(wr_data));printf("read data from sd : %s\n",rd_data);if(strcmp(wr_data,rd_data) == 0){printf("Write data is equal to read data!\n");}else{printf("Write data is not equal to read data!\n");}return 0;
}
在开发板上测试的结果如下图所示。
将SD卡从开发板上取下,插在读卡器中进行验证,可以看到,确实创建了该文件且里面的内容也是通过代码写入的,只是创建时间不对。
参考视频:
正点原子手把手教你学ZYNQ之嵌入式开发