【二】SPI IP核的使用

【一】SPI IP核使用:传送门

基于qsys通过spi外部总线协议对sd卡进行读写操作

一、实验平台与实验的目的:

​ 正点原子开拓者、芯片型号:EP4CE10F17C8;还需要一张sd卡。

​ 该实验主要是利用SPI IP核驱动SD卡来实现读写实验,在这个实验中我们要了解spi使用方法核学习sd卡的读写操作方法。

二、系统的搭建:

image-20230807134951130

(1)nios 处理器的设置:

nios II/f

image-20230807135042083

其他的默认

(2)sdram controller配置

image-20230807135322824

image-20230807135333855

(3)SPI IP核配置

image-20230807135427808

(4)PIO ip核配置

image-20230807135456948

其他没有展示的ip核配置均采用默认的配置。

三、顶层文件

module Qsys_Spi
( /* 时钟复位端口 */CLK_50M,RST_N,/* SDRAM端口 */SDRAM_ADDR,SDRAM_BA,SDRAM_CAS_N,SDRAM_CLK,SDRAM_CKE,SDRAM_CS_N,SDRAM_DQ,SDRAM_DQM,SDRAM_RAS_N,SDRAM_WE_N,/* LED端口 */SD_MISO,SD_MOSI,SD_SCLK,SD_CS_N
);//---------------------------------------------------------------------------
//--	外部端口声明
//---------------------------------------------------------------------------
/* 时钟复位端口 */
input								CLK_50M;
input			 					RST_N;
/* SDRAM端口 */
output 		[12:0] 			SDRAM_ADDR;
output 		[ 1:0] 			SDRAM_BA;
output        					SDRAM_CAS_N;
output        					SDRAM_CLK;
output        					SDRAM_CKE;
output        					SDRAM_CS_N;
inout  		[15:0] 			SDRAM_DQ;
output 		[ 1:0] 			SDRAM_DQM;
output        					SDRAM_RAS_N;
output        					SDRAM_WE_N;   
/* SD端口 */
output        					SD_SCLK;
output        					SD_CS_N;
output        					SD_MOSI;
input         					SD_MISO; //---------------------------------------------------------------------------
//--	内部端口声明
//---------------------------------------------------------------------------
wire 								clk_100m;//---------------------------------------------------------------------------
//--	逻辑功能实现	
//---------------------------------------------------------------------------
PLL 								PLL_Init 
(.inclk0 						(CLK_50M		),.c0     						(clk_100m	),.c1     						(SDRAM_CLK	)
);Qsys_system 					Qsys_system_Init
(.clk_clk                (clk_100m	), //             clk.clk.reset_reset_n          (RST_N		), //           reset.reset_n.sdram_conduit_addr     (SDRAM_ADDR	), //   sdram_conduit.addr.sdram_conduit_ba       (SDRAM_BA	), //                .ba.sdram_conduit_cas_n    (SDRAM_CAS_N), //                .cas_n.sdram_conduit_cke      (SDRAM_CKE	), //                .cke.sdram_conduit_cs_n     (SDRAM_CS_N	), //                .cs_n.sdram_conduit_dq       (SDRAM_DQ	), //                .dq.sdram_conduit_dqm      (SDRAM_DQM	), //                .dqm.sdram_conduit_ras_n    (SDRAM_RAS_N), //                .ras_n.sdram_conduit_we_n     (SDRAM_WE_N	), //                .we_n.spi_conduit_MISO       (SD_MISO		), //     spi_conduit.MISO.spi_conduit_MOSI       (SD_MOSI		), //                .MOSI.spi_conduit_SCLK       (SD_SCLK		), //                .SCLK.spi_conduit_SS_n       (SD_CS_N		)  //                .SS_n
);endmodule

PLL IP核的配置

image-20230807135646089

clk c0输出为100Mhz相位偏差为0;clk c1输出100MHz,相位偏差为-60。

image-20230807135706954

image-20230807135718343

系统的RTL:

image-20230807143012025

四、引脚的绑定

