【二】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 2023

package require ::quartus::project

set_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_SCLK

set_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);	//写Byte1
    Spi_SDWriteByte(arg>>24);		//写Byte2
    Spi_SDWriteByte(arg>>16);		//写Byte3
    Spi_SDWriteByte(arg>>8);		//写Byte4
    Spi_SDWriteByte(arg);			//写Byte5
    Spi_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);	//等待返回0x01

    return 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(); 				//读取响应0x00
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0x00
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0x01
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0xaa
		time++;
		if(time > 254) return 1;				//超时退出返回1

	}while((r1 != 0x01) && (r7 != 0x000001aa));	//等待r1返回0x01,r7返回0x000001aa

	time = 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;

    //连续读直到读到开始字节0xFE
    while (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个时钟周期脉冲

    //发送写扇区开始字节0xFE
    Spi_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");
    else
        printf("SD Reset Succeed!\n");

    if(Spi_SDInit()) //SD卡初始化
    	printf("SD Inint Failed!\n");
    else
    	printf("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");
    else
    	printf("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);	//写Byte1
    Spi_SDWriteByte(arg>>24);		//写Byte2
    Spi_SDWriteByte(arg>>16);		//写Byte3
    Spi_SDWriteByte(arg>>8);		//写Byte4
    Spi_SDWriteByte(arg);			//写Byte5
    Spi_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);	//等待返回0x01

    return 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(); 				//读取响应0x00
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0x00
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0x01
        r7 <<= 8;
        r7 += Spi_SDReadByte(); 				//读取响应0xaa
		time++;
		if(time > 254) return 1;				//超时退出返回1

	}while((r1 != 0x01) && (r7 != 0x000001aa));	//等待r1返回0x01,r7返回0x000001aa

	time = 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;

    //连续读直到读到开始字节0xFE
    while (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个时钟周期脉冲

    //发送写扇区开始字节0xFE
    Spi_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");
    else
        printf("SD Reset Succeed!\n");

    if(Spi_SDInit()) //SD卡初始化
    	printf("SD Inint Failed!\n");
    else
    	printf("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");
    else
    	printf("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;
}



实验结果:

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日于武汉书

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ 计算 拟合优度R^2

解决的问题&#xff1a; 拟合优度(Goodness of Fit)是指回归直线对观测值的拟合程度&#xff0c;度量拟合优度的统计量是可决系数(亦称确定系数) R?。R最大值为 1。R%的值越接近1&#xff0c;说明回归直线对观测值的拟合程度越好&#xff0c;反之&#xff0c;R%值越小&#x…

C#小轮子:自动连续Ping网络地址

文章目录 前言Ping代码异步问题 前言 工作中&#xff0c;我们经常用到Ping这个指令&#xff0c;有时候我们需要Ping整个网段来查看这个网段上面有什么设备&#xff0c;哪些Ip地址是通的&#xff0c;这个时候就需要Ping指令 Ping 代码 我这个是批量Ping的代码&#xff0c;而…

Qt+C++实现灯带动画运动位置变换移动跑马灯图片轮播

程序示例精选 QtC实现灯带动画运动位置变换移动跑马灯图片轮播 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC实现灯带动画运动位置变换移动跑马灯图片轮播>>编写代码&…

七道Android面试题,先来简单热个身

作者&#xff1a;Coffeeee 马上就要到招(tiao)聘(cao)旺季金勾银十了&#xff0c;一批一批的社会精英在寻找自己的下一家的同时&#xff0c;也开始着手为面试做准备&#xff0c;回想起自己这些年&#xff0c;也大大小小经历过不少面试&#xff0c;有被面试过&#xff0c;也有当…

vue3+ts使用antv/x6

使用 2.x 版本 x6.antv 新官网: 安装 npm install antv/x6 //"antv/x6": "^2.1.6",项目结构 1、初始化画布 index.vue <template><div id"container"></div> </template><script setup langts> import { onM…

Oracle到DM实时数据同步实施方案

目录 1 项目概述 2 需求分析 3 实施操作 3.1 历史数据全量同步 3.2 增量数据实时同步 4 问题总结 4.1 字符型非空约束 4.2 字符型唯一索引尾部空格 1 项目概述 将Oracle 11g RAC生产环境数据同步到DM8分析环境&#xff0c;Oracle数据库大小1.5T&#xff0c;日增归档10…

【flink】Chunk splitting has encountered exception

执行任务报错&#xff1a; Chunk splitting has encountered exception 错误信息截图&#xff1a; 完整的错误信息&#xff1a; 16:30:43,911 ERROR org.apache.flink.runtime.source.coordinator.SourceCoordinator [SourceCoordinator-Source: CDC Sourceorg.jobslink.flink…

静态时序分析与时序约束

一、时序分析的基本概念 1. 时钟 理性的时钟模型是一个占空比为50%且周期固定的方波&#xff1a; 实际电路中输入给FPGA的晶振时钟信号是正弦波&#xff1a; 2. 时钟抖动 Clock Jitter&#xff0c;时钟抖动&#xff0c;相对于理想时钟沿&#xff0c;实际时钟存在不随时钟存在…

一个简单实用的线程池及线程池组的实现!

1.线程池简介 线程池&#xff0c;顾名思义&#xff0c;就是一个“池子”里面放有多个线程。为什么要使用线程池呢&#xff1f;当我们编写的代码需要并发异步处理很多任务时候&#xff0c;一般的处理办法是一个任务开启一个线程去处理&#xff0c;处理结束后释放线程。可是这样…

典籍研读+书法精进 暄桐「见道明心的笔墨」课程开课啦

8月12日&#xff0c;《林曦老师的线上直播书法课》之「见道明心的笔墨」就要开课啦。林曦老师将带我们去往中国文人精神世界的后花园&#xff0c;一起阅读《金刚经》《老子》等典籍。是不是很期待&#xff1f; 在2011年&#xff0c;暄桐成立的最初&#xff0c;课程便是面向零基…

外网通过ipv6访问家里设备

目录 1.需要整体理解如何在外网连接家里设备。 2.路由器打通ipv6。 3.移动光猫配置ipv6。 4.test-ipv6.com测试成功&#xff0c;但是ping不通 还是ping不通&#xff0c;提出如下可能 5.动态域名解析&#xff08;ddns-go&#xff09; a.dns服务商权限设置 b.IPv6设置 c…

python中的装饰器的真正含义和用法

闭包&#xff1a; 闭包是python中的一个很实用的写法&#xff0c;可以使得用户在函数中调用该函数外的函数的变量&#xff0c;使得该变量常驻于内存中。 闭包函数&#xff1a; 输入是函数&#xff0c;输出也是一个函数。 装饰器的写法是python闭包的语法糖。 面试中经常面…

kubernetes基于helm部署gitlab-runner

kubernetes基于helm部署gitlab-runner 这篇博文介绍如何在 Kubernetes 中使用helm部署 GitLab-runner。 先决条件&#xff1a; 已运行的 Kubernetes 集群已运行的 gitlab 实例 项目地址&#xff1a;https://gitlab.com/gitlab-org/charts/gitlab-runner 官方文档&#xff…

渠道失灵?新零售迎来数据大屏新“大脑”

前言 **“新零售”**是以消费者需求为中心的数据驱动的泛零售形态,其核心是“人”、 “货”、“场”三者的重新定义与关系重构,而重构背后最根本的驱动因素是数据。新零售时代&#xff0c;数字技术不断进步、消费不断升级&#xff0c;零售业需要借助数据中台&#xff0c;发掘数…

MATLAB|信号处理的Simulink搭建与研究

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

CKA考试真题(1)-- 绝对干货!

目录 前言 1. 创建rabc权限 2. 查询cpu占用率最高的pod 3. deployment扩容副本 4. pod通过label标签指定节点部署 5. 创建pv 6. 查询pod日志 7. 排查集群故障节点 8. 查看集群可用节点 9. 创建包含多个容器的pod 10. 集群节点维护 前言 17道真题难度等级 一级 rbac cpu 扩容 …

Photoshop 2023 25.0beta「Mac」

Photoshop 2023是一款专业图像处理软件&#xff0c;它主要用于图像编辑、合成和设计等方面。 Photoshop beta创新式填充的功能特色包括&#xff1a; 自动识别和删除对象&#xff1a;该功能可以自动识别图像中的对象&#xff0c;并用周围的图像填充空白部分&#xff0c;使图像看…

oracle连表查询in后边跟另一张表中的字符串字段

今天在做通过in进行连表查询的时候发现以下问题记录下 我的需求是A,B两张表连接查询&#xff0c;A中有一个FOOD_TYPES字段 存的值类型为1&#xff0c;2&#xff0c;3 B表中的字段是FOOD_TYPE 存的是单个数字字符串 我需要where b.food_type in a.food_types 但是无论怎么写都…

面试热题(螺旋矩阵)

给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素 一看到这个大家有没有想到 就是一个螺旋形状&#xff0c;那这道题我们应该怎么解决&#xff1f; 我们先来仔细的看&#xff0c;它这种螺旋形状的遍历是先【右-下-左-上】…

Zookeeper特性与节点数据类型详解

CAP&Base理论 CAP理论 cap理论是指对于一个分布式计算系统来说&#xff0c;不可能满足以下三点: 一致性 &#xff1a; 在分布式环境中&#xff0c;一致性是指数据在多个副本之间是否能够保持一致的 特性&#xff0c;等同于所有节点访问同一份最新的数据副本。在一致性的需…