目录
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线。
SPI总共需要4根线来实现通信,NSS:片选线,用于选择需要通信的从机;CLK:同步时钟线,用于提供同步时钟信号;MISO:主机读从机写线;MOSI:主机写从机读线。
GD32F103系列的SPI最高速度为18MHz。

SPI的片选逻辑要比I2C的简单得多,通常一个SPI外设会有多条片选线,如下图。

所以我们想要与哪个从机进行通信,那么只需要拉低对应从机的片选线即可,无需像I2C那样要通过传送从机地址来片选对应的从机。
不过GD32的SPI外设好像只有一条片选线,目前我还没有见过有多条片选线的SPI外设,可能高级一点的型号会有吧
因为SPI为同步通信协议,因此通信要配合同步时钟信号,时序如下图。

值得注意的是时钟线有4种不同的时序,通过CKPH和CKPL两个配置位进行配置,CKPH配置的是第一或第二个时钟边沿为有效采样边沿,CKPL配置的是空闲状态时时钟线的电平。
SPI外设有好几种运行模式,分别有——全双工主机模式、单向线连接主机发送模式、单向线连接主机接收模式、双向线连接主机发送模式、双向线连接主机接收模式、全双工从机模式、单向线连接从机发送模式、单向线连接从机接收模式、双向线连接从机发送模式、双向线连接从机接收模式。
其实简单点说就是,虽然SPI是一个全双工的通信协议,但通过配置可以使它工作在全双工、半双工和单工的状态,配置十分灵活。
全双工的接线示意图

半双工接线示意图

单工接线示意图

简要流程:
说明:
TBE全称Transmit Buffer Empty,“发送缓冲区空”标志位。
TRANS,“通信进行中”标志位。
简要流程:
说明:
RBNE全称Read Buffer Not Empty,"接收缓冲区非空"标志位。
简要流程:
简要流程:
STM32F103C8T6有两个SPI外设,对应管脚映射如下:
| 外设 | NSS | SCK | MISO | MOSI |
|---|---|---|---|---|
| SPI0 | PA4 | PA5 | PA6 | PA7 |
| SPI1 | PB12 | PB13 | PB14 | PB15 |
现象:主机从机每2秒同时发送一组数据,同时接收一组数据
spi.c文件
#include "spi.h"
void SPI_MasterInit(void)
{
/* 初始化GPIO */
rcu_periph_clock_enable(RCU_GPIOA);
/* SPI0 GPIO: NSS/PA4, SCK/PA5, MISO/PA6, MOSI/PA7 */
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
/* 初始化SPI */
rcu_periph_clock_enable(RCU_SPI0);
spi_parameter_struct spi_init_struct = {0};
spi_struct_para_init(&spi_init_struct);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主机模式
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; // 空闲电平为低,第一个边沿采样
spi_init_struct.nss = SPI_NSS_HARD; // 硬件片选
spi_init_struct.prescale = SPI_PSC_8; // 时钟8分频,108MHz / 8 = 13.5MHz
spi_init_struct.endian = SPI_ENDIAN_MSB; // 大端模式
spi_init(SPI0, &spi_init_struct);
spi_nss_output_enable(SPI0);
spi_enable(SPI0);
}
void SPI_SlaveInit(void)
{
/* 初始化GPIO */
rcu_periph_clock_enable(RCU_GPIOB);
/* SPI0 GPIO: NSS/PB12, SCK/PB13, MISO/PB14, MOSI/PB15 */
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15);
/* 初始化SPI */
rcu_periph_clock_enable(RCU_SPI1);
spi_parameter_struct spi_init_struct = {0};
spi_struct_para_init(&spi_init_struct);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_SLAVE; // 从机模式
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; // 空闲电平为低,第一个边沿采样
spi_init_struct.nss = SPI_NSS_HARD; // 硬件片选
spi_init_struct.prescale = SPI_PSC_8; // 时钟8分频,108MHz / 8 = 13.5MHz
spi_init_struct.endian = SPI_ENDIAN_MSB; // 大端模式
spi_init(SPI1, &spi_init_struct);
spi_enable(SPI1);
}
void SPI_MasterSlaveFullduplex(void)
{
uint8_t master_tx_buf[16] = {0};
uint8_t master_rx_buf[16] = {0};
uint8_t slave_tx_buf[16] = {0};
uint8_t slave_rx_buf[16] = {0};
memset(master_tx_buf, 0x00, sizeof(master_tx_buf));
memset(master_rx_buf, 0x00, sizeof(master_rx_buf));
memset(slave_tx_buf, 0x00, sizeof(slave_tx_buf));
memset(slave_rx_buf, 0x00, sizeof(slave_rx_buf));
for(uint8_t i = 0; i < 16; ++i)
{
master_tx_buf[i] = 0x80 + i;
slave_tx_buf[i] = 0x40 + i;
}
printf("Master send: ");
for(uint8_t i = 0; i < 16; ++i)
{
printf("%x ", master_tx_buf[i]);
}
printf("\n");
printf("Slave send: ");
for(uint8_t i = 0; i < 16; ++i)
{
printf("%x ", slave_tx_buf[i]);
}
printf("\n");
for(uint8_t i = 0; i < 16; ++i)
{
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));
spi_i2s_data_transmit(SPI2, slave_tx_buf[i]);
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));
spi_i2s_data_transmit(SPI0, master_tx_buf[i]);
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));
slave_rx_buf[i] = spi_i2s_data_receive(SPI2);
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));
master_rx_buf[i] = spi_i2s_data_receive(SPI0);
}
printf("Master receive: ");
for(uint8_t i = 0; i < 16; ++i)
{
printf("%x ", master_rx_buf[i]);
}
printf("\n");
printf("Slave receive: ");
for(uint8_t i = 0; i < 16; ++i)
{
printf("%x ", slave_rx_buf[i]);
}
printf("\n");
}
main.c文件
#include "gd32f10x.h"
#include "main.h"
#include "systick.h"
#include "usart.h"
#include "spi.h"
#include <stdio.h>
#include <string.h>
int main(void)
{
systick_config();
USART_Config();
SPI_MasterInit();
SPI_SlaveInit();
while(1)
{
SPI_MasterSlaveFullduplex();
delay_ms(2000);
}
}

