【二】SPI IP核的使用
【一】SPI IP核使用:传送门
基于qsys通过spi外部总线协议对sd卡进行读写操作
一、实验平台与实验的目的:
正点原子开拓者、芯片型号:EP4CE10F17C8;还需要一张sd卡。
该实验主要是利用SPI IP核驱动SD卡来实现读写实验,在这个实验中我们要了解spi使用方法核学习sd卡的读写操作方法。
二、系统的搭建:

(1)nios 处理器的设置:
nios II/f

其他的默认
(2)sdram controller配置


(3)SPI IP核配置

(4)PIO ip核配置

其他没有展示的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核的配置

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


系统的RTL:

四、引脚的绑定
# 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

//---------------------------------------------------------------------------
//-- 文件名 : 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中输出:

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

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

(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;
}
实验结果:

从代码中我们可以看出,使用的是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日于武汉书
相关文章:
【二】SPI IP核的使用
【一】SPI IP核使用:传送门 基于qsys通过spi外部总线协议对sd卡进行读写操作 一、实验平台与实验的目的: 正点原子开拓者、芯片型号:EP4CE10F17C8;还需要一张sd卡。 该实验主要是利用SPI IP核驱动SD卡来实现读写实验&am…...
面试热题(二叉树的锯齿形层次遍历)
给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行) 输入:root [3,9,20,null,null,15,7] 输出:[[3…...
JVM—内存管理(运行时数据区)、垃圾回收
背景介绍 当JVM类加载器加载完字节码文件之后,会交给执行引擎执行,在执行的过程中会有一块JVM内存区域来存放程序运行过程中的数据,也就是我们图中放的运行时数据区,那这一块运行时数据区究竟帮我们做了哪些工作?我们…...
一百五十一、Kettle——Linux上安装的kettle8.2开启carte服务
一、目的 kettle8.2在Linux上安装好可以启动界面、并且可以连接MySQL、Hive、ClickHouse等数据库后,准备在Linux上启动kettle的carte服务 二、实施步骤 (一)carte服务文件路径 kettle的Linux运行的carte服务文件是carte.sh (二…...
19. python从入门到精通——Web编程
HTTP协议 HTTP协议的常用方法 方法 描述 GET 请求指定的页面信息,并返回实体主体。 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 …...
PostMan 教程
安装https://www.cnblogs.com/mafly/p/postman.html Postman 使用方法详解https://blog.csdn.net/fxbin123/article/details/80428216 postman进行http接口测试https://blog.csdn.net/five3/article/details/53021084 postman的使用方法详解!最全面的教程https:/…...
Http常见状态码
一、状态码大类 状态码分类说明1xx响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它2xx成功——表示请求已经被成功接收,处理已完成3xx重定向——重定向到其它地方:它让客户端再发起一个…...
C语言之位运算
一、什么是位运算 所谓位运算是指进行二进制位的运算 在系统软件中,常要处理二进位的问题 例如,将一个存储单元中的各二进位左移或右移一位,两个数按位相加等 二、位运算符和位运算 1、按位与 运算符(&) 参加运算的两个数据ÿ…...
c语言进阶部分详解(数据在内存中的存储)
大家好,今天要进行梳理的内容是数据在内存中的存储相关内容。 在C语言中,数据在内存中的存储是一个非常重要的概念。了解数据在内存中的存储方式可以帮助我们更好地理解程序的执行过程,优化内存使用,提高程序的性能。 目录 一.数…...
VIOOVI的ECRS工时分析软件分析:SOP的核心和特征是什么?
制定SOP的主要目的是为企业做技术储备、提供企业的工作效率、防止同样的错误反复出现、让员工作业有标准化的行为准则。以规定的成本、规定的工作时间,生产质量均匀、符合规范的产品。为了能够达到上述要求,如果制造现场的操作混乱,比如制作工…...
无涯教程-Perl - lock函数
描述 此函数将咨询锁放在共享变量或THING中包含的引用对象上,直到该锁超出范围。 lock()是一个"弱关键字":这意味着,如果您在调用该函数之前已通过该名称定义了该函数,则将改为调用该函数。 语法 以下是此函数的简单语法- lock THING返回值 此函数不返回任何值…...
SpringBoot案例-部门管理-前后端联调
前后端联调 教学资料中提供了“前端工程”,将其解压即可使用nginx,启动nginx后,访问:http://localhost:90 小结 开发流程 明确需求、阅读接口文档、思路分析、接口开发(遵循接口文档)接口调试 postman测…...
每天一道leetcode:139. 单词拆分(动态规划中等)
今日份题目: 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例1 输入: s "leetcode", …...
【C++】友元(含内部类)
一、友元是什么 我把你添加为我的友元,那么你可以访问我的成员。特别注意:它是单向的。即,我把你添加为我的友元,我却不能访问你的成员,除非你把我添加为你的友元。 以下代码可以让你粗略了解友元的使用。 #includ…...
SQL | 检索数据
1-检索数据 1.1-检索单个列 SELECT prod_name FROM Products; 上述SELECT语句从Products表中检索一个名为prod_name的列。 所要查找的列在select后面,from关键字指出从那个表查询数据。 输出如下: prod_name8 inch teddy bear12 inch teddy bear18…...
typeScript 之 运算符
工具: PlayGround 算术运算符 运算符描述加-减*乘/除%取模(求余)自增–自减 注意和--,实例: let value 0; console.log(value); //0, 先显示再增加后为1 console.log(value); //2,先增加后为2再显示关系运算符 运算符描述 …...
BGP实验
题目 IP地址配置 172.16.X.0/24为模拟用户环回接口接口 172.16.7.X/32为BGP邻居关系建立的环回接口 R1: R2: R3: R4: R5: R6: R7: R8: BGP邻居关系建立、宣告和反射器、联邦配置 R…...
pytest fixture 常用参数
fixture 常用的参数 参数一:autouse,作用:自动运行,无需调用 举例一:我们在类中定义一个function 范围的fixture; 设置它自动执行autouseTrue,那么我们看下它执行结果 输出: 说明:…...
vue项目里面有多个模块的服务,前端处理url转发
先看下vue的代理配置里面: 现在是在 /pca 基础上增加了 2个模块的服务: /dca、 /api 现在服务器的nginx 没有在/pca 服务里面做转发接受 /dca、 /api的服务,所以需要前端自己去配置每个服务模块对应的 URL 先拿登录的api 做示例吧: 先定义…...
web表单
在了解了 Flask Bootstrap 基本框架之后,我们来了解一下 Flask 框架的 表单( form ),以帮助我们创建交互式的 Web 应用,最后会有个提交个人信息的例子。 Flask-WTF 是 Flask 框架的一个扩展,用来做表单的交互,是对 WT…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
针对药品仓库的效期管理问题,如何利用WMS系统“破局”
案例: 某医药分销企业,主要经营各类药品的批发与零售。由于药品的特殊性,效期管理至关重要,但该企业一直面临效期问题的困扰。在未使用WMS系统之前,其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...
动态规划-1035.不相交的线-力扣(LeetCode)
一、题目解析 光看题目要求和例图,感觉这题好麻烦,直线不能相交啊,每个数字只属于一条连线啊等等,但我们结合题目所给的信息和例图的内容,这不就是最长公共子序列吗?,我们把最长公共子序列连线起…...
