【GD32】从0开始学GD32单片机(9)—— SPI外设详解+主机从机发送和接收例程
目录
- 简介
- 片选线
- 时序
- 运行模式
- 基本发送和技术流程
- 主机发送
- 主机接收
- 从机发送
- 从机接收
- 例程
- 主机和从机全双工通信
简介
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是一个全双工的通信协议,但通过配置可以使它工作在全双工、半双工和单工的状态,配置十分灵活。
全双工的接线示意图
半双工接线示意图
单工接线示意图
基本发送和技术流程
主机发送
简要流程:
- 配置SPI外设的时序,使能SPI外设
- 拉低对应从机NSS线电平
- 软件写一个数据到发送缓冲区
- 数据帧从数据缓冲区加载到移位寄存器中,开始发送加载的数据
- 数据帧的第一位发送之后,TBE位置1
- 如果有更多数据则重复第3-5步
- 等待TRANS置0,关闭时钟信号输出,释放片选线,数据传输结束
说明:
TBE全称Transmit Buffer Empty,“发送缓冲区空”标志位。
TRANS,“通信进行中”标志位。
主机接收
简要流程:
- 配置SPI外设的时序,使能SPI外设
- 拉低对应从机NSS线电平
- 输出SCK时钟信号
- 接收到的数据将从移位寄存器存入到接收缓冲区,且RBNE位置1
- 软件读取接收缓冲区,RBNE位置0
- 如果有更多数据则重复第4-5步
- 等待TRANS置0,关闭时钟信号输出,释放片选线,数据传输结束
说明:
RBNE全称Read Buffer Not Empty,"接收缓冲区非空"标志位。
从机发送
简要流程:
- 配置SPI外设的时序,使能SPI外设
- 软件写第一个数据到发送缓冲区
- 等待NSS线电平为低,SCK线开始翻转
- 数据帧从数据缓冲区加载到移位寄存器中,开始发送加载的数据
- 数据帧的第一位发送之后,TBE位置1
- 如果有更多数据,在每一次TBE置1时都可将数据写入发送缓冲区
- 等待TRANS置0,数据传输结束
从机接收
简要流程:
- 配置SPI外设的时序,使能SPI外设
- 等待NSS线电平为低,SCK线开始翻转
- 接收到的数据将从移位寄存器存入到接收缓冲区,且RBNE位置1
- 软件读取接收缓冲区,RBNE位置0
- 如果有更多数据则重复第4-5步
- 等待TRANS置0,数据传输结束
例程
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.5MHzspi_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.5MHzspi_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);}
}
【GD32】从0开始学GD32单片机(9)—— SPI外设详解+主机从机发送和接收例程相关推荐
- 【GD32】从0开始学GD32单片机栏目目录 | 兆易创新32位微处理器
目录 前言 为什么写这个教程 GD32与STM32的区别 教程 入门教程(基于GD32F103C8T6单片机) 进阶教程 高级教程 前言 为什么写这个教程 作为一名物联网专业的在读大学生,与单片机打交 ...
- 入木三分学网络第一篇--VRRP协议详解-----(1)
原帖:http://blog.chinaunix.net/uid-11654074-id-2857384.html 目录 入木三分学网络第一篇--VRRP协议详解 1. VRRP产生背景及应用环境 1 ...
- 单片机_PWM输出原理详解
单片机_PWM输出原理详解 理论篇 博主自己的经历告诉我,PWM波的理解和应用确实还是挺重要的,这里专门花一期详细介绍一下 什么是PWM? PWM,英文名Pulse Width Modulat ...
- MATLAB与51单片机进行串口通信详解
目录 一.51单片机与电脑进行串口通信 二.MATLAB串口通信函数 三.串口属性 四.示例Demo 4.1 MATLAB接收单片机发来的数据 4.2 MATLAB向单片机发送数据控制LED 五.总结 ...
- TI Cotex M3/4单片机关于寄存器操作详解
TI Cotex M3/4单片机关于寄存器操作详解 前备知识 寄存器与偏移量 位带操作.位带区 TI单片机寄存器操作详解 快速在TI的手册里面确认某外设某寄存器的确切地址 寄存器操作介绍 本文参考 前 ...
- 用Advanced Installer制作DotNetBar for Windows Forms 12.0.0.1_冰河之刃重打包版详解
关于 DotNetBar for Windows Forms 12.0.0.1_冰河之刃重打包版 --------------------11.8.0.8_冰河之刃重打包版-------------- ...
- vue设置html自动跳转路由器,vue2.0项目实现路由跳转的方法详解
一.安装 1.安装路由vue-router: npm install vue-router vue项目的依赖文件node_modules存在vue-router依赖,说明安装成功 2.vue项目引入v ...
- 华娱水浒无双1java,魔兽争霸3真水浒无双1.0正式版开局单刷完美通关图文详解
魔兽争霸3真水浒无双1.0正式版开局单刷怎么打?有什么技巧?很多玩家可能还不太清楚,下面小编为大家带来了魔兽争霸3真水浒无双1.0正式版开局单刷完美通关图文详解,感兴趣的玩家一起来看看吧! 魔兽争霸3 ...
- 巧学宾语从句――宾语从句语法详解 [宾语从句歌诀]
巧学宾语从句――宾语从句语法详解 [宾语从句歌诀] 2011年10月01日 重要提醒:系统检测到您的帐号可能存在被盗风险,请尽快查看风险提示,并立即修改密码. | 关闭 网易博客安全提醒:系统检测到您 ...
最新文章
- Transformer的PyTorch实现
- CF1B Spreadsheets
- 零基础学Python(第八章 for循环·超重点,本章会有几个简单的单层循环练习,后续会有针对算法的单独章节)
- 简单说说通讯设备的热设计
- hadoop应用开发技术..._Hadoop
- JS中的关于类型转换的性能优化
- 小程序 php wecahtpay,【PHP】laravel 实现微信小程序微信支付功能
- python映射实体类_将Python(django)实体类转换为Java的好方法 - java
- vue-amap - 基于Vue2.0和高德地图的地图组件
- 三星手机怎么把html,三星手机怎么连接电脑 只要四步轻松搞定【图文】
- ehlib的DBGridEh控件中使用过滤功能的方法
- Demo:第四章:Gateway网关
- 基于51单片机ds18b20智能温控风扇Proteus仿真
- 网上体育商城的设计与实现
- Android| failed to connect to /10.0.2.2 (port 80) after 10000ms
- LeetCode Task04 数组数组二分查找 0704二分查找 0069Sqrt(x)
- Hutool(Excel工具使用)
- 编写注册表文件修改注册表项
- 使用Fastdfs上传文件时出现中文乱码。工具类中的this.getClass().getResource(“/“).getPath()获取配置文件的路径时中文乱码。
- 南邮校园网CSDN 等部分网站图片无法加载的问题
热门文章
- 100多个flash常见问题大收集
- C++ stack pop()返回值 error: cannot initialize a variable of type ‘int‘ with an rvalue of type ‘void‘
- Matlab 入门 - data函数
- 软件项目管理进度计划-Gantt图和工程网络图,工程网络是系统分析和系统设计的强有力的工 具
- 如何克服卡尔曼滤波发散问题
- 阅读nutch.Analysis.jj
- Ubuntu_XMMS播放器使用
- 深入Unreal蓝图开发:自定义蓝图节点(上)
- web网页设计期末课程大作业:动物主题设计与实现——鲸鱼html+css
- 美度舵手系列多功能计时腕表伴你行走世界