STM32与FPGA之间的SPI通讯
SPI协议是一种高速全双工的通信总线。SPI设备之间的连接方式如图所示:

SPI通讯使用3条总线及一个片选线,SCK为时钟信号线,MISO为主设备输入/从设备输出,MOSI为主设备输出/从设备输入。
下图就是SPI通讯的通讯时序:
1)采样时刻,MISO与MOSI的数据才有效,高电平表示为“1”,低电平表示为“0”。
2)通讯的起始信号:片选信号由高变低;SPI的停止信号:片选信号由低变高。

SPI共有4种通讯模式,由CPOL和CPHA决定:
| SPI模式 | CPOL | CPHA | 空闲时SCK时钟 | 采样时刻 |
|---|---|---|---|---|
| 0 | 0 | 0 | 低电平 | 奇数边沿 |
| 1 | 0 | 1 | 低电平 | 偶数边沿 |
| 2 | 1 | 0 | 低电平 | 奇数边沿 |
| 3 | 1 | 1 | 低电平 | 偶数边沿 |
STM32的SPI外设支持最高的时钟频率为fpclk/2(STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz)。本实验采用双线全双工模式。



配置完这些结构体成员后,我们要调用 SPI_Init 函数把这些参数写入到寄存器中,实现 SPI的初始化,然后调用 SPI_Cmd 来使能 SPI外设。
本实验采用SPI模式3进行主模式代码编写,编程要点如下:
/**
* @brief SPI_FPGA初始化
* @param 无
* @retval 无
*/
#include "./fpga/bsp_spi_fpga.h"
void SPI_FPGA_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能SPI时钟 */
FPGA_SPI_APBxClock_FUN ( FPGA_SPI_CLK, ENABLE );
/* 使能SPI引脚相关的时钟 */
FPGA_SPI_CS_APBxClock_FUN ( FPGA_SPI_CS_CLK|FPGA_SPI_SCK_CLK|
FPGA_SPI_MISO_PIN|FPGA_SPI_MOSI_PIN, ENABLE );
/* 配置SPI的 CS引脚,普通IO即可 */
GPIO_InitStructure.GPIO_Pin = FPGA_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(FPGA_SPI_CS_PORT, &GPIO_InitStructure);
/* 配置SPI的 SCK引脚*/
GPIO_InitStructure.GPIO_Pin = FPGA_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(FPGA_SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI的 MISO引脚*/
GPIO_InitStructure.GPIO_Pin = FPGA_SPI_MISO_PIN;
GPIO_Init(FPGA_SPI_MISO_PORT, &GPIO_InitStructure);
/* 配置SPI的 MOSI引脚*/
GPIO_InitStructure.GPIO_Pin = FPGA_SPI_MOSI_PIN;
GPIO_Init(FPGA_SPI_MOSI_PORT, &GPIO_InitStructure);
/* 停止信号 FPGA: CS引脚高电平*/
SPI_FPGA_CS_HIGH();
/* SPI 模式配置 */
// FPGA芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(FPGA_SPIx , &SPI_InitStructure);
/* 使能 SPI */
SPI_Cmd(FPGA_SPIx , ENABLE);
}
/**
* @brief 使用SPI发送一个字节的数据
* @param byte:要发送的数据
* @retval 返回接收到的数据
*/
u8 SPI_FPGA_SendByte(u8 byte)
{
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(FPGA_SPIx , SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(FPGA_SPIx , byte);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(FPGA_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
}
/* 读取数据寄存器,获取接收缓冲区数据 */
return SPI_I2S_ReceiveData(FPGA_SPIx );
}
/**
* @brief 使用SPI读取一个字节的数据
* @param 无
* @retval 返回接收到的数据
*/
u8 SPI_FPGA_ReadByte(void)
{
return (SPI_FPGA_SendByte(Dummy_Byte));
}
/**
* @brief 等待超时回调函数
* @param None.
* @retval None.
*/
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* 等待超时后的处理,输出错误信息 */
FPGA_ERROR("SPI 等待超时!errorCode = %d",errorCode);
return 0;
}
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
bsp_spi_fpga.h内容如下:
/*命令定义-结尾*******************************/
/*SPI接口定义-开头****************************/
#ifndef __SPI_FPGA_H
#define __SPI_FPGA_H
#include "stm32f10x.h"
#include <stdio.h>
#define FPGA_SPIx SPI2
#define FPGA_SPI_APBxClock_FUN RCC_APB1PeriphClockCmd
#define FPGA_SPI_CLK RCC_APB1Periph_SPI2
//CS(NSS)引脚 片选选普通GPIO即可
#define FPGA_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FPGA_SPI_CS_CLK RCC_APB2Periph_GPIOC
#define FPGA_SPI_CS_PORT GPIOC
#define FPGA_SPI_CS_PIN GPIO_Pin_3
//SCK引脚
#define FPGA_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FPGA_SPI_SCK_CLK RCC_APB2Periph_GPIOB
#define FPGA_SPI_SCK_PORT GPIOB
#define FPGA_SPI_SCK_PIN GPIO_Pin_13
//MISO引脚
#define FPGA_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FPGA_SPI_MISO_CLK RCC_APB2Periph_GPIOB
#define FPGA_SPI_MISO_PORT GPIOB
#define FPGA_SPI_MISO_PIN GPIO_Pin_14
//MOSI引脚
#define FPGA_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FPGA_SPI_MOSI_CLK RCC_APB2Periph_GPIOB
#define FPGA_SPI_MOSI_PORT GPIOB
#define FPGA_SPI_MOSI_PIN GPIO_Pin_15
#define SPI_FPGA_CS_LOW() GPIO_ResetBits( FPGA_SPI_CS_PORT, FPGA_SPI_CS_PIN )
#define SPI_FPGA_CS_HIGH() GPIO_SetBits( FPGA_SPI_CS_PORT, FPGA_SPI_CS_PIN )
/*SPI接口定义-结尾****************************/
/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
/*信息输出*/
#define FPGA_DEBUG_ON 1
#define FPGA_INFO(fmt,arg...) printf("<<-FPGA-INFO->> "fmt"\n",##arg)
#define FPGA_ERROR(fmt,arg...) printf("<<-FPGA-ERROR->> "fmt"\n",##arg)
#define FPGA_DEBUG(fmt,arg...) do{\ if(FPGA_DEBUG_ON)\ printf("<<-FPGA-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\ }while(0)
void SPI_FPGA_Init(void);
u8 SPI_FPGA_ReadByte(void);
u8 SPI_FPGA_SendByte(u8 byte);
void Delay(__IO uint32_t nCount);
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);
#endif /* __SPI_FPGA_H */
工程主函数为:
/*
* 函数名:main
* 描述 :主函数
* 输入 :无
* 输出 :无
*/
int main(void)
{
LED_GPIO_Config();
LED_BLUE;
/* 配置串口为:115200 8-N-1 */
USART_Config();
printf("\r\n 这是一个STM32与FPGA的通讯实验!\r\n");
/* 8M串行FPGA初始化 */
SPI_FPGA_Init();
Temp = 123;
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(Temp);
SPI_FPGA_CS_HIGH();
Delay(10000);
printf("\r\n 写入的数据为:%d \r\t", Temp);
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(245);
SPI_FPGA_CS_HIGH();
Delay(10000);
SPI_FPGA_CS_LOW();
Temp1 = SPI_FPGA_SendByte(Dummy_Byte);
SPI_FPGA_CS_HIGH();
printf("\r\n 读出的数据为:%d \r\n", Temp1);
}
//use SPI 3 mode,CHOL = 1,CHAL = 1
module spi
(
input clk ,
input rst_n ,
input CS_N ,
input SCK ,
input MOSI ,
output reg MISO ,
output led ,
output led1
);
wire [7:0] txd_data ;
assign txd_data = 8'b001_1000;
reg [7:0] rxd_data;
wire rxd_flag;
reg [7:0] spi_cnt;
//-------------------------capture the sck-----------------------------
reg sck_r0,sck_r1;
wire sck_n,sck_p;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
sck_r0 <= 1'b0; //sck of the idle state is high
sck_r1 <= 1'b0;
end
else
begin
sck_r0 <= SCK;
sck_r1 <= sck_r0;
end
assign sck_n = (~sck_r0 & sck_r1)? 1'b1:1'b0; //capture the sck negedge
assign sck_p = (~sck_r1 & sck_r0)? 1'b1:1'b0; //capture the sck posedge
//-----------------------spi_slaver read data-------------------------------
reg rxd_flag_r;
reg [2:0] rxd_state;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rxd_data <= 1'b0;
rxd_flag_r <= 1'b0;
rxd_state <= 1'b0;
end
else if(sck_p && !CS_N)
begin
case(rxd_state)
3'd0:begin
rxd_data[7] <= MOSI;
rxd_flag_r <= 1'b0; //reset rxd_flag
rxd_state <= 3'd1;
end
3'd1:begin
rxd_data[6] <= MOSI;
rxd_state <= 3'd2;
end
3'd2:begin
rxd_data[5] <= MOSI;
rxd_state <= 3'd3;
end
3'd3:begin
rxd_data[4] <= MOSI;
rxd_state <= 3'd4;
end
3'd4:begin
rxd_data[3] <= MOSI;
rxd_state <= 3'd5;
end
3'd5:begin
rxd_data[2] <= MOSI;
rxd_state <= 3'd6;
end
3'd6:begin
rxd_data[1] <= MOSI;
rxd_state <= 3'd7;
end
3'd7:begin
rxd_data[0] <= MOSI;
rxd_flag_r <= 1'b1; //set rxd_flag
rxd_state <= 3'd0;
end
default: ;
endcase
end
end
//--------------------capture spi_flag posedge--------------------------------
reg rxd_flag_r0,rxd_flag_r1;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rxd_flag_r0 <= 1'b0;
rxd_flag_r1 <= 1'b0;
end
else
begin
rxd_flag_r0 <= rxd_flag_r;
rxd_flag_r1 <= rxd_flag_r0;
end
end
assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1'b1:1'b0;
//---------------------spi_slaver send data---------------------------
reg [2:0] txd_state;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
txd_state <= 1'b0;
end
else if(sck_n && !CS_N)
begin
case(txd_state)
3'd0:begin
MISO <= txd_data[7];
txd_state <= 3'd1;
end
3'd1:begin
MISO <= txd_data[6];
txd_state <= 3'd2;
end
3'd2:begin
MISO <= txd_data[5];
txd_state <= 3'd3;
end
3'd3:begin
MISO <= txd_data[4];
txd_state <= 3'd4;
end
3'd4:begin
MISO <= txd_data[3];
txd_state <= 3'd5;
end
3'd5:begin
MISO <= txd_data[2];
txd_state <= 3'd6;
end
3'd6:begin
MISO <= txd_data[1];
txd_state <= 3'd7;
end
3'd7:begin
MISO <= txd_data[0];
txd_state <= 3'd0;
end
default: ;
endcase
end
end
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
spi_cnt <= 8'd0;
else if(rxd_flag == 1'b1)
spi_cnt <= spi_cnt + 1'b1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
led <= 1'b0;
else if(rxd_data == 8'd123)
led <= 1'b1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
led1 <= 1'b0;
else if(rxd_data == 8'd245 && spi_cnt == 8'd2)
led1 <= 1'b1;
endmodule
STM32依次给FPGA发送数据123、245并接收FPGA发送过来的数据,通过串口打印出;FPGA给STM32发送数据8’b0001_1000。
FPGA接收的数据为123,则点亮led0灯;如果FPGA第二次接收到的数据为245,则点亮led1灯。
实验结果如图所示:
STM32的串口打印信息:

本实验使用的FPGA开发板是基于 Xilinx 公司的 Spartan6 系列 FPGA,型号为 XC6SLX9。
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年
由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A
在许多ruby类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..
之前有人问过这个问题,我发现了以下clip关于如何一次设置一个类对象的所有属性,但由于批量分配保护,这在Rails中是不可能的。(例如,您不能Object.attributes={})有没有一种很好的方法可以将一个类的属性合并到另一个类中?object1.attributes=object2.attributes.inject({}){|h,(k,v)|h[k]=vifObjectModel.column_names.include?(k);h}谢谢。 最佳答案 利用assign_attributes使用:without_prote
我有三个模型:User、Product、Offer以及这些模型之间的关系问题。场景:用户1发布了一个产品用户2可以向用户1发送报价,例如10美元用户1可以接受或拒绝提议我现在的问题是:用户、产品和报价之间的正确关系是什么?我如何处理那些“接受或拒绝”操作?是否有更好的解决方案?用户模型:classUser:productsend产品型号:classProduct:usersend提供模型:classOffer提前致谢:)编辑:我正在使用Rails3.2.8 最佳答案 警告:小小说来了第1部分:设置关联我建议阅读Railsguideo