我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
文章目录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.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
我正在使用DMOZ的listofurltopics,其中包含一些具有包含下划线的主机名的url。例如:608609TheOuterHeaven610InformationandimagegalleryofMcFarlane'sactionfiguresforTrigun,Akira,TenchiMuyoandotherJapaneseSci-Fianimations.611Top/Arts/Animation/Anime/Collectibles/Models_and_Figures/Action_Figures612虽然此url可以在网络浏览器中使用(或者至少在我的浏览器中可以使用:
我正在开发一个Rails应用程序,我需要在其中找到给定特定偏移量或时区的夏令时开始和结束日期。我基本上在我的数据库中保存了从用户浏览器接收到的时区偏移量(“+3”,“-5”),我想在它出现时修改它由于夏令时的变化。我知道Time实例变量有dst?和isdst方法,如果存储在它们中的日期在夏令时与否。>Time.new.isdst=>true但是使用它来查找夏令时的开始和结束日期会占用太多资源,而且我还必须为我拥有的每个时区偏移量执行此操作。我想知道更好的方法。 最佳答案 好的,基于你所说的和@dhouty'sanswer:您希望能够
我有一台生产机器和一台开发机器,都运行ubuntu8.10并且都运行最新的phusionpassenger。当我在osx上的本地开发机器上使用ruby1.9.1时,我想知道外面的人是否已经在使用带有ruby1.9.1甚至1.9.2的phusionpassenger?如果是这样,请告诉我们您的设置!此外,有没有办法在apache上使用phusionpassenger同时运行ruby1.8.7(ree)和1.9.1?感谢您的指点,我在任何地方都找不到任何提示... 最佳答案 是的,从某些2.2.x版本开始就正式支持它,我不记
我刚刚在我的Ubuntu9.10服务器上安装了TeamBox。我使用提供的服务器脚本在端口3000上启动并运行它。它的运行速度非常慢,从另一台计算机连接时每个HTTP请求最多需要30秒。我使用链接从shell加载TeamBox,一点也不花时间。然后我设置了一个SSH隧道,它再次运行得非常快。我通过此服务器上的apache以及SAMBA等运行了大约30个虚拟主机,没有任何问题。我该如何解决这个问题? 最佳答案 我的redmine(ruby,webrick)太慢了。现在我解决了这个问题:apt-getinstallmongrelruby