# Copyright (C) 2017  Intel Corporation. All rights reserved.
# Your use of Intel Corporation's design tools, logic functions 
# and other software and tools, and its AMPP partner logic 
# functions, and any output files from any of the foregoing 
# (including device programming or simulation files), and any 
# associated documentation or information are expressly subject 
# to the terms and conditions of the Intel Program License 
# Subscription Agreement, the Intel Quartus Prime License Agreement,
# the Intel FPGA IP License Agreement, or other applicable license
# agreement, including, without limitation, that your use is for
# the sole purpose of programming logic devices manufactured by
# Intel and sold by Intel or its authorized distributors.  Please
# refer to the applicable agreement for further details.# Quartus Prime Version 17.1.0 Build 590 10/25/2017 SJ Standard Edition
# File: I:\zhong_hai_da_data\My_task\20230719\gs_qsys_spi\Qsys_Spi\output_files\Qsys_Spi.tcl
# Generated on: Mon Aug 07 10:50:43 2023package require ::quartus::projectset_location_assignment	PIN_E1	-to 	CLK_50M
set_location_assignment	PIN_M1	-to 	RST_N
set_location_assignment	PIN_F15	-to 	SDRAM_ADDR[12]
set_location_assignment	PIN_D16	-to 	SDRAM_ADDR[11]
set_location_assignment	PIN_F14	-to 	SDRAM_ADDR[10]
set_location_assignment	PIN_D15	-to 	SDRAM_ADDR[9]
set_location_assignment	PIN_C16	-to 	SDRAM_ADDR[8]
set_location_assignment	PIN_C15	-to 	SDRAM_ADDR[7]
set_location_assignment	PIN_B16	-to 	SDRAM_ADDR[6]
set_location_assignment	PIN_A15	-to 	SDRAM_ADDR[5]
set_location_assignment	PIN_A14	-to 	SDRAM_ADDR[4]
set_location_assignment	PIN_C14	-to 	SDRAM_ADDR[3]
set_location_assignment	PIN_D14	-to 	SDRAM_ADDR[2]
set_location_assignment	PIN_E11	-to 	SDRAM_ADDR[1]
set_location_assignment	PIN_F11	-to 	SDRAM_ADDR[0]
set_location_assignment	PIN_F13	-to 	SDRAM_BA[1]
set_location_assignment	PIN_G11	-to 	SDRAM_BA[0]
set_location_assignment	PIN_J12	-to 	SDRAM_CAS_N
set_location_assignment	PIN_F16	-to 	SDRAM_CKE
set_location_assignment	PIN_B14	-to 	SDRAM_CLK
set_location_assignment	PIN_K10	-to 	SDRAM_CS_N
set_location_assignment	PIN_L15	-to 	SDRAM_DQ[15]
set_location_assignment	PIN_L16	-to 	SDRAM_DQ[14]
set_location_assignment	PIN_K15	-to 	SDRAM_DQ[13]
set_location_assignment	PIN_K16	-to 	SDRAM_DQ[12]
set_location_assignment	PIN_J15	-to 	SDRAM_DQ[11]
set_location_assignment	PIN_J16	-to 	SDRAM_DQ[10]
set_location_assignment	PIN_J11	-to 	SDRAM_DQ[9]
set_location_assignment	PIN_G16	-to 	SDRAM_DQ[8]
set_location_assignment	PIN_K12	-to 	SDRAM_DQ[7]
set_location_assignment	PIN_L11	-to 	SDRAM_DQ[6]
set_location_assignment	PIN_L14	-to 	SDRAM_DQ[5]
set_location_assignment	PIN_L13	-to 	SDRAM_DQ[4]
set_location_assignment	PIN_L12	-to 	SDRAM_DQ[3]
set_location_assignment	PIN_N14	-to 	SDRAM_DQ[2]
set_location_assignment	PIN_M12	-to 	SDRAM_DQ[1]
set_location_assignment	PIN_P14	-to 	SDRAM_DQ[0]
set_location_assignment	PIN_G15	-to 	SDRAM_DQM[1]
set_location_assignment	PIN_J14	-to 	SDRAM_DQM[0]
set_location_assignment	PIN_K11	-to 	SDRAM_RAS_N
set_location_assignment	PIN_J13	-to 	SDRAM_WE_N
set_location_assignment	PIN_C2	-to 	SD_CS_N
set_location_assignment	PIN_K1	-to 	SD_MISO
set_location_assignment	PIN_D1	-to 	SD_MOSI
set_location_assignment	PIN_J2	-to 	SD_SCLKset_instance_assignment -name IO_STANDARD "2.5 V" -to CLK_50M
set_instance_assignment -name IO_STANDARD "2.5 V" -to RST_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[12]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[11]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[10]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[9]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[8]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[7]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[6]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[5]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[4]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[3]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[2]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[1]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_ADDR[0]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_BA[1]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_BA[0]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_CAS_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_CKE
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_CLK
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_CS_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[15]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[14]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[13]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[12]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[11]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[10]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[9]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[8]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[7]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[6]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[5]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[4]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[3]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[2]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[1]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQ[0]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQM[1]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_DQM[0]
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_RAS_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SDRAM_WE_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SD_CS_N
set_instance_assignment -name IO_STANDARD "2.5 V" -to SD_MISO
set_instance_assignment -name IO_STANDARD "2.5 V" -to SD_MOSI
set_instance_assignment -name IO_STANDARD "2.5 V" -to SD_SCLK

引脚绑定的教程:

https://www.bilibili.com/video/BV1N14y1x7VZ/?spm_id_from=333.999.list.card_archive.click&vd_source=044bb9c2f51f99e36e8b7693fa67ba9b

传送门:

五、eclipse中的软件代码

(1)实验一:将下面的代码编译并run as -->3 Nios II Hardware

image-20230807140643719

//---------------------------------------------------------------------------
//-- 文件名	:	Qsys_Spi.c
//-- 描述		:	利用SPI读写SD卡
//-- 修订历史	:	2014-1-1
//-- 作者		:	Zircon Opto-Electronic Technology CO.,Ltd.
//---------------------------------------------------------------------------
#include "system.h"					//系统头文件
#include <stdio.h>					//标准的输入输出头文件
#include <unistd.h>					//延时函数头文件
#include "alt_types.h"				//数据类型头文件
#include "altera_avalon_spi_regs.h"	//spi寄存器头文件
#include "altera_avalon_spi.h"		//spi底层驱动头文件alt_u8 SDReadBlock_Data[512];		//(读)扇区缓冲数组,512字节数据
alt_u8 SDWriteBlock_Data[512];		//(写)扇区缓冲数组,512字节数据//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDWriteByte()
//-- 功能		:	往Spi中写数据函数
//-- 输入参数	:	txdata:需要发送的数据
//-- 输出参数	:	无
//---------------------------------------------------------------------------
void Spi_SDWriteByte(alt_u8 txdata)
{//往Spi中写一个字节alt_avalon_spi_command(SPI_BASE, 0, 1, &txdata, 0, NULL, 0);
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadByte()
//-- 功能		:	从Spi中读数据函数
//-- 输入参数	:	无
//-- 输出参数	:	readbuf:从Spi中读取出来的数据
//---------------------------------------------------------------------------
alt_u8 Spi_SDReadByte()
{alt_u8 readbuf;//从Spi中读一个字节alt_avalon_spi_command(SPI_BASE, 0, 0, NULL, 1, &readbuf, 0);return(readbuf);
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadByte()
//-- 功能		:	往SD卡中写命令函数
//-- 输入参数	:	cmd:Byte1命令;arg:Byte2~Byte5命令;crc:Byte6命令
//-- 输出参数	:	r1:响应变量
//---------------------------------------------------------------------------
alt_u8 Spi_SDSendCmd(alt_u8 cmd,alt_u32 arg,alt_u8 crc)
{alt_u8 r1;						//响应变量alt_u8 time = 0;				//超时变量//SD卡的命令格式如下,6字节共48位,传输时最高位(MSB)先传输Spi_SDWriteByte(cmd | 0x40);	//写Byte1Spi_SDWriteByte(arg>>24);		//写Byte2Spi_SDWriteByte(arg>>16);		//写Byte3Spi_SDWriteByte(arg>>8);		//写Byte4Spi_SDWriteByte(arg);			//写Byte5Spi_SDWriteByte(crc);			//写Byte6//写入命令后,附加8个填充时钟,等待SD卡回应do{r1 = Spi_SDReadByte(); //读数据time++;if(time > 254)	return 1; //超时退出返回1}while(r1 == 0xff);return r1; //命令写入成功,返回响应变量
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReset()
//-- 功能		:	SD卡复位函数
//-- 输入参数	:	无
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDReset(void)
{alt_u8 i;			//循环变量alt_u8 r1;			//响应变量alt_u8 time = 0;	//超时变量//发送至少74个clk周期来使SD卡达到正常工作电压和进行同步for(i = 0;i < 16;i ++)Spi_SDWriteByte(0xff);//发送CMD0,需要收到回应0x01表示成功do{r1 = Spi_SDSendCmd(0,0,0x95); //发送CMD0命令time++;if(time > 254) 	return 1; //超时退出返回1}while(r1 != 0x01);	//等待返回0x01return 0; //复位成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDInit()
//-- 功能		:	SD卡初始化函数
//-- 输入参数	:	无
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDInit(void)
{alt_u8 r1;			//响应变量alt_u8 time = 0;	//超时变量alt_u32 r7 = 0;		//响应变量//发送CMD8检测接口条件,若r1返回0x01,r7返回0x000001aa,则表示检测成功do{r1 = Spi_SDSendCmd(8,0x000001aa,0x87); 	//发送CMD8命令r7 += Spi_SDReadByte(); 				//读取响应0x00r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0x00r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0x01r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0xaatime++;if(time > 254) return 1;				//超时退出返回1}while((r1 != 0x01) && (r7 != 0x000001aa));	//等待r1返回0x01,r7返回0x000001aatime = 0;//此处省略发送CMD58命令//发送CMD55+ACMD41,收到0x00表示成功do{r1 = Spi_SDSendCmd(55,0,0xff); //发送CMD55命令if(r1 == 0x01) r1 = Spi_SDSendCmd(41,0x40000000,0xff); //发送ACMD41命令time++;if(time > 254) return 1; //超时退出返回1}while(r1 != 0x00); //等待返回0x00//此处省略发送CMD58命令return 0; //初始化成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadBlock()
//-- 功能		:	读取SD卡一个扇区数据
//-- 输入参数	:	address:扇区地址
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDReadBlock(alt_u32 address)
{alt_u8 r1;			//响应变量alt_u32 i = 0;		//循环变量//发送CMD17命令,收到0x00表示成功r1 = Spi_SDSendCmd(17,address,0xff); //发送CMD17命令if(r1 != 0x00) return 1;//连续读直到读到开始字节0xFEwhile (Spi_SDReadByte()!= 0xfe);//读取一个扇区512字节数据for(i = 0; i < 512; i++)SDReadBlock_Data[i] = Spi_SDReadByte();//读取两个字节CRC校验Spi_SDReadByte();Spi_SDReadByte();return 0; //读取成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDWriteBlock()
//-- 功能		:	写入SD卡一个扇区数据
//-- 输入参数	:	sector:扇区地址;buffer:写入SD卡的数据首地址
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDWriteBlock(alt_u32 sector, alt_u8* buffer)
{alt_u8 r1;			//响应变量alt_u32 i;			//循环变量//发送CMD24命令,收到0x00表示成功r1 = Spi_SDSendCmd(24, sector<<9, 0xff); //发送CMD24命令if(r1 != 0x00) return 1; //写入失败,返回1//发送若干时钟for(i =0; i < 5; i++)Spi_SDWriteByte(0xff); //送8个时钟周期脉冲//发送写扇区开始字节0xFESpi_SDWriteByte(0xfe);//发送512个字节数据for(i = 0; i < 512; i++)Spi_SDWriteByte(*buffer++);//发送2字节CRC校验Spi_SDWriteByte(0xff);Spi_SDWriteByte(0xff);//连续读直到读到XXX00101表示数据写入成功r1 = Spi_SDReadByte();if((r1 & 0x1f) != 0x05) return 1; //写入失败,返回1//继续读进行忙碌检测,当读到0xff表示写操作完成while(!Spi_SDReadByte());return 0;
}//---------------------------------------------------------------------------
//-- 名称		:	main()
//-- 功能		:	程序入口
//-- 输入参数	:	无
//-- 输出参数	:	无
//---------------------------------------------------------------------------
int main(void)
{alt_u32 i;if(Spi_SDReset()) //SD卡复位printf("SD Reset Failed!\n");elseprintf("SD Reset Succeed!\n");if(Spi_SDInit()) //SD卡初始化printf("SD Inint Failed!\n");elseprintf("SD Inint Succeed!\n");for(i = 0; i < 512; i++) //初始化写入数据SDWriteBlock_Data[i] = i;if(Spi_SDWriteBlock(0, SDWriteBlock_Data)) //写一个扇区printf("SD Write Failed!\n");elseprintf("SD Write Succeed!\n");//    Spi_SDReset();
//    Spi_SDInit(); //SD卡复位并初始化
//
//    if(Spi_SDReadBlock(0)) //读一个扇区
//    	printf("SD Read  Failed!\n");
//    else
//    	printf("SD Read  Succeed!\n");
//
//    for(i = 0; i < 16; i++) //读取16个字节数据
//    	printf("0x%.2x,",SDReadBlock_Data[i]);return 0;
}
实验结果:

将在nios II console中输出:

image-20230807140751958

将sd卡拔出,插入读卡器中,打开电脑的WinHex软件,打开sd卡

image-20230807140944574

选择对应的磁盘打开会得到下面的界面:

image-20230807140356275

(2)实验二:sd卡写操作

将main函数里面的注释掉的代码打开:

//---------------------------------------------------------------------------
//-- 文件名	:	Qsys_Spi.c
//-- 描述		:	利用SPI读写SD卡
//-- 修订历史	:	2014-1-1
//-- 作者		:	Zircon Opto-Electronic Technology CO.,Ltd.
//---------------------------------------------------------------------------
#include "system.h"					//系统头文件
#include <stdio.h>					//标准的输入输出头文件
#include <unistd.h>					//延时函数头文件
#include "alt_types.h"				//数据类型头文件
#include "altera_avalon_spi_regs.h"	//spi寄存器头文件
#include "altera_avalon_spi.h"		//spi底层驱动头文件alt_u8 SDReadBlock_Data[512];		//(读)扇区缓冲数组,512字节数据
alt_u8 SDWriteBlock_Data[512];		//(写)扇区缓冲数组,512字节数据//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDWriteByte()
//-- 功能		:	往Spi中写数据函数
//-- 输入参数	:	txdata:需要发送的数据
//-- 输出参数	:	无
//---------------------------------------------------------------------------
void Spi_SDWriteByte(alt_u8 txdata)
{//往Spi中写一个字节alt_avalon_spi_command(SPI_BASE, 0, 1, &txdata, 0, NULL, 0);
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadByte()
//-- 功能		:	从Spi中读数据函数
//-- 输入参数	:	无
//-- 输出参数	:	readbuf:从Spi中读取出来的数据
//---------------------------------------------------------------------------
alt_u8 Spi_SDReadByte()
{alt_u8 readbuf;//从Spi中读一个字节alt_avalon_spi_command(SPI_BASE, 0, 0, NULL, 1, &readbuf, 0);return(readbuf);
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadByte()
//-- 功能		:	往SD卡中写命令函数
//-- 输入参数	:	cmd:Byte1命令;arg:Byte2~Byte5命令;crc:Byte6命令
//-- 输出参数	:	r1:响应变量
//---------------------------------------------------------------------------
alt_u8 Spi_SDSendCmd(alt_u8 cmd,alt_u32 arg,alt_u8 crc)
{alt_u8 r1;						//响应变量alt_u8 time = 0;				//超时变量//SD卡的命令格式如下,6字节共48位,传输时最高位(MSB)先传输Spi_SDWriteByte(cmd | 0x40);	//写Byte1Spi_SDWriteByte(arg>>24);		//写Byte2Spi_SDWriteByte(arg>>16);		//写Byte3Spi_SDWriteByte(arg>>8);		//写Byte4Spi_SDWriteByte(arg);			//写Byte5Spi_SDWriteByte(crc);			//写Byte6//写入命令后,附加8个填充时钟,等待SD卡回应do{r1 = Spi_SDReadByte(); //读数据time++;if(time > 254)	return 1; //超时退出返回1}while(r1 == 0xff);return r1; //命令写入成功,返回响应变量
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReset()
//-- 功能		:	SD卡复位函数
//-- 输入参数	:	无
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDReset(void)
{alt_u8 i;			//循环变量alt_u8 r1;			//响应变量alt_u8 time = 0;	//超时变量//发送至少74个clk周期来使SD卡达到正常工作电压和进行同步for(i = 0;i < 16;i ++)Spi_SDWriteByte(0xff);//发送CMD0,需要收到回应0x01表示成功do{r1 = Spi_SDSendCmd(0,0,0x95); //发送CMD0命令time++;if(time > 254) 	return 1; //超时退出返回1}while(r1 != 0x01);	//等待返回0x01return 0; //复位成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDInit()
//-- 功能		:	SD卡初始化函数
//-- 输入参数	:	无
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDInit(void)
{alt_u8 r1;			//响应变量alt_u8 time = 0;	//超时变量alt_u32 r7 = 0;		//响应变量//发送CMD8检测接口条件,若r1返回0x01,r7返回0x000001aa,则表示检测成功do{r1 = Spi_SDSendCmd(8,0x000001aa,0x87); 	//发送CMD8命令r7 += Spi_SDReadByte(); 				//读取响应0x00r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0x00r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0x01r7 <<= 8;r7 += Spi_SDReadByte(); 				//读取响应0xaatime++;if(time > 254) return 1;				//超时退出返回1}while((r1 != 0x01) && (r7 != 0x000001aa));	//等待r1返回0x01,r7返回0x000001aatime = 0;//此处省略发送CMD58命令//发送CMD55+ACMD41,收到0x00表示成功do{r1 = Spi_SDSendCmd(55,0,0xff); //发送CMD55命令if(r1 == 0x01) r1 = Spi_SDSendCmd(41,0x40000000,0xff); //发送ACMD41命令time++;if(time > 254) return 1; //超时退出返回1}while(r1 != 0x00); //等待返回0x00//此处省略发送CMD58命令return 0; //初始化成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDReadBlock()
//-- 功能		:	读取SD卡一个扇区数据
//-- 输入参数	:	address:扇区地址
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDReadBlock(alt_u32 address)
{alt_u8 r1;			//响应变量alt_u32 i = 0;		//循环变量//发送CMD17命令,收到0x00表示成功r1 = Spi_SDSendCmd(17,address,0xff); //发送CMD17命令if(r1 != 0x00) return 1;//连续读直到读到开始字节0xFEwhile (Spi_SDReadByte()!= 0xfe);//读取一个扇区512字节数据for(i = 0; i < 512; i++)SDReadBlock_Data[i] = Spi_SDReadByte();//读取两个字节CRC校验Spi_SDReadByte();Spi_SDReadByte();return 0; //读取成功,则返回0
}//---------------------------------------------------------------------------
//-- 名称		:	Spi_SDWriteBlock()
//-- 功能		:	写入SD卡一个扇区数据
//-- 输入参数	:	sector:扇区地址;buffer:写入SD卡的数据首地址
//-- 输出参数	:	0:成功;1:失败
//---------------------------------------------------------------------------
alt_u8 Spi_SDWriteBlock(alt_u32 sector, alt_u8* buffer)
{alt_u8 r1;			//响应变量alt_u32 i;			//循环变量//发送CMD24命令,收到0x00表示成功r1 = Spi_SDSendCmd(24, sector<<9, 0xff); //发送CMD24命令if(r1 != 0x00) return 1; //写入失败,返回1//发送若干时钟for(i =0; i < 5; i++)Spi_SDWriteByte(0xff); //送8个时钟周期脉冲//发送写扇区开始字节0xFESpi_SDWriteByte(0xfe);//发送512个字节数据for(i = 0; i < 512; i++)Spi_SDWriteByte(*buffer++);//发送2字节CRC校验Spi_SDWriteByte(0xff);Spi_SDWriteByte(0xff);//连续读直到读到XXX00101表示数据写入成功r1 = Spi_SDReadByte();if((r1 & 0x1f) != 0x05) return 1; //写入失败,返回1//继续读进行忙碌检测,当读到0xff表示写操作完成while(!Spi_SDReadByte());return 0;
}//---------------------------------------------------------------------------
//-- 名称		:	main()
//-- 功能		:	程序入口
//-- 输入参数	:	无
//-- 输出参数	:	无
//---------------------------------------------------------------------------
int main(void)
{alt_u32 i;if(Spi_SDReset()) //SD卡复位printf("SD Reset Failed!\n");elseprintf("SD Reset Succeed!\n");if(Spi_SDInit()) //SD卡初始化printf("SD Inint Failed!\n");elseprintf("SD Inint Succeed!\n");for(i = 0; i < 512; i++) //初始化写入数据SDWriteBlock_Data[i] = i;if(Spi_SDWriteBlock(0, SDWriteBlock_Data)) //写一个扇区printf("SD Write Failed!\n");elseprintf("SD Write Succeed!\n");Spi_SDReset();Spi_SDInit(); //SD卡复位并初始化if(Spi_SDReadBlock(0)) //读一个扇区printf("SD Read  Failed!\n");elseprintf("SD Read  Succeed!\n");for(i = 0; i < 16; i++) //读取16个字节数据printf("0x%.2x,",SDReadBlock_Data[i]);return 0;
}

实验结果:

image-20230807142041922

从代码中我们可以看出,使用的是altera公司提供的API访问程序alt_avalon_spi_command()对从机进行读/写。再要在qsys中设置号spi工作模式,然后给spi访问程序alt_avalon_spi_command()输入正确的参数,即可以获得用户希望的结果,需要注意的是:

  • alt_avalon_spi_command()仅对主机模式有效,在从机模式接收数据需要直接访问spi数据寄存器。
  • spi内核不匹配HAL支持的通用设备模种类,因此它不能通过HAL API或者ANSI C标准库访问。
	---晓凡 	2023年8月7日于武汉书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/26418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文学透设计模式

设计模式是什么&#xff1f; 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案&#xff0c;代表了解决一些问题的最佳实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 说白了&#xff0c;设计模式对于软件开发人员来说就…

K8S系列文章 之 容器网络基础 Docker0

什么是Docker0 使用ip addr命令看一下网卡&#xff1a; rootKitDevVps:~# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host…

优维低代码实践:对接数据

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

数据结构--单链表OJ题

上文回顾---单链表 这章将来做一些链表的相关题目。 目录 1.移除链表元素 2.反转链表 3.链表的中间结点 4.链表中的倒数第k个结点 5.合并两个有序链表 6.链表分割 7.链表的回文结构 8.相交链表 9.环形链表 ​编辑 10.环形链表II ​编辑 ​编辑 1.移除链表元素 思…

windows永久暂停更新

目录 1.winr,输入regedit打开注册表 2.打开注册表的这个路径: 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 右键空白地方新建QWORD值命名为:FlightSettingsMaxPauseDays 3.双击FlightSettingsMaxPauseDays,修改里面的值为100000,右边基数设置…

Node.js |(一)Node.js简介及计算机基础 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;关于Node.js&#x1f407;为什么要学Node.js&#x1f407;Node.js是什么&#x1f407;Node.js的作用&#x1f407;Node.js下载安装&#x1f407;命令行工具&#x1…

ebay儿童书包产品CPC认证

儿童书包是一种能够盛放书本或者文具的包。现在的书包五花八门&#xff0c;以普通的布料或者是帆布等制成&#xff0c;有背带&#xff0c;包内一般分栏。一般分三种&#xff0c;背在身后的&#xff0c;挎在肩上的&#xff0c;轮式&#xff08;可以拖行&#xff09;的。 一、美国…

树,森林的遍历,以及其与二叉树遍历之间的关系

树和森林的的遍历 树的遍历 先根遍历 以下列树为演示 首先将树转化成二叉树&#xff08;孩子兄弟表示法&#xff1a;就是每个节点的左边连着它的左孩子&#xff0c;右边连自己右边的第一个兄弟&#xff09; 然后把转化为的二叉树进行先序遍历&#xff0c;中序遍历 进行先序…

再次斩获第一,文心3.5霸榜国内大模型

目录 1 什么是文心一言&#xff1f;2 体验与文心一言对话3 文心3.5霸榜国内大模型 1 什么是文心一言&#xff1f; 文心一言是百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xf…

【数据结构】Disruptor环形数组无锁并发框架阅读

Disruptor 是苹国外厂本易公司LMAX开发的一个高件能列&#xff0c;研发的初夷是解决内存队列的延识问顾在性能测试中发现竟然与10操作处于同样的数量级)&#xff0c;基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCn演讲后&#xff0c;获得了业界关注…

linux内网穿透应用场景有哪些?快解析有什么用处?

随着网络技术的不断发展&#xff0c;无论是工作上还是在生活中人们对网络的依赖和需求越来越高。Linux内网穿透作为一种创新的解决方案&#xff0c;为我们提供了无限可能。 首先我们了解一下Linux操作系统。Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基…

用Python获取链家二手房房源数据,做可视化图分析数据

前言 数据采集的步骤是固定: 发送请求, 模拟浏览器对于url地址发送请求获取数据, 获取网页数据内容 --> 请求那个链接地址, 返回服务器响应数据解析数据, 提取我们需要的数据内容保存数据, 保存本地文件 所需模块 win R 输入cmd 输入安装命令 pip install 模块名 (如果你…

UML-构件图

目录 1.概述 2.构件的类型 3.构件和类 4.构件图 1.概述 构件图主要用于描述各种软件之间的依赖关系&#xff0c;例如&#xff0c;可执行文件和源文件之间的依赖关系&#xff0c;所设计的系统中的构件的表示法及这些构件之间的关系构成了构件图 构件图从软件架构的角度来描述…

leetcode357周赛

2810. 故障键盘 核心思想&#xff1a;自己想的笨办法&#xff0c;枚举s&#xff0c;然后遇到i就翻转。比较好的方法就是双端队列&#xff0c;遇到i字母原本往后加的就往前加&#xff0c;然后读的时候反过来读&#xff0c;往前加的就往后加&#xff0c;读的话就从前往后&#x…

git 版本控制与合并

一 git概述&#xff1a; - Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理软件开发项目中的代码变更。 - 它允许多人协同工作&#xff0c;记录代码历史变更&#xff0c;并轻松管理多个项目版本。 **Git的主要特点**包括&#xff1a; 1. **分布式系统**&#xff1a;…

小研究 - MySQL 分区分表的设计及实(一)

随着信息技术的快速发展&#xff0c;数据量越来越大&#xff0c;海量的表查询操作需要消耗大量的时间&#xff0c;成为影响数据库访问性能提高的主要因素。为了提升数据库操作的查询效率和用户体验&#xff0c;在关系型数据库管理系统(MySQL)中通过 range 分区和 Merge 存储&am…

c++:day4

1.思维导图 2.shell函数获取uid和gid&#xff0c;并用变量接 #!/bin/bashfunction fun() {read -p "输入用户名" necho uid:id -u $necho gid:id -g $n } afun echo $a3.冒泡、选择和快排代码整理 /**************************************************************…

嵌入式一开始该怎么学?学习单片机

学习单片机&#xff1a; 模电数电肯定必须的&#xff0c;玩单片机大概率这两门课都学过&#xff0c;学过微机原理更好。 直接看野火的文档&#xff0c;芯片手册&#xff0c;外设手册。 学单片机不要纠结于某个型号&#xff0c;我认为stm32就OK&#xff0c;主要是原理和感觉。…

窥探系列之Mybatis-plus XML分页查询

mybatisPlus分页查询原理 searchCount字段控制是否查询总记录数 com.baomidou.mybatisplus.plugins.PaginationInterceptor 该插件拦截sql&#xff0c;如果searchCounttrue&#xff0c;则使用sql解析包jsqlparser根据原sql生成count语句&#xff0c;另外关键

分布式应用:Zookeeper 集群与kafka 集群部署

目录 一、理论 1.Zookeeper 2.部署 Zookeeper 集群 3.消息队列 4.Kafka 5.部署 kafka 集群 6.FilebeatKafkaELK 二、实验 1.Zookeeper 集群部署 2.kafka集群部署 3.FilebeatKafkaELK 三、问题 1.解压文件异常 2.kafka集群建立失败 3.启动 filebeat报错 4.VIM报错…